JFIF     "" $(4,$&1'-=-157:::#+?D?8C49:7 7%%77777777777777777777777777777777777777777777777777"H !1AQ"2aqB#R3b$Cr4Ss%Tt&c$!1AQ"a#2B ? }XuAo)8^ IƟ`vUp9jY0Ǧ w)E허2jU`SEKw5]kSno!]:?jc\غV7/9N+{t#8zd/޲3F/=ź3GNquV"/4:{z%ۣI'D@ %88^f}VV)S_2ed^Mx"͟?UC62Q%чmO͓ cq0rŖJ\Õ_Sݶ'|G.q޾D U]nP%EF>˲E"d&'f2s6H]4w IS˶4VbaQ+9]XtNx:M0JNxϙ⟟"{nr;|{%vo\z-wc,*|k}-m55o4W9ؓw߱Yzk .=/oϡȴ^9ҧʹamtQԬZ]4?egjrQ}+)MleE]MPEn!`IK2RUEwVIoͷcp;lśe7΄uN ;rПV8|e\׹9Y-V_G.)XԢOv<;_"ڜ]ߙEr݊'K{KuBJ}KI}24|"v)/ʻo5)6-Tjd7.C]Q&lU,Yk1P4~UKZs|$kX6+屷CUq+N(jlGrpG&UB3#k3\9qfg7O8Kim(AJOO~C#e`i0wĦij$cWh<dtQߺ"NOtG+ZǪ]b5%]v5$)u|qZ柡s-rۖu$MKڎCmN_V'/1u,21pvlc>қeNnֺ|bkl=lǷNOʣlz*]»vȎ[)j[fs[]:s#m6Qt6*Q+`};ßj[F_jcv`r#w}|k<ڞ/r53N8>Kh q_-_??@enſEܥ\D\YAEo+ ޟd}IcY7+t{=ɩ>}i\\JfxzVdSzᔢ]Q^CJի\iceitMM5hڦg')^ et#ۯ"ÿfF->4iؤ2ݷ6#p6^-R̫gETj^I.kӽUp~D9[:/>h> \gJ|ۿؘ>ml9jMK =+*2i=0RiͶۗV{"u]IH`9J_˹KƼK$X-|=ve/ bjxw.9i%NqVJcFYKcTtO,F;%67vYb8֝qq0tUt=DvawsS~~Edzr^F-v{c++ݔ\|9Iy #nOavOY=3690Tcrilwa\˓m$?箵S6U c(.~R7suMhqcMOnKoc*ȣȩEd'J ܜk*_q}%M/7c.|;trddbsdcJev85̤iW Ę 8C# .딖e$sk80^\J众2)Nm~|Idj_ O+6ǻ#(MIz4Qo:օY,:q]̌"lK}{F]ζ)h>ʶ ^ue78_G#rqv$wkk[Q c+վ+ĸZΝFB]VzoiJRke&Kgom_7Wef_7,osJɽE%lzBt>mRs)v8'P0ֲtrOg4p_2`GlhYڦDF/ӚKmtm'P2kqU765fJY:y؊.ox%8V_ִ̌ܞjpqwЮQ;iUcNoOoٸcY w*4soӵkqf$?-jy~0{>?DaL8XL/ɞo+'8 {ʸxգj#Dy)wk̘e۩+%}~;ڼ5xek|y-%ڱ-ʜe:EEScÚ5z|r'&I&яF*F7|[nRF =(4ۖ@. n7@xx:N^8Bg%u/ny6&dR{?8U_Q6Z߯-oh.NR]} qi6~H(j7*uF&l&o8ts]/P89:jW*$w׹Ӌ FxpsCJi.7N q4WU_}7*M#qWiصnk'4ݍl*t^ c<'d:~͗enFQRz9v~ddoTZ̚k7X(wUswO̙fոҁՕ[$IAI>WW~ĪEѢNoeutYߑ-Eixιpxq{FnyfRrjqU᫤]>wPU8)Y-7Wbq㛋w:7ܣ].j%K:y4] %9$I%pT(󨪙VqiYٓ4y~5S/XTDZM2lȪ; S~Kx:(Mn0';-{*qV&|W3S+\֔a{R{s=lYmN9Fn&o'}Vi( ?*qV5ѼCNsM饏zߴ$^O69@ ,$y|jE;gW/u|M?3+ZՕN86յw%|QO㏏S\E#ddsgl+Scl3~~CԕQľ?5_ z߿t11OĶ0>oB9E/SOSk+b&Yn>$툧eg) "!܉(1 uBoJ)/t/,:=7M+1ܺ#CmS^Nz 6[u&]+|Dfj:uZ5-Z^TjMtm>cȳ NdT_,M#Ex;pt۴ͮ#!N iKl!zPծ~$1SiO} HI&g Bf)b%Ko̧kumEnص;V?j>nltOMVۆl>.WueYaw2+qK,?uHiqqSM}~gu3xbcWSy/Xc{%sZ]uaUM;7:cb5G97'7þյW,;$ܛyVjl޻y7S;o6gf.Tг[7/i1Z^rE cUF'P1-?%u&q{fw~27ޡ ^w$?SwP[=R3Y73 4x(Kk&rLȫMKn:RjcI?3Al`vض[POĖSYujj6v+-[xҵ=~zNN>\ɲQ/uufo*e6l;31붏.>w6=7#7dFDc%ƶTbd;2/=?Asr! ~ZSS~I"9y]Hn,ĊJ7S}cK"amCg3yP=RQɤW}t;-{F+v+RɔڎB?º{SV묖kۏmK~%.Q;OfEf_Y/F-V-MdD)m.ZՍ8Y*h[g/6ydmCc[rdfʾ䖗gd$^֍^ʅѻL|<[݉\߯RiJUo';œN?B smS ܹkس,mRE^ѣlJ&.ċ԰YO:޼f\Z'HCѯU[ʩ1ff4S-٥YxTIGLiыr }L)edׂ*l|ٚuoxӿnWkTbbVm zT_'"x5Vިxo1ج^Fq6Sd3ws'/ڞ6m?}1OsRGݝ+,~ڬ%^p1ef5c25vq~﹉ă[r-eq] 8+/ESj}?mUE.xYK3"oƔ^Y9I]I ޑ" &*4.Jâ}ټQbXKJ񽼀ncg`+riܭ_'Bֽp%bX'7cB}WPm|zHָLJhj~E>i~Z$297|_hyΕ&s}ZϷ *j]:v.HK<SP8`Pƣ)r ,}8Wk[ArHgn=о7:J]TTP>OOj J_KyB\Ԥrm嬷ȫr{ݙ5R(FRЪ6q}KLmR'eޖz6[YތesYYL5Tr7s\^rؙV͸컬j5d?yk'b S }kra^ߚRH)[sg.fLM\u= vJQ]rVkZuoN}#G?yjO%|i2fKoӰღC P_Ϳ6Zr{e/m$i}9 G2')YG9KY>|1ӫ +v+i;h\Q@˿Lӭn˖ 7ck>Vr.D0)hC<˄4"0[eԬݭe+l2s3ss oX]1r]+VK vI;mZ')R6e5=/i@]H^Z۬՝EW.jƆf{8mXMV~_̝z^VR}T63}}k3+k3:j1Phlpi{欍BȽ}6w73GtUZv>4eUj$ xz$$D/߇ߟI"uk̜aƪ*ke/F:dһ_PE1ݡkp(5ʏ-ɮ{Yllԧg!ܝ g]i-umεŸxOê^=PR ##XeMy%2L~󜺶Hm ݙ2t_ƶz7'\Z4T<"AM-&xaC]a5.huQ۫$cMμ|h;.J.o߸sE-zU{d];|YLSMvSEneNKr1B[]NeonNߪ$4̘FPrkxޱ=0lr7Q%=$KQ;0r*XKdGۃ*]w-npᬶ\tt4>Dc[Ouo3/)-WҴ xs71eԤm*ٖ웗H''.Cnmy]݊Kra[9)Y#2U6d7tf.[R.GdE>#O_.+-K`{KonR_ÕM/)?:F,Xo1ƽRmz8C]lD %(x+d2Ah+\CCLJ!D65x\ȼv)\Nrp*[YُfL*PyVΚuWA K4hyYdwihNIy#ub?4NDϐ'4 :nFe(o%ve@@xl-k%QƭRP&kεMŪ-Ys2u ]T!}8*TQnZ}v =~mԧyDM&8K>2|Bnugܷ.wvCs̼5F^ubES7ݢM&4Ź-~mKx1((sr!M5uy\q)oy|a)ˣ,A?w"T휳2\F}PR-<2%`~4Z5\W"(USkGpT(~Qj>ɰ쏳ǓSKKx's]nEf'.iݙL>Moƹk7ݭ[.г6lk<;?)#E]xFU7'>vF%R;t:Җs}NSBWX=Y8ث}~G)S^^ƽwR[)/Fm-ڞTK~˓Z]U;RQ=M/"NԝP[-Y9t_8V+}P?Ue{M/O&WWKvc#r'KM'p[±vtpRC/W|7K2Rfm;ljm%Z]^T[6}6iTC }L[uxg7(Z}. SRI)jҞzȶ쳢oYRw$ŷ"J\ǭw{u'R taF{;3hHB\RP(*ZQ]y;;k٥nWbGKv-V?NDҞkd9@z LJ}Kc9C*?V-*[*۸-0.|󲝳ߗZK#%_OFGF$kC$[NNJ7Yn[k~Xzc+Sʲuhsw^^4+nElbƮKD,}YLV=i=|p|_=b5mȵ(~,em#Xƥ.sVoEaWXc.lY uG\m';'*\ӆ}|˯UfQBvo}/"zw + qvMrQ[[AdU2ٽCGgjؖS~Ev%9">$_2Sߚ%ѽ7jX(t#21r{̬F]b()?r[Rı)W[O/6]XL9 vuLh-Ȃ9"'7f!Փ䮿Bf}[lag֧]?Pc#D9EmfK7o*})+n!]qIo^FrNVNo!Eƃd#OP?%ۋ(mPu93ۣ{}2&$%cZ߯LҚY);U afԶd,*'6_?B:R~}^̬~mJ+vC}Ѩe"MY+mi :s쥸;iJeYvBddeK|#5/mzR]F2 JHUU )/S{Ic$=: W)>} @0#URsR=w"L{+ɞ)d|*qq2>[nƨDۋ-G[6½J|{Ѿ4MwyG-Σ Ze{ug>2|'zΤ2%xՑ*<Q̥T')uLkjn(zF-JOR}wn~FV5zq2m'^VS=7Y^RdfeO)>EpX붚w*r*w˿^kڴ{J;K۔sRŶU]p\zn@dx6[+yeH[_m_/I&mv|M5&&-G"v۴^{vg8Y(K_~h0e AxfrzڬkhS/Vy1ϯdW3'͹}{'V-:MW(V/ͷ*E7s\EmEW}bUr'k,P{9?B֫ #[uNrB,wo^{fdF(5tRf.2J-/:~ t0M"d_/c^32*q]yLl^2[ݥZc*vtm213r'tSuM-Խ#o/HF+2VEpmǦޟS?Rs+t:u G8n,Ԛf,hY8SX*rKf>+cpruɬ=DMrXgϸ:~ɲ ~]'5'kElw\=ڞAG&')G9R\_̝1K;nPg&T(ի[^Jҟ"qoӸ.W}3mF>'$<\U6-~?x?B~{^xkpv-vlߣe빹j\(ښsuu6lH(qoaYt?x8}Ie '@b%TݲygV.+O9/W4MsCMuFjYzG.{ds.k(>G~K?ni-=R r}r ?s̥%l5Ϛ9IN6~۩RĢWNʾE[|nb.HY—קWkr1ҺշMNDp)^¸R:w;u1 12]T/Uiʹd%2OC2K*r5S]g凫5 UQ.ȫ– /i91njFkQxuJ1rn%XDžy?s˗վuMGƋ/m^J*RsF))uF,'l{=|nFm9:N\%u#tnXE->e2Y0PũjUȨEŭ|'eʹ[o{Ցms%CGg/}t|snzrvm\g}cÊ94Pvg'L}ّg궮ԱߢO^f.W-sT]M˔ېе<^Н'KuNn_Vl8*Kж^ xsuW51-ᅱFzƉT-kY/9wzDޯ/XlW)gypǚjDɨ~{ݤHCim.[>rqE_Uرx/>|L64%aj;fxӱF(K֓J9՞ -K> I_5Enn´&=Oc%o̟IJZF$۲5I9Wݚ n.WTuѲӏ[4U/9.2zX5\j3ĎEsMq4%9.d[7јc9eNa+sjE';%s#ɤ`ףS=WI쫢.Mv:j/[3:rTF_zt:.z%udW%]xܮVz$Vŗ49[^y.խN~M&mx+wGR~_4KC[ʻ:v>03߶v9x-Mȧ$c:lrCWjeg%ֹ_Nh՝Qɏj^ϛr^.>WhlE5yֵ6\W^确]*гc&^NI[oCDn.ߑ!,m&M_/'Mn$s\r^8|uSZZ1|LV<(zq׮xmٚZƏ%.Ԁs^2𱸒O#&,s[mײ9kޖCoSq&俙qxP.N] 2UǎsM2iN.f r[mcQZmFُE{#[TbҔ*sfaSrn^8N<\_'MarJ6 EQғ|F[S'[~q~kmn[_x?B f5Q١X=g(~[Cx}GO ĺo'e)~dq(Ot`sN=~heu ::m'Cjj>~5V柙cyQD%uqEc{[l^U O]b~eŦۑ'W3&' 2V.^D%G S6\wYNO$. O+^ŵG~haEs^=1*bICzFF4O#,Wu3허ekB\I'tWMߩOG3iFz{rgeM9g r] i3gk&u1r/1kVgR-ɿuF .^;3;?3큦bN̂r4ovMkڞ}[:,IVG<};*-2",>K%bK2Ƨ[w!)ˤ;d?4%Ul2ږec4#ōIw^R_/TFX+*FM[F|a'ߚ2SIMeVGn ~&Y Ym(?ԛ],=|сG4yjk"Q^~ԗ^c,qqrg^-:Uc[E8>>k|nS..LBIc>3i|ZEZXAqm nuOm<; X~mrK=~ ƱrSN<U!F΋WS/|t?K)zd} ,C"ovx?bբs3mX3桭X֖˦kFddhg}$ggSo5jL*NdJis$ EQ\v=0HxzyW~FT_Ƶccg,&=_V(%kq+_÷O'[_[Uڽv F $Ξ9n5EN/4Yy/%*} .jΔ`V_6\VͲohzfOgޯzpj}y}v:34WH;+x7ӻu<ݦ"mJ/=>eoD֣c4kXW-[}٬6;t[Na_• _5i5˗sٴ]+e;Joj㼶ۙyLumo5&F)F\ {(sm_M>gzcr)KU̠Ħ=VDd'h;-aŤ9KٰqQܫަazMp4bk9 UX.ͮ]KeS5Uq[¹X0ɦ6]roFjʧ2׏6/C6eQE5KӰmsFnIz&`z팡-ٯ.ixyك?c2//z6M4W[]_"?Õ[? Vfvӳq]I5(d|MʝzcC*mN>B2gD+><e:Gh %UkW%zJ8k_ˠ=KFRfw{sŖ^q\/{v[Ω}gLjT[t_ޕg6G~rkkMcSRKբ54?SAûO1o%[>5/R~CioNdNʛćh>f6H8c/<1xd[ŦCEk.9"ej?w&O6^ژR[vrQ.z㎩f6:V8}hi2z~ s-w]+|I9s_C~>-S&9ZFVLf7-d'pՠplJ#mm؎s(?Ʋ?/A%_sXuGNnR}_dq>1ʍ|У3]NXYZʷ/&ܛ彖LS? 6]"_t5qP5Kq]^m91jW暹U6-5WU澦M0˵f2ӪǮ.P~? _nEJTcTei)ٳrۣ%x %gs}7l9'tb~dXst# r?}Weaq>=+to)7،E*vn\e_,\NFxcivz]tM˼?Oԝ2Zrλs-ĺEtonIIfm/9^[^EBUjOnr6vI& l]%0")2䒶-+R*zyX<> -X9GUo^xYQ8ιvixٔa\t)hv}ьոVU~tK,=_wLLa?TYIo]$`N6cbi?#7;MRt<.~Q-mob\\g5췍 ڌ_?8nfJN/Y͢n3?_sϩ{HiְPo'yS??_jߡWi5q? MWȲ)8a]lLˏ--b[TXlΫRy;o5뜾$HW.mm?շG[Ƀ seo5Q}Le%*،«~uU{R$t\^%!weX:G('6WupTS&~8=jo?2_PϖE[nf6Tٯ;GLW)NM[o*\j%.gb|䭹noOX:1R)UTj74˓]D_bʝkzNI.9|^G`KeQ{mOjX/sR7evdgi7qm}ތW&4=~|YY)?7Oj}xXkF×4c.l?i|b[5Ή5j-[Y\z<茲Z$Ff&o;gErǩݦ̪/q[&[/9uuzi;PS^_/?]=ΕqK~ӛ5'NM[m_Ϲc'[oӯE#g߂vvGNRo϶o5Ǩ[ɉtov2~i<7iSȜN(G5+/ٛMTܣukj鷣/$1˒!Mxr\ߤs1ZuMQȌ^]c$CXrj#N/˦Ķ9]Nzê5zi;W,v!ŧD6zğ7uR5^MW}>igl2U2nXo{}_w]&vte\Z3 MEEe/ 2s㗼S_bIղTI}|[Ye/c]*̪9u/DmyNxSDgi `Z?.RFj۪'~.[KVb޺o濡to?E#[.^y=q4F8ڎ/GX\.YW!Z.ѕtt:?gYYyU%Uw~ri>ȦKhg,5/=>V?TrN4aWO,oӕ7-SRi*"dܽpuaVQÞd-#J2Nr:#``ѧWR-F?I-T -cOT2pr?þזgE\Ij~L9%EMoџUؙt8_eYΧWjU}e9y9z/#TT-2dLt3H=ڼcKb'"uIٓ'[[߱F~\2]r%C]^VCLjm[cJNryf}ջ.[DEoRՒb'>fVy_c6[K4Na5>{ɳaw/Uj.Զ_K~?IeJ7OQx3IgFc*جɊǽ-o3Ӭp / ]7V*ENܜ[r/tOJΉw*ʨ*JFN^.WZeLgUwKi/M9y8dkOᛊHxGĶM*&#h/U|6D(uFyE5hYxiSEVm^D|,ۿCj;<*ouOkYpΔ2{x-L] !k2ا#IM'a7:M}M1Y儭Mnk[/;4Uwkkɫ%aɔoXVV$m;2Z4i9:>\Yů= ?[{t6,~!c`Un+dW.gKyIB]l+3kض(\MZ\}>k\C~閹l[ů]VNtƸr몮X+U>v'nv{y7s[г̭9Ctvt% GqT8=wa(6\Rd柮YWv^Fd^\+緉,+=-^S"k:NVu o[_TIѝ椯bF/G㿏dΙ?T}K-T)W>s?3M)V*,;P\,}B u{rDexڥVFfw}47׋w}]Դ 1dmk1V%/'T:Fǒ_TEe[l/l/ٯc{Ƀ[~`zj⾥r}Vܪ{M8Qv]$mU]8J2MngcxY?鑞.9HjxSy.fS(|]MgcK2$(jRQ3XO|<f:Jq4& fw|$N )A8ת99 mFNM*Dϒ NoIa9i9y?:D⻧߇\7ɧ]mu"-˥5/w̨_ 7DK['[2"(%xzT\*GT"+<,yX.lEJrfo?.4N;l>jmZߣ5FdB3\r,t,./S]Q{tm5lӕT~A [fv7Iہc: ΪN7I]2(|o$NLW"#~Dͭ=v-Mv{-lqn{I3xn'6.=DƟܖަ~deQV;k2Ei\[bӴ1_]OhZl朠&t3xkei+c\'ZԪ'hK梿X@cTԫ#emIz6e^i?8 NBc̆f+MׇdC]YSd%lώ8-c7eι/}_con/no\핍~[WNReXMo+اn ?#Ͷ-AUFN1V4!y,{1a$S﹑;Ǚr"__[o) xk}7EI/riwؙ7mR}`|yrEVdo/B# uٳiNQKQkᑑ^d@/=ˑɒ768fsuor9=7ףܹճpMr-$1uySOZN?đrqզ9F q=.!T?ػ bf{¯q=$^:!ES߿ Fu\OS,8e^UוS^hF4BQƺȪw-kF39@X06 Fv=Q^|ƞ5}2tnmG_|Λ(|%](-5>KȁN$=6lq).12 V6m$ׇlOcҫܸ K{;ľ>+Q?Rx-Keu uMy$i B}G*h$Q -W[-&a"[i\}~Ek$<~c{MffS eS.#\^lMiytު]9S{u4 {DFޅSź}R ]R$y;r/P̙3niXMt;&!rxw\ZFmQ"w\L{^۔K&/gr:m=2%5bwE"^e[\$ɟPi!U_rdS2d?=[!(I.rC QZEim%}|YmzZ_ά<ۡLQM|` ybPȏ}?]Eu[`kҫgFb~F}Q8NP>5lӳ^-K%Q}$sx7SvnfTƸ|Kzd'_ⰽח$4L Y?qy32t j2e ȜrJ{mبhۍUU'p#8y'ѝ=i+Tĩo7WYyČkL5؝M=%"Nt}eXW)N.~sv5pɮ sSQ[+-/}kVk'FEɩ9SE&T=&\緵 --tf.9Ѳ4_##_ɱTFV؞~YTddS&s=䟚Fb1._5}~gM'p#,U hs--XG wtԹTi7M:GYK5'^W?C>_Gq/S&d| k_gO ӊiJeHU G_ Êg#),}-:5>V1emq}t}q?meKU:BqJeiPɗ#\$sI} Z生ƫoo=V=pVcUg"%wEm叡vIdhrȔ~F]p58_.,O|'Ɇ^L!c6OWӷ{x9?Fp?ceOuT+Uɵݹ&gx9i퓃sxGIm}_3Īr#:ԣ?4בc[jö#B7KʌWNo)=+c }YvP{lv^r+5Vxx_:~=̌Q}CTy+Wh鸚f$101뢊F[#--Y\i@l)W8/E>8nlj/ktOľ,q*[sE[]:?ZeQvŔɺ|j(Wx,LW=:S?κq%81c)jJvODLiW,{96vr-2}-EH,}%3k#l5gl~x__W Sڎ 8YJQvA=QIWju6-X9$kWЩCI4UWd'&O/Cf=Pi/#+>n$KYst܅y4ʷD^~%~myj,s_4Q}΍Cή;SW:h=Ff{.B/inȇo=-T͸OY2}hlK}.m7-z?,f-/^b\QWs/_͔/3In[6M;l ygؼ!WUË_)D9YL4_>f}ϵ3hV5Oѣ(l8?L4蹥[-Э=7V{&ʢPʼ*3cMz>u4@[oM gKS[jy"Lھzɵfx)GE`ֿ.=kJ>/iˢ[j-qץQC B@o V(ʯG?Bܻ\I>=K-].(vOE.5׮=/Pf^&$caY9{3މ%YOxZ~6Z;;ԗ.NJzş/YϖĜ%ѿO^tY$ν4|e}2ɶU9A؜h˺LrIm%J.|I]kG|DzU k4'(T\9߱^!z -:mW^ <= <^2*;Seq(6ªsHf5ʸO{Ilr~G uJY^k5X_y;5'59O@ƣ̶>pnCOvNwX4oUUf]Џe%MV9Xm9]x'Q=82z)c/~1\~LSow>ﺍƻUql~Sqo羘sk}VjG71kYؽ]b4qnMӡ; w@̇IL㿗[43)]=v*)EH'a񖳋ҎTkxuXGK& ZIR(M8?:ixJp-dmckpu*%N^-7E3='ceE&';_J'Mw𶥏Y9+d9+>!e_Sn|VX -TZu]Ģ/6\ckr /ޗ/z[y.N:*k$ }Yǭ}GUm^-%dm;K_#ctBsg2:8rz-VE|T w.}w9NEPGnoCe8/&3qT}MJ̙Mۗ~哳,-WI_Bsh+~͛vN{ZdYKݲkr%+lo*re-ه?:vYqFfCsqMXRķ{yqgrx.oǓ\xdڗ_ZC9WomX|KmV_%UJܷr$drȳL~MoKyYLic Jq<1$UuٯTד374s<ĕ96춉r9 pGc9=p^:)ZJb&VӝXٽ 0/X& ۳*_ԙƏ.5J 6<$$6B0d_d?hqd>XCe- wO@pg:.>$.Ϣ~L޲|,{-ɪ2.u/Ds-[ُiVIWK5M#Fܭ3?x.)ۣ,wJ)Ȳڣ-#fbdq&Tͧ8Q,YqQ)/R­?\k˔[p_+ogzP[6r^o}_kT}JiJ;<ivEH8wI@MOPʊ\#+$%PDF-1.7 GIF89;
ANDA PELER
Server IP : 182.253.108.180  /  Your IP : 3.138.116.169
Web Server : Apache
System : Linux sma1wiradesa.sch.id 4.15.0-213-generic #224-Ubuntu SMP Mon Jun 19 13:30:12 UTC 2023 x86_64
User : wijaya ( 1017)
PHP Version : 7.3.33-10+ubuntu18.04.1+deb.sury.org+1
Disable Function : pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : ON  |  Sudo : ON  |  Pkexec : ON
Directory :  /usr/share/usermin/quota/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ HOME ]     

