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.144.37.240
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/authentic-theme/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ HOME ]     

Current File : /usr/share/usermin/authentic-theme/authentic-funcs.pl
#
# Authentic Theme (https://github.com/authentic-theme/authentic-theme)
# Copyright Ilia Rostovtsev <ilia@virtualmin.com>
# Licensed under MIT (https://github.com/authentic-theme/authentic-theme/blob/master/LICENSE)
#
use strict;

our (%in,
     %text,
     %module_text_full,
     %theme_text,
     %theme_config,
     %gconfig,
     $current_lang_info,
     $root_directory,
     $config_directory,
     $current_theme,
     $theme_webprefix,
     $module_name,
     $remote_user,
     $get_user_level);

=head2 settings(file, [grep_pattern])
Parses given JavaScript filename to a hash reference

=cut

sub settings
{
    my ($f, $e) = @_;
    my %c;
    if (-r $f) {
        my $k = read_file_contents($f);
        my %k = $k =~ /\s*(.*?)\s*=\s*(.*)\s*/g;
        delete @k{ grep(!/^$e/, keys %k) }
          if ($e);
        foreach my $s (keys %k) {
            $k{$s} =~ s/^[^']*\K'|'(?=[^']*$)|[;,](?=[^;,]*$)//g;
            $k{$s} =~ s/\\'/'/g;
            $c{$s} .= $k{$s};
        }
        return %c;
    } else {
        return %c;
    }
}

sub ui_button_group_local
{
    my ($buttons, $extra_class) = @_;
    my $rv;
    $rv = "<div class=\"btn-group $extra_class\">$buttons</div>";
    return $rv;
}

sub ui_element
{
    my ($e, $c) = @_;

    # Allowed elements to use 'type' attr
    if ($e =~ /^(a|button|embed|input|link|menu|object|script|source|style)$/) {

        # Set default `type` for common elements
        if ($e eq 'input') {
            $c->{'type'} = "text" if (!$c->{'type'});
        } elsif ($e eq 'button') {
            $c->{'type'} = "button" if (!$c->{'type'});
        }
        $c->{'type'} = " type=\"@{[quote_escape($c->{'type'})]}\"" if ($c->{'type'});
    } else {
        delete $c->{'type'};
    }

    # Allowed elements to use 'name' attr
    if ($e =~ /^(button|fieldset|form|iframe|input|map|meta|object|output|param|select|textarea)$/) {
        $c->{'name'} = " name=\"@{[quote_escape($c->{'name'})]}\"" if ($c->{'name'});
    } else {
        delete $c->{'name'};
    }

    # Allowed elements to use 'value' attr
    if ($e =~ /^(button|input|meter|li|option|progress|param)$/) {
        $c->{'value'} = " value=\"@{[quote_escape($c->{'value'})]}\"" if ($c->{'value'});
    } else {
        delete $c->{'value'};
    }

    # Trigger `autofocus` or remove it
    if ($e =~ /^(button|input|select|textarea)$/) {
        if ($c->{'autofocus'} =~ /^(false|0)$/) {
            delete $c->{'autofocus'};
        }

        # If element is empty trigger autofocus
        elsif ($e =~ /input|textarea/ && !$c->{'value'}) {
            $c->{'autofocus'} = " autofocus";
        }
    }

    # Set default `class` for common elements
    if (!$c->{'class'}) {
        my $c_;
        if ($e eq 'input') {
            $c_ = "form-control";
            if ($c->{'type'} =~ /password/) {
                $c_ .= " ui_password";
            } else {
                $c_ .= " ui_textbox";
            }
        } elsif ($e eq 'textarea') {
            $c_ = "form-control ui_textarea";
        }
        $c->{'class'} = " class=\"$c_\"";
    } else {
        $c->{'class'} = " class=\"@{[quote_escape($c->{'class'})]}\"";
    }

    # Collect all other independent attributes
    my $attrs;
    foreach my $attr (keys %{$c}) {
        if ($attr ne "class" &&
            $attr ne "value" &&
            $attr ne "name"  &&
            $attr ne "type")
        {

            # Parse and add data attributes passed as reference
            if ($attr eq "data") {
                if (ref($c->{$attr})) {
                    foreach my $dattr (keys %{ $c->{$attr} }) {
                        $attrs .= " data-$dattr=\"@{[quote_escape($c->{$attr}->{$dattr})]}\"";
                    }
                }

                # Add all other attributes to the tag
            } else {
                $attrs .= " $attr=\"@{[quote_escape($c->{$attr})]}\"";
            }
        }
    }

    # Check if a tag must be closed
    my $e_c;
    if ($e !~ /^(area|base|br|hr|input|link|meta|param|source|circle|track|wbr)$/) {
        $e_c = "</$e>";
    }
    return "<$e$c->{'type'}$c->{'name'}$c->{'value'}$c->{'class'}$attrs>@{[html_escape($c->{'_'})]}$e_c\n";
}

