JFIFC   %# , #&')*)-0-(0%()(C   (((((((((((((((((((((((((((((((((((((((((((((((((((" ,.Fh Ch@ 10D``DBB h4 @dX bD iD ІI$TBB'$"`I)Eb`(m9@0hb&!1114  b` Dh "lTH)TAiN  A" hf%n£!aY4hcC"5J2#Tզ@ #(a`QI+JHB8h@!!!hSMNhC4$11SB!`&2Dc(p*`"XE b!IJ&0C41 b `hL0JHLi1L -XX`ݚb% )*Cp& ! $40)!b䜢hC@D 6JJቨ4B!`b `0@ b`&ё^IÆ LO7dX h@)A "I`6H !L'@ DQ B!Bj4  L@ @hb&%$ D LQ~7ҜtZ&pӘ b `&)F؆` 7DBB&qI:LVF2B1 5iL4$ mj4 @ @ b`0b iS` 14V1l˦I7 @` L&ȒB[lC!FlIY +@!"!%$  HX J00CبDE18! L r2ϳ>Tس:=8Ӓb  & !`) "0$EMSIAL6D$B`&BBX&1C CT4h! @@4 0Yf |,tCE\T}nn` b$1AN&$ &IS`0118` 4  9_^8B14yꞿ3wlK 7 &@ 0@ @ `Ȓ b( +$2DR:]Z3cqcAȴNb11@#@18b`!upyt|z8lZ+]}3:zKcwA9SUU5AJ   2LUp*HR+EUEvF2qIW8)-JYDUQ  b `16!B& n$I9y~yntpX"QE,m[&C44 b ``@  BQ0&:Qud J7*"S-5(J7U@`  b1n.2/| ZrJY]3~ڕyצ1Ͳʬ3}[9NΨWVun}Tc~g6g=Mq6}GKsx b``L!nu"6ڬQ}_4 4IMtSҫ(610 b`],k4r:\_GOn骻q[,C*ͳԖzhUݐ9w L01 L& hQm(4d]nNiF wfG&ܱx*uθIbBʤSnܢaFj(@`8箄Ꝿ&IltgxgɻM%Mږ{z)]vSqUټ& b`0CT 8&`% '** -L/(4$cךRjp.h @1b!  0n7ʮB Kt}UF˞tr\7Jϖ~%Ҹ[!hUqp!&7Č1] *O4צN.Ǽt0!J%S101CC&1 Lh b bey ?fW7Ƨ,ʒ2t}֚m[PzvvF@ʀ  hb @%(#!!bBâM4BF=x Pցdd'YS̷ͬ 118h`bSv\>}Ux/ޝ7UI5h,pܞ^[U9=&v8@I!(XjaS,S3]av(KWP4j` -#ݒ7Jն&W"1t!^0 ! &X2y=yomNz.zVwfKݚж26ϗMa5L0C]q$8EQTl;yj]\U:znT62U f%uLb!  o7Q/{jyϣCJgS[oޮOO>_W6O~oC,2T`44\3zc(B A\cuݛU4컗AK2B6vǷ\n9WXQ,y:Bz` `4 @C&r_'RdxyNu <SQUM+#S⎬7v㦩K]Jy:KX5b`!!)*d 1RYn+έӚKUJX7U3˟EA}lŪe6@@  b b4x2\>|z^WvB{3^S׺Np^kέ㜅VզhW6rw{xz=)@h+ !daYZC.~mQniڲ7|0Qgj_J}l;8Po)Ά>4 @dtsNqОgͷ>ǻ \T`ыfNf7(pu9|]͙c{#(h1 @ @}6yn;*SHI*Bj"9̻&{y]4գ7>Wf~םZ0niMRsTH/NL` @ `/9ywVY-tkZJ~sGCz|z[cV-KX+csSTWu6kK2"2QiM b6y֝^]k׍ʻK=U**MVK2R.ZE9}v6{i1m]jZҌRUJ)De%dR*K~eS>-у$eͮsuκh%lGNl8#~:n5Yߎqf?L'@ @ojȲ*d.ܴn3q$ngլNKbS%{ߓ\qM(zOk=R͕zX_~=hE'J]\YA&]ƣLk4>5tdUFm8ʋ+7T+K-%3oU]kRKV=cNjkCiGY)s󝧂뫟CX=na\^ RgOA5F|-P_ew9jWM;暜Q}rUh;p_>|+ng<%̙uӧ>phss.SE67FH[W+8sc<=3Z_FJ^Mz('.Rǖ=<}<=hr7Z6v"pV-:jS٩}vf2UeYN\K JN*|y.!~O{ k#;1rt݃:>8sVL]*gs*-dY*Wdnb b&@?=1Ms*|ZW3VY.+ӋcSZg EWfgvZNDeSBWʋ$ӟLu?CԎvܚ/\hُR]zu3&UWZRvj^l[֢3u[ةZ2=Ox]wԥΛbyu͝p뚫3UsaVX;I>7~xgpa;_կM5yĔ1dD׳<K}*D&P&@18{N]n)E=Mg_811YGE) "J cMQ]e3>_Q=:f]IzTQS US-izΛ$Iv3Q]]JM$[VT *N5-eBHJO<侴euRVzseOv--m(JƬi`jKڹW+n}1Z^.sLyq9}4/sw@ZH!]M&y،l-nq沯Ets'mi9E: Q"Z 5ֽC^mkV[ʝ>]3n2,#\B `T(U6-N,gF~&[bB^w*<=UÎ+mBePW:IPڪ7䫲anm J0 Pg=iQpڎz\~-kRqXl9]O.w}Ku&kSuHS $BRee:̢r fnYmSE9Hr3PQuVE 6AM "vty|yU.Y!nm4kqB.N4UdF鶫,qLں[e ⒅kYknpwBϓU>^Ѳ+214E8,:"=YվٛG\N{UǭJ1؆( -Rd [ۏͣ1f^6%fF$sB̠YUӲs]0 &\Z\_dL)f{!f7}6_w5SYŵUUYe]=73uԌybv#3]ё+fXx?ί'jĪZ'KZCOmVg ٚ5![omjbїxue ؒuU̔g5ziW:7':]Uˎ:ur;ês솅Dq#$BGVQ}cWQd.ŋZ5yrhgg^1ʎxGo|u?=%[V63fH41ӿFBwwnlӯǵ*vp$FJdi::qӏ^|{sF5skb+b;+ɳǽy9mIAJ1ɚz9j]<+htU!lNZ`tafcʍ4⁳G/LJ|TZ5%TͲBLSd-.ط%ؓ5ˡæRdĉV bc@$::v֋oV\fwtr~.V:2.8n.YX͎hk1.Jvտ}ڸm볧-%\s^Lݾ}fƥ<;9 o-^,/B9T,ųXҬ o,4 hxiӛfR-zlFfR&oSG/G=fl"#o %$4W٫#1e;Y(62+W4:lt#:;1[G3YfzseN8dًI8Oy@ԉ``&!#8Hs3_OFRثRulvth;Ì:dl @TqVR* ˣnsuX4%y:f2h]KƣVi%:f'w?LkU?,iÑIg]B%6aUiUg&>zuƧM_5^^Z役:stNg\Y+6ٞEֹgZγV5vkD-d=y55(&: F%`Ȏ-@ 9}l|dNPGDWmp%܍=mbZFlӺ23jqъuپY|| FxiP+$'*싶M+oEșPBf x8O;)3:!319t5!K kϥ:o 鞖3;=QY٣ܘ0JCM`I5f|֭sb)[b6xe8Ne!Bq2c8&(Nv񺭁TmdB6AI"^OOA(D#4o,i󶞼 ^ϯɽEz{κչ  J2# J0lewn~̚!)N(џLbU9:x}qҲ6m~/LmҘ>F蛖޿q]V FbRF|qV]ب5ltO՜&e\u5N\&\تP ʕ^dKN}!F'3ԌIT-!Ќ\%||&zcy].:yٿ,n㨍vL1I"5I4ЇJ+y_4t[Aݦ>f:i2\2eP۱kqED1g۟NxǫOMJ4uH\EūB ]I!["IHl>GW t0peEN]2_g:nm#7S{qR7.ŲAVL,qhJ A$n,iօ7>]0g3MiKkK^#PJ8@LjVD,kU yz̪|NKυI@.v}5wy}~cLIWw!o )E(JT1RjґW{!#4}g(CD%bJ+WKO+ &3doFtr걤Zabb!ͫ7%ѯךU-Ăj*ÿУTҷ=|<=X[q6*iC"(d'"$- yyTnh-|z]fSn'dZ1Ky} />u_3\8 Nz8~GLP;iHvL@`SM"1`8x`q/mAI}E9qOןơ^r2U`JP,cBkW!$I)d+bܩir+уXJ-)~tc>&ĂVB-K_?z$. h0R)F@9"ʑe>\z\;5P:M9u9ɮsaOz{qҬsq6ȦN@gm ;\$8' #R#%M_28ІU[j,#"˟P=++| g!4n^䪶 i5P$ϮYCc`Wr^010#Њr3$H ۀ29# ?ӯ ,q=ی;G0O,, 4A@83s3o !<5-׼ 1?430D$a ;8cO4 ̲9G&o4 1ͫ?8<3w>9? 6 8E Ǡ~ߙs,< ,/1\O8<:Հn:,ӽDb.4'8+Jr<<9]+rˑ0 <8"CP/ < s c?2<O;x7}000 Á(N5M0ϯFo<Q!w0 # L4Ҏ +1`=LѨAuM 8 @h  Ϊg0[8d_o|n00 8 whhtS/-ŸsC8 0 07o8$ڍ"ʘq{ T2ѱa0sFsrљu[ ?Nz2"8fɒ{Oc1+3vzM|"D:I}KYaLω` 0 G+(+f?)ŖR+}0q@{1'7#:w4VO0 $βէFS4LBer JeN*/ =A1=$l\Ӯ@j.檄kz%eqe^PU콹4x=3` X?Rʺn.Z׍x)y"ř?21l6oW5O䐘eނ͠@{B2y^%kZ*ogxBVW`h9mh]zXX,нP,ۍ44&}=fJ4E6~JC 06}+n'Ui1᠗$ClLE՝)[T@Ub̶&R3[gXPB =J(B41|xs}Px蒲@[5"J۲syo#$;X#L z\,;tEfwҸ,=ěeӽ'O (7=u~*"x(Q$I0Nm5ͬz hEb0?%0+l2ͻXl RH#rA/TmXb̪?>޻|P:}f}Sb*QnW4{5\@9I{;MWjMxs1;1dY~>r[WRlW2 UսKzrIv6G'1gglOrm"(zLfo`Tx0fbhmNW= [c3 $'4jy32`$^vԩWW|[|{TFg4CPaڝ {X6]0[Ö4W`'LqϊJ.,3U[1[v Q!!FuZe$ eQw?ieg]TL-N @X-nqBٸGV'd H- 47O3y=Q ,swwF%"wXMhO{5! p:;K(o;1O6`.9I~hŶͱ]Yqưpmaƾk^'y; S!",`8t侑5qGZw)Ayw/<^?Oz1tӪ($S]n91#T2yJφ |R|3sJ(]U+G{a&Pd>i6ClR|2Ռ7Cgخurڛgs.3uo=p,!5bh-?KM)UzUk81ְ` ZIy6 qJN-ե5ymχ7cl,iX .CR oz⯫y/R褻kPp20%˄c`6HapC[q7C(Dz0DG ϴu{m[˯Ac" i;?vGms$יg,h?(sc}^?Z׼s8&IGhDm?Kosy[r)| Cq{د}4} /{ePE4]s։* -?ۉ붻]:+m'Wum}njj(l*J }mKVT~ 5o|YYLPK,"jC*6i<}}mg*ޏ<2cs|㐓bI/v}Hvw gM$YQm<}}}}ڡ, 5`na%mv}]}UhQۼu,0<2}5uSU[-3lMUQ }d[a-9qLԻƥŻIu<<}g}mD#͓[}3qԗq[\^|+ (ŵP }qqe5=߷ (Bt597=#a*8^ȁ 2y`Åqi}<887w]Գ=xQD\}Dr)XI 1ϻ( cK<u6YqJ|4tu<o0[$-| 4&'=M}R&ʨLs.Uij M\_҂B!wh,o3g]o4Q7u ?o.\o(iˣ&CpLTz7ʙqoyv1 ԄajAĕ]ȘYB1/&aжiv1$J]I1ڂ#y ہ+-AiVmmRYY̺}S*_އ #׭qrBv YR̿XB=kqYH8Dvq%=j1 PUjץ0,#>!Tף,8Ns|i,:$BWpہ0NNʼTrJkY?4@K_oYa @1沩(SgaA4Q6HwF!J`7pVhָe"֬jO>$,JdLTf9BV;(L\ h7 6: /[)+R1.?`2UM|r*Mѫ/-?H@l!M*"% d͖<HbģHo몯H^nTG[-9#%9I"9MԾtd%yhGN Z`˿LJܘ1 3Zޥ0ږ[z hfɎ$X览N7a./m ՖG]8_:)]`9xw(F.&n$6NJ?[^F GYdn΄΋9>z nf`~@lֽL&".qj'1q8hWݎ's@;B ;fdBy|q=S$`RD>]F ig ^%"MHt4SIK+fe Tnf/޳tdy%[1!Jdx'@^PMoxMS{TPfB|^*}'sUC-JA!AFx(i؟.C` \nx<졆|nxYPd(n`/fL#2t>#DũE^?кq OmNkoȚ6Y?7*&-BA0QRj`鋘LϡL61O{˗&T܊TΛ7 q5tfԯ09mKxt\6j0"4x/\ҙ UL}%jXƄ QYgк87d]G#aPJHrCN\xKg 3]Jy1`\` Ә![MͅX\ΖABn %̃rd@fL*tf]>]x*G~|ˀ`1|>;;_`GEqIؔ嚜 o3TrUsqǣĭ`FC1No}~`?52%f o5P  B楩I<$̙G.4v|ͬa,U+)7v1yP&_6WcBa1g$љWx5G!TxHRbL>}UȈ26KNV_OAk-eT~0 ""3›O1Pg(>L<^F,hF㘘ټËlauVGW5$Švչ6b.3N?/4Ow!~& <~0"1rvb Qf0U5Ax=Fo3S1z9f|1/&E(q3dq1&F #(Ua<DM@Af.gI똏fnD$;2?05(B A) >0Kp|~ED6EFZFv癩>Pڥ0 ӛf. Tk3mGVc0Qf;,ƥT`B"ߖ7*s iN*3("U FbqBbZ - ma_\X5 3.(drb;R`@58q@T&bfj\|{Tӹ$4AهB9itbĪہ̮D{ud\%jc5Ɍw,Le"m̠\xꙮkC ʔLD鷕x*D1(~?P& f&3Y1[c`J LzARspa\|t(TWJlKT"z 3 +ɨ80&]>Mg;0Ll&Ll61Z { MRSn(-=:fP& 8]!ryI'U,ynX\ n?sB9$̈́ u6`6/Y3Sd%1)w< 54QO8nYSMd&@k&<[DkcQ>&\ [%N:VA g08AyG淟R4qډswD:AOyDm<*\Mg3zA0c"țDɄf. ,jH#U6;U}1M 83y|kcBk889?Lچpd4L\W 2g&SLue~2-2!¦"Ӗ]:)&m;4bA?0k8d?)}AkV,&E(hڅ۸2S5Aq0F"UbYө䉗JF:6ry.6CMw> b&'5M{P|]T}8_3P,X Bc{A(7g9кӅr(55HDRiS~I:M_Ǐ  ZÕZ-k54ZV3Mg2/̹ڢiYԊD͐c]#6чu>Lv"pAFaJh]Q,MFfܢ&)>@CG(X%ߒhF(m.U?i.q.مw2c('kXIcbZkXD&#lGu g\)KlP#B`P[y}sR(UpAn%MW>fqԳM/|5FOm?ٷbx*ٚ-X9BP16ճ0ԲPP^cuyLF*e Ù­ -Ar8ybf&l$>!WQHQ.TIL ,?y"zmճ65Vq|L_"Sgϸ 72Ϸ#5Rܾb{["R>#9&e0Nf|6s5 ]?3:`"Γm@'gfLyybXq #=7VsE2%ĠG,y,n~"T~ _eXt^Q70ٝA7%ÑAf%3Vr(ij7@{_a_}od橶bdgGGɈlw˜' u.fYRh96i[9!>2p@*1&ELf`B~TM3F᱂\N`J AAH |e0sPJ|1>'L1L t nLQ cqsLUIQu4^a )u; о`vNbJP!1&Œ,.Rݳe(Gb}ޠ06mŒ`Q5ܙ#[`hP 6 cu 36`־& :3>SmxuxNMFdԐ9$G 4;++!>L(bdGRG pgʟ)3cܻ ~e3Hdiѷ)"T??ŋU"aENbw .SP@@_&l*1<8arcj/POܰ!48v ˊ|w3PrՄmCju594"~Q:LA[&-ϊc:f#YQ)="T{ajW><>D8[ǃz ExF6IOb"/"e֜965 &x&c̍h ~X9|K`01gm; 0md<Ӏ*3 >%]%Ӷ>4\AC85LY|@(vu{]:d5c::VzjzF]g.1dM3]bQn* "'6Vӯ28 e? Lu3IcaZ|T؉fm5:gyd9Rq:nǗ0:6ZvsB`G˘Lh8Oèf^Jk깠kMԸ~"nCop&. mS̹p{3b\ML;1|op܍l~_~&7<+\4g a+2 k_ߙ[ S7"ĚU@ѬGP&=7]=(!>cdːa,:~\r?i>ij28̵=<vm<)pc `1CهX õ{0t91 _`'cQf(\ehP" #YA>Q>yzS7Jy 1X Bp8s,v|G";_1+ks7#LZpڐ ?81|+fB\OB8<aMf*ȃϻo~5yֻ$ïb91LA1{xU5") E`+byg<\_lZ3aE c`;O0f,KNDd0XHN3tW3 OlHCaoUܒ&"Pه[2 FZW0'KWOt/ A δ 㹇E5L,JJ6DmZcdtRf w'1h_bc|ith\h/{XWfg`r#v=s<\ĻlkRkaٚţO:[鿿ogʡ<|h1TqNau3.,Y`c! Di3qۙaVh~ˆE<4бWf A{g2rL>8ljocMCAMO ';Q|Tv8&h5nqW IUdO\9P6y<fG&OT|8А&-22fp\tl~4zllCq] L9wB tiX\Fܠo~h?y/~AP*~ OãR(q` SfKN gfE]4hɈ6c Bkܮ3p=; DBAg0? =ˁS|Kt2ci4F3gJpee˪*~qwՐۏa4b1}S55 Cs EbE˸Q#4yCv{L^%XЈN6 ǭª&H*qsWv+gFuAƢy)MfhB2@PC '%}k"Lϑ9"z]BUΝ5@9&5';\>%H;u tۄ8V`zo3{@>'_l6dG+f:;A]BM;@D87"u@r2}t[ ¥4ll&>r!\O6&}n0!=`8'pftؾ=k1Yf(|uOrZn4(cuQɞ /L - T؊ۅ<ZEb*~&vߐ%+Fa*YSFM7/n&d5&i\>0@c"h#h n` `8 >=Ȍ(u`RǑ46`4{&R(H``7 abo"`ݻ5,('7j =5f\ '``g1|L˜"Vi[3HbT1g>`|č米?5_r~IjY 2p=kX0L4jLP-!Pr/gQm37}魻N}Ri`C4ŋ6,]R=ӷL#)(eF'%i&0L| 2UOULjliIP|b:}Bd_]vjvSCWe5$Q0>6!1A "0Q2@a#PqBR$%3?l/;?(g=T3iMm#D =>J~¿h,%_\rB>Q_qSMi3*:t(h{TR|aYR[oϧESFZ5`ܿ07a_8")&])5cbzԯF7KGz(JHP(F3X>?T6ʄJJޞJ dͰp&a)x]R~7NɘY18hHRĝL|2~#갢Sn<ً1ѓr]ٴq'>[\LoQ`צeBTf[ٌxmcgr`_ؾ!ݐ660-EQ  Ɂg@SC^&\z'Q8B= a?)?P:U?N@*>4}BrgX:;\N7jМ QK&ZNܯT6a6oa㸍練0d8E+`rVuhhhD3q=x멯oݙQfg<x?ӦÉV2?=`͟H$DXt`?TEeS'5g !{Aw~O2k'%8?6 1bmxls48>Hx55T[|G"0~{L`KPT4oU1c6|OF. >"De? FasQ^ʬrMne@3`d4tDDn8?2VC+VxHLeV748M* qU?M7& r g.Ѵ'o&\̀]` MqF*D,hA14l"\"@&T.f<r.2)&}0i#Qdƃ&nD3L@|@r"&#ɍs Eړ:cHfvd"G*fA\YB@S\X Fngt&,Yr*E!CDf mbiwd49Аc2uPr%&PCLlw\EP?1BSO(7#(☎B V0h@0SQrfn!kv?uw5LT!E "+2%}eAv`@Wc͒30+26Tc>fn<RT(9ֱO+n&W˦?UDZJAdQ`ZBAq0e*`"㈊Ld0X36fR@, #aJ?a 00GQ B% jfe WώE7iF ӏ(2}1:&e3A:%E]DPT A(DF=YTUm%d EPy<@ k mF^ft *Gs\DmšmAK列N2?gI8.0#%0 Fb. .f(FqV&P:vhtCwb-& ˇ#Lώ&#E!Rp'Og( xXX%,[V`Y LX!65mG],|Y*> i )4wdDs\f44M5Al|J8 f 3ןfyeSl"3]1X̸O+s"saWqR)yTccLCP, ;qh 4}y!IɴEv{9T2EXٚlG&@&W,j 3ǽ@j&zG&bLTno'cƘ<OLx=?(ؙȵʴRZ/R<֝Mq, nTյ$Yk] e`.u'V-w!h cOc4Y61&Rǁ1)4bG ,Cs 3'B@+1bg[Q4‰'˽&, o'"T5=`UvOj?BrC 8C XP ɤ:kȌMԻ1&ogG[@@aQp34_B QP_ hVbb C:c-h.!A ω 81J[ل'&)(ۣ,'X)\A 8D=Bo]7[{1QCP3&#Ez/gܻc~]q`QRf,eT ͤL=5#MC:.1\PT-8w Gan|c"%Y0LMٛ&L rsSd8u+W/Rè@E\\٦զ}1zQ,b~;"k6)F:YWc2TLnjb6ۓ9.><~1,NEn '74o_(*lD+u wӜٕDƼ@G3(e&lQзd@l. ȪjÓlUٔHn!:l"fL9v5hIu ǍL-o7:7EK.crmը௙ u3c]XGȘS2}#XZ?dO)f!ɐAbfG8T3OfpiNrfX)4cN2"F4!ʕL֠]ZŇLɷ%~e3܄E؈a/D>zzc=5V>L[i'b 'C>R#I( eR@9修euaFt`ŊSN]#bqk 3 j75lU*n}jOMWfUT0nf"!%zːc&gڥJ*i؜BV n|@7:0}bf\7M@9@gPМ`i 4j&p1m5?Qfk!ְcd luR>L'м̄&*}?툊怇J 0MfM) NՎs;)rlT=" (?9ɅZul@;%R&}: ^yVԛ# g-@@PRLg94^C>`&\.G7gйUL1 *: QDsAjqDl2-4u7Eox`܌c˼/*'fd*9㱆 D ;da:K2gbeh4{FҡgL |v3 }P ?tz`Ț&&o"{preLB$5fl L6وAn &3za)ϑŴ(ϩɄMQ\ޣ 3>=v~n ԐVPTWLĹnt̛3W=ØuqyYؿp}:O?g'ŷY(vf ,gQ9AL^?1!+n$֢&*`հ3$Ծ &5)\\@:&0E!iS3y7/URA<~"1e0YFNioIB;?Lpl=1V1w0`Nd`$C#O-ϙp!vkZ˹.fn|NɁOBdԽ(݇_-l3i0FԢ7+fLe9*D h;Ob`F1<"ڑح(E`Owֻc(VUlY{slc5UGESowѹ oy0Q{v剷 lsI6 FƖ8cimJIΓ 7TQsQ9F $h1"U/]Ps2+7s73YO|U|ΠS vcB=Tǘ>aۧjN3(côRƣ){Z;_@\P#؜gK=2͸1-qs:"1+*~`Ρ r"6b&mη$j 6>aTPŚ#1`R FD֔My$fI`ʻC3. >aӱ*1%g'i1lJfxjPðvېnk_%8 Q)RY4SLoɧ>lhđ&*&"ϑWj 8f_Xv3#B:;116&@caS0?`1sR37-b!q7Fa+4cE;S&Z3t;rq34)jQAJ!}c]@>`E3w]ҳ/8pÌe\I›, "1!cjTfb[if_i~ߨ4=ndY(L5cGQ ̣!5l=S2w,π-Fk6&wľ͆,UCJgP9cswbPݍ&ǰq0o*`C ȕ cF+/@B˩5fU #62‰(h۩2b`m4xm1V/lZo}VjiT/n 05w2Ʃ7b`<> 5٠%z"wv.n,LYWjfJ3wBm(w@\D|)d=Yqs?=f} s}&W4&Ĵ Cj}34s\m 0 &, &Lm3 % LTʶ.<x>FTLP"u-8 x (@QJy̨ʂT7?1}?n (6u^#>eh@Nnf*I<)aaԛ{}֊c]f{ UGT;,ͦʠyӌr1#nZE标80a+`DT\˘O(ٔa7`N[#6H#[YIU>'͙(M:.e/ONfRk޿> aٿ`G?~!^a£SP)cPD"l& 6ȇ)1t&zn|0dAc#'Calumb 4 ֩L=fݸ2oy.2I$\@=%?.$γc.U' &6yhc4~DžYz<fc@X~è81]K n DRk Ž &QbƣP pэZ:ljX3ʕ?ݎѲ(G` (z(PԅB~`kS6xM,bmwQ_ 6rMp{CdСG՝K cmؓG'ӲTnTM4k#Q{1O\GeN(QbsDO{h:Kihc'\?3(PBgmA)g 9aTq%#8N0m^T\cXQ%…^(/au9羳:o=3 =n-~Q~މs>0f' dΟ?n:cW=vKCiREd]|E9=(faݹ[9d* 8۳ݣeJ0}BKtLdIPGcGTıa+6M/" e'af[:^ 03~z?7*TqG Px{e`Q+yh'*94omJ(1W/Ɠ^+LJӓNJy-_$^kS h&fn!fusB'j9pV!8 0(蹦*9U4Xgc{ZUVӧw=Wf8TXңe\we7D ;>@SZ;VjgqHjP9 z#"bȯpcv)(et+w4gQ ,{ Ži$5EX:m/\ntXG\Frv~,Ԩp5! cL%O*Lx yŦqdGh 7cnSz L mgZl0G\dSݼ.UiVm FVcNH9dעlUe^,.I&Q&] mѝL/賀A5aS4s|CQqpoIH;|e_ -!Rb}/2tu #r@Uy6Tܻ0Phu]Ļٹ_@lrLдt#T1OS76~uwliΧ[L}LLiM9!7_O? IuZuNv,/!P25䷴CrwhcVI *U5ϢDiYLtaUvp}:'{?]fmst' LzMVrMMʮ0uFS naW7Ԃe Fk;B>Ƕ5D7w2&dK~ُƂڴ,;2Z{CX S5Mh?-$JfID£&x,-q#DQO2tj@GI @!Sv.2JvY*d &V`~#=27Z,TrdBCdFW K Tz;x5qTæʙ`;*kHQ&\JyݳKBt fL :|zt^c;`3 +ɀ-V;t)>au?7Xw`GRwAWHSnNeII*pU)ai0i8i9ͧPѥfcXUV6zy4Hg]NJ<-=֪xbS3fYOC. Py֩+kYZylg 9j:D3d\އoipwa6@nbq#\A}#R5Bl p2U]& %QΓ~3TLG]BbIU MӒcl*T&t9c=eC'~:;6x5@M4 [BnդM0I>򫅨VOt525j=wS1 0b L{mkEUm O *c T̸AiD? N;Ӛ`s^ǀ檓N|sPL:6HI?Eޙ'sf2M}'TjhhwL)? 煨 J Nu'䎉 ԅB4NU|)Tm<8B0k{2`i &KCe~Mޚx!%OA!Wk|!;T$S%L;kiTxG ײ4AL&ТJ.#4hmGhZ$4LtUeˡK*r{:s^D\TwPnQ[t\5ee=UG7˞je0GC,T8|P}(lsjhvwdOWgUtr;\DM)eEOHM >Rxs|BM:M}6aFbD eQ1[߽?P 9ke6N|hyD:9jpj6?ڎ-Kz*sDKay %^цAoyK)4nj]ˮ78uXjz5"汲!QQI=75iͳu}Ձ;V*}!TKC8nys]0etPmaS`CT*Tvb:ױ9*g<L0ʧU|!&eaL'϶DM$g53ZLk)#-S'wDcwE{0G;Ii#6?CrnC +Ϟp$puC=2oY4t 4FC9/Q:~'ET\t?T󯺰u8{C3B+'^XdamZZM<:}imް׎D+rKanUi*e"ςZs2潤CsS]sNRζqFHh4asۍaxJG9 ̕"ks7u)ѽLOE>>.suU?&hTUy}T!@& ?%̣| K]yoU#p#ZUDdNl9!R hNNg0[[o&O@p/ݯC3ȦRf+9إM)džA:J7@ˌqOK y]^3cNDѠqkXX~Z*AW,&קk*qglamFtqJ-'B|m>hwk!6!S)yBcW NmUZUIˈ.Ϫj܌~vFG$Ba;fVm:k x]^GN 1t /Tۓ@h+¼rW2gOݗ.Jxl"y%MzE1[uy,s-p,MTo8xʯ k $1Y:ȉ&̨;ÃVߧi dˠ "9BOȧ訸; i]c-{p ԉZ°1gif2nv2ZCW5[Pup|i ԜRʨX}I4w@Mny'K):\全9O%KxA Fn_٪*&i {%.*È*m &gR$Le" .$8OTT\*H:~F_ 5BF1ͱNrk Und*.|sޓ=Ld*6x2 v@)I/S5=%_ACFTثZ.xÒכxfd']%J܈#Ul4:({.|uꦵ'j{a~0r9#U4!apAd@ȣ190&hM_Ē)UUmsM%d 3o8ɵ=k:T*qLwu]h]chʓK9:_ө41{y.n|-@Wgl(:\D4cm:d Ow<_T8biS{d.ڃ(!UߴUMo7LԨ}mp:O: ^5*|MSK~`zSm\T ]& Mq+HTK YQR`s3΋ "'NߚܷunKv2/o㖍`vl&iA:VmE,9zg`̞>G^IwyTШzOM%4&@*O4; ˾*=}Bk?BaoA6 Csns M`Yy,"=S3{qn*'ڵrrՊ||LUHnJC) ve1/-ѣ@U[Uȵb*omx`n.Ӵ>*SLUZ *Ea6L\UR\w{g6Dhk[g)LdeW02BV5U6U,— cG `LOU8}~ K op|4q9(\~_=m7CdxH9eMl*ᕉ1Թa2D(܏TaBTUՄf EPC ?Obu'5Hk8G$y*cCrՅrM9Nyk>GTXdnoh4Xj;]9#YM­2ڲ3*joEARzeh@rküOWwZoU^69)'Xی;kߒcN ]< s'{lp Vi3R[ T`LE^ GS: P L}} (႘Ӫ}AlwEZzev#޳)Ȯq}C{bSֻ;IT}6s]:y)\ֻ{`L;%{zFKO}Uv-oHhQ2һK Y~LeڻPrDMwbby'rcTL$Z\2ǸT+*8Qt8]R)cAsdxDz6CDagXܩvk̦uQsdPqa|2ۏ`V w u6Fzho]m&wtX|>!<]Q1Z\ӡ݀ aȄ[qasUM>XrN ~LJ 0H}&ɨj=ʿı ^_> us{39+Z~%iv#03uO_תUeq| f}eR:,E ۗ.{`U\@ dgM04c6cj R3Xl@{n5X1-fࠉïNg~~wsDR:(rJ"\yŻ_o=ʼnZ*ѻ.4a*1uaouُ$I,)c[F@^ TGR&Mbnc\wMw*vVS6yL8džjvU ԅ@Mvy{D3=rYSkC@V@3QbP!q|Vڎ#Pf2{F2}!pT{M7T{F\L_0[5ZtTgi]97Y.ըfJf,s'D:6Yt}U4q-4cB>+ cmƲpvmh:ZGf32'k#o<'' _Bcd'CM#fp5k,;OV=G3Muj\eԕA-e1 ʬ4l9l)hÏUITk- y޻PŹ}ڸ50yQ'U2O@!T<SF6/H z7?XN3 BS)0-9s!SiTUw.HҚL~)\eT{Fly*.,Qp:gy. h-O`]}1ZPkP t:rXϧ^⏉8CM2c-&,y9MvXމCp8fmJuY,tHS]k:&x`!ۊ`r)lKS».R~~J9iuuވK{лO]W ?"Nh(uBJm췍܀sToKM.Cyho6{`CCvYpv9be0U??D *X~ӡ_4T'"ԤX㪥JjtđIu)w犏aT @{6X|Ml.rGf!V3+UȔ %Z|Ujze0ֹ^1OT>0 %2HX'vN+Ҝ!7'hp8+v.,R=Qѻjxѧ'dMH?HnX\=3AuĻ>^G_m 8\K@XAAk" +YsUt5vi?cLhI_5 <)JC /Ak\6,0;&)s h&q9SKU@^5_,ͱ$&nȷOݷڧ 7u^G89y m _,Rg/pjǘҫu0\?J]#\w"b=G%γX7l8a-[QU}r=B$h MUJLƦGS3@*\"B4E /VnͩNw,**Z[V.p9gpXgյ̧kc)ou7Bh 5U@FJ&,~t7)%37 S@sns)YMtn w4'xisWXa>o%kV}G'TU0vg J~lp*D$&vgkTwU%Yi^!b\xF+' ! 3\:G5iDWhGO*iЫwMwD|qvc*5C-aDjp^k V5 (7kKi7ywYoTʨ,B9rriL]60QR *`mVaY.e1R뾩Yn5c/k{xuﵤhCFjپ冤Vh zPab4^eRbgB 4]+_񓪜SC9[QG:Q`Y+Qw̬ v@ R,^,. 짖W _yDu๲-b>.$ )'}^'. {jWR9hb(6IVh ӬxI6Z1U Nm.ޣ4E@f>Ues-)*3 u=UJna 8:~K M-ܰm̧wd+I*imw Ri&Uh>VysnԳ>"2yNv%Pb:T.a5T=\S({*G^EhZ>G5هع өrU 4XC_Ul8 o5 Vو|uDasuDO%-0n5XgUpK+#2UiR6N 3G uBײa5u9gIN+*7O$H0Tn{qla_4O@RÅkXItEGqpyjihOͭ'>IUQu6,vg)Qc~ùh=Ή*}Wq ~ձ|UEYTqW.-y&Sxl_%in&v_y{oTG#%xy,E==ځ}C -oRHxuk L%昦A(qf|N7%Li-+ j#柕O4a_n!'(o9wK{ UOV絽:wbsAbUq5˟$r uL '*75n MB- /u:܈Xj&QNMyNH0TOUJ U0u[$o__ 9hqftY'J8; ?A2F2\dYtWh^ke*ƅp^j +|+Z>DmH B]Ty,#<=s ֛MٔZHsPF\8TiK˹NӅ:h(H\Jc&tJ}jnlo}CI!::<5\wBQK"3Qp51:eZ=Ät*# [)ܺ֙Jߴ?6hӒTkZ*zu|8GSktN|62f'3FOڵҝSH0ZKnj=ڰr֕!U|`1è?-ph >Jr: ~M`ۼwC%(Nn{ h ht]\0tL|~Knhø.q7D G  jANt#EO.`89跜1isCUkATiSkq#O^L@y2E x',(3 JpM9,Q蟲aC1L dWg{x|;vbkd杈uVaT׫knK ˭h,q&VEPf3RygbP2rjS]G>I"AG&$.ϧ$Yk/r{s4ւO c&5 h[(u!7x*5&89?>wu$2CR5F_[>u*=QΥiH=f.4l@-}#54@ ΉO0 l2?ҝ#0iO̩>. L ~X[`L_iAAx[XbuY8GЪT&gp9X٦:&yh 5%pRG%8&jS&LTXvjyn`{<5x4Â\9kچ4Laa^֜X.M4íf%;`G2~s ƛ;aRWm7 y"b0,hSmggԬ\>X:~kVGftRGyoᕼc|װezER4GUFPě.WX he< Z>NO85L5Uꖵx9amٻE$9 PH7~JmLtJӣZUS*FEauV.9+hȧze&73#^yg 4ˮh"7k {af ?ȉ[tZ<-XTʹ q,YXurr=L#iӺj~fL--jkRDwGkRѧUFe+w/E+ O_ XXQ|韆Dh Uw S=2 X[IRL1a.V"lxXZFWQZ] Ht9:uGV5U1oJ0Ktb:2~v:ջ6;Իv4O5 pfts\78S)fD+y놚A' (դ$2YC*9Tu*bXG \/<P hWejgiemmf<5@u 2Uud4i# dm;03 y/SA@L1-Uh0sGDse:CYV͵̭p;Rqꁹ->#Ś)wn(X!^UfL.ȧxx*7Cx,%?*|T Ҥ zͻFJM5?򫦣ˀp>{aЅyzMZbmn4 QʂZV; pKNTr@*bJgFDd7Soxuf>eb 9`\ZM;; 1.NCCs>KXiLW=G1g-q Ku[<|a'U9fF){DU*ݝg+HeJdazDiZu1P /GCD=ե{G%"oCJm*!B."J D>J"ᜅO8ےy͟{@~C R\rS=!0ѧK#yT!(tE"hF$y J.*X\1{ySjATZAy*7,I-p/ ȱkH^U 0<5?6g.7,'{]?!°檃!c/uLko#]"3թSi{8F)u6{QRrjh^e`mQ.k nBZ.Сu wcoVm4+ԫj}793 wz|,;3!'a\4+}X OC:V: v&ʐܚj2$ƫ 75Nmi"K}eLC |fD?LZ DHQ-YLA t2tj81br`yp٢d*9pNLɎmAeUUmFSk9D,Vm+vMN +߽hsC~jMu f^7%xSkO|K`&[%G 25 pH*:xOUP 2ElЎרּ`AzAne%By&O2N~JfsЧ*urJk*}"O>ES9,8;zhUs-$Z*\ ᒢw"3Nmi.D[*jUBրT8wM#Q‹]}HzK]rWf^v4:j[}\5ge7M; ʁN\f2|YaZLĪJ5y$E9 a);҅[kKwFs|tXO>"%od.wXi TV+Y= YI瓳w*.اR=5V u DHIMl&G޽s3~S XpZ,N3.+NCϢ6)TkUCK/y[~ɞhb*Y!֓:}\;OTЈUܟe?Hȷ3AS0 OHd8c^e/+zK]N*0}BDDfh ҷ7u0Ч!5i9gT}hUˋTeg)T?#x,h$xD4MHj]hBT M~ z7Jm':[96K ;o-Ӝa-)BfM'5W(;iWR4*Zs\YեUeGR9*g!y5DqNx}{MZS{IM7G$Ԟ8Vlߛ5V#JSPĿLp22X?mLx7fsM&%vJMߝVq3&"u+i<\$'^cg-@緸<x[:5 i>iЧa̔Ǵ) ei.:+U:cȦ֋DѪh>iL}8N}\ BVΑ$!s44^hwW>:xpY[gPp:*ױ||y[we4d4ٜ±sM9@Jz$cPee>nMSԂn\Lڊ/S57ky-s ADn 57}`.7-J^x)eArW,۫mVHUi,EKDTWf;ܘ@b< @ve`m';Xu:02޷{[PTe@$$4[jqGhy'g@QRx,> O&BgdӦ$U,)؆9 f(*f?־\ڔTMʡZ|0V0]:A5 ǻNjnaSΙi'h26Vo5I3dpH' 쎬*K^ZǫM!6e 2FɏU(Xch'-e`0SшżDesFt \52䫋*} >)o\Wm)0ŷyD41akUK'MVّs'!S\tE[F\&b2Lg iJ{p橐Y颖FSFDt 0N!>yPi) j0P (TELW%  OB{u<0oJ״Nf+#%a⒨Pm4¹M0aC۟А_wUUgaw, 4wE q g$BV;eӪҗi晢J*)t'876x`8uL1˄B$nOC^ Ѱ` ^5.)o/erlRۏtn3oBegTc̷{1ZdWg*WԈ FTo?!Ҭ]0px*,j2SCT+E0%9‡bwaSӼCrsm0sG<UҘB9ܴ6ho!&!UC/e[=*UamVEa`p۷zEIM֞+TtpX8yLaJQr]\OT0 peCDw-k{߂Ԇ* fn0حŶePi{h9L&؀ByVSlpJq98[19*!Uwӵf,R mv3 AY -%i)UTCa4dcc{ydZ8\UnU>O9?v7k!kw]!aiT{ƖP[K|]TYXxzsLT2=.e=Z\;v9 Oc]rW^ ΩR;PLIsX^*-k2Ui1%R@˪uWI]PڦRH'1>OӚCȏ /rQq,cSjwIғbpQ$[O9se; ӣ[ao^hP%Fj'?%/|*FnwWt=>LZ(妨TyVLNl>.ᕏ;CMZTAkcf]QQn傘3 ohdKO|rXJ]Iܴ 8Z+\Vl/a, ³bIWɐ\3GhEmrU|DuԢiRBmUtmrjԫW1H](˻q.s_{ɓ0u4Tj?OfA:a&_&\<=Pi0{5*Lx^4oq: xUuwM@I\Fқ1iz%Ps olUsbӏSM9AT/ʂq%RDStt鳆 ]} }ZdIaM"JaW`gtF3$Ð9x+̆Ɠ@ˉJp,6Qu>]Sh#Lp  UTS#7yP&zǗX9.VsG4LTmHnAE0HP̭knp6p*w901qE5Bvlvj3Q2\z8L!'9+F߳ly'CQwݪ'޺X;iOS\l v-Yn!uXhiϚb|d%bu'2x&ccyJ-aOjwU ;)9BJ*fLseP4fn/U-dG ƈlysR_=*¹5o$8jT#^J{x5CڲJ *MDD:SD+Sp W{U3s'䯜<x!59[7ИCEVRᓪ[ӢX3> 9o)/]/" M`qچqopS\hQwo,Ȫnm~JSdnNtHY57_|Sʬ{rΪH1$+ʛYCFaSi/rMw77NeuFF\uR 1QO-9!9ʧLn'5S: uTE&G&T%ZR9yu'HPiקCZC ȂE;6-_s-ks `Q)6!KZ\yM8-b 4fߺw,Zv؍&Gka WFBteb[Ҍ@sXb7`~(" qqOڌ{@sWw=[k XSsia^O-7:}REĽ%֋bMXGT N\wqf_lh ~zdW 35cNH^- m~^J%Li U*=h*99 dNי m'4 =^ . gU,})H-2=\qvQBiܲ-TNSk槌5jA`v] ƔSl\nƗD Dp˞cU:&Gsfc0ċ[2+[ leUstO0+Mw,M9җOY~Ik,|k UAg*ė.W,M  ]jaҜrȭNU= W$2FP8H,8L;J" M~92~RhoO"2P a0Y!b ơӉt*FNe[48Sʁ#00!„uN!B%JjNS` fm<[L X]u6=murq ) HCŒuV?wݴz*2tM*Tis=ѥ1HSܖDo(yOsH*o1O(CLӜ-v{ˍ̢skU:z+`G$O g/?Z5Maf^0o v̑™d1Ls I|,[%ZrWDJ*خ.>ISME6sT%V͍ UIU5c龠]nssa eisne9w'D 璥y!N;NkF\]uE"lY%1!;@Fn赘9jNV$"5:c)d+FY&gT~#%Vg+[ {<ˉ4JB6ۡe0 #67'i"sa$oz-vy8ndYRyT4 ?ֹ/oSV>U8e &2s޷-p,NAnV ՍCy2skþisx ϼSZ2o S\`<t9"I俁Nvd֟MJFن>*R Sj Zŧz@]Y^wPYZO9ALr~MYsT(Ѷe5. X~*8+-CSih]#H,*=֪n-R4PsGBTE=i'(Md2Sv:mҟ{@DCT`ys*d3*38JwB9'l/v* WpƥS~jwCvVzQEPlrofyFI5<&Qu٫S\Ot#j"'gJZfUzuTkK}+_^ jnq7wS:bGIW&O%yuBC( ؎!U(L vRӞQSgRK%We4ܮlQ'V3r` M,SN5`^ MpR'`*TJ\RXzTMVΪ-<{ȈJim뾉棄{f曊#\kǂJ%BoEwR匩;Uk|r@iV ''Z-'#0eIg;U'dNÇ!hPUFj])Nb&TOlL> V]᧽,8mVĹT8+XִHh:'2m uAµRT*WRz&419tKP8dUVXF7yhGvy/]R(aB eU"ntLl:k}a4Ʃ97~ =IWtF\NM+To$é7@T$ocZ eW]Nn5淘iXBo&HE{JJEك\i,>6uH) ND&} r^S_&*Gk訷wE,gz 7xEu "M*Y OIr*`yU5r^TAQP*+Xp 몴oBuѣOMU570莫z,嘒\5 _CA8g$3PcJ!90V"s`}|2M[f p9YUpYF\Z/ԫ{9J;UYCZ9v7gOk9ͤ*oI̪o:j}1u^L]MډT=}ٜh~r͒ʬUJu@2{MIw sn*,IW0vxO+HtUM:cD2s䍆hr4ө5\"~ix鰦SW!9R)ٷg&)T{LL*$#;lz|Ka{KQ{Y=Rxie}Bc n2X иsrVku1~j)\w! 8rꯦ3sHTa>IvF״r5wODi <&4 S[̑MBtԩb+:QȕBduqU ^o//lt+zL;,ް=NY"%E)FHx&rN 2U M-m2fJLBf LtswŎ ijUo;MÒ0zQĨ;;D^rr_(duty-}Ӓjb~ٽZfa uE2=9F>J/en|?p$Z69;ܿ5%O}Jgx9qpRz]a>jCi>ͪKG;+°&ꆫZQ@dUVdi)Akp{ ENо.l`)W) %hMb~ xvU٧~k E$^NlIth1NkɸTs6RՍD4*nžpN&DHPEc*7xE1D(vFS*Ԩ[żAAJO)>2d?OOvz6~jj2Y rw\RG{!ba-=OagQy"XSs#+v?6rN ouT5c4V7*'s)ﵤԞM9s0uIR$~0LFY}U;iGXU:#2:"DTd*dR{8\Ce \Z b~yަ~mGxћ] ufb}nG%@ BNNl#]5S+D禊Ri><>f\^zrMhh:(VR$ǂ'6jaVUDGUwltU;E7x-6E4Vtk#6SHjR.DgR}:&Ϣ;Ng%15n7\(maiញԞM@t7{ \;C^HB2kS97 fBe+5G2WխRi.{Qh=G0 ,‘vAN$:W35W7U#z쪔Lw__:d*] O%~Sp5^[bhlQs2/)z;5iBq.Ъ}Xݐ7؆7VT, Io8}GnTZ> hXpXv统2TF4hiS{-tX|S4ƆC%jЂ湠>J@@X;6 _4>US6YFY咎0Sja$O##$ /4W4\Nj)r@N e8l+Mkipo~d'5{ajw^9m&M♘3bknYN/x,FWv޺WeaSRc0%7uXLs[L%f|4!n)wFeTQֶe'G u(ӧ<ƪu3LjKIbZp 0oi>ebnT(!4 U*m8 K2Dv%:Xz6.rprn'z.h-JɍXی9> LZ%awj# m26l'DFK٢Uz4m> Ana ھ|+轌&g.i'[ ^Is# LjeW3B&Ys'ՍO N i-2GEA tR⃏vSpd+<G=CSi6*r]6V|%Xs)-M7`l] a&e;CkOC#AYiXk 89љ j NB;=.yTR,7{\rv(U#019?-$NS0=ػ{f::AkI 0|D61.:}3rM|D˺ǒrBU|"}DڜUZ+u3+w=C -Dvnlޙ*e$4ݪ4(lg,NY >8y.-mrjpvbvJC$*gCaC(Or2`Ach&.U{xf6X;a,Bu?X7$O8Uq>ʙ u`Ԯs .7. $stnvpgM<􅀾7{+}]֗d2uh_Vv%E&Lu32lXaMsT7o{IwlߖʂXSgx0Ϻ83PܜUkR~G J\iʣ"|s>AT3=>JȊtۉ [FϚ̔GM}(rA \sC]-eaj8Sk^-QB%5 fsu%]"NjRITxl9,}FcWg׃roŰ_.iuNU{&-N?못OF3F&i4UټMdHtU sȬ%}rjmQ%B*(PBjjZVHżOD:W8s{Wd2EUnL)%6Ub,Y==9'ɐtld\!P֖˓qL!hAdN nmV2= ִ 3 Vnג Dq#T{aꂵ BÚ3eSWxM祖תv 3d-B. Xa;CmXSaUa*yM I=Zݮns|FjDfs@?UfYP^mSLU;mpӲ %5?Ui!a9Z׺ R֑~G)X"ThwsR=Ld7K@]a:g%b(U^915 A'xm$|kZ^i } sN{r懚U reZd4 tZl(hOU0O"4Ҫwµ̔@s|Tҏ{: koV!3r&ZnU}Wd}u.qz#Q !4!s@~~UЧv}Ou#;_"֛ޑ桽Uܙ|kN¯sT #Ԉ]J%=vTe S&9f,Pky1Z r6$?6)%Fi=Rw& L1؆C'4.h0>h_dSB&sxjNܼhahC[5T\Cs|M'-|wfL/7UPjI?W4*HwQĵǝ:UZoHt41 } wB%ak4`v T4f9ʕa9}wBZ2V:$5EbL>tO>˧jkʕAQw?%<_3"LB=#Ȧ,{f]$;kxО\:Cs#=`?]NkHeyhi҃2VB r\i<7FIĺteZhRXU.ͱ)aMa=&ӱ^շ=ӏZJqZWx&TU4DTЪjMKWiWsD(ŚIUAȝ=L..cs/Ɨ݀g'e&:>9oX4.n;pcSt-DHF[7 VW\Z4^X& m h@JWS(_4{6%bnCÚzrXVRa{X*"X=1 d^2(ves̯֗&}j=א⪁.>U80IgHT]}6A٠Z e;ӕZE:O8TϮ;FliCϼZgW mrźOS-ہn碝]TyܧLWqƆ3h>j/ 'bg,=[LǠޛBz)D<5G0}j̀B{H>\JjQUhTTi~ .mQ]u<7;Oo3)w\-FJ g`ZoD)x)9#s.i$w^a9X߆oHj6v\:Ueg7LFp/n\6g}jRCi#ԕ x,3s(ViNf*Ҡ*E혒R%FkLֹn nR):]ҪDCQM-yJ!qCp#CITm[!ScZchvjv75O;WeN|әMIn'{jpdHdQ.YAavlz Wn:'8#>ΛUJ֍tySCfnW6T#)we@(-i@V_ ?6P7 | ķyftx {FJv,sbp? #.5 R/5%4Gf8wBKxMzmNצT.oߪkq M8OZ_)~OK?/~[R⭯_U?]OS<U[E7˹t*r }\= xk+Ѷd VNhZNIxfۂk- sc< jՇKG4h:Tzʺ]tD>aO5lMk/!T3ƎMD0uw5ID*uYXCr(3iSwy9pQ_JNFEnb&2u]>jKFlgihEcYלx.h=Pc]*uNWJH+{L9/9*X~j`溵7̥N:vs)4O3_ُ5]ի'+O%rQLsWeL9'"B| 4 jA @6y*[ʏ n+qWe)c\k9gaSAѸj` <̗@3UO<; N qDCG4uz5/ShQPTSOQL`J>HY:\VEd tVΪ DL(CvMkG=Sapzc C mV6&zKsMt8N})i§!P:sDv^V'kwZgMT}'3HJ7QnP" zJTY2O%Ul @ ,c:mZT fB##`ZuPŴQt0BR:Xw @,fQć8>< f"Y49&fgeU~5^]:e`7':u,S\Y[1ⷕ(5j? |G#.|gzefx8ukB?i|X:#A([:FY;1 j-ϒ,{-O'-P3£e'Q06-ZG%IkTꝘ[[S[w3fٵ)B jhh@#cJM*T䝢MJi~i |3& תcZ4().ˬ*iS>P4ʡ2<¤׹+| .m@AM2ܑ;2Pa -Lw.+0!krK[iڴd}L vТgEKMU vasyƂ:ibiu\C1qUjƊW8{IO ݖQµ=:/@:Eu'f#U DʬᄤrzØ_uW[!zUPQ2m%vχ[cZτB2[ݟig4l[MZ [Fgލ ˸Jϓ^#{?U$1RŗU+L E%4{QŞuV=h{E5 EBSD>aǪ:.ĶTve4vB湁j4RUèR:UxWh>^4k @<9JFD#/0 rThv@)Z<iDɘPB2nav=3z_U|0U.l6Mit}5yoT;2?D;63{;ٍX:Peʥ0Z֋O*0>;]Й__CtE>s`\@N*q2F6hJK ^\ײ59(kNcz*"sr~ Ɩ "Tq[>}"|!n? ^Z]2|jW^+FJ(^ժߺj|@~[Y nqG^_ .+?g:Q؃zwRvD3qP~mLj+UDi4ieg:ފ"ȡ =QԔ@lm0S"v#Ѩ]Tc G*g'?S5s,4 C pOuMaΌ;\c]6MNwBJզXNuЅu,{90@>6㩕O Q[aT ->K?(#jM+z}nޡNm-<|dJ)^2\uL'*:Ҏ)ky(wTtxX_ItOJ8*cʂu+N-{rPA6i $CBn`p2U,F\\^2sAŎu>aqշ8wxTj ԘVsYEvM[t(EQ71Qy-E^S02Oe}ۺ5k=l%3ۢk5脑Fa= qtQ&;HC~%wGl>Hf5*3n~'r#[S#p02ۊl{@'(!f:#gʔwBO0SXV[fRZ%> L;NSfZW~ZJq4pqO5Zl+ Zk:m#qʻJ9BsI1FX snXogU!JvA(ӞIB+w6=T:XG=y\; R(s1f<=2@Ts](/P 6l;P5rR,LYV״ N4[4tN:X29UUN.}E,7>V1f%We,óy^:'CD a9>IτriK HFYl-uԴǩ 6J' Mͣc!=[̑Aˎy,vy#vR$Ss^L(ex{>6TSo)nYڭvu <[ .K~uG[y4%Ti܋2VG S堟 MG{sx;Y+kD OԢ2)ۄxڏQ.UZg,1('8eVX`KNzme[gS3MWyPUKtWgϥJ~ 17T^`d 'xRbI(=:nrpmo/Wdjk 7ef՝S]|DmV4R O4V)T/K9B5hѩikgO5%Nxy]Xcc*K4E܁i"-/$7lȢ@Q(#]|zq,daQ]Zg&ʻƏNXmPzT Q\HBm>(*7_, niGTiyx#De@MdO܁sO"@UO n#YAVoS^oǺy=dmi=q;SLWhewO|dn7mi*$ !5vJ^OiVh> lfuLIkD\xZ#6Hٸiin B^:L8OĆtFѭMq23T+==Jcԩ[ﻗU|p^J޿L/ɡ2{xA{gf@EG0\>9a˪K:Y#FӔs+-kE>\â8rz#D̦Sr\6́Xwq H-czϨTi>Jn\*j%sTܱ}E>J!Jķgc4Rö=IOe\JOY *?YXe?Sm ŸQ#O8o uHBwOG;=p SE6ZV'Z7ڿ̫r[*frD5+:ֹ*6l:x'9O;yu ?VUJUjS1nV5۷wg?lj0NDѬ#pU=Gj٨FBZrMAuC4Ji$D\Ni}z-T65ZV2*2+a麩68QiVgQ>"sRk"AL&*Ew֤s@:V xF$3 lA. hԮѪk /SMJG)O3(z)_$S7JJvO%g\@lڻLN}oq޳z}~~TfSNKS8"&ssP`cIB\G23Nhx!E[ta ,~6ia֕ڮ`jğl=iPlt~OɅ)J gfkS䫙x~}'dZ ݵnPbwڬKNGW0Sƒp,5Kvb^@BgogvӓSmw5jZUxty*bicXV_SCZ^~kxOwz%ժa&qmG谴i vchS mJv> -Leτlے_iĠe:簎FY N~W1q2VWa^#I޷?'o=m]vdB#ll$aooX'=<^ݟ߳ǧ/Oُ,jL=[ڟ)TK/\Q=/M]9 'UN&閫g JۿمʋUTuW0拭nj~A>V>ϟT)%4jUnr@,q2LftB 2d45'SkA5:ըiG)P>^)nwU  >4r $ ٔYk {Y8 sd7.J؟5E ]z#P8z!fT _4l)ԣj+jVJNBJrMRRj:tg/#%%wTP*V$P੘jWsF[v*3Uqg5T2ghpp*t5 WFU].m60]9O%ӻ8&sXvW8eĪ6ʎoC4V AD%T' ȢP7.kPw6Wl ܲV({a䩙` gَ~y>K5_F}@'ŧ}Kyo-?]OG'/Ysf>c|eLϑ> !7~  q脲\isSu> 0_B}E.[9gD.)Á̎$KLiR۞qJd^hV'*o$2%`"S Vj:ݍQCdeqdV;5jX[oSs΍`_Gn7hG캎s 7FĈapwL6AiT3!xI˒kČh OǾ|SqfPs DӁb!kD<q4؉k>W"F7_tQ0Q i꣢ᖪG"@o!o|ܚ#5],"Lpea>zɥj|ef4֎gԍ(F']MOoK]d inmȐ &юMyq@Ore_P, wzY&NtOUjS(CC"ouF\ Ag?(>4V z"SG4LS fs觢>/V%ًXnN+ RkT!W͖Q1]>O F(XoKmi~JFl:l"u m-k:h4Ph`6yf 2uD-LDSIaez.ਚtYhAKg%9ׯD5ѦFڬwB pGX_e//o;4^}܂VKEMRM,~v5=JƉ)}V^t2sӮjeTL.`,5Bg4doa*l}Gg2-:Ӓ&T8ŭ*t)Nt\ch戹nNJ '0z-lوL}.tG⩹|&dBu OA [q=%bg5 2!S{s69Ǫi%FQM}` *a c<pz S l:eRC$jUkEk8^܎[FpCVWihϚi FO{5s_ZZr7K\~m01K/`6EW@ EHwq;zpS2PKt(憋aX'~QQ%S9&Fg%sA4ӈUrnPV'V˧E!s]vL6BqP T6YQmkd=3i{X@Я{=eTeBr0+~Ti> d>m9I^!-?4j1úQ"mKX[mvk6 Dw3g }"~p&h6WqfU!i1: ǩnp[t`ٟrhsn-AZ*:´R.k]uIXnZUk3P{k6C:,V-ӺQH:tҦA7waaXvqqT60 Tk~4#":S,jù~F]7KXnqs`&"KXJ)ucg/P(Cdz`z`/͞MT1~s]/@r *-xcF'w% 5kC1;E@_Z@p!YW.yT:>a,U">%aulp8jqyu$Th~9*ȯLwEڎ=#[B~c jsV25C5$I/FK9xXa_ C搏Y2YHk k񚅏yL!QbX ;)TɹZNPĹjZr̦3ؓwąs:N9@Bn9Ŭs.M=o ]e\h4+=U3P(N e"MFl:,ڀO9 =76QPB!Fzt^=G jiw=!s|=NK IvxRcVKꏪ*Ylkn#6Pe&9uVs{܏Dw ̂{.XOrtw%OxSۙ'w^{NcT67˞9,!zӛKb|SiXB32Ng5V/cɸ]5iZvv/l'Nw M㫑mџuW4Ѹ'H@TWkv7R<KLKxHXw5ָ"i .]ƦsO5Ru_5GT0{1憐-vl(*Ttb)0xpiNPwPThiC|s)=f765x'8O-\ A9ce4xf)aȻX*)9SCEsRI$a'`^ݽj*1GcrUmqZrbiT47gjxl#UVT0C8Ӣ5 Ou{9rٮ4r*6I樄Ջz#IYQR ҡz=C9h门=q9 KaVeMt$ꛃ5#!apo5*ln5nchx˸}+K .szeU?86!cs^C K|psnnm*2eȾ26xFkgT hAiU1vIyק*x%# c(SvB1)j+EFWyW{g/dvBW*Y^iվ[)eT*;aoGs.qxqm-nz.4o:?xZsEUs5nOS^hQ tO*i0N(cIy'c@ آS<W#SPMny#5}ymw~Y*Ns.q19Q b|"dJZ=Hl5(}AN=HGc̹(u9M%*LiRDs cJ@{]@Wg*T)CM3G9&ϦMᎰ993:Pv"Bu0Ak9d`Y iR1ڂ}Ȋ̔D;1⭈s/oUw@ڔ\eBF8UB'ZySk|O Kď }IO]cdm:-T"9&^?Tlq0JnAv)g+m3ncU7|>ypmpSS+JohmkG Y5=Z14x("bUJUh+]T6x-dHkd4%Z.r)Xu2S h)7UzSsog7vzmHTm,> `4zXc5; lxNȠ!"%ǒrUS Xr%,%?[ 2~}-Cռ붅Vs`94N~g۱,6Pj4l.)9aRR(Z@Ca|@U<{"v 182*x8;w~yKOXSd\GQ)6VjԼc\s1RF}sAUw2+X37ѫU5hOd| uR`-3/F7 | Zb*ꅃ,iQBpU W yUl ;lzNUoe%¦q0n2h'\A iq^=BaG v=9k@U:' (b\cR'l+VKU!BQɡVw ~+TӤ7sUDt' Qq4.\1cZp194v"'N֟%OsV6:䱔ǫNg0<,}cy^is`8qb0Ŕ!#%P\ ṖRQf[7㘔ݐdl!T 8u`PB}z/7ʄju9l5ͧV p!qƣ" A&faL$&ytB'eM!"}HQjJ%\٪D"!{dj)5ֺ+vPg-[SuA n6g$pޅvk!ȃ$ϬK6{ksY(QȊm^s;ޘXjK\Rܲ@`1M4)]HAI$=*WYRC+c~}Uz>u_lмz4=c'g_~T._ @!ڷa@"U<^z_~~/@*0G~iF\n_?K_Ex$ ZUY>緈w~X\~z+^6#7~z5z]M0dܭhOAՍ_櫽K/1A .JT^z'Hѷ~VЊGp\Ki>?Uz\"EJIRz1as~'Mz>3юo ryBw6?1 >"[NE_/KYCRJ~?/1;r߸&ehk1;r)*ܗа oWԌ}.\r=JE~RT}ULFW[lsbp&low._EJr+֥~ z?c0{.RV&*[^(Korn>w kftbʑ??g2J5^*TQ%z+ֽ.z GzxQzo_ 4a*fg8éf2#wT|Ns>m!GЊٷϪ*GV R>Wj쎫Uo7/>WtQ]4??rEܹr2C>RpޜD:Bݦfrs2J5ϭJ^%7j.nja$8QF; +fh #>cLEޅ6鸍a ?螧JeIHM+ٔj`b*tB`gHpZ <²MvE87ԇii7gңY8cb2d+Xß iGv} 53m@Cu~/YhXq;???bjU?NvVyG3N& mVsTJ2u>gѝNNs؈<,U1(*yNDU8NI\.j3 OF&my+oqڑݛ@|5;F6mmχգf=JYcxiP.+,E+vlWE:qU3<>*e.Gac/߻ .^ڥC͌?'藔T"yϜN2m֝Oah6W^"9!Ҫ{2y%L"c/@ s JqnqҪ8.Pi)J.eB%qiҖ2=lXoy!Jvmy4\?O?Lyf_&+i9gn!T ,]$ZXtw 4vN_RU<`q63TT*@ͭ2>=?賘YrKhGC yC;D"ݮ3\4Yt!kEt4D*Ǣmlf!H2ҾOmW3Lh7+;z=L:̱حFf}fplhHW(Rp=L`Oa2vوe(  >|eN9)%Snoq}״"=&]0*ovK;Lg̳rwmԽ[]]"hT[Sa|%{bDlıF!)vy/>6t32j#+LmZ=c&F.[9vR&p`U [x8579;s1Q)q803cb.7n|SfmyBHIv?0PusNjq8bQC/2Q<\7=sn[+5ۙrQ:tvE2}c=೩Z_/IkYQ0ޥ⧺tY,c5<J4jzjp0į< 6oLx,E\;\y0#U3Xf2:/?ybs"@W5 -BPȘ$;P)pep+-@E ۱h ]616_,*s0}MojDbKTP/RlN!8rNEXS }}u,u7^w/>#a-x0z DG&_hX`AaP+؋|G!gh;AWAn&sKPҍq*%q:ypzvs@Ydv b\S?8Gt,ra g#TBcؼkOc˯HrIGf#LLJd,N<gTBsWCA0ےqX_?y1|\3PZg?f9\yGtCL6q:',& -ܼ{pmy{\ARk<^Sľ(HK-qRg107R> IЋ^[+\שjHkIot@-⧴7V,*9 R\UyTVuaBbƿ1 dQ`v= @WX3RSٙAkXZ~IZ*4a.iC6T,wlJnGDKu.j+FRlg3r;.mSyx{WgQ*Yt{1op0/b3M>YMj:-ffA3 p#_qa!-kCSLxyByn#Ek~. ⏴ ֈ RnOyxM w͌0ΰ[ܭB%ׂ__B&x..lDJDZh[l +5F:x;K+ʲ\yW_@?ISo[i<ՀܤWvFe? جck-وj([ݿf06]I/]dUT&8/4/_3+2GRj*AM O9bur*),% o}L20~5L}(~xِ!8l܋ßxfʍbt5ůKK=ԼjnmdDϢT3ݧDmLΑlMu2W*0Dqc%a" iEi¸Ut0A/vcEyx@} *ΪQ^ﳬWsbdNC=EFf5xmoȔCWib(4ЧZ[փ0)FA+t>ʽyN1lƯ>0x8gAl+lA2FI=C(3G1S48?쳵 1!g-j7wnT,rlN u㙀#_ beĪ`e-?t;xoq^[7y*srnR13kOeG ەKsDG0TgIm&"*.|x]y{҅즾"&X :F Z;/ZVrusx/%zwV[{5^E0PTo]{]|LLM  חS  6(~g1*ҹpmE _SpZ{և-5 ¿ܯ; d9Έk$i TR&~ȣCL;1u ݹv`! \c~%{u}1^2͐5VƢKuMJ)PցcE bX_%3^<ŠeQniGyJW_ΊbcgCg'Ɇ8\UJpz,C/ ;ڊe*p}eԷ s̭l0*Rw (J2V7dSY,\^D,YcS^^cP@n@ l?(ljivþ[RTcm,x C qz^ h5)okѴL@lk^pi03sfR!W{ruE0޽P7WF*pFS' qa#kɰ }u'T 2rxѾV /5i7Ra"ԦkSn DS @ߺjNf >=̅vfeԍƱ(]g/i\Ӌz@}ӦXiP&N 3Gr7v-h;AA08Het;@TldM7Ѯ8Vhׇ"t-_.#e PglqAvR~IV|_JU)wmF"rU*B(ڷ v `j)?Kf:+R˗P*>ڞX & iӨyzAn?L*vʺq:g!ZbF+Jx9eLьF``R\',xҜ% k^ 4,j8L3r8(b(d̥]UgT/E.AX7X< L¹8z] >&Өyc/u?h5SD#x.+y`tvB4 %{,”fǥC} ܣG ԏo X$wgYeCPa)D!pP=H !P_(w:x״Vf%F_/(J> 0 X0Aw {17drn򍣱8N58or=C\eTs\F*.iZ/""NCƠ6b=Y fFp'_1Fa,~^ѹvL%NZ;Ҹj6^Y]NKQ9jZy .[;i euʸf0S߿IN{KCg\(-~ fA`9ka.`:]bRl~nMw^ n)*,/@rA^rGJ"SwXT&Qi?X9d\zeDzE@濾 ™)uɴ=_i\;]>k#~7,b75tStpyMn%OG3⢪y~hLcah3veÈnV꾦0/y}fwf h{`r{ΜRq&HMɸ?|i4(, saky4 =LX`10̂^|GBM;(3O,Mnd,C|)4VD3l@v!`(')il"++(cXcJۓ,z@ Mw!RَCh9J- ϓlP|[.lxD(XcP!Z A* -;G1*̳Es+̶yS=:"ʻLjoe(#b[ ϼn:Nx(cWeK^)=9v 阱YU{͙;R=it%İw1&˃L+e nkcɺ5W_{]X73E)cNO[?׬~y3]pP9v')@P܍+y:L}v_ R9iCغ1l&MP9 3.ؖO0ipo;u12ͣ-l4W4Dte_aX#>ޱQ!YW{JþiN7eȩ2 \P]XI\ y[Z#s{KmM)%<ܬumf:Fߙ7uh&(RPlr8͜)rۆe&<`Ru6cY@]piX`!RH֎ =P;n=\3" AH\5'M GP*pF"_Dv8ʑ 59w6Cv uc`N,eBݢlS>`-zSG0+US_`INk(O#[X5n58|T[2蘜)> Rl*FI>:]zinwOMu_-R՘8 LZ4S(̞|J^eC8ԶMj80 kb:#䍥t?x Mijz"y4)RKHŦ\T2X4#ڱ݄۳n,'vY"mx5 ;+(}k ˿lJpo#/(lUQp8S5NC/-76j%+Lps0iS i:<@h}UӞbĠ~߃aQ7#*v}}7GGGQ$X֥reۥuqC1ʧk/X7pJuVo3Ji%FפZ:VR 8Vdeu-o=k@-.E~D~9j 2ccT2+fR-*myqnLeTEn9˔,5b] N(Q 2ݞXmxNqf/-WT3,LQa|.:-Ws&1BJKUUQu/ Xqc̰ K(uf UkHL~ "YYƷaSa3jÒn3908#_X4@5α}pt; q *m\AZ8r2ƫEهR> pza=75{Ģ2W*:@Z KoX%k~􉉧P@5RI 6UET_vN{7dfk_!N9%PY7ۤu!PSTBUroA%-H"2XVGIsIQP|п rB5.p}`!riC&n tKzL˘DF55+;O러DYElA -grl= 91Q+.%JSS2ӯՌ/^1!􂥀ٞ+hc^Ng>pdu9:M37+5g,EEkAy*lFQnZ~C}!WpwU7*k f6fQQjȝ扺:ڟȧ l kmAp@fC#ˡLqs/$;uu; ^S7+M:Cдۊ ;&ic.CgMU}'\!{%z5X:55\CQTW8fk>0  w,yB9<v1{ܪb6ǴW\thP c|z |6|qqgM9V:B ?FT9ה(7G N8 iw rx! jϡ(ݙxX2ʶcK5}i=u5Cs+Eqhj׈EU_)!_mK xb珙@r̍l2{9C,T(r\rCb\S'J[';K#؊!*UŠ jcuD5)ia*c̷muoAd`@k Kb1קoN`DR¸ , zb%骻AϤtλ4Q,|acEyuYֹ-|A9/iV^jTF/LOvpOc9C=?빉Pfrzw Ɋ<Z.˗>ފfM;Fh3V% P',-+a] KgDZZVq9D+f=f RK|7 1eQGwt9T'i˓ԏ ^O9Qt0 Jĕkkc:>[~q Dh<=&`{dJΠ BxWPt%T~{i(vnPF 5KC"H2\Wi'ٹ|8P 3 ĸ1$t} P3g.%{n{ -*k$OA<@k^{VF rŴo=wίhT 2v)itsrK)0RQ&̩[tg\k YmZ "gdIPљiӣ=}fgl8l}c.ǘ4*`fj\/ъAg̤pO35B_2G7QsʨhRၥI{푱at\{h![LJhcu>D׵L@3([|Fq<ĺq4#L^jA8}9#ѷA% 1K_V`菊j`JsZ, n5|DX t.}%ٜK^ p7+2TPjffn&b..`HԏJ6kI*hVSe+j>ePɖW (prCo8W"8"eXΐYMaˮE5\0QX#q1A/!~ϛ"BfAyc]!N ^ٍve(YA3CvьMҔ\4 i8HNPeLk^1,a. =!'V2r^XK֫`!*AǼ!u# -:@׹ Z_׏JE%1S-naNefdK;ezd 4')6;Wr˅)Sw٨ w {JuxB/fL=Jd2ڰဿq*1ײj0lX:jMޥ59=a_~%x/`=vf]VWY`VHhkTy?ݽo +,D6Kޘ:Bڕ ‚S$93k7WU|fiD5|2ߔGRӻvw !2'<WEo{lK!hvz[e1}fo~¥3 _1S_蓏*mR:Vp8qvo@ 8CVK@;$ eBs̛\AC #K䎉fU>&p*X/tpD\"X Jd,2e h-W0ֹW0-!;%.D!uԱo Geѭӓ MRPe/*%̴u89Umt'U*avC]ʼn6}JB GKU+xtu_tE,Wԕ_N1Tq1T}?NOw0  WlPKtK!*]He|ͽGl)cMÓ")KŐzEas>*O:h4Bg+/ DhlٰykY̺f.HJAH9rDWq2tb6Se۪K*,gC@%U0tWȻAD|Gg˒0qbY VZ0:V]Z")}Y4ltNyqQM13D 9ZΦbG&nҠÉg\$p3&%UI}J8ޓ( N}ҙfIc26w@z+S2sc,?Xg^c$FIqUl {_&Tͷ̺z۪_އ+D1b^gCpA\m>]+1`R_T ;sa IZ|[9s1"n 5CJ(auqݭP:O4.s+=rgЎ2v< HPn/O8sbA|0<%8^FtA9԰Zw4*s,r8@ hpgBU+o7NEq @w ѾoNd\W2oa0sI"'Ml)S30f5o,nQLBk84:i)LxodPkg\ L/wK./t k}BV%7kC7eÇ$h! xGZ6 &z?+LcgrԩUӴLgC1sӦmdh:N'W~-LLsT|z%-X9h[|%%Ur)Q:l+͗|CQC qRx; tJ 4_Y͘|fep;@ KcL '|z852w& 0&QڽY(EI#7ˈ[K Կ|c?R4ωe3)G Y2q) ?XVx|L`QMavMҀ YQX%mpg3M`x J+oˊ-0_k?_n:1-# n*vjs/jxO[U :_R{C|S5IpV E[ tDxALZ22FAܹ§KPv,B FNVp?E}f$;L; K_\w۲sS>n:^W3DsMʲ/OB$Zt=|L"~Yrkl@BO p1R¾H0M ڨA+mħ荆qJPz8PJ@59" C QIT6yn.v#\φmmQj ^s/Ky#*"!6C |J&֘2%ӈeK%0y[x5ᙅK"!^b2Z2i˙˟ e)Eݯeqȥ5j,3]lVN2ƫ5bo11u)~gz~"r_I{( =sɃT{˼u~)9g%.XE)Lf |XI~R3/WI!=#)):J^.Zqܑ[@aGfiޏUYvNndE䧳LTTJ^qPY%'A8b㼩WM?1%V^.Q%{mpSC;#0$ƈWmTˑ5.ԡ7QU~R,\6R!:]A V @x=}fơLy#=pSW A&h>LQ,VY"u9[v)̣j2xf'a'Zy23#v} ݡs"l=m9(&%q*VT N%x[~&%~Sx%x X[^#ӹz̫S4dv:CGPy마NYНۀ.VVD`r }%z~ ahr1b ~GRSvJݵs2>t2q՗z9ˎ𣈬ll_(JzrӨ>!R2z03gY>Ъ]61.5Xeݩ(/ӈZΦK7q,Ctu3 hQn}2)eJTfp)Й+x#3_fMƷeU{%T;·B!$3bkhr{w+'94d]NAx=j S+* m9-`u8Ҿ%Er|D5 O̓){]0T8@<[F?5@r⯷]"-X!O \`b5Y6lZc`&rbanK?@FrE a[D6_M4 JLq3+yQy=">龡+'%G70j*eHs=X2Y]~ muYH}‹28^Sb8QT՟hL#x9Zxk~!¨|\6$p }%7tK' ̎YU3rzO6Jۙ3P ү@f YY޸] a2uC,hs ;,wcmcP}cNC A0[qK?(bYiy 8G8{xѴ+ߙ*@k6^N\cqEB0 ![4švKWvPR\b-WB}Eܶu<ǰO*O7ַ+fvؖ0 FkSb^ }n͋ףFN5dPNY(/-VT#oըfImn6Ʀd@B_ISў+:Ni]Z2^.`Kex?)J2uM%b yFXf%D g)k1!jwGO*usn7S2wI@ʢ1fs 2RJ6R 7)?Mbfzn'L"&E 1'z!R^FK!}Iӊ ]bZ@Jt$6^.v3+ PIJ)2FsYi+OUVMyDK_dǰˡq9Кa va-de`ģ|Z:=yo`Xw1緘δJqYr^4|t乱G,X)X,S \PGb_I`cv&H9%V `&6ʼ so꒩ 1CAo,))>,Ez-OA g0] aK֞GШ1yd[T6&؏q ,t`G_&s19Pg!'7 9 fwg3I.eDc>F)ΉLWY Y4xL &,m2weN!ĽgTh仪LqZMd&Xad\1243+q 0H)}QJE* Fk"G>m>ӓa!"{ѝ}i  pLXQ\< /o߿݈L 1 !y 6`dӻ2nX)^,ZU2F'2q}rTAaVUNжYje\=]fFYږ\/3$}.Yr.kCNh ;Z~k%-.;!Ny"}W&#y.s(=/wؘ>VO#jiЇ 6kTE ETu4,}AGXo/uܕ9+r8+ a!eM1$p0>"Pe F\g_5QQN;C [m3 ee_pg‡SZ|[?dfVl8-mJ{2G\־g'mSM;1M%ҹz' Omkn`/ӆ?D:ԡCyͺd -f/Qx_aZuJi.WlDR=/*EcnTL}!=WXoC|-v3u9k5"y&|X,\;8`<,GCkDܨ)v@XfEL-"\*s([߫}gC2E%:*3iPX9ݶf27ԗ6@4*c!ŘOɣa _s+8秆i<6|\(y_)a{,'78;[ O{\4.(BQ<!/oio.ml)2b!q d˿MbfT0j[he]hyD`/3w~I\^(&,ےÇ'C QLЩӿĻe<& `1/ PwQp=YY|ŝa1nZa~sh혠IkԻW YYu\J.uqa).A+sS)Z"7u.!ʤ y{Lyt˭}Z>k)2:21yu7K;83Ծrena4zz1s/G$̻Q#AX%te}"Ef51s3[T/j n6n*g5*+iNP.Q" ʳGf;Fgf6Z7*ӯM!y6d37K0JƠVvgkk kgK5\R]w2_s,}Ha5V(ԀYyS%_ MIB=XP,p'#l%QuSN}FTZ`A\p}s*TɆdٿYf<ޮekΆ$AϼQa)_R`~ܰ5gl0 0=_V(^@|Po3,s}HfWf}|Yf!~ l#"eHg蜲AkѦ98B2D+R et3p2IbSal˼04#7qljۼ@}%,m^rÔ5 ^&pkњFJ0@ -_/_#Gt=ߥ&*z&~Wa\תvj[3|g'̹,a73ƜC:u3b[q&P;3n٤~f4l#h0;eN$.\׭Ds_nI[L($S#jG1:e[[a :Mߍ9sKG1fJ"T/H?TJeB:lqIbgpWl>pd9VѿD 0FZ\8L_iOyuTՇٍ̽`c6#ӬxhT~s-Yؕq3n2J`M3*?b }#GeVyFof<.0˩YcÈ0[N"Id9뼭urK.˲[)A=Rs9Mq2#|ͿO7~ʂh1_mk#~o05 D6Q+_8h0TA8~!In߹+sWJt:mg}љ=xVfe{9N_Z0a!b2uO/CzMl+c*Xbά>@ĥ ÝJ+ҏ~p[s 5pY͉OɹQƊ 1fj|Ҍ bz<.;:Cp8<ǡQCmjqL ܱKG J/B+W2x3e uw5'ZY@;ObGAylz˸oG,_+MarV|E f\}e}B,tg>'򏥱{YC@;OCs&q(Yٹlʬ@]IONv2^j , ^tF%/JO-6F]JD!n5۔5̱ooS,_ghe;ןJN zX%t/֏T$+g/zCP5V xP/5wpVj㯩o512]-#\6u0BqWXegEom|EuS"J-Z&k/gLp2ZMYNs,F: r/w}6l[xunU6D62#]R-n+"bS۷SϠg៊4CJl_ Ŷ*=SV#|33"9pva7U&,-JjncG\5GflXruE 0[ \<^~YSe-qybX5/\?tI*o\X*efؿB[]#dOywNgR#Srňs%0?(, M3W_i|c]H;} PsSj$|ɣm+ y>ψSs(:z;j=G5yFTZ^ 0WxYL@PTIR3#oWaGy'b.\\cޅMS\0S1pŸ~?lLfX/?eMw ߂ozhu#g`z.,0ј NB 0f<0S~+@nj癖J'i+9en1:fjcё0Sƥ-L{Dys. vzJW̪Z2M2hG&68F,hseCGej]89V]X[2E~_> 2؜dr]B ۼx)܃Ŷ:ܲ ecĢi{\G<aIȕ/+q.q*N!O"V$1}Y7>VܚeGcuocBV0jwcmh,By16Uvv< 5BXLy)ĽVx' Fr0XwaN.20vgcQc4H>&ǙytKH/_2,]#-ݼ̉g)Ei71w4Gn)_ $J޻MGNf-SamڋE3NqU>鈀PƥF-i=EvVF9-U<7*~Ia-ku21O ڐs#Z5qc1B+9n1@=tӆqʩAyw,ZÍ`+os)"/%@ Jή%^L6/yA,iT&rSrwܥGgICבLOgfg&0E d4;Sg2@5fSAD:Wyn zX#y~U7f̸X1G\U{k7=$vCATDw7q1ü;1 T'RX0;S>^.' %pZ>aH:i*q?}=Q J.:J(OCܔf#I 0TKHY۩-e; ިL՗A7.We20tv:ٝ~g0*ߖZ1!LqiC.gb+70p`UjGh9G[ q:yr,d|Զ 㙞 ?=sq&Y=>O-n0JaADj~uMIk.vDsS&Еq閇0Ic^f)q^f9B\fu q 1+܉TvJ{&r`擴^U~s0Bf nTEEwԵ2;/~p:]~Rƻ"#:OKq^%s Tc{ʰ~띟SЇiv G9obvche\3;@-n=#_g̹Ri)}=ƍP_!uNn{[KW|JHXv1)fC]MGw ޠ]ǫ|1С\,xX]cu /9Q; v2qb^%Sat);3Me|Cr9*a`3^nwz.g̥qԮӡLG? LRcge8άܢVxR/y%<}Q==Z6c楛8L<@ k=c9 %)%Ǧ'3~ g>˧4coO9"s7ـvXwgt#ɡFJҳF0І2C9d¬wjAϏI2B.(+)Ybbnc+3G0X;ͣN"`]7ܞݝLЅ(>DžʶQ{NjS]S]B:4x%,U0:Ϣ>=KVF}陔X+ai3a7yvZ4 )ڃ[NDwO^~#ѣP2:Kc-k.Ȋ^k%2\˄szjLL1&qqʻ3dt;MADP+c{jh ^&Z^ +%{âh-bծe~#5[PFcB2-0%n2{LѼb,~aSqȫU6ī6ʋQϻO>'N% '1y}<¼L!bopsb\?hz4WzQb{ b'<]#kUQz9+-+Q! :1MOE\el_&wYMGImOV?812zƹ'ȷgg3LՓ` 5R"7틤.`NS%ʆ=9 wrzns9ۜ_[/iVcF-oXm0MR <>Dqǎ=Ftxϙ]5s^~gs4+7RmPV~jq (P+<1#0Q vD܎J`ZV{5I5h/9$sQ6(;kSdeivJQbssmi~c8Ks~==" L32cxr-LLgN`2Ѭ0)JDt}*d.Rs0q]%T-hJ tjLtOi nvo:6A/Z8Snڥ=xiޱ7,F 8gZICلmKY܍PܳWȈM!ꌚ/1q^;9h= SQ̒R,NW~T6޾ed1~]x: +]438=Ǡw cQTucqb2[zIk˰B.D37(wvy*3g/K9xeJ; zY{J[*.)^%~Ck8ZW%刱m3!0CX/LOĻ,>J}(J%Td~H}ѱCf/a'S vm GInw bs-ǟS!d۝RG>5Īnc!vaX xYUJu\o-;17K{x/ea}NJLf lN*4#| ]f_uwswĿ 3Vlpg΃!B4%,Of}7 Ny%ܓwR/ѯ^/5 jsң屪˞ۨ$+2f]RN/9~+PN*@sLĠ~Bʀʥ ݾƥYUoZ5,6TgUBW jU[۟H*Ú0 &.1p#ڠWAiB]~0}+db ]vEgh):͈I(`|?tcQXDLwgNɨzae>S718w h}Y3T W:?Ƭ{況}0lpao$t6C6}fW_9DIg^LgP S!h?skZ `{|5P{ m!g:"MLC= Y}q+SF22R(Ļōyo"&Ne|+-7a5 48ӿ_ގe GH˸+q`'Bgz6{~=XY 'tD=1OdN7AeTR'3^YdNDy&} z-S Д3{eG {"[.o;QxkT#fMf+g0|*KXās^Cܖ3~bm { ]=u8z)ܠh+1]]ݏGc(ؚ51@iLx0f\RseHɁ͞bX㈻"Z;$X(:89O;b/>}'髠> $-%TuY#MXbep pFpƝb")<^.eq ON\)}!3p {:VX Por=4Q؎=As X(^3<̪L,}ĵ% `勸<@z^2Ma(-lG׫X'BVu/XqT[IQ0&UNV#/f$kxzzkIe724yzo2Wa4@ vt c$hY9ε7%w% Aٞ`H8'()%u3.~4py;.Sxk՗fSobѤ̡ 萯 (gs˙YzC lp@daLYnk{f1vnK\YL:K) *K&,ρ~gmR/KdQ=WчCT\Ks238vp#Ib-c>P@a&h%Fos~"s"Am1L#,D𘾒`do&fqQA4I{ē$*@Uڍ8qK[-j z mtq+0flHg9pL )݄v`c>#<х}@p%ncџi"T1+u.+d^*P1n}y#SL9G_eu=݋1}aD>X6:[;YzOОIwl;AHxرQ-r ӇYi3Lt^20E3znsL0i[rvwNc}7\Tj|ٽ ǥ?2S!2ƙ]#fK^BO1,KJ}-W傰xJ1 秴/i 0Y-}3-(h-N_.W!ZU0UҎ(s,50u3(ryl k? ; {,AؤyW_>wij .k hNҜe8/ܞDvҷ/ C6 e1h/T2n!0_ޑj"*ܗ*-Tfx+`-7D.w*_Ϫߤ12ޥ9ͶOy 89yv &&q4b"wR%Lm_7U(2  2F̺AvT8ZT`)92ֶ̠w 7r].XPm79A3"~浘 }(<#['M X-̙U3Q^дP Sh9>m|])5JhTuIPY-& s49ΦLŲ yi/c%C}fZzz2_K=cu7Eyֽ1Ms;&,[UQl-o%T؆ ;J`'0pԾ:Jke{TJ;s`5(<] J ߈^wV%<{\ʧG \2|Jo%f(c~&r,W5Oiǘ`M Ûo%h !pA(`1 (CEch,_i5Lc8O*Q`M B 2BW(Uz$0&w˳` 8 k3EH\u1̫W FڎT蟑ƫ76Bf~4kq5ƿmX4jR`)rU&Kd5_hMlalLD&~pr$zj8g%z Me\eFFiӏFopz03]3ǥF %PJi/^s|"4Q.bb.1V1j1rCzЍ5g)}a'I1ݧ[L0 `T,};jf#; *uC*5`ORܑuҥ, 5X(s(1}Kg@QPSL/\Muxj%fbtnKჸktXQaW `)!_ 'e@%#6pfd,'w-"* uIpaMv*rf.9"eB5ǰu! _J+9^&FS̽4>/?J>Y|jjcY(pMosuj˴"<: }BzMO1ǯ2#؏} <z&Sy(VJ踄N%WWĪg!JMs9y#pplЕ¿4dǙm۴\Kc)xU@(F>C\xZjc2AcW-t3aќG.9EW 5 3bi2rܻTeeЗJXBU60"Kݱ`?/7>҈"1sZu{nb wipt{ʑ1Nc*Ѡ/rfE=b^950 ]Y* pq@}e${"QI4cw#~ص~7rfQeoDNefjԫ~!cN.fp_F%W9fs*gʢ@:Mn Ö9>!Zi_AwsQJ3̊Cz0Lquwc$ aԮ '%̹ ֮Q` XLjJ?;1)rzX_2^&&aRu#hwacΌD,%Ĭ-aB;-L Ś#E"d^gmeu"Ըe[M'0uVBb& (Oe|&ԮRW-~7%M,;8`:pJgH:8\ L5u Hu5ƒGJ+vk{RbQK{eCw@^(Ꙧ9D}e&ۇg9ɂ:!G0)f}.{C tq.4@0g zF0f;.C`Qn7:FRʲl ` ΄]Mb--2Hx"NyaUѬq3pwCi~!fsG63E{ť="[~AzNO5D 8*ݟ cKs=<Hi L_ڗ >`Z\ 90Q.C<AN8 4@rcw_lu,>{?M"򹒍x ͬJ2a3̵,Û,S"YmJQ+3SҽN`9ԋ1hz*i2fxf(&ebpT?V X>f/<]j1^#JFGBmn1`.U #k *?Pmx<gH7C ʷj26_#3D_KF8f#cҨ'hdI^TJb1 v&&Z͕ ip5;tPTXqslFi#9jg̨w&& d{N!sewk~_$ Ji#YrPUb;f"/Kc9i z5Z=<55ʢl @|,3*>铄,9L$! ϡkѿ]>5˛Hϣ~YjQ1T?uۊdڹ2^͗bc"S''dNMxA͹ _hP|qEcPlT jϰe!.-;yҭi ]r\Acۜ 5g&Suh>uc3}jvBb`.zi G702gP̪bY/=QLh0 d:KkY|3cI!\j+2,GXvc$!8^J:A^3:B5DrK<2=BPXJC8RрIoG'P eep]y _#9=C߬2F 6i J.g/Emr>:=(ff,tZ,C3ZoE)I@~4@fHĢOX8|DBɾrDe_X9 7! CC}>"TR@ Yߢ\KĪ7d73}bw>YFRS*(=sa}a8lje}XR`7M9/Ҩ_yd..k d֘ W^0,%J :_XY`C/A+Sp34#Bimeqś*醥0yk:53b* ǘ=Jv2GieR$\QS}c(ێ,`)ŸH<@nJV(.sA4u7٣B\oLs*THzWTR\3} Lښh|]hk,4*k>hNg EKzi~Ie@W@/Ó@`gcѴJL ~\lZmm[򩢧91;s;u(-re^]18"WA,oi%·lF+zv\T,J0I>JNJc9%WZ=22*ehZE=AUxQɿi}Y\E!$վT'DycmpVly3( FKt40bS<:* 535 ]~[:8=>2Lec^ԩY|76^)1u/ "WT*.UI% o3 rɛ9f~Rnl6̪-;N{6&-Q>%t59 =SD} =35aAy5*Bh+mX7q/.:S8 n[9oPw+טp)`]_3c7)'9әUܰWŌh!f&G"^L2ZhДMZD:y!:?4Z[{gK~9 v>O%7|K_XGYYLCO}X=e*!1AQaq 0@P?/TR ҿ}B.\ ./B.\r.\HAGqcYKH:8Z- ^ r˗/+.\peƢtr/~\aU@˗t(0Eȸ.C \(HL :?rѹr˗`˗\ yʉ 8?ĹqK?}<=1[3=P@:J333a!/_\r_K.__a6 uG=02/aHtFTRt*S:$r˗*ErTJ+ tW򨒥tN+RJRQb躃^eAX._*T}oYR W&[`UiQCfU|dNL#Ѓܹ}.\}_}/*J\qbqܸ *U[]ZS]@5T*TAper˃.?Q%tu ~@6&B1s_n\Yrˋ/(zBTI_en-q!c ҄_*T\/*TR}n\}n\Yrآ+miHrM%JTQ%u*.qt#lhtCۡab=0ƒK#$u\2չrοJ+/qa O)D8f՟~~%uRu.\} _CsGAp /u]rUꌾ K.\Zҥtj$a0^J`Dfaf#% ;ch[$]#|oM*!P%J/lJJ+e1_ٔʘrԯ}.\r\}*T+ + l|B涞 2#ԓ.5io_edͶYr˗JRq ˗._\QǢ e"G>BQ(*$p`˗.}C*;.\r\}jWa:nΠbi{x* +u/K -(,wF* ވLڮMsX[n\r.\rR'f/6XvkLJ< n㺪eOF A* M\a2X\uV+d4U!"`w2yFXZLPr .CGt%n;]*B `[ekIܴ(䟿撿rӹ0z;T0Q8HTAi,splXUo+L`Bj|ےpӎ^vAUTUr@ ݩxF- ҉6X`JVt*422tr˗_r J*_NB|gh6 fc ANs%$5& 2]FE\`awt$K.`=//]W{rXIPHͰ]/l h4גBlHƱ#߿ig~0)#Q^B}~/1~isX(ϘK? O)׼ǩœ| F":_*W0e9꧜ف!NsSUKB:0 !JoRv00B#< dSxKY/d`y Ц^‹c^/ e88)Ef!PӃ{0I9!npHW .,y贝0rǟ5r` P{ioPw8B4)ōj*k+LvS2riʆT =o3#Vf ;+ODA, B!n=Ņxq9MBϓ=cf%n7-o%[*ϙV Ru}@+E?\rѹUnTJDK1V9F$yQ! ՕbdaR8+V-)9KHHW>yuhw$%5˗._r.\W*\+}vEzd(l0(LZ>_T0A r|m,%Zd:Jk#s{|22%baizIf; R[*r9>0m5V򚔆C~f YuӮ 7 BhrԿ.\rɨof);`S# " X' 1.Xq ]S!qr#?SdHrX^ ̏:5QtuS&Gi/v/99IsXr?/B082Y_Әjg?Ws[O*j]460T/:=e4XX-yJIv쵨<̮r I<1K˿}wLx)P!osAոm;:QPJ)^׵6OD*& $NJ_E4^ ljRҹr$RPG"DzcUn W]JRU[J*7 q!k~JU7.ԫFv)ح@%괡_.;3yY7CՓ.J pS~KCβJݝ[ Bo_*TIRWST\z,<Ϳ;#i}=o+o"L*Y\ޞC-eeuIyW*j& cvsA-koG ZP&>.(14ODVwwwzV>+ @ ү?#P ZDHqV2ŔN!S-_C8xn$_w.\_wy`堨5SP=[]Uhߨ+%Iʺ!^d"-r׹R=Ve}t#TWM} Ǚj'kLPY1P1 #c{j0{B|l2s0e?Tf4ypwFoʵ 65D"_ '%. C@mcǍy~&mR{ 7J/H N_rr%J#/^zmm^4Lny%Q3''=8MAoklJFNפx-e˭+R[Ÿ.Uq 7.\"UmIP`̺֋UAZ&K8! |sr'ĻN**WG~0UxCoAMrR)Q 1ejleϭTZ\ L RԡV( %r7(򐥗FJk&ْ+l;nT "^6Al? QEB7i3NU z Jkk0ч㔙f}X=+E| =Cf⮾kC?5JӰnJM$q 77)RV9Db;g{3jø ^ M!{J&ST8b0|}n\KrkM ᙪg`N%fi)nX\Bh*~An?79^,雔? 2˵`f+ 5Pmu;[26aB[*z2,!MI2p9WBmc,tzWr˗/L[)+ZvsCڊٓ:EXJcjnݔCrw _L܌ؽH)ݧ=@;pU/%QvS98㜭q?wrSO8L]aY#;wѥKiK)c7)c@L"/[1JXP`h[y!EboeZqO{C#\_._/(4rRJ3ʸ"9b5De]l"I))?v2ª#bu+'E`⇵k{ݒ)ǿ`BW9\%V, ݕajk*O*WQ̹D׶:G{D|Lcx| |Z u;yϠQ)2❘V0Z%>' ¥={9 J^rl!,!߸2, JN-`!b`D[QYvcf`2`)q77gSjB6>!ݼk˗/r%]*k#rf7ӈ4$57,`B4#*e)G?,1̺"ިʇSk-tTݨX hEs%%==u<8c;y$Iڢ_O37`|& *u}HTj;?bielU<dHcC,sD$&651ah-`ERJjXh` 2_D}Zu> Ha# ʸƔ6?}ʘG$KKK< r,1 r^2 1â!:^qm̗zi~!ME{=OVHv2Y._}oQ2T66R^}H?~lEk9S }eƕ=O If¢0bXb7sv<.\zy"C5olLD\!6 Kr}o\r+øj&ʣbxZr66.0h#M`킣UNQ,9 7ZEZhri-,QU-_!b_r~LEN% Y8Vˢ\˗/w-eD dv>'iዀ6"C=/SP3Co?ք׾n6'{A*ntf.ܼfg[h4ݧ?n豎!}X4Z!)J7fdg3pc+}ށTN8M{d0n䲂csΧm>"[Sߊ.ϧw r˗._J7/tPiI_ ɦxc d5c0O5Jk$BVe!M[FwׄX1 bCzeӫ@څa pd瑗y.pT>Q9H˓u}ru/jW}.\˗/_[k r ÿc3d0EoUKh0֯f*|b:neоY ʚQMN#ZbJ||b#1!b#IYb8a&£0Uo!J^7zG+"+B1vP/Sʌ_JVRY~S+vy!TiBɸ|[2%Ɏ^ kJ;[C;pTX:ZWbam4ps}53c2g[xh0(bP+g>f~#j3m Y2BT1Wous<L4~Xu *~be˗/\"i&)㝻9_k'e3l-a8/ A._^,&wRHϘ+ z3b@LTz&^G{jig鸎ځM/o+3 ԳRrd4BM :nI++Њ+~(D,)\Gn<:e1w8~f7\2}La_bm5 U8#22OUM=хJ*A.U{\+-ێ5L?.L9mJ0TJw?WՍ]^UM^Xy{6(w," Ev ;QB|`X7 ֘|{JzW|2W.\r ev~ }?|:Vb<8}~*)q Rӽivj $2pTh @}e0w~=Ȥso!SyvCq~? ," v{':#&jr Q}3c6 6'j-MرSJR&tt<^RT>gץ -GDqKƭЪqUqy !pʏ"ݱk{Q5,?f~茗U]߃xbݯ!T U?uL7LI!n$ϯw/u@sɳFpb:(ٜ&v<-!Ji5]ahj|NpqG7īf-gzWXp.FX}TD嵍/\< lwA-R]ц7NX- i>%tB!rls%;.rRJ޵*_|U$WoJ])yM3cOi8BG &)t"J6K* ^pk$%+Px}Pp+ T&IQpς ئ$c+i v0Ơ.܌bW!Zq4y!YrC"b6VHJsv\?BG3v<6b ؀g!|0GXɹ&\  C?8drSl͓fGo$e)cZ5'ҩO M[RRw3ȳ+x7e8EsaqUeFEApJE$Ko }o$z*hsk|gs(J.f!E ὏ `,yC}J"ӤKxNA.=]мɦTӲv34=(+ T1k)L`XR* (+[_c3%UJ][%s+?ځ t(4MɬnMe=̻@a@Ƽt T's>F½ɞ?L.U}FE[%y8T NrDB QH @m$ +xȕZ3@f"3 &11T31&tX#ζ4d 5/C"i UKɖ/0P_{k _o,8y^1nx˲1f,u+VDt5߀)C@<:g`pBVicАѷ]ku._lwe[""mƛȝcpg[}(S`ԯn&N3ɃFgusf#0eqY 0 NnuƬ^Y9 P( lL@N Өy8M<8pJMn 036S4\9u-g CWkU˗._KЮ͙0mCE %ak+>M]1~bs,vCΎ dx=8LZ00ො^o |=تUDϴ^)7V\^Sұm (> bjŽ3 1k'#Y2'FNQP9P'?&zԩ_.=YkіU#s_H&L(szf3ty5`'Qs4$4ld|cU+w F1Qq3ev0KȌn y ZjF>X{ X5EuƠ/wHnͷK7%$U33U|f6cPaM{ƹn)7Vþ+/m`3N tݚs~ N`?*`rU'(Y.Ԥ1#،aYGm) X%,j*v~ێNQ{[W(!| Ǭ Co5W1Q3HsX<]AgT6?K*OOWm!23ow=NB/CR[%L_ aB˖K",Dl4oX}Ec{+NC_ <}#цH22ҊPB EW )X 1b X- XD p3v"adv%j ¹ca-Kޣl;Wg/ F/Y&Wj+8lßlq2N*YN0_7‚lՙ> ͋!&צ,[L R>Pq%I/P'ǤXc0I?셞*/aXA PŶ im>J4PdZŔpOP[,߄Y@+UeeIYbSukQ3`e#Cp4ªddcMU2VKa.Wc Ԧg]7Byxo>=h~6~|j#W%.15Uo_㾗/꿃SWt=_/10)tx ˥e?T ^~FE b ž'.lT<69հVDhoL4nk_-Id`66s^t+ )Z4i 5`[dޠ~Ț[jA1uNTSBhƫs1/&cSMiLpKxqV>+aRp!/?bA6ʬh [1gNDZg?_V5N4jpq;|E=WR F_'/g_1 Vo\Ꮏ&W7J '-UhTAC/"FemyBdMe®3p/Zb?Qn6a@$2Nl8#@9le#.!9pzPXIS +q*z4kjs?8.Rv0.Pa]438gI@8 mfh<4T$݈^/A(/mmyTXmeI{KfnԗUf%aܐ[-N*YK c'x녮gljbqw`wr *C[cpɗ7lӫy X0̉oR^U]2 k8[aOTk./ByBllNDǢ zr힇_ORkǘY,J;MQ*˗6S 4nɥbR0K];*y<b,WQ36 ]=t|v `ÃMxC Ac@J3{UԢStcU 9=gT9S 2I?C2]ciiBhFfnb[bml65rexLULY>8(j.l\1UZDsue ,MkEBƞU_%kbvqQ&j^,y82ZFh"-d N`0;lmr wREE0k`, b8:Z%ĻPklN;c+8e_[wc8ߗe;AsGvW඲_dG¿~12[ǨZudҥ}]+p؏@ 0j_cVgٙc:W}XF6W JSpߎ*[Tp*RKr܊%xnʐ,N3_b| D4 Xp C" \iż FypMd&Yj` q4t%*ʇp_`j9Xv-FQlee~ tlbCXZL)DҳErj-<8\N&9cx`51F\YcWGtn_5ÕL+JB;aB hdI|vJHgjbQDG9ya`pfzFԿt 5Zu,4Ŭ5z׹Ah!Bj)V4h$vn+%L2grI(Ҷcɗ& 9sPagI{@T)(xuZj'8 uݩ{@XBȮh9HX ^E k0,- `J?-R#1FU<{U'h4@ x@̢ͫ>(s {Q= Ѻ qIW`RgRPR эoe4GMQ«k:iݟ0A@!\mB/ٍEaEFy&]}7 fMxaBw/9_-ƗOy mf*gB'Nj#/rղ&e0`*#bz^K Ib}(ow rH y\RІfҲ0m KTFRx‡vfPw}60he Wn IJaE1Klp*6?K@ݷ4'Bt )DNOnvBrPxizI WEoq3_;edx旒3Yj^NVvޛ:Ra34鴅Ijͮ$M[WX{"7堂W;Ɓ`8X@+eBPbOk֪hKuhYWʦX[0X+u)\ؼ*˭AbRႋ78#~ H19r\jk_eheB[4LD9F(KYnuĽC燹Eu`m?ilpt_vT`L_ ewXAXq$ե>X*v=H ,HU5pXRW\]2SD#BXt)صr*3Gyc_/e]z^a_)A>)1MATi&*-ʋdٞ[< ׅѦ0 3^EIP21|\ Mבw2xܸm j~EcwKu8D \&<%.-2z4(F^ ^"ʽT@Rv!!m9~g;n-YQq+ U:ז&ZY#T;1KE3ZOoGیO,C~~;_^o4D'LJ%ƈ@C1EH_^C߃hN܁i}.b_P5DEKϙA8DYMiZ̪ruX@CIZzKds X.Ȳ1峼m1L-n YE(k+P\Ns` g5* whYfa}Rռ9,  o^m|,CTc4B*۫?@ qC^~+pbA1X[ "98z6w u ט[39:{wB<,_>ŦNx8}3F{oL}:φʳ)| .HA/*%)[Q^gWjZ(Ⳙd6^9dEٶx ?8Pq~nYH54n!7Z/,%O J:vZkl]=einRVJ%^V5I, f)I! [  Y.aC"cuF%+ڥUi4NDcsPG{9*YAv9p"` ~3)$L~tB֒kno0缩-Ear =t Ҹgh n g 2:*Urb#IG'r ,b_;JSP"#PET`5ⸯ)2,/cYc/L3h@P _r3z>5b]XXW֢ڣ`g#RF@Xp ԡanԘ!U@b--Ӧ̀A򈣹c0ѷ@p9WBAF!k)"Få۟rK.K$jŀvVJ#] n"3sD*̠74Ns) 蔭SWd$o <Կ vJn{1Er_Ob 2vCcp{V".yb_[`j0ૂ9fOӦ7h+ǔ{Lr dlu˵>l|&<1;Sa2`+54qBn|XmVë;QjsX @LSVDolJ5}a\=x͐x?.ގa.ʍ^fIR-E* gf`uD0B` ."Oz(K֭P#$雄R.+W>e7]-O-*PY)36\(\J:=(sr*"3p."rTw)6`F1r /Oirso=ATډ)wOhP_ aevPN`՛ٍXچÑ S^, i@j ;ucgDV ,3 Qp=Owq`,s$.42? G"vƍRryʖS]$DWQ0ȞA`Bn{z$Xza[Ь), L{q XE/3F2c ^'g_$շL(fjw nf)lD b#~e}U lgdm2W Eh\ybB[VD.KeoK(X ̝)/hb6Xskx_1Uu&4TbXUwtx&\Lr o9 jɄ"zk j2 4AAZr鵹!#'S08mM׉)ŵUǡ!)nO1-n\5(ܟTG>TZ\czTv 0@W1 @0mRvRtdJܼR@F2"54BA6(gLkBiNfRQݼ*0<,805 g>=2R/m/5Ev7;"79n 32 %5Щ3Qƻ ȿ "WյGU QSN-·.{ʞm.WjN'Ԍ,(ڰ`Q%,.@|B O[igOx,(cQs*8!A@!Z\"ɲd vvM(hM#$=Stϑ׷8cZvF#SOLِb!: U|P*VZb֡O+SK/cPx r {/"ٽc30a,KTBf{|Ӵ3Cee\[a0ivQ0PZl3\ҳFc Pg VJ.`Uyq=xu+46\h+ m{]xϰf@>#v㰖)^V.1I퀘 [XٖuZ)`&l{٪9kGA(Uut@]T+y_*\p=fZS*X~p܅m9m* u3oԷ+m!Ig7L_buRXj}Pϸ's7 b ObK#~b-(b\sLD, dҏtfR/;ui%^qb to!PggkXAScNC}ojHb1:fKF qN?.e? qPi|v" &bKq tf"0b]>#V.QpswMcz,@[~b~Q骸U&6 [!+/}azHe2f-K8q1N֪UNSe.,7pDh>z%w497a*Ulmh"sb k75{bP]Q'Ee5wO+uUDwfljE0[w!,чɘY+[؊A}/~]hj?eAgQ<35V' ;o/,lEe. Q6pY2 X 7*S;.+־ɚAՌO.YV\3^<#;(F奱O~Q򧐝vX׳*1v0# M)FhFfDfY~߷NGi'AKq 8Ɛl}V;B&񕯍"D(Ͳ|BEN އ/t$e[CvdCL MYmƎ.TWp =xүew@c%h̨UJ5@Ҧ)pneYJ0P-"aq`7oi,!bZ+q( y9Y̻q8R e8I46eY"[RsHhز4A…o^p^7ܢrg2ݟ\;.ị% l%B`QyG\F[[\% Ơ ^8DUzUl*D5'1EW y;p56b( :7v~(MmSVn`:VB+l"o-;̸ՋL:D, fw~rF#: ߴbΘ%&ILdJq \4m.qv*ٵcDW,3,ᭅFBh@f y7Vr1-qU@!yWn-ET%hY6S2ĭR(Kl.8n4 'l#ԸF.c>cVhj#( 9v>nS(ޮ+ L>B,6:Uj,LS^@fP֕ y,he*5dȇvQbr#SAu0Z{̿ĭLV(6/zeо%r8r4#-%Rsi̳<_/CY2 >ks28{K"\1l^Ɲ&b|i!X\H`j+,B >a* l׻.pJr@-¥Uo-SXR7[d3b5CXams2}43اDF2𜟨KwEƍ/Z1Nd2n'޻NTd|Mx)?fm][~ɗv% Hߒ Ar‡1iмX4X&ʓA~.Z.GI^v|BUɘ_4Kڲ,!A;( (`Sd+GuWtq rZSW5UCMRPXL1m8|@g8̡tlm,]ʪe.4M1}v2rITĭ9~PH߆qdhFܙ7M@yUanEE1lZZ0Pq@uK^W gWA1gx-S%n}Y" "3;'0h[Co-("@}b_mANvNo࿹_wCINvGL슕h?/v|~ S~?a-XҲ+?0TZFfN/E?r1Jⱏ*{ؽ1.U.0s*xw|ᘤ$~I&]C=K0]'ܴ 9}x2i4Xd<&Ƞ6)|d3c .&N{\UԶ6]V aшPA]&ͭZsSg򷃛2*M%^M&u 7bV@/rQMfDQÇV1q8"Emw+_{&$J+;@4gO̲ ˳߁>D/ &Z;GVV^m)|R5H&6=`~ѨnYdzR`M".Eb]R}EBoo`Z&Jx ߙfj5=E" | !Y3&p ι"$g;˪$D4,5M]HJ&X`6,FTE ]+CYV]7Y]a .lͱE 4#1b3 h],tby bpX%%su[f Qnh&Q|D \HoA;`4؇!7AnKa3guBpL(o}K'Wq F \PK$̬qIN},?07)-iܳq5 m2Y (#k|gXR'?#QBo Հ!m}@.˚Arr%-]Y<ȋz&L&{[ÿ2zLc : 3-:4/}iwPch#|Vh|ƹ5rGwmf:w2RL+QqnY4شuD Vv Wj1򺶧kAR+egb*ཏK÷7@4%1M]%T%VX@$l{fvsօ ^W9~V{%S$<"; #6_=΄w}RAbV*bGSR\c4;Lf%DqKswᘀKsMF;BGt뗣 2*qVfh6_lr+$GDwv_'<{)cy9qpV\Uj=ʙlRZA"Φs_;#Hbe&!%> Pԡ!4xҔ”_DTÚnT E]7W%"휆Tv.ͿE€8)e*]{FexĶedTƝFH[Lbv @tTC|Z\pJaAVjZ;cT*)n_!%ў26Θow&f,o `,Z8+ g S78n+$.L/"k}{ Jah*Q~q7|ef+ł1Ʉ2[$c,eAiZhU}$s,G/ TYj"AbweCB̓=ux]Cl&l|4ZrPX45ah  y5`=;9e 4A ئ`yk&XQc C5M0ԳA!o*!0e68GLb` –/(\U˥%b'f bVDI>*)|2C5q!/4=[LH6%i!abEVjU2D~X/UQz>Pܾs6*bes XF ኹJ>lb8鋯A:'"xS!MT%(WB {DAn7oy\GGZ!d}Bh %A*(QG{j:d n -,$f [Bqm)"Lh UP5X[. ~+ 3׮ oaߕhTL,E"c@U]h,נcKpro2t[&y3gzK}F^$mUj1}#wcU1˿1Ífo'VdzIH[tL)ipN9Z*dآiZeQxã'fgyO] jRq To(Xٖ`2)⩑1cx< ٘z0A H򙋖g*C_A,(7!@0@[0I&уTVtNnWN ٘,.'m_*un֠q-%j6f)R ^{K+UF6T/q>6QVeUq3-D%VW[weoUM7ž#f@UZ 5(/$ei+loqi Ւ֮i5J;a3VŸW*[ZUPH8&;$4%a|lq@&oD8@oo/.R.^,?WIBgeЗ< 򦒺NT=2c!ڲ@?XcK xaV]> Lb}SjI4q9LRan}(:Ȍdy1Q0)뙈c7yn*FQ[``mf5{ &hes3=j]RYwjR)4QT*WiVhSl9bʁXj! fkLJ0;wKnT,hyBk>heWK)7=<Wjp*9 eXj7r|aPzv5vq{tS<@*!E2P ^pA6QIGgYvHKxgf!nw+LDVdWzdw ֗=HK0W^ yYww P1߷UKGmoiYNe-=n9sEJxǖ`97'NG EU8ݙƕe C 1J-pDIh%Z x?'a97}O64@tLR|G&theԔ B("d&a.Hee.q~h [0>FV/x -mt@4Z)dEY[fc?2LSASM )6ALU#$sNPZx9qnŸ4#c 4@wt0_JؓU;18#[ SFVXcyNM;*)*̷KGvA "0Kҭ,J"xӏ(jaYŘ7+\ ɔLQa2ih ;TstTD丘 Gf@{Љ?r zVفQ9n_׆v)wڋXaS|۽],q|E0!:RS= Zd/89^+kb uDWl)v8 Af{4(Yyn̰䯍{ {Z6DkRQ@  C^&jD!6q~ _@zH|FZ ՗0Qq{I|"Pz-AC6L-D)4spq`b0^c Kv,(U|EAnp6xށ\.c ^#(1TeLO%bR >JS 0eAH6ʄiWQ4;eTw}xD7 3;'%s~Ҟhܫx};&2}יnnљP+, qe)O0C men6Zl çDRT2|nʎg~ s~`/QUa_{Nߘ3 ADD] 5w `N AV(Ķi5NۻBO'~mt1(\BPM7uF!md ,rlآ^WF'v52PZ܈~'0ZV6P3,K">6[?b/s;vr^QK ZU;xT^LKg,n4/Ҋ(I9 V wC֠*Zp #O5zn@[`)w*K5͙nBK0k9̍5ey.~>b zqZC|Oٖg[3h |3n|Jpnr>ʝ?+*7VPLMb[Qݍ2,e ^ 7:$Di-I:{ڢRS`ʭݞd?]U/ Y8G+}٪Ke ɮPGI"ɭ `4Ɇn8`wlH3% l=V]$0B#N_c/aDĠS4J3z`WeCEwTb9|o1E/bw"v=q7:i.CCheEŰ('sMr֮4"`d{ogԣj7b kR@b/u0(f&ں7ĺ3wQ4y%,4_I[MKS#' K;#;a(:ebJs~WCIxVa]O7o8 Id +/۰'`nMO1I\> 79{Nu7@IPttn,4qW@UZ" v@{>c)u|MȽWI [N"#V>"AKS!vfzK8Cؗɦ[xBRaa5ŹLs`˖!PÝQK'AwCݧs&j)[Y}ٖ2O1jA%:*eKP^}sƎ* + =Rh(}"!>l Zw>e#Er+,Os|^RF A/-=$g,ݡ~1l40Bd)m'Aߚ1J,.kL>E' c~o o0\d{_$טd'\N f@U@{$X 8{'5?hcn5чJ>e/D{6U;fW9XG}[!q13a@,6W3whdy!fQiw*XEx_XfM A`̢\Y,qzc,}{&vndv˫ۂTļbŃ._E˗J2~^b8e203E@EA@.Y'&_]i< qy|a8z#6HRŜc#4/xPJyy7`1t=UCkaϸZpfDpG?A2$h(A5v67 NUocEUy{m%Mȗg0^<%"Ҹķ0UhUe;ZqqF$n}s9J(tV(a9\;I 7p:1vkYFmf:+w)N٥v/V+Lʿ3ATo&>IH&f誶*\sY;k[Yг1"& `Y{pK|EWe;%V4Z*Tۇ1b/"pA AKja q͌.j9U1[BG8#,d- sZLpt`oiU> )&Y9!.,RwRYPXC"FXlxln9۩3Y< je,e+6"v]۸6V>p#q7M|Z& M'i4xѭExq1PѵʉQ6hN&OeӦA"hd0ea1 mҎZk %j=Wt RR? G"b*Km~hW1/e9|sNnOZy UQV8_ʃQ['y$1,A98`rUX~܍@A,AAsYVjߘ7.i\L˩+e"HBhQh*{֫{/Yj*SM!dQzc:0ˌ9`~^ 56e&HVaX*uor;M*, hYi̻*0^a#ܫDފN: 8!x+k Xc*8>S0{\ Zô6.XP. B`sCZ?(ca K0K 1:ZE[.1,^;&:x*2{%; robTohV+g>zCNbmeawU+K0b8|=g*d4=ĨTRG~bj*) oAi0\EK 6"Tþ"l 4- vN>LA*{%ADXPDwao0Y}r0GȏZfw?Mܰ.iw.( S& 9UPo.sٜKo/><$UFx%071>F6KHq Dz\pC厭w% .*%`x8nұ,݌tTBext.rE.1vS UdA z4\k*LA.h{?IGDt)4XnBlĺvg},UܼKn̽^tgLnxJ9^3l"$aȁ LM/h;ʦz+%l5 ۫( ӵ8 z[.1uW8^ZoKswXKLb/ q"D= xD\b^}VWYL+dV̦h /p/,BDwTzo{!՝o苓f#kf࠱>ABl](ɨ' CfjhYi5aB A/1q )E(o2?Ɗ`{>6"Vdlt֮*x:Q`6dіZĿDW3z]#`4q*O䕝:i@Pr4xw-^+\9yA̴Y9&܉t +U_XPhq J dœ[_:Lo|!գ)kO4/2z0?!wO8o [>(jbT7/GcdGEc Md75!v BF<1,!*"3LtJK%NzBu OYG1bH{Kpeo%o a ʼney06&Lp5 hP^cXU{?1qLeÊ#}0[͑XWT<#+%K\SH0_I$UMҷAt#5<߆t >J=%SG#A>Jt*#m/D0O ހ~п ,W_P#H`.%-ۿL _+%7/*dZgYA~N|,H+3ƩO7~na0aWZZӱ>.6w@ TVm }BGDx%)dV4&0 f,4Khulpb\ӱ 3*Q0iUO([FO;NIED BII0_r1%l%o1v"09.!pkj4XRXpѝ|+6prŘn] n^R# U<!#(8Hiem - [N`J.T҉S" ROhk %8 u >)geQx`.q,qZ"i/e0>UqNJ\x @+rհnahG7yB!(D(̳-X'4f9[6;YP̦t35<]*%[ɇ]@nOD6#Nҽ^0yE^oh0`e;&-@yd(tx)1p'`R^E-5{Ga[_-9TեxL+m8ipV\rXqxE.ձwNupܶ9ycu)فR9:ҨMވj1Yj*!u]$\-l /£y\1i撬=Ls>Qiw`fT0dШimiX&e8TUv"( Eȯ#cpN,z0n2ZwYZP*A oga`@ Q/i -@蝪FZDM©_0 :\ΦuM\ E6V8ք GB@l|ah+Ȱ*Q'lE^fop] sYsnarX" CR%8h 䔥neIp3F0?0 hyv!^fji4eNK[x8Gj f%af/?c/ZK mW?K9A]!fvq-{h4(Q @+ZG嗟v\*,fEl^&8q[LP"rlj4 XZ;B"US l>).&. \P{)pfx2>/¬r/xx/F%Q_L|?!Fc|U_ n](N|B#P86Y蛁#agAEA18A)QxJGr.0T3W*̣]땊)*W IV]w"]Y .VS.9yT(հe3[ķc wTQ!|Nڌ\ZKxE :'v"ۊkUE3F\ g'uk,Q{45JᎾp*4J7rCe./5.a–-OlNF#+Ǹ*Ih+4{ 1%zhS<˛--,[9=S1q12˷`Hsc0S\ U}Y>X.Q)(7+5)Jw3eRkAv`s[@8weg\@5 -L@ o /1pV{Vrhde &Z b{.uu(Ec 72ؖdHg&4S-Ūyfd܊hhrܸw !ouZюoU2;`ٕX+rJRZ=Մُh6\@vąMψ:C0qIP"X9 5~.YT#DS_4\ :|8"OEs; Z*ٰ(G6gBPKߐ|{FdP3JI^ OxfeMܮG@oQ?vnR (cH ؐXm@~@Ǡ¶!EL7=VPUwx `/~1ZsL+F^* (t$SDKXwQ!aPM zŗvpj%=!h8%2FfzEiYBzO &bt hUñ#\+*d/+?nPap!N@F.ɖ{ a(%Ǚ B\b&t}gii[lG&6h<D;~.LӔ|dQ,;X4^kQl̉@5sXv0\#c!(mjYJB<76ŵ1U|. ZKffx3HD9) Icc.#H1e) ֡ܬ$]|O!x3r6c.m&88Hܗ+Dj@о&{0EQJiT8E QM/&g[oFO.[(jX$w$ݰ9:XMҸ!n%U-A@ ƒ6@~ |1H06T^ҎKGh ʦx~ݡ(]Z-Vbfod0'ZWlsC*4)8ahP#I%D\m=4 +>VM'ZHUFKwy](BQk - .2ϔT axUvz.YWX9ʨsacnC@385)_#ꇡNB0]Aġ,=&40 `'qolø ~+ R-]Q*OG4)!⢓B`= JwSU}پ,Xbcn\Me7'VL)bōm!>,ߊ_TA99P)Uce\STF=KpA FB,Xk[.qfgҟqݸjj: >b2m& [;z,0a;b D?3xhX7bʗ%p4{;#._15߷.T;oQq"'kaP^HװE#]F]V=t[)l=%e 3qnz/4; Uh JtFĺS[fV*,XF3Z@Rk4!ayr?˨9.ha a Jހb7<_اH ,uQ2y ;֭C\wz!106,F{y!ώP7D*w+bzIrOܵqK}6dDLO %,Sz')a SK2+%Ĥ7 *%5AЇ k EP8hdR[8-5Ƃ#d:dإJ卛{:[s{O":(d+ R]d] %Up7(EGs]#dǁ%7B›Hdp/w_ kjnY N1򂶷xH׶0 ɿy.W^y px{>!֤mmzT.wuM©!r xw^ݒKUT.5V0Ʌ aq w%Fyfj 7mK Kt'3UL.5,ɿ~mbĜMͳNp=1+f5Chbw\LȆ%$RRM#..9R炇?E!Db(;ɛٹ+x~ѱ&f#׉IND40JD[|/ /N V5T>%4k&]YcnR\,2p+QcK2<nW,̡RȬI6ylu$ |DmfK)-МSeA_æi;?iq&Vjterxc]q,YP\K`'Ld"&l~N +9uCaRmލR&oFpTFW١Z`]۟$uen"e~MYu}@{PmB\:B5( SDgR..)soc`"fy)^P!P:f3%`^J1jwHQ@*a _snpl>eCT S+'{.V U(1PXl@2AZ-|9yiW #O %w3k6afjJ;cM0yJ>FlQAPVS-賠86ja@$Z7eKf:?!k * xB cs̽UJ%+d|M,@ 8{scmÈW-65 KufrԸ K"(!^y?FeW@`lf~zYSg[T3f7@nez]C.0@]r,vX*w2 fj+tUC1^x{Ǹb;OB qG,ޣDEzj-`&lwءy ?gAĴ Cp゠#l 7g1{XU0.m7;0U\.RβUU0 [[Fp\"9@QqAlLveL兘-a+X ~O$Kdǖ:n!WhL؈-֏ܻ/\qR͌ÌZ8}I-jƘ`eK07!% /UɚnT",o(p~P-г&.~cJ#"@&_, ̂°ަe FT 9L o2p 2P+d`hg{vo_1-~cO1TOw K,L栂ޕ&D@u.٬ƊU.}ȚV4dU[pJ|ҖU dX¨̧Yȷ-UhDvѳaR0Y%obQT^pGȈ-K(Jdv -` ûw+D^ NϕR!M_IWGRЂ{ M%jNkFR[;G3 PwZ +/aEu>E#dyVf xXov7$%)ڹG)0zj#AjY mxFū D.2#"vp/p6=s(ܮQ悆X`m7XJȇgq`Ӄmka ̶M)RB`[ &-%kTӪ10q6 0z+#+f#A|$o%C?؏TWLXx؎TY6|Ԫa0@HH0Td ZVeArQ|m"4 >me,5; +DB(lR[̯$ P )BCD\`Mwa97b(&3h[tԁeQx(\$b#Bs 095BEуnWS8)|D=tЕPKh]-MXP2f#Rp:V's0vx<&=uiو# mm  s7M16` KdV(`ef<^ҸL[\"]1ޠ|0<^UycG$9?bˈXrbjhx @RsWKޭ6ЃW,pWf|j KU;\Y^a! M(.Xc*86 er+U'F_<]=q\30C Ҍ9.,Bs 2R͌J4 ǵHÃFYPGÌ3^̗`z3n l I4m̃v7Ohdr5* E& 5`_Mie3%hɉp1Z(k@*E'^<+uɉ]pCL[Y%TlDUt ;V']kp0]& \D(:&iu瘿Hmy/%^& ei-V,))> Z?e[4!ݓb)" W{+eӕ5y2,w,W9zK/B{f­|6 F&p+z{P鵗Rl<3{z*Ux+qa|"ysBu@zYD,9^K5K}P-艔K>f躋y/#2Gdqn` ^ )Kaǔ) \WB-90 ys2[Lfc|H*̭@IK95!t'4L2EM!21 kj92 @L̕ͅv0ӄ11|UpQ79En`1,g'yx (K k!0ґ:ROҞ{Ѐ0md=jEdۮ1 B%j Utn]^cmԤL5,‹cl5Z muzZ wK7uX 1mBO+k0q6L7V` sNa=HC:a4BÁCݿeͿ)y-4F@ H9Y,ړ!|  ,ض\j`(Ja}NNQ t,@q]EBPF.ES-ҡFF4{1-GSIjfuQz|3#3j.Fc8z1hGst옩(.UU 3xc v%,&UxC +B6=ܾOd802›KnnAo}XgF͢cϽ/N?1R>-+u^ ȍxUnC`UQvCw;_pT-0|~ 85[r>V7Jm{>(Ÿ%DSfŨ~ Dq$@ ˂pG8eU&6 xcbC6Y%%%0p!mh+G@a-P2-=ڵ+Mvek,H!*gy3ʀ2r% 3^B6(wP= 4)G4TZEH3V<[A@#!@>)vxa'/cmJ(Pa8^c7}" ʯ+9[/P\@@#PV]QmObC)`ܡ'}PŏiQ'6;h4kY%̳1"![18eq`áYi.D>yFg.Z?aIu`}B_yeVA 7UKA9ؠΎuN.a! *iAM-*F LiĬvܵ~vm>NH`KeSFY3B*cN2F#sw˖䬹AeYZ R\KX{׬v$.k ʿ3Ef֦'W=0T_Kck^n-ZB0\RJ ReA%u5P ɫ ߃ kɹ_l m p,edgS{m{s ,hzki͡y-7͑ m+=)xSp{gG`XJcU9fhbL@4. ng\fgţl~&,&6E-n,&B LejQ Fy4TZ6_RR`t^5 4i棣}9鰘"O,f?LYA˜y]) ]+0gQBI FqU/]ыx0zL)%P3Ig̼.) 12W%\j~JٚK mZal٧a @jWC/71 L+eU,<L5dR,69ơ+Zn? rȌ.+)2CAR#;25< @;=1O67:Y"~5I%]8UċBk0ǥv&Wj8PXJ,68HY;U~SGJ~dꩤyEL֒T/yd ŰǴh18\Rf$Ӝ~V 4l֌*6é<}"u(c xavf ۹:%% %؏sf1#1!oZ_8\`T֮Xg&lC`ULP9MLFt}lk\@+Mʈ1x(K3b-&Ҕj6@`!QV.Xl4>Ax啇6URLD6r7KkԲM/u TDA/k6@Q5QY a*6Xg +nԠ">pK3Q.8qMl.*/G0O`jyߋF[W.<\Tqh8MHY#7]ŀi4"a;qG Nw;%F6FXSWuf1_ht+X9IzJNjS`H+meyb7 1^I_ Zns/ ;-2 Jlo.30.w=_5R(Wph袹L:/1_k6h-# &4@s?PF0R(s'"8NLq5TSHMʱV ᴄ Zv%8jxQ*E,(9EjhةH m7x'.L@lpZ`\Qa,Y,65k6g^ˤe`K>'.Vrfq LJ М6isV*}0Q;Eo)W̒jзm wJ-0%w?A tj7S$q^if.LF  Ma!0ۺOИtèXZ$)g v}m? *>e\Ee>3V>PU߂)v0]ַ0#L9WٌTBq[m_|{㢚Jȵ T[e 8VܬjR^ TF4 hdlcdp b[j^^%L7pzfQ>udAc~tPc3TA¯n{Q2q~ 9VK:T@aL/pL0,L:eo!* Jh.jR󽌤n@>%%%TA.23<Ŏ Vn?fHm1dSLüe^Ⱥ+p2(cظ4myaaZh^:CI䀛unr4$6֌;FXuBzc?3FǵyHr=6?*=Vd'## =1 JeB[oJ(Pji&ܥ[dFTyDP!:S:[Hp@t!?drߕJnʌÍ1=Qh'Q>VXj":ˇTXd@HfX Q7+!~:'J1Fd̞` )hӥ%* )fO  ,%j\-~Fzad3/KYEky'.˦4%\/.<U*4)enlX..>hip&Y@ɔ+TUƌqTńܭm xauyn8ͽ՚fƽRiO7cN <N\O*R\W剪6S^;p+G33FEb{YXU|ehG<" v(T¥1J]!w"81FjUN#-;́?U 2Ytw&#)` $I.@4\JhʔpVXN&Ռ!~ C MC}+
Linux 4gvps.4gvps.com 3.10.0-1127.18.2.vz7.163.46 #1 SMP Fri Nov 20 21:47:55 MSK 2020 x86_64
  SOFT : Apache PHP : 7.4.33