Current File : /usr/share/usermin/quota/quota-lib.pl
=head1 quota-lib.pl

Functions for Unix user and group quota management. Some of the functionality
is implemented in OS-specific library files which get automatically included
into this one, like linux-lib.pl. Check the documentation on that file for
more functions.

Example code:

 foreign_require('quota', 'quota-lib.pl');
 quota::edit_user_quota('joe', '/home', 1000000, 1200000, 1000, 1200);
 $n = quota::user_filesystems('joe');
 for($i=0; $i<$n; $i++) {
   print "filesystem=",$filesys{$i,'filesys'}," ",
         "block quota=",$filesys{$i,'hblocks'}," ",
         "blocks used=",$filesys{$i,'ublocks'},"\n";
 }

=cut

BEGIN { push(@INC, ".."); };
use WebminCore;
&init_config();
if ($gconfig{'os_type'} =~ /^\S+\-linux$/) {
	do "linux-lib.pl";
	}
else {
	do "$gconfig{'os_type'}-lib.pl";
	}
if ($module_info{'usermin'}) {
	&switch_to_remote_user();
	}
else {
	%access = &get_module_acl();
	&foreign_require("mount", "mount-lib.pl");
	}

$email_cmd = "$module_config_directory/email.pl";

=head2 list_filesystems