sub ui_input
{
    my ($c, $v, $s, $d, $m, $t) = @_;

    # If old type input used, support it as well
    if (!ref($c)) {
        $c = { 'name'  => $c,
               'value' => $v,
               'size'  => $s };
        $c->{'disabled'}  = "true" if ($d);
        $c->{'maxlength'} = $m     if ($m);
        if ($t) {
            my @t = split(/\s+/, $t);
            foreach my $t (@t) {
                my ($t, $v) = ($t =~ /(.*?)=(.*)/);
                $v =~ s/^("|')|("|')$//g;
                $c->{$t} = "@{[quote_escape($v)]}";
            }
        }
    }
    return ui_element('input', $c);
}

sub ui_span_local
{
    my ($data, $extra_class) = @_;
    my $rv;
    if ($extra_class) {
        $extra_class = " class=\"$extra_class\"";
    }
    $rv = "<span$extra_class>$data</span>";
    return $rv;
}

sub ui_dropdown_local
{

    my ($elements, $dconfig) = @_;
    my $dconf_toggle          = $dconfig->{'tooltip'}         || 'tooltip';
    my $dconf_container       = $dconfig->{'container'}       || 'body';
    my $dconf_title           = $dconfig->{'title'}           || undef;
    my $dconf_container_class = $dconfig->{'container-class'} || undef;
    my $dconf_button_class    = $dconfig->{'button-class'}    || 'btn-default';
    my $dconf_button_icon     = $dconfig->{'icon'} ? "<span class=\"$dconfig->{'icon'}\"></span>"             : undef;
    my $dconf_button_text     = $dconfig->{'text'} ? "&nbsp;<span data-entry>$dconfig->{'text'}&nbsp;</span>" : undef;
    my $dconf_ul_class        = $dconfig->{'ul-class'} || undef;
    my $dconf_li_class        = $dconfig->{'li-class'} || undef;
    my $lis;

    foreach my $e (@{$elements}) {
        $lis .= "<li class=\"$dconf_li_class\">$e</li>\n";
    }
    return
"<div data-toggle=\"$dconf_toggle\" data-container=\"$dconf_container\" data-title=\"$dconf_title\" class=\"btn-group $dconf_container_class\">
    <button aria-label=\"$dconf_title\" data-toggle=\"dropdown\" class=\"btn dropdown-toggle $dconf_button_class\" aria-expanded=\"false\">
        $dconf_button_icon$dconf_button_text
    </button>                                    
    <ul class=\"dropdown-menu $dconf_ul_class\" role=\"menu\">
        $lis
    </ul>
</div>";
}

sub theme_ui_checkbox_local
{
    my ($name, $value, $label, $sel, $tags, $dis, $cls) = @_;
    my $after;
    my $rand = int rand(1e4);
    if ($label =~ /^([^<]*)(<[\000-\377]*)$/) {
        $label = $1;
        $after = $2;
    }
    $after = "<span class=\"awobject-after\">$after</span>" if ($after && $after !~ /^\s*<a/);
    $label = trim($label);
    my $bl = string_ends_with($label, '<br>') ? ' ds-bl-fs' : undef;
    return "<span class=\"awcheckbox awobject$bl$cls\"><input class=\"iawobject\" type=\"checkbox\" " .
      "name=\"" . &quote_escape($name) . "\" " . "value=\"" . &quote_escape($value) . "\" " . ($sel ? " checked" : "") .
      ($dis  ? " disabled=true" : "") . " id=\"" . &quote_escape("${name}_${value}_${rand}") . "\"" .
      ($tags ? " " . $tags      : "") . "> " . '<label class="lawobject" for="' . &quote_escape("${name}_${value}_${rand}") .
      '">' . (length $label ? "&nbsp;&nbsp;<span data-label-text>$label</span>&nbsp;" : '&nbsp;&nbsp;') .
      '</label></span>' . $after;
}

sub theme_make_date_local
{
    if (
        (
         (get_env('script_name') =~ /disable_domain/ ||
          $main::webmin_script_type ne 'web' ||
          ($main::header_content_type ne "text/html" &&
              $main::header_content_type ne "application/json")
         ) &&
         !$main::theme_allow_make_date
        ) ||
        $theme_config{'settings_theme_make_date'} eq 'false')
    {
        $main::theme_prevent_make_date = 1;
        return &make_date(@_);
    }
    my ($s, $o, $f) = @_;
    my $t = "x-md";
    my $d = "<$t-d>$s";
    ($d .= (string_starts_with($f, 'yyyy') ? ";2" : (string_contains($f, 'mon') ? ";1" : ($f == -1 ? ";-1" : ";0"))) .
     "</$t-d>");
    (!$o && ($d .= " <$t-t>$s</$t-t>"));
    return $d;
}

sub theme_nice_size_local
{
    my ($bytes, $minimal, $decimal) = @_;

    my ($decimal_units, $binary_units) = (1000, 1024);
    my $bytes_initial = $bytes;
    my $unit          = $decimal ? $decimal_units : $binary_units;

    my $label = sub {
        my ($item) = @_;
        my $text   = 'theme_xhred_nice_size_';
        my $unit   = ($unit > $decimal_units ? 'I' : undef);
        my @labels = ($theme_text{"${text}b"},
                      $theme_text{"${text}k${unit}B"},
                      $theme_text{"${text}M${unit}B"},
                      $theme_text{"${text}G${unit}B"},
                      $theme_text{"${text}T${unit}B"},
                      $theme_text{"${text}P${unit}B"});
        return $labels[$item - 1];
    };
    my $allowed = sub {
        my ($item) = @_;
        return $minimal >= $unit && $minimal >= ($unit**$item);
    };

    my $do = sub {
        my ($bytes) = @_;
        return abs($bytes) >= $unit;
    };

    my $item = 1;
    if (&$do($bytes)) {
        do {
            $bytes /= $unit;
            ++$item;
        } while ((&$do($bytes) || &$allowed($item)) && $item <= 5);
    } elsif (&$allowed($item)) {
        $item  = int(log($minimal) / log($unit)) + 1;
        $bytes = $item == 2 ? $bytes / (defined($unit) ? $unit : $item) : 0;
    }

    my $factor    = 10**2;
    my $formatted = int($bytes * $factor) / $factor;

    if ($minimal == -1) {
        return $formatted . " " . &$label($item);
    }
    return '<span data-filesize-bytes="' . $bytes_initial . '">' . ($formatted . " " . &$label($item)) . '</span>';
}

sub nice_number
{
    my ($number, $delimiter) = @_;
    $delimiter = " " if (!$delimiter);
    $number =~ s/(\d)(?=(\d{3})+(\D|$))/$1$delimiter/g;
    return $number;
}

sub get_time_offset
{
    my $offset = backquote_command('date +"%z"');
    $offset =~ s/\n//;
    return $offset;
}

sub get_theme_language
{
    my %s;
    foreach my $key (keys %theme_text) {
        if ($key !~ /_xhred_/ &&
            $key !~ /body_/  &&
            $key !~ /right_/ &&
            $key !~ /_level_navigation/)
        {
            next;
        }
        $s{$key} = $theme_text{$key};
    }

    # Pass additional language strings on initial load
    my @mod_extra_lang = ("virtual-server");
    foreach my $mod (@mod_extra_lang) {
        if (foreign_available($mod)) {
            $mod =~ s/\-/_/g;
            my @extras = ('scripts_desc');
            foreach my $key (@extras) {
                $s{"${mod}_${key}"} = $theme_text{$key} || "";
            }
        }
    }
    return convert_to_json(\%s);

}

sub get_user_allowed_gpg_keys
{
    my ($switch_to_user, $list_avoided_system_keys, $self) = @_;
    my %keys_;

    # Also get the keys for the root user, when operating on virtual server
    # so the master admin will see all root keys, plus the keys from given
    # virtual server
    my ($user_level) = get_user_level();
    if (!$user_level && !$self) {
        my ($root_keys) = get_user_allowed_gpg_keys($remote_user, $list_avoided_system_keys, 'self');
        %keys_ = %{$root_keys};
    }

    # Switch to remove user first
    my ($switched_user) = switch_to_given_unix_user($switch_to_user);

    # GNUPG lib target
    # For Usermin `gnupg` for Webmin `webmin`
    my $target = foreign_exists('gnupg') ? 'gnupg' : 'webmin';

    # As we call it not from the module set it manually
    # to bypass init_config() call leading to an error
    $module_name = $target;

    my $gpglib = $root_directory . "/$target/gnupg-lib.pl";
    if (-r $gpglib) {
        do($gpglib);
        my %gpgconfig = foreign_config($target);
        my $gpgpath   = $gpgconfig{'gpg'} || "gpg";

        # If this is Jamie's, Joe's or Ilia's machine, where also a private key
        # may be available, it would be possible to list the keys on dropdown too,
        # as those keys are distributed and should never be displayed to the users.
        # If one of us needs the keys to be displayed on the dropdown we need to hold
        # Alt key before clicking Encrypt entry on File Manager context menu
        my @keys_avoided = ('11F63C51', 'F9232D77', 'D9C821AB');
        my @keys         = list_keys_sorted();
        my @keys_secret  = sort {lc($a->{'name'}->[0]) cmp lc($b->{'name'}->[0])} list_secret_keys();

        foreach my $k (@keys) {
            my $key   = substr($k->{'key'}, -8, 8);
            my $suser = $switched_user || $remote_user;
            my $name  = $k->{'name'}->[0];
            $name =~ s/\(.*?\)//gs;
            if ($list_avoided_system_keys || (!$list_avoided_system_keys && !grep(/^$key$/, @keys_avoided))) {
                $keys_{ $k->{'key'} } =
                  trim($name) . " ($k->{'email'}->[0] [$suser] [$key/$k->{'size'}, $k->{'date'}])";
            }
        }
        return (\%keys_, $gpgpath);
    }
}

sub get_user_level
{
    my ($level, $has_virtualmin, $has_cloudmin);
    $has_cloudmin   = &foreign_available("server-manager");
    $has_virtualmin = &foreign_available("virtual-server");
    if ($has_cloudmin) {
        &foreign_require("server-manager", "server-manager-lib.pl");
    }
    if ($has_virtualmin) {
        &foreign_require("virtual-server", "virtual-server-lib.pl");
    }
    if ($has_cloudmin) {
        no warnings 'once';
        $level = $server_manager::access{'owner'} ? 4 : 0;
    } elsif ($has_virtualmin) {
        $level =
          &virtual_server::master_admin()   ? 0 :
          &virtual_server::reseller_admin() ? 1 :
          2;
    } elsif (&get_product_name() eq "usermin") {
        $level = 3;
    } else {
        $level = 0;
    }
    return ($level, $has_virtualmin, $has_cloudmin);
}

sub switch_to_given_unix_user
{
    return if (!supports_users());

    my ($username)   = @_;
    my ($user_level) = get_user_level();

    my $username_params = $in{'username'} || $in{'switch_to_username'};

    # Fix to emphasise that only root user can supply a username as param
    $username ||= $username_params
      if (!$user_level);

    # If username isn't set set it to remote user
    $username ||= $remote_user
      if ($user_level);

    if ($username) {
        my @uinfo = getpwnam($username);
        if (@uinfo) {
            switch_to_unix_user(\@uinfo)
              if ($username ne $remote_user);
            $ENV{'USER'} = $ENV{'LOGNAME'} = $username;
            $ENV{'HOME'} = $uinfo[7];
            return ($username, $uinfo[7]);
        }
    }
}

sub theme_get_webprefix_local
{
    my ($array)               = @_;
    my $webprefix             = $gconfig{'webprefix'};
    my $parent_proxy_detected = 0;
    my $parent_proxy          = $ENV{'HTTP_COMPLETE_WEBMIN_PATH'} || $ENV{'HTTP_WEBMIN_PATH'};
    if ($parent_proxy) {
        my ($parent_proxy_link)   = $parent_proxy      =~ /(\S*?\/link\.cgi\/[\d]{8,16})/;
        my ($parent_proxy_prefix) = $parent_proxy_link =~ /:\d+(\S*?\/link\.cgi\/\S*?\d+)/;
        if ($parent_proxy_prefix) {
            $webprefix             = $parent_proxy_prefix;
            $parent_proxy_detected = 1;
        }
    }
    return $array ? ($webprefix, $parent_proxy_detected) : $webprefix;
}

sub get_text_ltr
{
    if ($current_lang_info && $current_lang_info->{'rtl'} eq "1") {
        return 0;
    } else {
        return 1;
    }
}

sub reverse_string
{
    my ($str, $delimiter) = @_;
    my @strings = reverse(split(/\Q$delimiter\E/, $str));
    return join(" " . $delimiter . " ", @strings);
}

sub ltrim
{
    my $s = shift;
    $s =~ s/^\s+//;
    return $s;
}

sub rtrim
{
    my $s = shift;
    $s =~ s/\s+$//;
    return $s;
}

sub trim
{
    my $s = shift;
    $s =~ s/^\s+|\s+$//g;
    return $s;
}

sub trim_lines
{
    my $s = shift;
    $s =~ s/[\n\r]//g;
    return $s;
}

sub replace
{
    my ($from, $to, $string) = @_;
    $string =~ s/\Q$from\E/$to/ig;

    return $string;
}

sub replace_meta
{
    my ($string) = @_;

    my $hostname   = &get_display_hostname();
    my $version    = &get_webmin_version();
    my $os_type    = $gconfig{'real_os_type'}    || $gconfig{'os_type'};
    my $os_version = $gconfig{'real_os_version'} || $gconfig{'os_version'};
    $string =~ s/%HOSTNAME%/$hostname/g;
    $string =~ s/%VERSION%/$version/g;
    $string =~ s/%USER%/$remote_user/g;
    $string =~ s/%OS%/$os_type $os_version/g;

    return $string;
}

sub product_version_update_remote
{
    my ($latest_known_versions_remote, $latest_known_versions_remote_error, %versions_available);
    my $software_latest_cache       = theme_cached('software+latest');
    my $software_latest_cache_extra = sub {
        my ($software_latest_cache_original) = @_;
        my $software_latest_cache_extra_csf  = theme_cached('version-csf-stable');
        my $software_latest_cache_merged     = {};

        if ($software_latest_cache_original && $software_latest_cache_extra_csf) {
            $software_latest_cache_extra_csf = { 'csf' => $software_latest_cache_extra_csf };
            $software_latest_cache_merged    = { %{$software_latest_cache_original}, %{$software_latest_cache_extra_csf} };
        } elsif (!$software_latest_cache_original && $software_latest_cache_extra_csf) {
            $software_latest_cache_merged = { 'csf' => $software_latest_cache_extra_csf };
        } elsif ($software_latest_cache_original && !$software_latest_cache_extra_csf) {
            $software_latest_cache_merged = $software_latest_cache_original;
        }
        return $software_latest_cache_merged;
    };

    if ($software_latest_cache) {
        return &$software_latest_cache_extra($software_latest_cache);
    } else {
        my $packages_updates_mod = 'package-updates';
        my $packages_updates     = &foreign_available($packages_updates_mod);
        return { 'no-cache' => 1 }
          if (!post_has('xhr-') ||
              !$packages_updates);
        if ($packages_updates) {
            &foreign_require($packages_updates_mod);
            my @packages_updates_current = &package_updates::list_for_mode('updates', 0);
            if (@packages_updates_current) {
                foreach my $package_current (@packages_updates_current) {
                    my ($package, $version) = ($package_current->{'name'}, $package_current->{'version'});
                    if ($package &&
                        $version &&
                        $package =~ /^(wbm|wbt|ust|webmin|usermin)/)
                    {
                        $package =~ s/^(wbm|wbt|ust|webmin|usermin)\-//;
                        $versions_available{$package} = $version;
                    }

                }
            }
        }
        theme_cached('software+latest', \%versions_available);
        return &$software_latest_cache_extra(\%versions_available);
    }

}

sub product_version_update
{
    my ($product_local_version, $product_local_name) = @_;
    return $product_local_version
      if ($theme_config{'settings_check_remote_updates'} eq 'false');
    my $software_versions_remote = product_version_update_remote();
    return $product_local_version
      if ($software_versions_remote->{'no-cache'});

    # Remote versions
    my $product_remote_version =
      $product_local_name eq "w" ? ["Webmin",                           $software_versions_remote->{'webmin'}] :
      $product_local_name eq "u" ? ["Usermin",                          $software_versions_remote->{'usermin'}] :
      $product_local_name eq "v" ? ["Virtualmin",                       $software_versions_remote->{'virtual-server'}] :
      $product_local_name eq "c" ? ["Cloudmin",                         $software_versions_remote->{'server-manager'}] :
      $product_local_name eq "f" ? ["ConfigServer Security & Firewall", $software_versions_remote->{'csf'}] :
      "";

    # A work-around to fix inconsistency returned by `module.info` (i.e. 7.3.gpl)
    # and what package manager provides (i.e. 7.3.gpl-1).
    if ($product_local_name =~ /^(w|u|v|c)$/) {
        $product_local_version =~ s/(\d+\.\d+\.\d+|\d+\.\d+)(.*)/$1/
          if ($product_local_version);
        $software_versions_remote->{'webmin'} =~ s/(\d+\.\d+\.\d+|\d+\.\d+)(.*)/$1/
          if ($software_versions_remote->{'webmin'});
        $software_versions_remote->{'usermin'} =~ s/(\d+\.\d+\.\d+|\d+\.\d+)(.*)/$1/
          if ($software_versions_remote->{'usermin'});
        $software_versions_remote->{'virtual-server'} =~ s/(\d+\.\d+\.\d+|\d+\.\d+)(.*)/$1/
          if ($software_versions_remote->{'virtual-server'});
        $software_versions_remote->{'server-manager'} =~ s/(\d+\.\d+\.\d+|\d+\.\d+)(.*)/$1/
          if ($software_versions_remote->{'server-manager'});
    }

    # Compare versions
    if (
        ($product_local_name eq "w" &&
         $product_local_version &&
         &compare_version_numbers($product_local_version, $software_versions_remote->{'webmin'}) < 0) ||
        ($product_local_name eq "u" &&
            $product_local_version &&
            &compare_version_numbers($product_local_version, $software_versions_remote->{'usermin'}) < 0) ||
        ($product_local_name eq "v" &&
            $product_local_version &&
            &compare_version_numbers($product_local_version, $software_versions_remote->{'virtual-server'}) < 0) ||
        ($product_local_name eq "c" &&
            $product_local_version &&
            &compare_version_numbers($product_local_version, $software_versions_remote->{'server-manager'}) < 0) ||
        ($product_local_name eq "f" &&
            $product_local_version &&
            &compare_version_numbers($product_local_version, $software_versions_remote->{'csf'}) < 0)) {
        if (&foreign_available("virtual-server")) {
            return '<a href="https://forum.virtualmin.com/search?q=' .
              $product_remote_version->[0] . '%20in%3Atitle%20%23news%20order%3Alatest" target="_blank">' .
              '<span data-toggle="tooltip" data-placement="auto top" data-title="' .
              theme_text('theme_xhred_global_outdated_desc', $product_remote_version->[0], $product_remote_version->[1]) .
              '" class="bg-danger text-danger pd-lf-2 pd-rt-2 br-2">' . $product_local_version . '</span></a>';
        } else {
            return '<span data-toggle="tooltip" data-placement="auto top" data-title="' .
              theme_text('theme_xhred_global_outdated_desc2', $product_remote_version->[0], $product_remote_version->[1]) .
              '" class="bg-danger text-danger pd-lf-2 pd-rt-2 br-2">' . $product_local_version . '</span>';
        }
    } else {
        return $product_local_version;
    }
}

sub string_contains
{
    return (index($_[0], $_[1]) != -1);
}

sub string_starts_with
{
    return substr($_[0], 0, length($_[1])) eq $_[1];
}

sub string_ends_with
{
    my $length = length($_[1]);
    return substr($_[0], -$length, $length) eq $_[1];
}

sub array_flatten
{
    return map {ref eq 'ARRAY' ? @$_ : $_} @_;
}

sub array_contains
{
    my ($array_reference, $search, $loose) = @_;
    return (!$loose ? (grep {$_ eq $search} @$array_reference) : (grep {index($_, $search) != -1} @$array_reference));
}

sub array_unique
{
    my @unique;
    my %seen;

    foreach my $value (@_) {
        if (!$seen{$value}++) {
            $value =~ tr/\r\n//d;
            push @unique, $value;
        }
    }
    return @unique;
}

sub get_before_delimiter
{
    my ($v, $d) = @_;

    $v =~ /^(.*)\Q$d\E/;
    return ($1 ? $1 : $v);
}

sub get_chooser_button_template
{
    my ($onclick, $icon) = @_;
    return
"<button class='btn btn-default chooser_button' type=button onClick='ifield = form.$_[0]; chooser = window.open(\"$theme_webprefix/$onclick, \"chooser\"); chooser.ifield = ifield; window.ifield = ifield'>
  <i class=\"fa $icon vertical-align-middle\"></i>
 </button>\n";
}

sub directory_empty
{
    if (-e $_[0] && -d $_[0]) {
        opendir my $dir, $_[0] or die $!;
        if (grep !/^\.\.?$/, readdir $dir) {
            return 0;
        } else {
            return 1;
        }
    }
    return -1;
}

sub hash_to_query
{
    my ($c, %h) = @_;
    return $c . join(q{&}, map {qq{$_=@{[urlize($h{$_})]}}} keys %h);
}

sub head
{
    print "x-no-links: 1\n";
    print "Content-type: text/html\n\n";
}

sub module_text_full
{
    if (!%module_text_full) {
        %module_text_full = load_language(get_module_name());
    }
    return %module_text_full;
}

sub strip_html
{
    my ($string) = @_;
    $string =~ s|<.+?>||g;
    return $string;
}

sub format_document_title
{
    my ($title_initial) = $_[0] =~ /(?|.*:\s+(.*)|(.*))/;
    my ($product, $os_type) = $title_initial =~ /(?|(.*\d+).*(\(.*)|(.*\d+))/;
    $os_type = undef if (length($os_type) < 4);
    my $title = ($os_type ? "$product $os_type" : $product);
    $title =~ s/\R//g;
    return $title;
}

sub current_kill_previous
{
    my ($keep) = @_;
    my $pid = current_running(1);
    if ($pid) {
        kill(9, $pid);
    }
    current_to_pid($keep);
}

sub current_to_filename
{
    my ($filename) = @_;
    my $salt       = substr(encode_base64($main::session_id), 0, 6);
    my $user       = $remote_user;

    $filename =~ s/(?|([\w-]+$)|([\w-]+)\.)//;
    $filename = $1;
    $filename =~ tr/A-Za-z0-9//cd;
    $user     =~ tr/A-Za-z0-9//cd;
    $salt     =~ tr/A-Za-z0-9//cd;
    return '.theme_' . $salt . '_' . get_product_name() . '_' . $user . '_' . "$filename.pid";
}

sub current_running
{
    my ($clean) = @_;
    my %pid;
    my $filename = tempname(current_to_filename($0));
    read_file($filename, \%pid);
    $clean && unlink_file($filename);
    return $pid{'pid'} || 0;
}

sub current_to_pid
{
    my ($keep) = @_;
    my $script = current_to_filename($0);

    my $tmp_file = ($keep ? tempname($script) : transname($script));
    my %pid      = (pid => $$);
    write_file($tmp_file, \%pid);
}

sub network_stats
{
    # Get network data from all interfaces
    my ($type) = @_;
    my $file = "/proc/net/dev";
    return () unless -r $file;
    open(my $dev, $file);
    my (@titles, %result);
    while (my $line = <$dev>) {
        chomp($line);
        if ($line =~ /^.{6}\|([^\\]+)\|([^\\]+)$/) {
            my ($rec, $trans) = ($1, $2);
            @titles = ((map {"r$_"} split(/\s+/, $rec)), (map {"t$_"} split(/\s+/, $trans)));
        } elsif ($line =~ /^\s*([^:]+):\s*(.*)$/) {
            my ($id, @data) = ($1, split(/\s+/, $2));
            $result{$id} = { map {$titles[$_] => $data[$_];} (0 .. $#titles) };
        }
    }
    close($dev);

    # Return current network I/O
    if ($type eq 'io') {
        my ($rbytes, $tbytes, $rbytes2, $tbytes2) = (0, 0, 0, 0);
        my @rs;
        my $results = \%result;

        # Parse current data
        foreach (%$results) {
            $rbytes += $results->{$_}->{'rbytes'};
            $tbytes += $results->{$_}->{'tbytes'};
        }

        # Wait for one second and fetch data over again
        sleep 1, $results = network_stats();

        # Parse data after dalay
        foreach (%$results) {
            $rbytes2 += $results->{$_}->{'rbytes'};
            $tbytes2 += $results->{$_}->{'tbytes'};
        }

        # Return current network I/O
        $rbytes = int($rbytes2 - $rbytes);
        $tbytes = int($tbytes2 - $tbytes);

        @rs = ($rbytes, $tbytes);
        return serialise_variable(\@rs);
    }
    return \%result;
}

sub acl_system_status
{
    my ($show) = @_;
    my %access = get_module_acl($remote_user, 'system-status');
    $access{'show'} ||= "";
    if ($access{'show'} eq '*') {
        return 1;
    } else {
        return indexof($show, split(/\s+/, $access{'show'})) >= 0;
    }
}

sub get_default_module
{
    my $mod_def = $theme_config{'settings_webmin_default_module'};
    if (!foreign_available($mod_def)) {
        $mod_def = $gconfig{'gotomodule'};
        if (!foreign_available($mod_def)) {
            $mod_def = undef;
        }
    }
    if ($mod_def &&
        -r "$root_directory/$mod_def/index.cgi")
    {
        return $mod_def;
    }
    return undef;
}

# If "WebminCore" basic functions are not
# reachable throw UI warning to restart
# Webmin manually. Inner ref.: CXX1010000
sub init_prefail
{
    # Usermin is always fully restarted
    return if (get_product_name() eq 'usermin');
    my $pversion = get_webmin_version();
    if (
        # Affects upgrades 2.103 and up
        (!defined(&ui_switch_theme_javascript) && $pversion >= 2.103) ||
        
        # Affects upgrades 2.022 and up
        (!defined(&get_http_redirect) && $pversion >= 2.022) ||
        (!defined(&create_wrapper) && $pversion >= 2.022) ||

        # Affects upgrades before 2.020
        !defined(&parse_accepted_language) ||
        !defined(&get_default_system_locale) ||

        # Affects upgrades before 1.974
        !defined(&get_buffer_size) ||

        # Affects upgrades before 1.982
        !defined(&get_webprefix) ||

        # Affects upgrades from before 1.990
        !defined(&getvar) ||
        !defined(&setvar) ||

        # Affects upgrades from before 1.995
        !defined(&webmin_user_can_rpc)  ||
        !defined(&webmin_user_is_admin) ||

        # Affects upgrades from before 2.000
        !defined(&get_webmin_full_version) ||

        # Affects upgrades from before 2.002
        !defined(&miniserv_using_default_cert))
    {
        load_theme_library();
        do("$root_directory/web-lib-funcs.pl");
        setvar('needs-restart', has_command('systemctl') || $config_directory)
          if (defined(&setvar));
    }
}

sub post_has
{
    my ($key) = @_;
    if (%in) {
        if (grep {$_ =~ /\Q$key\E/} keys %in) {
            return 1;
        }
    }
    return 0;
}

1;

Anon7 - 2022
SCDN GOK