/proc/self/root/lib/python2.7/site-packages/yum/
38.135.39.45

 
[ NAME ] [ SIZE ] [ PERM ] [ DATE ] [ ACT ]
+FILE +DIR
Errors.py 4.265 KB -rwxr-xr-x 2020-10-01 17:03 R E G D
Errors.pyc 9.084 KB -rw-r--r-- 2020-10-01 17:03 R E G D
__init__.py 304.096 KB -rwxr-xr-x 2020-10-01 17:03 R E G D
__init__.pyc 199.779 KB -rw-r--r-- 2020-10-01 17:03 R E G D
callbacks.py 5.64 KB -rwxr-xr-x 2020-10-01 17:03 R E G D
callbacks.pyc 6.232 KB -rw-r--r-- 2020-10-01 17:03 R E G D
comps.py 31.586 KB -rwxr-xr-x 2020-10-01 17:03 R E G D
comps.pyc 26.872 KB -rw-r--r-- 2020-10-01 17:03 R E G D
config.py 49.886 KB -rwxr-xr-x 2020-10-01 17:03 R E G D
config.pyc 48.018 KB -rw-r--r-- 2020-10-01 17:03 R E G D
constants.py 4.516 KB -rwxr-xr-x 2020-10-01 17:03 R E G D
constants.pyc 3.42 KB -rw-r--r-- 2020-10-01 17:03 R E G D
depsolve.py 74.046 KB -rwxr-xr-x 2020-10-01 17:03 R E G D
depsolve.pyc 46.906 KB -rw-r--r-- 2020-10-01 17:03 R E G D
drpm.py 12.851 KB -rwxr-xr-x 2020-10-01 17:03 R E G D
drpm.pyc 10.834 KB -rw-r--r-- 2020-10-01 17:03 R E G D
failover.py 5.005 KB -rwxr-xr-x 2020-10-01 17:03 R E G D
failover.pyc 5.236 KB -rw-r--r-- 2020-10-01 17:03 R E G D
fssnapshots.py 10.159 KB -rwxr-xr-x 2020-10-01 17:03 R E G D
fssnapshots.pyc 9.752 KB -rw-r--r-- 2020-10-01 17:03 R E G D
history.py 61.125 KB -rwxr-xr-x 2020-10-01 17:03 R E G D
history.pyc 53.306 KB -rw-r--r-- 2020-10-01 17:03 R E G D
i18n.py 20.437 KB -rwxr-xr-x 2020-10-01 17:03 R E G D
i18n.pyc 16.05 KB -rw-r--r-- 2020-10-01 17:03 R E G D
igroups.py 9.313 KB -rwxr-xr-x 2020-10-01 17:03 R E G D
igroups.pyc 10.218 KB -rw-r--r-- 2020-10-01 17:03 R E G D
logginglevels.py 7.904 KB -rwxr-xr-x 2020-10-01 17:03 R E G D
logginglevels.pyc 6.511 KB -rw-r--r-- 2020-10-01 17:03 R E G D
mdparser.py 6.258 KB -rwxr-xr-x 2020-10-01 17:03 R E G D
mdparser.pyc 7.58 KB -rw-r--r-- 2020-10-01 17:03 R E G D
metalink.py 9.188 KB -rwxr-xr-x 2020-10-01 17:03 R E G D
metalink.pyc 8.836 KB -rw-r--r-- 2020-10-01 17:03 R E G D
misc.py 39.567 KB -rwxr-xr-x 2020-10-01 17:03 R E G D
misc.pyc 39.58 KB -rw-r--r-- 2020-10-01 17:03 R E G D
packageSack.py 40.786 KB -rwxr-xr-x 2020-10-01 17:03 R E G D
packageSack.pyc 41.88 KB -rw-r--r-- 2020-10-01 17:03 R E G D
packages.py 84.104 KB -rwxr-xr-x 2020-10-01 17:03 R E G D
packages.pyc 84.514 KB -rw-r--r-- 2020-10-01 17:03 R E G D
parser.py 7.973 KB -rwxr-xr-x 2020-10-01 17:03 R E G D
parser.pyc 6.499 KB -rw-r--r-- 2020-10-01 17:03 R E G D
pgpmsg.py 53.5 KB -rwxr-xr-x 2020-10-01 17:03 R E G D
pgpmsg.pyc 38.268 KB -rw-r--r-- 2020-10-01 17:03 R E G D
pkgtag_db.py 4.864 KB -rwxr-xr-x 2020-10-01 17:03 R E G D
pkgtag_db.pyc 5.061 KB -rw-r--r-- 2020-10-01 17:03 R E G D
plugins.py 28.1 KB -rwxr-xr-x 2020-10-01 17:03 R E G D
plugins.pyc 29.097 KB -rw-r--r-- 2020-10-01 17:03 R E G D
repoMDObject.py 11.231 KB -rwxr-xr-x 2020-10-01 17:03 R E G D
repoMDObject.pyc 9.17 KB -rw-r--r-- 2020-10-01 17:03 R E G D
repos.py 16.528 KB -rwxr-xr-x 2020-10-01 17:03 R E G D
repos.pyc 17.383 KB -rw-r--r-- 2020-10-01 17:03 R E G D
rpmsack.py 70.255 KB -rwxr-xr-x 2020-10-01 17:03 R E G D
rpmsack.pyc 58.355 KB -rw-r--r-- 2020-10-01 17:03 R E G D
rpmtrans.py 24.845 KB -rwxr-xr-x 2020-10-01 17:03 R E G D
rpmtrans.pyc 22.544 KB -rw-r--r-- 2020-10-01 17:03 R E G D
sqlitesack.py 69.763 KB -rwxr-xr-x 2020-10-01 17:03 R E G D
sqlitesack.pyc 53.751 KB -rw-r--r-- 2020-10-01 17:03 R E G D
sqlutils.py 6.271 KB -rwxr-xr-x 2020-10-01 17:03 R E G D
sqlutils.pyc 5.602 KB -rw-r--r-- 2020-10-01 17:03 R E G D
transactioninfo.py 33.784 KB -rwxr-xr-x 2020-10-01 17:03 R E G D
transactioninfo.pyc 30.293 KB -rw-r--r-- 2020-10-01 17:03 R E G D
update_md.py 25.9 KB -rwxr-xr-x 2020-10-01 17:03 R E G D
update_md.pyc 21.722 KB -rw-r--r-- 2020-10-01 17:03 R E G D
updateinfo.py 18.29 KB -rwxr-xr-x 2020-10-01 17:03 R E G D
updateinfo.pyc 16.36 KB -rw-r--r-- 2020-10-01 17:03 R E G D
yumRepo.py 83.852 KB -rwxr-xr-x 2020-10-01 17:03 R E G D
yumRepo.pyc 64.827 KB -rw-r--r-- 2020-10-01 17:03 R E G D
REQUEST EXIT
#! /usr/bin/python -tt # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Library General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # Copyright 2005 Duke University # Copyright 2007 Red Hat import os import re import time import types import urlparse urlparse.uses_fragment.append("media") import urllib import Errors from urlgrabber.grabber import URLGrabber from urlgrabber.grabber import default_grabber from urlgrabber.progress import format_number import urlgrabber.mirror from urlgrabber.grabber import URLGrabError import repoMDObject import packageSack from repos import Repository import parser import sqlitecachec import sqlitesack from yum import config from yum import misc from yum import comps from yum import _ from constants import * import metalink import logging import logginglevels import warnings import glob import shutil import stat import errno import tempfile # This is unused now, probably nothing uses it but it was global/public. skip_old_DBMD_check = False try: import xattr if not hasattr(xattr, 'get') or not hasattr(xattr, 'set'): xattr = None # This is a "newer" API. except ImportError: xattr = None # The problem we are trying to solve here is that: # # 1. We rarely want to be downloading MD/pkgs/etc. # 2. We want to check those files are valid (match checksums) when we do # download them. # 3. We _really_ don't want to checksum all the files every time we # run (100s of MBs). # 4. We can continue to download files from bad mirrors, or retry files due to # C-c etc. # # ...we used to solve this by just checking the file size, and assuming the # files had been downloaded and checksumed as correct if that matched. But that # was error prone on bad mirrors, so now we store the checksum in an # xattr ... this does mean that if you can't store xattrs (Eg. NFS) you will # rechecksum everything constantly. def _xattr_get_chksum(filename, chktype): if not xattr: return None try: ret = xattr.get(filename, 'user.yum.checksum.' + chktype) except: # Documented to be "EnvironmentError", but make sure return None return ret def _xattr_set_chksum(filename, chktype, chksum): if not xattr: return None try: xattr.set(filename, 'user.yum.checksum.' + chktype, chksum) except: return False # Data too long. = IOError ... ignore everything. return True warnings.simplefilter("ignore", Errors.YumFutureDeprecationWarning) logger = logging.getLogger("yum.Repos") verbose_logger = logging.getLogger("yum.verbose.Repos") class YumPackageSack(packageSack.PackageSack): """imports/handles package objects from an mdcache dict object""" def __init__(self, packageClass): packageSack.PackageSack.__init__(self) self.pc = packageClass self.added = {} def __del__(self): try: self.close() except Errors.RepoError, e: verbose_logger.debug("Exception %s %s in %s ignored" % (repr(e), str(e), self.__del__)) def close(self): self.added = {} def addDict(self, repo, datatype, dataobj, callback=None): if repo in self.added: if datatype in self.added[repo]: return total = len(dataobj) if datatype == 'metadata': current = 0 for pkgid in dataobj: current += 1 if callback: callback.progressbar(current, total, repo) pkgdict = dataobj[pkgid] po = self.pc(repo, pkgdict) po.id = pkgid self._addToDictAsList(self.pkgsByID, pkgid, po) self.addPackage(po) if repo not in self.added: self.added[repo] = [] self.added[repo].append('metadata') # indexes will need to be rebuilt self.indexesBuilt = 0 elif datatype in ['filelists', 'otherdata']: if repo in self.added: if 'metadata' not in self.added[repo]: raise Errors.RepoError, '%s md for %s imported before primary' \ % (datatype, repo.ui_id) current = 0 for pkgid in dataobj: current += 1 if callback: callback.progressbar(current, total, repo) pkgdict = dataobj[pkgid] if pkgid in self.pkgsByID: for po in self.pkgsByID[pkgid]: po.importFromDict(pkgdict) self.added[repo].append(datatype) # indexes will need to be rebuilt self.indexesBuilt = 0 else: # umm, wtf? pass def _retrieve_async(self, repo, data): """ Just schedule the metadata downloads """ for item in data: if item in self.added.get(repo, []): continue if item == 'metadata': mydbtype = 'primary_db' elif item == 'filelists': mydbtype = 'filelists_db' elif item == 'otherdata': mydbtype = 'other_db' else: continue if self._check_db_version(repo, mydbtype): if not self._check_uncompressed_db_gen(repo, mydbtype): # NOTE: No failfunc. repo._retrieveMD(mydbtype, async=True, failfunc=None) def populate(self, repo, mdtype='metadata', callback=None, cacheonly=0): if mdtype == 'all': data = ['metadata', 'filelists', 'otherdata'] else: data = [ mdtype ] if not hasattr(repo, 'cacheHandler'): repo.cacheHandler = sqlitecachec.RepodataParserSqlite( storedir=repo.cachedir, repoid=repo.id, callback=callback, ) for item in data: if repo in self.added: if item in self.added[repo]: continue if item == 'metadata': mydbtype = 'primary_db' mymdtype = 'primary' repo_get_function = repo.getPrimaryXML repo_cache_function = repo.cacheHandler.getPrimary elif item == 'filelists': mydbtype = 'filelists_db' mymdtype = 'filelists' repo_get_function = repo.getFileListsXML repo_cache_function = repo.cacheHandler.getFilelists elif item == 'otherdata': mydbtype = 'other_db' mymdtype = 'other' repo_get_function = repo.getOtherXML repo_cache_function = repo.cacheHandler.getOtherdata else: continue if self._check_db_version(repo, mydbtype): # Use gen decompression on DB files. Keeps exactly what we # downloaded in the download dir. # Backwards compat. ... try the old uncompressed version first. db_un_fn = self._check_uncompressed_db(repo, mydbtype) if not db_un_fn: db_un_fn = self._check_uncompressed_db_gen(repo, mydbtype) if not db_un_fn: db_fn = repo._retrieveMD(mydbtype) if db_fn: # unlink the decompressed file, we know it's not valid misc.unlink_f(repo.cachedir +'/gen/%s.sqlite' % mydbtype) db_un_fn = self._check_uncompressed_db_gen(repo, mydbtype) if not db_un_fn: # Shouldn't happen? raise Errors.RepoError, '%s: Check uncompressed DB failed' % repo dobj = repo.cacheHandler.open_database(db_un_fn) else: repo._xml2sqlite_local = True # Download... xml = repo_get_function() # Use generated dir. and handle compression types metadata # parser doesn't understand. gen = mymdtype + '.xml' ret = misc.repo_gen_decompress(xml, gen, cached=repo.cache) if not ret: raise Errors.RepoError, '%s: Decompress DB failed' % repo xml = ret # Convert XML => .sqlite xmldata = repo.repoXML.getData(mymdtype) (ctype, csum) = xmldata.checksum dobj = repo_cache_function(xml, csum) if not cacheonly: self.addDict(repo, item, dobj, callback) del dobj # get rid of all this stuff we don't need now del repo.cacheHandler def _check_uncompressed_db_gen(self, repo, mdtype): """return file name of db in gen/ dir if good, None if not""" mydbdata = repo.repoXML.getData(mdtype) (r_base, remote) = mydbdata.location fname = os.path.basename(remote) compressed_fn = repo.cachedir + '/' + fname db_un_fn = mdtype + '.sqlite' if not repo._checkMD(compressed_fn, mdtype, data=mydbdata, check_can_fail=True): return None ret = misc.repo_gen_decompress(compressed_fn, db_un_fn, cached=repo.cache) if ret: return self._check_uncompressed_db_fn(repo, mdtype, ret) return None def _check_uncompressed_db(self, repo, mdtype): """return file name of uncompressed db is good, None if not""" mydbdata = repo.repoXML.getData(mdtype) (r_base, remote) = mydbdata.location fname = os.path.basename(remote) compressed_fn = repo.cachedir + '/' + fname db_un_fn = misc.decompress(compressed_fn, fn_only=True) return self._check_uncompressed_db_fn(repo, mdtype, db_un_fn) def _check_uncompressed_db_fn(self, repo, mdtype, db_un_fn): result = None if os.path.exists(db_un_fn): try: repo.checkMD(db_un_fn, mdtype, openchecksum=True) except URLGrabError: if not repo.cache: misc.unlink_f(db_un_fn) else: result = db_un_fn return result def _check_db_version(self, repo, mdtype): return repo._check_db_version(mdtype) class YumRepository(Repository, config.RepoConf): """ This is an actual repository object Configuration attributes are pulled in from config.RepoConf. """ def __init__(self, repoid): config.RepoConf.__init__(self) Repository.__init__(self, repoid) self.repofile = None self.mirrorurls = [] self._urls = [] self.enablegroups = 0 self.groupsfilename = 'yumgroups.xml' # something some freaks might # eventually want self.repoMDFile = 'repodata/repomd.xml' self._repoXML = None self._oldRepoMDData = {} self.cache = 0 self._retry_no_cache = False self.mirrorlistparsed = 0 self.yumvar = {} # empty dict of yumvariables for $string replacement self._proxy_dict = {} self.metadata_cookie_fn = 'cachecookie' self._metadataCurrent = None self._metalink = None self.groups_added = False self.http_headers = {} self.repo_config_age = 0 # if we're a repo not from a file then the # config is very, very old # throw in some stubs for things that will be set by the config class self.basecachedir = "" self.base_persistdir = "" self.cost = 1000 self.copy_local = 0 # holder for stuff we've grabbed self.retrieved = { 'primary':0, 'filelists':0, 'other':0, 'group':0, 'updateinfo':0, 'prestodelta':0} self._preloaded_repomd = False # callbacks self.callback = None # for the grabber self.multi_callback = None self.failure_obj = None self.mirror_failure_obj = None self.interrupt_callback = None self._callbacks_changed = False # callback function for handling media self.mediafunc = None # callbacks for gpg key importing and confirmation self.gpg_import_func = None self.gpgca_import_func = None self.confirm_func = None # The reason we want to turn this off are things like repoids # called "tmp" in repoquery --repofrompath and/or new1/old1 in repodiff. self.timestamp_check = True self._sack = None self._grabfunc = None self._grab = None self._async = False def __cmp__(self, other): """ Sort yum repos. by cost, and then by alphanumeric on their id. """ if other is None: return 1 if hasattr(other, 'cost'): ocost = other.cost else: ocost = 1000 ret = cmp(self.cost, ocost) if ret: return ret return cmp(self.id, other.id) def _getSack(self): # FIXME: Note that having the repo hold the sack, which holds "repos" # is not only confusing but creates a circular dep. # Atm. we don't leak memory because RepoStorage.close() is called, # which calls repo.close() which calls sack.close() which removes the # repos from the sack ... thus. breaking the cycle. if self._sack is None: self._sack = sqlitesack.YumSqlitePackageSack( sqlitesack.YumAvailablePackageSqlite) return self._sack sack = property(_getSack) def _ui_id(self): """ Show self.id, but include any $releasever/$basearch/etc. data. """ if hasattr(self, '__cached_ui_id'): return getattr(self, '__cached_ui_id') val = config._readRawRepoFile(self) if not val: val = '' else: ini, section_id = val ini = ini[section_id] if 'metalink' in ini: val = ini['metalink'] elif 'mirrorlist' in ini: val = ini['mirrorlist'] elif 'baseurl' in ini: val = ini['baseurl'] else: val = '' ret = self.id for var in self.ui_repoid_vars: if '$'+var in val: ret += '/' ret += str(self.yumvar[var]) setattr(self, '__cached_ui_id', ret) return ret ui_id = property(_ui_id) def close(self): if self._sack is not None: self.sack.close() Repository.close(self) def _resetSack(self): self._sack = None def __getProxyDict(self): self.doProxyDict() if self._proxy_dict: return self._proxy_dict return None # consistent access to how proxy information should look (and ensuring # that it's actually determined for the repo) proxy_dict = property(__getProxyDict) def getPackageSack(self): """Returns the instance of this repository's package sack.""" return self.sack def ready(self): """Returns true if this repository is setup and ready for use.""" if hasattr(self, 'metadata_cookie'): return self.repoXML is not None return False def getGroupLocation(self): """Returns the location of the group.""" if 'group_gz' in self.repoXML.fileTypes(): thisdata = self.repoXML.getData('group_gz') else: thisdata = self.repoXML.getData('group') return thisdata.location def __str__(self): # Note: You might expect this to be .ui_id, except people got used to # the fact that str(repo) == repo.id and used the former instead of # the later when they wanted just the .id. So we have to live with it # and use .ui_id explicitly. return self.id def _checksum(self, sumtype, file, CHUNK=2**16, checksum_can_fail=False, datasize=None): """takes filename, hand back Checksum of it sumtype = md5 or sha filename = /path/to/file CHUNK=65536 by default""" try: return misc.checksum(sumtype, file, CHUNK, datasize) except (Errors.MiscError, EnvironmentError), e: if checksum_can_fail: return None msg = 'Error opening file for checksum: %s' % e if isinstance(e, Errors.FIPSNonCompliantError): msg = str(e) raise Errors.RepoError(msg) def dump(self): output = '[%s]\n' % self.id # we exclude all vars which start with _ or are in this list: excluded_vars = ('mediafunc', 'sack', 'metalink_data', 'grab', 'grabfunc', 'repoXML', 'cfg', 'retrieved', 'mirrorlistparsed', 'gpg_import_func', 'gpgca_import_func', 'failure_obj', 'callback', 'confirm_func', 'groups_added', 'interrupt_callback', 'id', 'mirror_failure_obj', 'repo_config_age', 'groupsfilename', 'copy_local', 'basecachedir', 'http_headers', 'metadata_cookie', 'metadata_cookie_fn', 'quick_enable_disable', 'repoMDFile', 'timestamp_check', 'urls', 'mirrorurls', 'yumvar', 'repofile', 'multi_callback') for attr in dir(self): if attr.startswith('_'): continue if attr in excluded_vars: continue if isinstance(getattr(self, attr), types.MethodType): continue res = getattr(self, attr) if not res and type(res) not in (type(False), type(0)): res = '' if type(res) == types.ListType: res = ',\n '.join(res) output = output + '%s = %s\n' % (attr, res) return output def enablePersistent(self): """Persistently enables this repository.""" self.enable() try: config.writeRawRepoFile(self,only=['enabled']) except IOError, e: if e.errno == errno.EACCES: logger.warning(e) else: raise IOError, str(e) def disablePersistent(self): """Persistently disables this repository.""" self.disable() try: config.writeRawRepoFile(self,only=['enabled']) except IOError, e: if e.errno == errno.EACCES: logger.warning(e) else: raise IOError, str(e) def check(self): """self-check the repo information - if we don't have enough to move on then raise a repo error""" if len(self._urls) < 1 and not self.mediaid: raise Errors.RepoError, \ 'Cannot find a valid baseurl for repo: %s' % self.ui_id def doProxyDict(self): if self._proxy_dict: return self._proxy_dict = {} # zap it proxy_string = None empty = (None, '_none_', '') if self.proxy in empty: # got 'proxy=_none_' proxy_string = '' # this disables default proxies elif self.proxy: proxy_string = '%s' % self.proxy if self.proxy_username not in empty: auth = urllib.quote(self.proxy_username) if self.proxy_password not in empty: auth += ':' + urllib.quote(self.proxy_password) proto, rest = re.match('(\w+://)(.+)', proxy_string).groups() proxy_string = '%s%s@%s' % (proto, auth, rest) if proxy_string is not None: self._proxy_dict['http'] = proxy_string self._proxy_dict['https'] = proxy_string self._proxy_dict['ftp'] = proxy_string def __headersListFromDict(self, cache=True): """Convert our dict of headers to a list of 2-tuples for urlgrabber.""" headers = [] for key in self.http_headers: headers.append((key, self.http_headers[key])) if not (cache or 'Pragma' in self.http_headers): headers.append(('Pragma', 'no-cache')) return headers def setupGrab(self): warnings.warn('setupGrab() will go away in a future version of Yum.\n', Errors.YumFutureDeprecationWarning, stacklevel=2) self._setupGrab() def _setupGrab(self): """sets up the grabber functions with the already stocked in urls for the mirror groups""" if self.failovermethod == 'roundrobin': mgclass = urlgrabber.mirror.MGRandomOrder else: mgclass = urlgrabber.mirror.MirrorGroup ugopts = self._default_grabopts() self._grabfunc = URLGrabber(progress_obj=self.callback, multi_progress_obj=self.multi_callback, failure_callback=self.failure_obj, interrupt_callback=self.interrupt_callback, copy_local=self.copy_local, reget='simple', **ugopts) def add_mc(url): host = urlparse.urlsplit(url).netloc.split('@')[-1] mc = self.metalink_data._host2mc.get(host) if mc: url = { 'mirror': misc.to_utf8(url), 'kwargs': { 'max_connections': mc.max_connections, 'preference': mc.preference, 'private': mc.private, }, } return url urls = self.urls if self.metalink: urls = map(add_mc, urls) def mirror_failure(obj): action = {} # timeout, refused connect, and HTTP 503 may retry e = obj.exception if e.errno == 12 or \ e.errno == 14 and getattr(e, 'code', 0) in (7, 503): tries = getattr(obj, 'tries', self.retries) if tries <= self.retries - len(self.urls): # don't remove this mirror yet action['remove'] = False elif e.errno == -3: # unsupported checksum type, fail now action['fail'] = True # No known user of this callback, but just in case... cb = self.mirror_failure_obj if cb: fun, arg, karg = callable(cb) and (cb, (), {}) or cb action.update(fun(obj, *arg, **karg)) return action self._grab = mgclass(self._grabfunc, urls, failure_callback=mirror_failure) def _default_grabopts(self, cache=True): opts = { 'keepalive': self.keepalive, 'bandwidth': self.bandwidth, 'retry': self.retries, 'throttle': self.throttle, 'timeout': self.timeout, 'minrate': self.minrate, 'ip_resolve': self.ip_resolve, 'http_headers': tuple(self.__headersListFromDict(cache=cache)), 'ssl_verify_peer': self.sslverify, 'ssl_verify_host': self.sslverify, 'ssl_ca_cert': self.sslcacert, 'ssl_cert': self.sslclientcert, 'ssl_key': self.sslclientkey, 'user_agent': default_grabber.opts.user_agent, 'username': self.username, 'password': self.password, 'ftp_disable_epsv': self.ftp_disable_epsv, } if self.proxy == 'libproxy': opts['libproxy'] = True else: opts['proxies'] = self.proxy_dict return opts def _getgrabfunc(self): if not self._grabfunc or self._callbacks_changed: self._setupGrab() self._callbacks_changed = False return self._grabfunc def _getgrab(self): if not self._grab or self._callbacks_changed: self._setupGrab() self._callbacks_changed = False return self._grab grabfunc = property(lambda self: self._getgrabfunc()) grab = property(lambda self: self._getgrab()) def _dirSetupMkdir_p(self, dpath): """make the necessary directory path, if possible, raise on failure""" if os.path.exists(dpath) and os.path.isdir(dpath): return try: os.makedirs(dpath, mode=0755) except OSError, e: msg = "%s: %s %s: %s" % ("Error making cache directory", dpath, "error was", e) raise Errors.RepoError, msg def dirSetup(self): """make the necessary dirs, if possible, raise on failure""" cachedir = os.path.join(self.basecachedir, self.id) persistdir = os.path.join(self.base_persistdir, self.id) pkgdir = os.path.join(cachedir, 'packages') hdrdir = os.path.join(cachedir, 'headers') self.setAttribute('_dir_setup_cachedir', cachedir) self.setAttribute('_dir_setup_pkgdir', pkgdir) self.setAttribute('_dir_setup_hdrdir', hdrdir) self.setAttribute('_dir_setup_persistdir', persistdir) ext='' if os.geteuid() != 0: ext = '-ro' self.setAttribute('_dir_setup_gpgdir', persistdir + '/gpgdir' + ext) self.setAttribute('_dir_setup_gpgcadir', persistdir + '/gpgcadir' + ext) cookie = self.cachedir + '/' + self.metadata_cookie_fn self.setAttribute('_dir_setup_metadata_cookie', cookie) for dir in [self.cachedir, self.cachedir + '/gen', self.pkgdir]: self._dirSetupMkdir_p(dir) # persistdir is really root-only but try the make anyway and just # catch the exception for dir in [self.persistdir]: try: self._dirSetupMkdir_p(dir) except Errors.RepoError, e: pass # if we're using a cachedir that's not the system one, copy over these # basic items from the system one if self._preload_md_from_system_cache('repomd.xml'): self._preloaded_repomd = True self._preload_md_from_system_cache('cachecookie') self._preload_md_from_system_cache('mirrorlist.txt') self._preload_md_from_system_cache('metalink.xml') def _dirGetAttr(self, attr): """ Make the directory attributes call .dirSetup() if needed. """ attr = '_dir_setup_' + attr if not hasattr(self, attr): self.dirSetup() return getattr(self, attr) def _dirSetAttr(self, attr, val): """ Make the directory attributes call .dirSetup() if needed. """ attr = '_dir_setup_' + attr if not hasattr(self, attr): self.dirSetup() if attr == '_dir_setup_pkgdir': if not hasattr(self, '_old_pkgdirs'): self._old_pkgdirs = [] self._old_pkgdirs.append(getattr(self, attr)) ret = setattr(self, attr, val) if attr in ('_dir_setup_pkgdir', ): self._dirSetupMkdir_p(val) return ret cachedir = property(lambda self: self._dirGetAttr('cachedir')) persistdir = property(lambda self: self._dirGetAttr('persistdir')) pkgdir = property(lambda self: self._dirGetAttr('pkgdir'), lambda self, x: self._dirSetAttr('pkgdir', x)) hdrdir = property(lambda self: self._dirGetAttr('hdrdir'), lambda self, x: self._dirSetAttr('hdrdir', x)) gpgdir = property(lambda self: self._dirGetAttr('gpgdir'), lambda self, x: self._dirSetAttr('gpgdir', x)) gpgcadir = property(lambda self: self._dirGetAttr('gpgcadir'), lambda self, x: self._dirSetAttr('gpgcadir', x)) metadata_cookie = property(lambda self: self._dirGetAttr('metadata_cookie')) def baseurlSetup(self): warnings.warn('baseurlSetup() will go away in a future version of Yum.\n', Errors.YumFutureDeprecationWarning, stacklevel=2) self._baseurlSetup() def _hack_mirrorlist_for_anaconda(self): # Anaconda doesn't like having mirrorlist and metalink, so we allow # mirrorlist to act like metalink. Except we'd really like to know which # we have without parsing it ... and want to store it in the right # place etc. # So here is #1 hack: see if the metalin kis unset and the mirrorlist # URL contains the string "metalink", if it does we copy it over. if self.metalink: return if not self.mirrorlist: return if self.mirrorlist.find("metalink") == -1: return self.metalink = self.mirrorlist def _baseurlSetup(self): """go through the baseurls and mirrorlists and populate self.urls with valid ones, run self.check() at the end to make sure it worked""" self.baseurl = self._replace_and_check_url(self.baseurl) # FIXME: We put all the mirrors in .baseurl as well as # .urls for backward compat. (see bottom of func). So we'll save this # out for repolist -v ... or anything else wants to know the baseurl self._orig_baseurl = self.baseurl mirrorurls = [] self._hack_mirrorlist_for_anaconda() if self.metalink and not self.mirrorlistparsed: # FIXME: This is kind of lying to API callers mirrorurls.extend(list(self.metalink_data.urls())) self.mirrorlistparsed = True if self.mirrorlist and not self.mirrorlistparsed: mirrorurls.extend(self._getMirrorList()) self.mirrorlistparsed = True self.mirrorurls = self._replace_and_check_url(mirrorurls) self._urls = self.baseurl + self.mirrorurls # if our mirrorlist is just screwed then make sure we unlink a mirrorlist cache if len(self._urls) < 1: if hasattr(self, 'mirrorlist_file') and os.path.exists(self.mirrorlist_file): if not self.cache: try: misc.unlink_f(self.mirrorlist_file) except (IOError, OSError), e: logger.error('Could not delete bad mirrorlist file: %s - %s' % (self.mirrorlist_file, e)) else: logger.warning('removing mirrorlist with no valid mirrors: %s' % self.mirrorlist_file) # store them all back in baseurl for compat purposes self.baseurl = self._urls self.check() def _replace_and_check_url(self, url_list): goodurls = [] skipped = None for url in url_list: # obvious bogons get ignored b/c, we could get more interesting checks but if url in ['', None]: continue url = parser.varReplace(url, self.yumvar) if url[-1] != '/': url= url + '/' try: # This started throwing ValueErrors, BZ 666826 (s,b,p,q,f,o) = urlparse.urlparse(url) except (ValueError, IndexError, KeyError), e: s = 'blah' if s not in ['http', 'ftp', 'file', 'https']: skipped = url continue else: goodurls.append(url) if skipped is not None: # Caller cleans up for us. if goodurls: logger.warning('YumRepo Warning: Some mirror URLs are not using ftp, http[s] or file.\n Eg. %s' % misc.to_utf8(skipped)) else: # And raises in this case logger.error('YumRepo Error: All mirror URLs are not using ftp, http[s] or file.\n Eg. %s' % misc.to_utf8(skipped)) return goodurls def _geturls(self): if not self._urls: self._baseurlSetup() return self._urls urls = property(fget=lambda self: self._geturls(), fset=lambda self, value: setattr(self, "_urls", value), fdel=lambda self: setattr(self, "_urls", None)) def _getMetalink(self): if not self._metalink: self.metalink_filename = self.cachedir + '/' + 'metalink.xml' local = self.metalink_filename + '.tmp' if not self._metalinkCurrent(): url = misc.to_utf8(self.metalink) ugopts = self._default_grabopts(cache=self.http_caching=='all') try: ug = URLGrabber(progress_obj = self.callback, **ugopts) result = ug.urlgrab(url, local, text="%s/metalink" % self.ui_id) except URLGrabError, e: if not os.path.exists(self.metalink_filename): msg = ("Cannot retrieve metalink for repository: %s. " "Please verify its path and try again" % self.ui_id ) raise Errors.RepoError, msg # Now, we have an old usable metalink, so we can't move to # a newer repomd.xml ... or checksums won't match. logger.error("Could not get metalink %s error was\n%s: %s" % (url, e.args[0], misc.to_unicode(e.args[1]))) self._metadataCurrent = True if not self._metadataCurrent: try: self._metalink = metalink.MetaLinkRepoMD(result) shutil.move(result, self.metalink_filename) except metalink.MetaLinkRepoErrorParseFail, e: # Downloaded file failed to parse, revert (dito. above): logger.error("Could not parse metalink %s error was \n%s" % (url, e)) self._metadataCurrent = True misc.unlink_f(result) if self._metadataCurrent: self._metalink = metalink.MetaLinkRepoMD(self.metalink_filename) return self._metalink metalink_data = property(fget=lambda self: self._getMetalink(), fset=lambda self, value: setattr(self, "_metalink", value), fdel=lambda self: setattr(self, "_metalink", None)) def _all_urls_are_files(self, url): if url: return url.startswith("/") or url.startswith("file:") if not self.urls: # WTF ... but whatever. return False # Not an explicit url ... so make sure all mirrors/etc. are file:// for url in self.urls: if not self._all_urls_are_files(url): return False return True def _getFile(self, url=None, relative=None, local=None, start=None, end=None, copy_local=None, checkfunc=None, text=None, reget='simple', cache=True, size=None, **kwargs): """retrieve file from the mirrorgroup for the repo relative to local, optionally get range from start to end, also optionally retrieve from a specific baseurl""" # if local or relative is None: raise an exception b/c that shouldn't happen # if url is not None - then do a grab from the complete url - not through # the mirror, raise errors as need be # if url is None do a grab via the mirror group/grab for the repo # return the path to the local file # if copylocal isn't specified pickup the repo-defined attr if copy_local is None: copy_local = self.copy_local if local is None or relative is None: raise Errors.RepoError, \ "get request for Repo %s, gave no source or dest" % self.ui_id if self.cache == 1: if os.path.exists(local): # FIXME - we should figure out a way return local # to run the checkfunc from here else: # ain't there - raise raise Errors.RepoError, \ "Caching enabled but no local cache of %s from %s" % (local, self.ui_id) if url: (scheme, netloc, path, query, fragid) = urlparse.urlsplit(url) if self.mediaid and self.mediafunc: discnum = 1 if url: if scheme == "media" and fragid: discnum = int(fragid) try: # FIXME: we need to figure out what really matters to # pass to the media grabber function here result = self.mediafunc(local = local, checkfunc = checkfunc, relative = relative, text = text, copy_local = copy_local, url = url, mediaid = self.mediaid, name = self.name, discnum = discnum, range = (start, end)) return result except Errors.MediaError, e: verbose_logger.log(logginglevels.DEBUG_2, "Error getting package from media; falling back to url %s" %(e,)) if size and (copy_local or not self._all_urls_are_files(url)): dirstat = os.statvfs(os.path.dirname(local)) avail = dirstat.f_bavail * dirstat.f_bsize if avail < long(size): raise Errors.RepoError, _('''\ Insufficient space in download directory %s * free %s * needed %s''' ) % (os.path.dirname(local), format_number(avail), format_number(long(size))) if url and scheme != "media": ugopts = self._default_grabopts(cache=cache) ug = URLGrabber(progress_obj = self.callback, copy_local = copy_local, reget = reget, failure_callback = self.failure_obj, interrupt_callback=self.interrupt_callback, checkfunc=checkfunc, size=size, retry_no_cache=self._retry_no_cache, **ugopts) remote = url + '/' + relative try: result = ug.urlgrab(misc.to_utf8(remote), local, text=misc.to_utf8(text), range=(start, end), ) except URLGrabError, e: self._del_dl_file(local, size) errstr = "failed to retrieve %s from %s\nerror was %s" % (relative, self, e) e = Errors.RepoError(errstr) e.repo = self raise e else: headers = tuple(self.__headersListFromDict(cache=cache)) try: result = self.grab.urlgrab(misc.to_utf8(relative), local, text = misc.to_utf8(text), range = (start, end), copy_local=copy_local, reget = reget, checkfunc=checkfunc, http_headers=headers, size=size, retry_no_cache=self._retry_no_cache, **kwargs ) except URLGrabError, e: self._del_dl_file(local, size) errstr = "failure: %s from %s: %s" % (relative, self, e) errors = getattr(e, 'errors', None) e = Errors.NoMoreMirrorsRepoError(errstr, errors) e.repo = self raise e return result __get = _getFile def getPackage(self, package, checkfunc=None, text=None, cache=True, **kwargs): remote = package.relativepath local = package.localPkg() basepath = package.basepath if self._preload_pkg_from_system_cache(package): if package.verifyLocalPkg(): return local misc.unlink_f(local) if checkfunc is None: def checkfunc(obj): if not package.verifyLocalPkg(): misc.unlink_f(local) raise URLGrabError(-1, _('Package does not match intended download.')) # We would normally pass this to _getFile() directly but that could # break backward compatibility with plugins that override _getFile() # (BZ 1360532). self._retry_no_cache = self.http_caching == 'lazy:packages' try: ret = self._getFile(url=basepath, relative=remote, local=local, checkfunc=checkfunc, text=text, cache=cache, size=package.size, **kwargs ) finally: self._retry_no_cache = False if not kwargs.get('async') and not package.verifyLocalPkg(): # Don't return as "success" when bad. msg = "Downloaded package %s, from %s, but it was invalid." msg = msg % (package, package.repo.id) raise Errors.RepoError, msg return ret def getHeader(self, package, checkfunc = None, reget = 'simple', cache = True): remote = package.relativepath local = package.localHdr() start = package.hdrstart end = package.hdrend size = end-start basepath = package.basepath # yes, I know, don't ask if not os.path.exists(self.hdrdir): os.makedirs(self.hdrdir) return self._getFile(url=basepath, relative=remote, local=local, start=start, reget=None, end=end, checkfunc=checkfunc, copy_local=1, cache=cache, size=size, ) def metadataCurrent(self): """Check if there is a metadata_cookie and check its age. If the age of the cookie is less than metadata_expire time then return true else return False. This result is cached, so that metalink/repomd.xml are synchronized.""" if self._metadataCurrent is not None: return self._metadataCurrent mC_def = self.withinCacheAge(self.metadata_cookie, self.metadata_expire) if not mC_def: # Normal path... return mC_def # Edge cases, both repomd.xml and metalink (if used). Must exist. repomdfn = self.cachedir + '/' + 'repomd.xml' if not os.path.exists(repomdfn): return False self._hack_mirrorlist_for_anaconda() mlfn = self.cachedir + '/' + 'metalink.xml' if self.metalink and not os.path.exists(mlfn): return False self._metadataCurrent = True return True # The metalink _shouldn't_ be newer than the repomd.xml or the checksums # will be off, but we only really care when we are downloading the # repomd.xml ... so keep it in mind that they can be off on disk. # Also see _getMetalink() def _metalinkCurrent(self): if self._metadataCurrent is not None: return self._metadataCurrent if self.cache and not os.path.exists(self.metalink_filename): raise Errors.RepoError, 'Cannot find metalink.xml file for %s' %self if self.cache: self._metadataCurrent = True elif not os.path.exists(self.metalink_filename): self._metadataCurrent = False elif self.withinCacheAge(self.metadata_cookie, self.metadata_expire): self._metadataCurrent = True else: self._metadataCurrent = False return self._metadataCurrent def _matchExpireFilter(self): """Return whether cache_req matches metadata_expire_filter.""" # Never/write means we just skip this... if (hasattr(self, '_metadata_cache_req') and self._metadata_cache_req.startswith("read-only:") and self.metadata_expire_filter.startswith("read-only:")): cache_filt = self.metadata_expire_filter[len("read-only:"):] cache_req = self._metadata_cache_req[len("read-only:"):] if cache_filt == 'future': assert cache_req in ('past', 'present', 'future') return True if cache_filt == 'present': if cache_req in ('past', 'present'): return True if cache_filt == 'past': if cache_req == 'past': return True return False def withinCacheAge(self, myfile, expiration_time, expire_req_filter=True): """check if any file is older than a certain amount of time. Used for the cachecookie and the mirrorlist return True if w/i the expiration time limit false if the time limit has expired Additionally compare the file to age of the newest .repo or yum.conf file. If any of them are newer then invalidate the cache """ if expire_req_filter and self._matchExpireFilter(): expiration_time = -1 # -1 is special and should never get refreshed if expiration_time == -1 and os.path.exists(myfile): return True val = False if os.path.exists(myfile): cookie_info = os.stat(myfile) if cookie_info[8] + expiration_time > time.time(): val = True # WE ARE FROM THE FUTURE!!!! elif cookie_info[8] > time.time(): val = False if not self.check_config_file_age: return val # make sure none of our config files for this repo are newer than # us if cookie_info[8] < int(self.repo_config_age): val = False return val def setMetadataCookie(self): """if possible, set touch the metadata_cookie file""" check = self.metadata_cookie if not os.path.exists(self.metadata_cookie): check = self.cachedir if os.access(check, os.W_OK): fo = open(self.metadata_cookie, 'w+') fo.close() del fo def setup(self, cache, mediafunc = None, gpg_import_func=None, confirm_func=None, gpgca_import_func=None): try: self.cache = cache self.mediafunc = mediafunc self.gpg_import_func = gpg_import_func self.gpgca_import_func = gpgca_import_func self.confirm_func = confirm_func except Errors.RepoError, e: raise if not self.mediafunc and self.mediaid and not self.mirrorlist and not self.baseurl: verbose_logger.log(logginglevels.DEBUG_2, "Disabling media repo for non-media-aware frontend") self.enabled = False self.skip_if_unavailable = True def _cachingRepoXML(self, local): """ Should we cache the current repomd.xml """ if self.cache and not os.path.exists(local): raise Errors.RepoError, 'Cannot find repomd.xml file for %s' % self.ui_id if self.cache or self.metadataCurrent(): return True return False def _getFileRepoXML(self, local, text=None, grab_can_fail=None): """ Call _getFile() for the repomd.xml file. """ checkfunc = (self._checkRepoXML, (), {}) if grab_can_fail is None: grab_can_fail = 'old_repo_XML' in self._oldRepoMDData tfname = '' try: # This is named so that "yum clean metadata" picks it up tfname = tempfile.mktemp(prefix='repomd', suffix="tmp.xml", dir=os.path.dirname(local)) result = self._getFile(relative=self.repoMDFile, local=tfname, copy_local=1, text=text, reget=None, checkfunc=checkfunc, cache=self.http_caching == 'all', size=102400) # setting max size as 100K except URLGrabError, e: misc.unlink_f(tfname) if grab_can_fail: return None raise Errors.RepoError, 'Error downloading file %s: %s' % (local, e) except Errors.RepoError: misc.unlink_f(tfname) if grab_can_fail: return None raise # This should always work... try: os.rename(result, local) except: # But in case it doesn't... misc.unlink_f(tfname) if grab_can_fail: return None raise Errors.RepoError, 'Error renaming file %s to %s' % (result, local) return local def _parseRepoXML(self, local, parse_can_fail=None): """ Parse the repomd.xml file. """ try: return repoMDObject.RepoMD(self.id, local) except Errors.RepoMDError, e: if parse_can_fail is None: parse_can_fail = 'old_repo_XML' in self._oldRepoMDData if parse_can_fail: return None raise Errors.RepoError, 'Error importing repomd.xml from %s: %s' % (self.ui_id, e) def _saveOldRepoXML(self, local): """ If we have an older repomd.xml file available, save it out. """ # Cleanup old trash... for fname in glob.glob(self.cachedir + "/*.old.tmp"): misc.unlink_f(fname) if os.path.exists(local): old_local = local + '.old.tmp' # locked, so this is ok shutil.copy2(local, old_local) xml = self._parseRepoXML(old_local, True) if xml is None: return None self._oldRepoMDData = {'old_repo_XML' : xml, 'local' : local, 'old_local' : old_local, 'new_MD_files' : []} return xml return None def _revertOldRepoXML(self): """ If we have older data available, revert to it. """ # If we can't do a timestamp check, then we can be looking at a # completely different repo. from last time ... ergo. we can't revert. # We still want the old data, so we don't download twice. So we # pretend everything is good until the revert. if not self.timestamp_check: raise Errors.RepoError, "Can't download or revert repomd.xml for %s" % self.ui_id if 'old_repo_XML' not in self._oldRepoMDData: self._oldRepoMDData = {} return # Unique names mean the rename doesn't work anymore. for fname in self._oldRepoMDData['new_MD_files']: misc.unlink_f(fname) old_data = self._oldRepoMDData self._oldRepoMDData = {} if 'old_local' in old_data: os.rename(old_data['old_local'], old_data['local']) self._repoXML = old_data['old_repo_XML'] if 'old_MD_files' not in old_data: return for revert in old_data['old_MD_files']: os.rename(revert + '.old.tmp', revert) def _doneOldRepoXML(self): """ Done with old data, delete it. """ old_data = self._oldRepoMDData self._oldRepoMDData = {} if 'old_local' in old_data: misc.unlink_f(old_data['old_local']) if 'old_MD_files' not in old_data: return for revert in old_data['old_MD_files']: misc.unlink_f(revert + '.old.tmp') def _get_mdtype_data(self, mdtype, repoXML=None): if repoXML is None: repoXML = self.repoXML if mdtype == 'group' and 'group_gz' in repoXML.fileTypes(): mdtype = 'group_gz' if (mdtype in ['other', 'filelists', 'primary'] and self._check_db_version(mdtype + '_db', repoXML=repoXML)): mdtype += '_db' return (mdtype, repoXML.repoData.get(mdtype)) def _get_mdtype_fname(self, data, compressed=False): (r_base, remote) = data.location local = self.cachedir + '/' + os.path.basename(remote) if compressed: # DB file, we need the uncompressed version local = misc.decompress(local, fn_only=True) return local def _groupCheckDataMDNewer(self): """ We check the timestamps, if any of the timestamps for the "new" data is older than what we have ... we revert. """ if 'old_repo_XML' not in self._oldRepoMDData: return True old_repo_XML = self._oldRepoMDData['old_repo_XML'] if (self.timestamp_check and old_repo_XML.timestamp > self.repoXML.timestamp): logger.warning("Not using downloaded %s/repomd.xml because it is " "older than what we have:\n" " Current : %s\n Downloaded: %s" % (self.id, time.ctime(old_repo_XML.timestamp), time.ctime(self.repoXML.timestamp))) return False return True @staticmethod def _checkRepoXMLMetalink(repoXML, repomd): """ Check parsed repomd.xml against metalink.repomd data. """ if repoXML.timestamp != repomd.timestamp: return False if repoXML.length != repomd.size: return False done = False for checksum in repoXML.checksums: if checksum not in repomd.chksums: continue if repoXML.checksums[checksum] != repomd.chksums[checksum]: return False # All checksums should be trusted, but if we have more than one # then we might as well check them all ... paranoia is good. done = True return done def _checkRepoMetalink(self, repoXML=None, metalink_data=None): """ Check the repomd.xml against the metalink data, if we have it. """ if repoXML is None: repoXML = self._repoXML if metalink_data is None: metalink_data = self.metalink_data if self._checkRepoXMLMetalink(repoXML, metalink_data.repomd): return True # FIXME: We probably want to skip to the first mirror which has the # latest repomd.xml, but say "if we can't find one, use the newest old # repomd.xml" ... alas. that's not so easy to do in urlgrabber atm. for repomd in self.metalink_data.old_repomds: if self._checkRepoXMLMetalink(repoXML, repomd): verbose_logger.log(logginglevels.DEBUG_2, "Using older repomd.xml\n" " Latest: %s\n" " Using: %s" % (time.ctime(metalink_data.repomd.timestamp), time.ctime(repomd.timestamp))) return True return False def _latestRepoXML(self, local): """ Save the Old Repo XML, and if it exists check to see if it's the latest available given the metalink data. """ oxml = self._saveOldRepoXML(local) if not oxml: # No old repomd.xml data return False self._hack_mirrorlist_for_anaconda() if not self.metalink: # Nothing to check it against return False # Get the latest metalink, and the latest repomd data from it repomd = self.metalink_data.repomd if self.timestamp_check and oxml.timestamp > repomd.timestamp: # We have something "newer" than the latest, and have timestamp # checking which will kill anything passing the metalink check. return True # Do we have the latest repomd already return self._checkRepoXMLMetalink(oxml, repomd) def _commonLoadRepoXML(self, text, mdtypes=None): """ Common LoadRepoXML for instant and group, returns False if you should just return. """ local = self.cachedir + '/repomd.xml' if self._repoXML is not None: return False if self._cachingRepoXML(local): caching = True result = local else: caching = False if self._latestRepoXML(local): result = local old_data = self._oldRepoMDData self._repoXML = old_data['old_repo_XML'] else: result = self._getFileRepoXML(local, text) if result is None: if (self.skip_if_unavailable and hasattr(self, '_metadata_cache_req') and self._metadata_cache_req in ('write', 'read-only:future')): # Since skip_if_unavailable=True, we can just disable this repo raise Errors.RepoError, "Can't download repomd.xml for %s" % self.ui_id # Ignore this as we have a copy self._revertOldRepoXML() return False # if we have a 'fresh' repomd.xml then update the cookie self.setMetadataCookie() if self._repoXML is None: self._repoXML = self._parseRepoXML(result) if self._repoXML is None: self._revertOldRepoXML() return False if caching: return False # Skip any work. if not self._groupCheckDataMDNewer(): self._revertOldRepoXML() return False return True def _check_db_version(self, mdtype, repoXML=None): if self.mddownloadpolicy == 'xml': return False if repoXML is None: repoXML = self.repoXML if mdtype in repoXML.repoData: if DBVERSION == repoXML.repoData[mdtype].dbversion: return True return False # mmdtype is unused, but in theory was == primary # dbmtype == primary_db etc. def _groupCheckDataMDValid(self, data, dbmdtype, mmdtype, file_check=False): """ Check that we already have this data, and that it's valid. Given the DB mdtype and the main mdtype (no _db suffix). """ if data is None: return None if not file_check: compressed = False local = self._get_mdtype_fname(data) else: compressed = False local = self._get_mdtype_fname(data) if not os.path.exists(local): local = misc.decompress(local, fn_only=True) compressed = True # If we can, make a copy of the system-wide-cache version of this file, # note that we often don't get here. So we also do this in # YumPackageSack.populate ... and we look for the uncompressed versions # in retrieveMD. self._preload_md_from_system_cache(os.path.basename(local)) if not self._checkMD(local, dbmdtype, openchecksum=compressed, data=data, check_can_fail=True): return None return local def _commonRetrieveDataMD(self, mdtypes=None): """ Retrieve any listed mdtypes, and revert if there was a failure. Also put any of the non-valid mdtype files from the old_repo_XML into the delete list, this means metadata can change filename without us leaking it. """ downloading = self._commonRetrieveDataMD_list(mdtypes) for (ndata, nmdtype) in downloading: if not self._retrieveMD(nmdtype, retrieve_can_fail=True): self._revertOldRepoXML() return False self._commonRetrieveDataMD_done(downloading) return True def _commonRetrieveDataMD_list(self, mdtypes): """ Return a list of metadata to be retrieved """ def _mdtype_eq(omdtype, odata, nmdtype, ndata): """ Check if two returns from _get_mdtype_data() are equal. """ if ndata is None: return False if omdtype != nmdtype: return False if odata.checksum != ndata.checksum: return False # If we turn --unique-md-filenames on without chaning the data, # then we'll get different filenames, but the same checksum. # Atm. just say they are different, to make sure we delete the # old files. orname = os.path.basename(odata.location[1]) nrname = os.path.basename(ndata.location[1]) if orname != nrname: return False return True all_mdtypes = self.retrieved.keys() # Add in any extra stuff we don't know about. for mdtype in self.repoXML.fileTypes(): if mdtype in all_mdtypes: continue if mdtype in ('primary_db', 'filelists_db', 'other_db', 'group_gz'): continue all_mdtypes.append(mdtype) if mdtypes is None: mdtypes = all_mdtypes reverts = [] if 'old_repo_XML' not in self._oldRepoMDData: old_repo_XML = None else: old_repo_XML = self._oldRepoMDData['old_repo_XML'] self._oldRepoMDData['old_MD_files'] = reverts # Inited twice atm. ... sue me newmdfiles = self._oldRepoMDData['new_MD_files'] = [] downloading = [] for mdtype in all_mdtypes: (nmdtype, ndata) = self._get_mdtype_data(mdtype) if old_repo_XML: (omdtype, odata) = self._get_mdtype_data(mdtype, repoXML=old_repo_XML) local = self._groupCheckDataMDValid(odata, omdtype,mdtype,True) if local: if _mdtype_eq(omdtype, odata, nmdtype, ndata): continue # If they are the same do nothing # Move this version, we _may_ get a new one. # We delete it on success, revert it back on failure. # We don't copy as we know it's bad due to above test. os.rename(local, local + '.old.tmp') reverts.append(local) # This is the super easy way. We just to see if a generated # file is there for all files, but it should always work. # And anyone who is giving us MD with blah and blah.sqlite # which are different types, can play a game I like to call # "come here, ouch". gen_local = local + '.sqlite' if os.path.exists(gen_local): os.rename(gen_local, gen_local + '.old.tmp') reverts.append(gen_local) if ndata is None: # Doesn't exist in this repo continue if mdtype not in mdtypes: continue # No old repomd data, but we might still have uncompressed MD if self._groupCheckDataMDValid(ndata, nmdtype, mdtype): continue downloading.append((ndata, nmdtype)) newmdfiles.append(self._get_mdtype_fname(ndata, False)) return downloading def _commonRetrieveDataMD_done(self, downloading): """ Uncompress the downloaded metadata """ for (ndata, nmdtype) in downloading: local = self._get_mdtype_fname(ndata, False) self._doneOldRepoXML() def _groupLoadRepoXML(self, text=None, mdtypes=None): """ Retrieve the new repomd.xml from the repository, then check it and parse it. If it fails we revert to the old version and pretend that is fine. If the new repomd.xml requires new version of files that we have, like updateinfo.xml, we download those too and if any of those fail, we again revert everything and pretend old data is good. """ if self._commonLoadRepoXML(text): self._commonRetrieveDataMD(mdtypes) def _mdpolicy2mdtypes(self): md_groups = {'instant' : ['__None__'], 'group:primary' : ['primary'], 'group:small' : ["primary", "updateinfo", "group", "pkgtags"], 'group:main' : ["primary", "updateinfo", "group", "pkgtags", "filelists", "prestodelta"]} mdtypes = set() if type(self.mdpolicy) in types.StringTypes: mdtypes.update(md_groups.get(self.mdpolicy, [self.mdpolicy])) else: for mdpolicy in self.mdpolicy: mdtypes.update(md_groups.get(mdpolicy, [mdpolicy])) if not mdtypes or 'group:all' in mdtypes: mdtypes = None else: mdtypes.discard("__None__") mdtypes = sorted(list(mdtypes)) return mdtypes def _loadRepoXML(self, text=None): """retrieve/check/read in repomd.xml from the repository""" try: return self._groupLoadRepoXML(text, self._mdpolicy2mdtypes()) except KeyboardInterrupt: self._revertOldRepoXML() # Undo metadata cookie? raise raise Errors.RepoError, 'Bad loadRepoXML policy (for %s): %s' % (self.ui_id, self.mdpolicy) def _getRepoXML(self): if self._repoXML: return self._repoXML self._loadRepoXML(text=self.ui_id) return self._repoXML repoXML = property(fget=lambda self: self._getRepoXML(), fset=lambda self, val: setattr(self, "_repoXML", val), fdel=lambda self: setattr(self, "_repoXML", None)) def _checkRepoXML(self, fo): if type(fo) is types.InstanceType: filepath = fo.filename else: filepath = fo if self.repo_gpgcheck and not self._override_sigchecks: if misc.gpgme is None: raise URLGrabError(-1, 'pygpgme is not working so repomd.xml can not be verified for %s' % (self)) sigfile = self.cachedir + '/repomd.xml.asc' try: result = self._getFile(relative='repodata/repomd.xml.asc', copy_local=1, local = sigfile, text='%s/signature' % self.ui_id, reget=None, checkfunc=None, cache=self.http_caching == 'all', size=102400) except URLGrabError, e: raise URLGrabError(-1, 'Error finding signature for repomd.xml for %s: %s' % (self, e)) valid = misc.valid_detached_sig(result, filepath, self.gpgdir) if not valid and self.gpg_import_func: try: self.gpg_import_func(self, self.confirm_func) except Errors.YumBaseError, e: raise URLGrabError(-1, 'Gpg Keys not imported, cannot verify repomd.xml for repo %s' % (self)) valid = misc.valid_detached_sig(result, filepath, self.gpgdir) if not valid: raise URLGrabError(-1, 'repomd.xml signature could not be verified for %s' % (self)) try: repoXML = repoMDObject.RepoMD(self.id, filepath) except Errors.RepoMDError, e: raise URLGrabError(-1, 'Error importing repomd.xml for %s: %s' % (self, e)) self._hack_mirrorlist_for_anaconda() if self.metalink and not self._checkRepoMetalink(repoXML): raise URLGrabError(-1, 'repomd.xml does not match metalink for %s' % self) def _del_dl_file(self, local, size): """ Delete a downloaded file if it's the correct size. """ sd = misc.stat_f(local) if not sd: # File doesn't exist... return if size and sd.st_size < size: return # Still more to get... # Is the correct size, or too big ... delete it so we'll try again. misc.unlink_f(local) def checkMD(self, fn, mdtype, openchecksum=False): """check the metadata type against its checksum""" return self._checkMD(fn, mdtype, openchecksum) def _checkMD(self, fn, mdtype, openchecksum=False, data=None, check_can_fail=False): """ Internal function, use .checkMD() from outside yum. """ thisdata = data # So the argument name is nicer if thisdata is None: thisdata = self.repoXML.getData(mdtype) # Note openchecksum means do it after you've uncompressed the data. if openchecksum: (r_ctype, r_csum) = thisdata.openchecksum # get the remote checksum size = thisdata.opensize else: (r_ctype, r_csum) = thisdata.checksum # get the remote checksum size = thisdata.size if type(fn) == types.InstanceType: # this is an urlgrabber check file = fn.filename else: file = fn if size is not None: size = int(size) l_csum = _xattr_get_chksum(file, r_ctype) if l_csum: fsize = misc.stat_f(file) if fsize is not None: # We just got an xattr, so it should be there if size is None and l_csum == r_csum and fsize.st_size > 0: return 1 if size == fsize.st_size and l_csum == r_csum: return 1 # Anything goes wrong, run the checksums as normal... try: # get the local checksum l_csum = self._checksum(r_ctype, file, datasize=size) except Errors.RepoError, e: if check_can_fail: return None raise URLGrabError(-3, 'Error performing checksum: %s' % e) if l_csum == r_csum: _xattr_set_chksum(file, r_ctype, l_csum) return 1 else: if check_can_fail: return None raise URLGrabError(-1, 'Metadata file does not match checksum') def retrieveMD(self, mdtype): """base function to retrieve metadata files from the remote url returns the path to the local metadata file of a 'mdtype' mdtype can be 'primary', 'filelists', 'other' or 'group'.""" return self._retrieveMD(mdtype) def _retrieveMD(self, mdtype, retrieve_can_fail=False, **kwargs): """ Internal function, use .retrieveMD() from outside yum. """ # Note that this can raise Errors.RepoMDError if mdtype doesn't exist # for this repo. # FIXME - maybe retrieveMD should call decompress() after we've checked # the checksum by default? since we're never acting on compressed MD thisdata = self.repoXML.getData(mdtype) (r_base, remote) = thisdata.location fname = os.path.basename(remote) local = self.cachedir + '/' + fname if self.retrieved.get(mdtype): # got it, move along return local # Having preloaded the repomd means we should first try preloading this # file as well (forcing it this way is only needed when dealing with # simple filenames). if self._preloaded_repomd: misc.unlink_f(local) if (os.path.exists(local) or self._preload_md_from_system_cache(os.path.basename(local))): if self._checkMD(local, mdtype, check_can_fail=True): self.retrieved[mdtype] = 1 return local # it's the same return the local one if self.cache == 1: if retrieve_can_fail: return None if os.path.exists(local): msg = "Caching enabled and local cache: %s does not match checksum" % local else: msg = "Caching enabled but no local cache of %s from %s" % (local, self.ui_id) raise Errors.RepoError, msg # Given the file already exists, is it a partial download of thisdata # that we can try to reget? With unique filenames, that's always. # With simple filenames, use the old expected checksum to verify # (assuming the existing file or part represents the old data but it # usually does). partial = True orepomd = self._oldRepoMDData.get('old_repo_XML') if orepomd is not None: odata = orepomd.repoData.get(mdtype) if odata is not None: ofname = os.path.basename(odata.location[1]) partial = (fname != ofname or thisdata.checksum == odata.checksum) try: def checkfunc(obj): try: self.checkMD(obj, mdtype) except URLGrabError: # Don't share MD among mirrors, in theory we could use: # self._del_dl_file(local, int(thisdata.size)) # ...but this is safer. misc.unlink_f(obj.filename) raise self.retrieved[mdtype] = 1 text = "%s/%s" % (self.ui_id, mdtype) if thisdata.size is None or not partial: reget = None else: reget = 'simple' self._del_dl_file(local, int(thisdata.size)) local = self._getFile(relative=remote, local=local, copy_local=1, reget=reget, checkfunc=checkfunc, text=text, cache=self.http_caching == 'all', size=thisdata.size, **kwargs) except Errors.RepoError: if retrieve_can_fail: return None raise except URLGrabError, e: if retrieve_can_fail: return None raise Errors.RepoError, \ "Could not retrieve %s matching remote checksum from %s" % (local, self.ui_id) else: return local def getPrimaryXML(self): """this gets you the path to the primary.xml file, retrieving it if we need a new one""" return self.retrieveMD('primary') def getFileListsXML(self): """this gets you the path to the filelists.xml file, retrieving it if we need a new one""" return self.retrieveMD('filelists') def getOtherXML(self): return self.retrieveMD('other') def getGroups(self): """gets groups and returns group file path for the repository, if there is none or retrieve/decompress fails, it returns None""" if 'group_gz' in self.repoXML.fileTypes(): fn = self._retrieveMD('group_gz', retrieve_can_fail=True) if fn: try: fn = misc.repo_gen_decompress(fn, 'comps.xml', cached=self.cache) except IOError, e: logger.warning(e) fn = None return fn return self._retrieveMD('group', retrieve_can_fail=True) def setCallback(self, callback, multi_callback=None): self.callback = callback self.multi_callback = multi_callback self._callbacks_changed = True def setFailureObj(self, failure_obj): self.failure_obj = failure_obj self._callbacks_changed = True def setMirrorFailureObj(self, failure_obj): self.mirror_failure_obj = failure_obj self._callbacks_changed = True def setInterruptCallback(self, callback): self.interrupt_callback = callback self._callbacks_changed = True def _readMirrorList(self, fo, url=None): """ read the mirror list from the specified file object """ returnlist = [] content = [] if fo is not None: try: content = fo.readlines() except Exception, e: if url is None: # Shouldn't happen url = "" logger.error("Could not read mirrorlist %s, error was \n%s" % (url, e)) content = [] for line in content: if not re.match('\w+://\S+\s*$', line): continue mirror = line.rstrip() # no more trailing \n's mirror = mirror.replace('$ARCH', '$BASEARCH') returnlist.append(mirror) return (returnlist, content) def _getMirrorList(self): """retrieve an up2date-style mirrorlist file from our mirrorlist url, also save the file to the local repo dir and use that if cache expiry not expired we also s/$ARCH/$BASEARCH/ and move along return the baseurls from the mirrorlist file """ self.mirrorlist_file = self.cachedir + '/' + 'mirrorlist.txt' fo = None cacheok = False if self.withinCacheAge(self.mirrorlist_file, self.mirrorlist_expire, expire_req_filter=False): cacheok = True fo = open(self.mirrorlist_file, 'r') url = 'file://' + self.mirrorlist_file # just to keep self._readMirrorList(fo,url) happy else: url = self.mirrorlist scheme = urlparse.urlparse(url)[0] if scheme == '': url = 'file://' + url ugopts = self._default_grabopts() try: fo = urlgrabber.grabber.urlopen(url, **ugopts) except URLGrabError, e: logger.error("Could not retrieve mirrorlist %s error was\n%s: %s" % (url, e.args[0], misc.to_unicode(e.args[1]))) fo = None (returnlist, content) = self._readMirrorList(fo, url) if returnlist: if not self.cache and not cacheok: output = open(self.mirrorlist_file, 'w') for line in content: output.write(line) output.close() elif not cacheok and os.path.exists(self.mirrorlist_file): # New mirror file failed, so use the old one (better than nothing) os.utime(self.mirrorlist_file, None) return self._readMirrorList(open(self.mirrorlist_file, 'r'))[0] return returnlist def _preload_file(self, fn, destfn): """attempts to copy the file, if possible""" # don't copy it if the copy in our users dir is newer or equal if not os.path.exists(fn): return False if os.path.exists(destfn): if os.stat(fn)[stat.ST_CTIME] <= os.stat(destfn)[stat.ST_CTIME]: return False try: # IOError is the main culprit, with mode=600. But ignore everything. shutil.copy2(fn, destfn) except: return False return True def _preload_file_from_system_cache(self, filename, subdir='', destfn=None): """attempts to copy the file from the system-wide cache, if possible""" if not hasattr(self, 'old_base_cache_dir'): return False if self.old_base_cache_dir == "": return False glob_repo_cache_dir=os.path.join(self.old_base_cache_dir, self.id) if not os.path.exists(glob_repo_cache_dir): return False if os.path.normpath(glob_repo_cache_dir) == os.path.normpath(self.cachedir): return False # Try to copy whatever file it is fn = glob_repo_cache_dir + '/' + subdir + os.path.basename(filename) if destfn is None: destfn = self.cachedir + '/' + subdir + os.path.basename(filename) return self._preload_file(fn, destfn) def _preload_md_from_system_cache(self, filename): """attempts to copy the metadata file from the system-wide cache, if possible""" return self._preload_file_from_system_cache(filename) def _preload_pkg_from_system_cache(self, pkg): """attempts to copy the package from the system-wide cache, if possible""" pname = os.path.basename(pkg.localPkg()) destfn = os.path.join(self.pkgdir, pname) if self._preload_file_from_system_cache(pkg.localPkg(), subdir='packages/', destfn=destfn): return True if not hasattr(self, '_old_pkgdirs'): return False for opkgdir in self._old_pkgdirs: if self._preload_file(os.path.join(opkgdir, pname), destfn): return True return False def _verify_md(self): problems = [] print 'verifying md' try: md_types = self.repoXML.fileTypes() except Errors.RepoError, e: prb = RepoVerifyProblem(1, "failed to load repomd.xml", str(e)) problems.append(prb) return problems for md_type in md_types: print 'verifying %s' % md_type try: self.retrieveMD(md_type) except Errors.RepoError, e: msg = "%s metadata missing or does not match checksum" % md_type prb = RepoVerifyProblem(2, msg, str(e)) problems.append(prb) return problems def _verify_comps(self): print 'verifying comps' problems = [] # grab the comps for this repo # run the xmllint on it # chuck it into a comps object # make sure it parses grpfile = self.getGroups() # open it up as a file object so iterparse can cope with our compressed file if grpfile is not None: grpfile = misc.decompress(grpfile) try: c = comps.Comps() c.add(grpfile) except (Errors.GroupsError, Errors.CompsException), e: msg = "comps file failed to add" prb = RepoVerifyProblem(REPO_PROBLEM_COMPS, msg, str(e)) problems.add(prb) else: if c.compscount == 0: msg = "no groups in comps" prb = RepoVerifyProblem(REPO_PROBLEM_COMPS, msg, "") problems.add(prb) return problems def _verify_packages(self): return [] def verify(self, items=['repodata', 'comps']): """download/verify the specified items @items = ['repodata', 'comps'] can include: repodata, comps, packages """ problems = [] if 'repodata' in items: problems.extend(self._verify_md()) if 'comps' in items: if self.enablegroups: problems.extend(self._verify_comps()) if 'packages' in items: problems.extend(self._verify_packages()) # what else can we verify? return problems def getMirrorList(mirrorlist, pdict = None): warnings.warn('getMirrorList() will go away in a future version of Yum.\n', Errors.YumFutureDeprecationWarning, stacklevel=2) """retrieve an up2date-style mirrorlist file from a url, we also s/$ARCH/$BASEARCH/ and move along returns a list of the urls from that file""" returnlist = [] if hasattr(urlgrabber.grabber, 'urlopen'): urlresolver = urlgrabber.grabber else: import urllib urlresolver = urllib scheme = urlparse.urlparse(mirrorlist)[0] if scheme == '': url = 'file://' + mirrorlist else: url = mirrorlist try: fo = urlresolver.urlopen(url, proxies=pdict) except URLGrabError, e: print "Could not retrieve mirrorlist %s error was\n%s: %s" % (url, e.args[0], misc.to_unicode(e.args[1])) fo = None if fo is not None: content = fo.readlines() for line in content: if re.match('\s*(#|$)', line): continue mirror = line.rstrip() # no more trailing \n's mirror = mirror.replace('$ARCH', '$BASEARCH') returnlist.append(mirror) return returnlist class RepoVerifyProblem: """ Holder for each "problem" we find with a repo.verify(). """ def __init__(self, type, msg, details, fake=False): self.type = type self.message = msg self.details = details self.fake = fake