Returns a list of details of local filesystems on which quotas are supported.
Each is an array ref whose values are :

=item directory - Mount point, like /home

=item device - Source device, like /dev/hda1

=item type - Filesystem type, like ext3

=item options - Mount options, like rw,usrquota,grpquota

=item quotacan - Can this filesystem support quotas?

=item quotanow - Are quotas enabled right now?

=item quotapossible - Can quotas potentially be enabled?

The values of quotacan and quotanow are :

=item 0 - No quotas

=item 1 - User quotas only

=item 2 - Group quotas only

=item 3 - User and group quotas

=cut
sub list_filesystems
{
local $f;
local @mtab = &mount::list_mounted();
foreach $f (&mount::list_mounts()) {
	$fmap{$f->[0],$f->[1]} = $f;
	}
map { $_->[4] = &quota_can($_, $fmap{$_->[0],$_->[1]}) } @mtab;
map { $_->[5] = &quota_now($_, $fmap{$_->[0],$_->[1]}) } @mtab;
if (defined(&quota_possible)) {
	map { $_->[6] = &quota_possible($fmap{$_->[0],$_->[1]}) } @mtab;
	}
return grep { $_->[4] || $_->[6] } @mtab;
}

=head2 parse_options(type, options)

Convert an options string for some filesystem into the global hash %options.

=cut
sub parse_options
{
local($_);
undef(%options);
if ($_[0] ne "-") {
	foreach (split(/,/, $_[0])) {
		if (/^([^=]+)=(.*)$/) { $options{$1} = $2; }
		else { $options{$_} = ""; }
		}
	}
}

=head2 user_quota(user, filesystem)

Returns an array of quotas and usage information for some user on some
filesystem, or an empty array if no quota has been assigned. The array
elements are :

=item Number of blocks used.

=item Soft block quota.

=item Hard block quota.

=item Number of files used.

=item Soft file quota.

=item Hard file quota.

=cut
sub user_quota
{
my ($user, $fs) = @_;
local %user;
my $n = &filesystem_users($fs);
for(my $i=0; $i<$n; $i++) {
	if ($user{$i,'user'} eq $user) {
		return ( $user{$i,'ublocks'}, $user{$i,'sblocks'},
			 $user{$i,'hblocks'}, $user{$i,'ufiles'},
			 $user{$i,'sfiles'},  $user{$i,'hfiles'} );
		}
	}
return ();
}

=head2 group_quota(group, filesystem)

Returns an array of  ublocks, sblocks, hblocks, ufiles, sfiles, hfiles
for some group on some filesystem, or an empty array if no quota has been
assigned.

=cut
sub group_quota
{
my ($group, $fs) = @_;
local %group;
my $n = &filesystem_groups($fs);
for(my $i=0; $i<$n; $i++) {
	if ($group{$i,'group'} eq $group) {
		return ( $group{$i,'ublocks'}, $group{$i,'sblocks'},
			 $group{$i,'hblocks'}, $group{$i,'ufiles'},
			 $group{$i,'sfiles'},  $group{$i,'hfiles'} );
		}
	}
return ();
}

=head2 edit_user_quota(user, filesys, sblocks, hblocks, sfiles, hfiles)

Sets the disk quota for some user. The parameters are :

=item user - Unix username.

=item filesys - Filesystem on which to change quotas.

=item sblocks - Soft block limit.

=item hblocks - Hard block limit.

=item sfiles - Sort files limit.

=item hfiles - Hard files limit.

=cut
sub edit_user_quota
{
my ($user, $fs, $sblocks, $hblocks, $sfiles, $hfiles) = @_;
if (defined(&set_user_quota) && defined(&can_set_user_quota) &&
    &can_set_user_quota($fs)) {
	# OS lib file defines a function to set quotas
	return &set_user_quota(@_);
	}
elsif ($config{'user_setquota_command'} &&
       &has_command((split(/\s+/, $config{'user_setquota_command'}))[0])) {
	# Use quota setting command
	if ($user =~ /^#(\d+)$/) {
		# Pass numeric UID
		$user = $1;
		}
	elsif ($user =~ /^\d+$/) {
		# Username is numeric .. convert to UID
		local $uid = getpwnam($user);
		$user = $uid if (defined($uid));
		}
	local $cmd = $config{'user_setquota_command'}." ".quotemeta($user)." ".
		     int($sblocks)." ".int($hblocks)." ".
		     int($sfiles)." ".int($hfiles)." ".quotemeta($fs);
	local $out = &backquote_logged("$cmd 2>&1 </dev/null");
	&error("<tt>".&html_escape($out)."</tt>") if ($?);
	}
else {
	# Call the quota editor
	$ENV{'EDITOR'} = $ENV{'VISUAL'} = "$module_root_directory/edquota.pl";
	$ENV{'QUOTA_USER'} = $user;
	$ENV{'QUOTA_FILESYS'} = $fs;
	$ENV{'QUOTA_SBLOCKS'} = $sblocks;
	$ENV{'QUOTA_HBLOCKS'} = $hblocks;
	$ENV{'QUOTA_SFILES'} = $sfiles;
	$ENV{'QUOTA_HFILES'} = $hfiles;
	if ($edquota_use_ids) {
		# Use UID instead of username
		if ($user =~ /^#(\d+)$/) {
			$user = $1;
			}
		else {
			local $uid = getpwnam($user);
			$user = $uid if (defined($uid));
			}
		}
	&system_logged("$config{'user_edquota_command'} ".
		       quotemeta($user)." >/dev/null 2>&1");
	}
}

=head2 edit_group_quota(group, filesys, sblocks, hblocks, sfiles, hfiles)

Sets the disk quota for some group The parameters are :

=item user - Unix group name.

=item filesys - Filesystem on which to change quotas.

=item sblocks - Soft block limit.

=item hblocks - Hard block limit.

=item sfiles - Sort files limit.

=item hfiles - Hard files limit.

=cut
sub edit_group_quota
{
my ($group, $fs, $sblocks, $hblocks, $sfiles, $hfiles) = @_;
if (defined(&set_group_quota) && defined(&can_set_group_quota) &&
    &can_set_group_quota($fs)) {
	# OS lib file defines a function to set quotas
	return &set_group_quota(@_);
	}
elsif ($config{'group_setquota_command'} &&
       &has_command((split(/\s+/, $config{'group_setquota_command'}))[0])) {
	# Use quota setting command
	if ($group =~ /^#(\d+)$/) {
		# Pass numeric UID
		$group = $1;
		}
	elsif ($group =~ /^\d+$/) {
		# Group name is numeric .. convert to GID
		local $gid = getgrnam($group);
		$group = $gid if (defined($gid));
		}
	local $cmd =$config{'group_setquota_command'}." ".quotemeta($group)." ".
		     int($sblocks)." ".int($hblocks)." ".
		     int($sfiles)." ".int($hfiles)." ".quotemeta($fs);
	local $out = &backquote_logged("$cmd 2>&1 </dev/null");
	&error("<tt>".&html_escape($out)."</tt>") if ($?);
	}
else {
	# Call the editor
	$ENV{'EDITOR'} = $ENV{'VISUAL'} = "$module_root_directory/edquota.pl";
	$ENV{'QUOTA_USER'} = $group;
	$ENV{'QUOTA_FILESYS'} = $fs;
	$ENV{'QUOTA_SBLOCKS'} = $sblocks;
	$ENV{'QUOTA_HBLOCKS'} = $hblocks;
	$ENV{'QUOTA_SFILES'} = $sfiles;
	$ENV{'QUOTA_HFILES'} = $hfiles;
	if ($edquota_use_ids) {
		# Use GID instead of group name
		if ($group =~ /^#(\d+)$/) {
			$group = $1;
			}
		else {
			local $gid = getgrnam($group);
			$group = $gid if (defined($gid));
			}
		}
	&system_logged("$config{'group_edquota_command'} ".
		       quotemeta($group)." >/dev/null 2>&1");
	}
}

=head2 edit_user_grace(filesystem, btime, bunits, ftime, funits)

Change the grace times for blocks and files on some filesystem. Parameters are:

=item filesystem - Filesystem to change the grace time on.

=item btime - Number of units after which a user over his soft block limit is turned into a hard limit.

=item bunits - Units for the block grace time, such as 'seconds', 'minutes', 'hours' or 'days'.

=item ftime - Number of units after which a user over his soft file limit is turned into a hard limit.

=item funits - Units for the file grace time, such as 'seconds', 'minutes', 'hours' or 'days'.

=cut
sub edit_user_grace
{
my ($fs, $btime, $bunits, $ftime, $funits) = @_;
if (defined(&set_user_grace) && defined(&can_set_user_grace) &&
    &can_set_user_grace($fs)) {
	return &set_user_grace(@_);
	}
else {
	$ENV{'EDITOR'} = $ENV{'VISUAL'} = "$module_root_directory/edgrace.pl";
	$ENV{'QUOTA_FILESYS'} = $fs;
	$ENV{'QUOTA_BTIME'} = $btime;
	$ENV{'QUOTA_BUNITS'} = $bunits;
	$ENV{'QUOTA_FTIME'} = $ftime;
	$ENV{'QUOTA_FUNITS'} = $funits;
	&system_logged($config{'user_grace_command'});
	}
}

=head2 edit_group_grace(filesystem, btime, bunits, ftime, funits)

Change the grace times for groups for blocks and files on some filesystem.
The parameters are the same as edit_user_grace.

=cut
sub edit_group_grace
{
my ($fs, $btime, $bunits, $ftime, $funits) = @_;
if (defined(&set_group_grace) && defined(&can_set_group_grace) &&
    &can_set_group_grace($fs)) {
	return &set_group_grace(@_);
	}
else {
	$ENV{'EDITOR'} = $ENV{'VISUAL'} = "$module_root_directory/edgrace.pl";
	$ENV{'QUOTA_FILESYS'} = $fs;
	$ENV{'QUOTA_BTIME'} = $btime;
	$ENV{'QUOTA_BUNITS'} = $bunits;
	$ENV{'QUOTA_FTIME'} = $ftime;
	$ENV{'QUOTA_FUNITS'} = $funits;
	&system_logged($config{'group_grace_command'});
	}
}

=head2 quota_input(name, value, [blocksize])

Returns an input for selecting a quota or unlimited, in a table. For internal
use mainly.

=cut
sub quota_input
{
return &ui_radio($_[0]."_def", $_[1] == 0 ? 1 : 0,
		 [ [ 1, $text{'quota_unlimited'} ], [ 0, " " ] ])." ".
       &quota_inputbox(@_);
}

=head2 quota_inputbox(name, value, [blocksize])

Returns an input for selecting a quota. Mainly for internal use.

=cut
sub quota_inputbox
{
my ($name, $value, $bs) = @_;
if ($bs) {
	# We know the real size, so can offer units
	local $sz = $value*$bs;
	return &ui_bytesbox($name, $sz, 8);
	}
else {
	# Just show blocks
	return &ui_textbox($name, $value == 0 ? "" : $value, 8);
	}
}

=head2 quota_parse(name, [bsize], [nodef])

Parses inputs from the form generated by quota_input.

=cut
sub quota_parse
{
if ($in{$_[0]."_def"} && !$_[2]) {
	return 0;
	}
elsif ($_[1]) {
	# Include units, and convert to blocks
	return int($in{$_[0]}*$in{$_[0]."_units"}/$_[1]);
	}
else {
	# Just use blocks
	return int($in{$_[0]});
	}
}

=head2 can_edit_filesys(filesys)

Returns 1 if the current Webmin user can manage quotas on some filesystem.

=cut
sub can_edit_filesys
{
local $fs;
foreach $fs (split(/\s+/, $access{'filesys'})) {
	return 1 if ($fs eq "*" || $fs eq $_[0]);
	}
return 0;
}

=head2 can_edit_user(user)

Returns 1 if the current Webmin user can manage quotas for some Unix user.

=cut
sub can_edit_user
{
if ($access{'umode'} == 0) {
	return 1;
	}
elsif ($access{'umode'} == 3) {
	local @u = getpwnam($_[0]);
	return $access{'users'} == $u[3];
	}
elsif ($access{'umode'} == 4) {
	local @u = getpwnam($_[0]);
	return (!$access{'umin'} || $u[2] >= $access{'umin'}) &&
	       (!$access{'umax'} || $u[2] <= $access{'umax'});
	}
else {
	local %ucan = map { $_, 1 } split(/\s+/, $access{'users'});
	return $access{'umode'} == 1 && $ucan{$_[0]} ||
	       $access{'umode'} == 2 && !$ucan{$_[0]};
	}
}

=head2 can_edit_group(group)

Returns 1 if the current Webmin user can manage quotas for some Unix group.

=cut
sub can_edit_group
{
if ($access{'gmode'} == 0) {
	return 1;
	}
elsif ($access{'gmode'} == 3) {
	return 0;
	}
elsif ($access{'gmode'} == 4) {
	local @g = getgrnam($_[0]);
	return (!$access{'gmin'} || $g[2] >= $access{'gmin'}) &&
	       (!$access{'gmax'} || $g[2] <= $access{'gmax'});
	}
else {
	local %gcan = map { $_, 1 } split(/\s+/, $access{'groups'});
	return $access{'gmode'} == 1 && $gcan{$_[0]} ||
	       $access{'gmode'} == 2 && !$gcan{$_[0]};
	}
}

=head2 filesystem_info(filesystem, &hash, count, [blocksize])

Returns two strings containing information about the amount of disk space
granted and used on some filesystem. For internal use.

=cut
sub filesystem_info
{
local @fs = &free_space($_[0], $_[3]);
if ($_[3]) {
	local $i;
	foreach $i (0 .. 3) {
		$fs[$i] = $i < 2 ? &nice_size($fs[$i]*$_[3])
				 : int($fs[$i]);
		}
	}
if ($_[1]) {
	local $bt = 0;
	local $ft = 0;
	local $i;
	for($i=0; $i<$_[2]; $i++) {
		$bt += $_[1]->{$i,'hblocks'};
		$ft += $_[1]->{$i,'hfiles'};
		}
	if ($_[3]) {
		$bt = &nice_size($bt*$_[3]);
		}
	return ( "$fs[0] total / $fs[1] free / $bt granted",
		 "$fs[2] total / $fs[3] free / $ft granted" );
	}
else {
	return ( "$fs[0] total / $fs[1] free",
		 "$fs[2] total / $fs[3] free" );
	}
}

=head2 block_size(dir, [for-filesys])

Returns the size (in bytes) of blocks on some filesystem, if known. All
quota functions deal with blocks, so they must be multiplied by the value
returned by this function before display to users.

=cut
sub block_size
{
return undef if (!$config{'block_mode'});
return undef if (!defined(&quota_block_size) &&
		 !defined(&fs_block_size));
local @mounts = &mount::list_mounted();
local ($mount) = grep { $_->[0] eq $_[0] } @mounts;
if ($mount) {
	if ($_[1]) {
		return &fs_block_size(@$mount);
		}
	else {
		if (defined(&quota_block_size)) {
			return &quota_block_size(@$mount);
			}
		else {
			return &fs_block_size(@$mount);
			}
		}
	}
return undef;
}

=head2 nice_limit(amount, bsize, no-blocks)

Internal function to show a quota limit nicely formatted.

=cut
sub nice_limit
{
local ($amount, $bsize, $noblocks) = @_;
return $amount == 0 ? $text{'quota_unlimited'} :
       $bsize && !$noblocks ? &nice_size($amount*$bsize) : $amount;
}

=head2 find_email_job

Returns the cron job hash ref for the quota limit monitoring email job.

=cut
sub find_email_job
{
&foreign_require("cron", "cron-lib.pl");
local @jobs = &cron::list_cron_jobs();
local ($job) = grep { $_->{'command'} eq $email_cmd } @jobs;
return $job;
}

=head2 create_email_job

Creates the cron job for scheduled emailing, which runs every 10 minutes.

=cut
sub create_email_job
{
&foreign_require("cron", "cron-lib.pl");
local $job = &find_email_job();
if (!$job) {
	$job = { 'user' => 'root',
		 'command' => $email_cmd,
		 'active' => 1,
		 'mins' => '0,10,20,30,40,50',
		 'hours' => '*',
		 'days' => '*',
		 'months' => '*',
		 'weekdays' => '*' };
	&lock_file(&cron::cron_file($job));
	&cron::create_cron_job($job);
	&cron::create_wrapper($email_cmd, $module_name, "email.pl");
	&unlock_file(&cron::cron_file($job));
	}
}

=head2 trunc_space(string)

Removes spaces from the start and end of a string.

=cut
sub trunc_space
{
local $rv = $_[0];
$rv =~ s/^\s+//;
$rv =~ s/\s+$//;
return $rv;
}

=head2 to_percent(used, total)

Converts an amount used and a total into a percentage.

=cut
sub to_percent
{
if ($_[1]) {
	return $_[0]*100/$_[1];
	}
else {
	return 0;
	}
}

=head2 select_grace_units(name, value)

Returns a menu for selecting grace time units.

=cut
sub select_grace_units
{
local @uarr = &grace_units();
return &ui_select($_[0], $_[1],
	[ map { [ $_, $uarr[$_] ] } (0..$#uarr) ]);
}

# resolve_and_simplify(path)
# Resolve symlinks from a path, and simplify the result to remove dots
sub resolve_and_simplify
{
my ($path) = @_;
return &simplify_path(&resolve_links($path));
}

1;


Anon7 - 2022
SCDN GOK