JFIFHH(ICC_PROFILE0mntrRGB XYZ acsp- desctrXYZdgXYZxbXYZrTRC(gTRC(bTRC(wtptcprtiPSP Вpu9b"PH+MC<žϓItu'b=o8vӆDD.Gh jLDf@S)"xp\xdD޸@q0^Ks(>ϛd? @5ARA((A(]|N-7<78qr>}7ΩrWvϋ>+gI&!4Qxsf:?9(mɦdy9$נTVm[x3X&$eg]9^tihkhٹj>@"`f}no0773 l^9DcWt!>>:Kwa>OJuvSo %3Ughvò"ݦ-22HHpu^6.=)J$'DuV㎾>ZN$m\R :o1RU.Z}NăTb '!׌_FŢBjg]5Ź a@(yяj) 6 H5%=THU|qSfΏC[s?qk7ĤD&P]>z@[³$|\=ܜ2|ݏƽd5StQ(uVINi:W5ﱏ珔WAS\{4V6w8תm9  ?f+voͧξyK˒ɉQqglq?kYqbj=O`d3JO_6oի>ӜY33$ U /c^(e )TmMar,/}O_)>qvQĝEL^2z*%d:|64Tcvms9h5l<ö !kSdLLINivZyxlЍd5K`IϮ5m=_n~G@n`yqol?6՗ˏRcz}(w%HYMsMqt9nmueovEI9qсa[s{O& wzd\Ua%L")T)fL`^ټQ$"} a59"ddE5;Z8"`zC7ayz "~om~iMeO]ʢG0c|_k'Љ_rGSfk,)ɘ@?9vc]9齛v9 #87|vr]uU_'W4 "@҃LV dwjEd@+7[ven3r%Eq?VD6ti{nO"I@'1C,i\}=k;g/~7LnGI[_z-%;f%lsItTxɛK ʊ_VnB^95Žnr|h>~?>n0R*GGnng鳓KI[^a2/]QTuT0+U|56v j-^yt^+/m%3T10._?f5[7ygezHl~>~8v5Y59+B&(.5ٿw/Nd.w~M5L܏FNl8v'n#3TmC" )*`|sg>D-J ^GERVIrf1ϩOGݠ>^+nC< vfF7ǢMK՜_M}W0v:rv :Nϰf8*\ h \kqar :+ߛMW̕;XP*ژY7iomOi4^€γ7_vnRq=\t(s}0C_5oC'vf4ٳ2S.٘N 3Kjߘ!*4Э6pc_1,;gܧ%t=ǒvYSm8s`wcl?73cN3}{rOյ `g'g.L/}uҚns&:`GW\Hy?uY|ֆaMB` H4t˳} oե Un="-MU`*Onse^^hYZJЙ>o6;Λ`k t$"*E_1a欳tmn1L𢘘g}[9ו)4.0hϗ(5Ao\h#B$dͽ>UX&$DQbgٰ|ys*&`8Cz~or,- "@M%<Ӝlc(7>4xRay"IW6=0UH_FNGJML)Ni}6mU ){x?4;s!+SP5'?0+bgE`P)-gζVzx Lt wg$T"21\ި]Պ " ]FNqBSʝWq]|'<_sB^DA0L ;筬s?Ek:p @zsKZ &Q`Bk<޾dSWI3YC۳9;Gug_s:[ 4gsҼZpl3;1wG^NXlk1Pcƀ}sKSUC:wS JIo l^w: o CW&OLMomr7Vb>L /dLT/.=.Vu[+J'?dK btRzji@Đ r#b¯K)Νqi"2+1k\*3,vpKةIqaS3L %drV\+LуsSD$OW/5(K+B>TѬ2bI&RiqG%J Igǫ6Ec6yEbXL\wzm~(}kkى+X==KN8gvhR3goa #n (_^ME*Qؾ^K.F= J}drH"7;?*"ejw BJc|QJS̜VN(`;ms$~sccTCc?SOY<ޜD:ow!kܞLDWčfsT }&ip䋛ݘg蒻҃>σ007: &QM_-U1y< lk"> .A&Y:h*t7"{|_I{xV125:st'XeE嶐aN5vWIo/o#[Ґ.y-\qF7:srΔ\反$h@szFY'<=.u)sUjvsv[)#gZSO7pwѦh`|#ljPهK4)ڜ+$j1֊1Pq #liZۄSzHM.ƻV͏[#C9.hqǬ]J>:xr˂&"*DMuG+}V(l \ʆ&ܟl_cy ydC&MLY6TUA4ac&#YUB),ʡj;aD߮']i>h$&*4]0fe˨('*+V#`La\y-zhdPrE_ѓ-ƹ N` lnbͰ_O\vfIe|JkPՋ"DreM],74_Q+[FY˖'`!d~M..74F$*~`b'ˏ ?CƯ%Ӯ[8U{ӿGiKYEѡm\yQ<_?܈xTE.or=qF&ٱgƱx6t:uob}tN٢ ܥ_IK/bpW6Ƌ-,֤Wl_.~ aN~NҸL[&%/Uߞ.#.oGzH-k]`M)gؖ 061f/eR{SgL:-4z(fDFҞl7iQ>k<=액a~ЅbM1zH^'N(7@xyb| -g),IVKYh`}8nʏWYGO?aat H!nPŒ)f,'UmbעxȆ2!*֮ӧEUi0Pͨ .<<*PwK7$&R]_jiЉ[}?.sw~}}tgV%jtPlj=-xK=hkcuv-E\vYbu#9z}ʝM|q> E܌6dı+Umaɐ?ؓ ;ȭ6&6wFׂC쫌iCGa[#w[xN|4=%!z$2}.Eig ʽcJ?dd2snO .&HtO"OMN囸=EӬ2:?4}Й ǗS"r&*[MkKA6@wƿvp&! ˭\y,+ Da~HmfpđJ*$sRHf7Pj6d(K\!CMؐoOr74v;<\1L](.;c?Z ,t.X=biKvjUGLIC,UF'qMbZ≩VJI\z]w s~PL,࿬Lj9sFvq*r$X_Zw\ K4}f~IȽ\3p60-[~B|!H5pZ38f7tsn5 ^KzCU6u@ޯf +=C u$ކf_mrXW&jT~]YsTofq}@sTΜ)~Ywh]sμ\\ OKXHfZ||ijdEx1+F0=@JyL&Ze4{.>Ҡ}yHW"6 XBZP3o/#ǐ4-4s}Zr:l"KBr4(L Ak'f'iIbXw 6䞬Rpb}Nj9gQgI2Z5ʅFiŵYm^|[UUe,1kJj`g<_g Og=Gi kslwfgLi\q1FGD-?}JcJ NNV7OcYkR8_~D3&.fv`!C1(\nbnjJr[\ѝ9ϒ9Թk= e}U{&85U5 % ֹ }Jҵ~]MW\`ޤrΞ \웝,gv;&'.9mU.6&W)RnK~))5kD<.7-/VCŘxiz>ʭG!͑k \&)Ha4xodncSp)jc+&kί.[+84L:Sd,XEX E$}bH77;Q4N>Xbl1U囕ڽYv'56s4n'+F(|v?ϏϏD{C6*nk`0pu:7`KjJ݂Jd<'4۵r+9|S;w|dHh\ ETUq,n"Zl2$¢~f),*/"x<{Eoq3QZ,>BlLh )I4v>*vҊ8D||7Ό\LM,ڵ8zSlHz {B1`M!u#ƹL_aj58*md>g)V';WG+do:u:v&*MT>>^/Jl{3a.uφwNcy՛@`{x2&ctmUUaM*fA9"t;Jt;čULV/hc"`5a*{1YkSbk9=mTgh61٧_=Vݽ鎲bhU}6Pv7B~(xlԓږKѿgQΣ^Mv ,h?Z߽~gMUij}Gۀ~/gS"$b)LushGQIog:߽Ɨa.7;OL?OvuMiu{piZ0>+(KiMJ9SjXhoب-@@ܮ7R6XQU=ZL$H36rqVNV1IddD:r+"H]9ywf|85D5M#9ֻ_fd琜H-ssi;b\7?\?\? !"1AQa #023@qrBPRb$4sC?f QZ+}d.]c]KT2#t]+]˸g컆{.˸g컆{.˺ok!h<W1iXo1~v$OhDаi" `y" 9?*4$oF?$ԮkNWYOh4Ǐh(Gf^o2k^]+4{"&F#)L:EA{ hZ&nWE:'o84 aSYMkEqM%ycmZرm|teV0'uZxRu_oZ9 ӽiAUžeSYhuXPhg×^W>LQt\T< (*k0jfF`a`~kV}~h;*.7hoi4'LHWa PuCM&ϺAyKPv_,@9 ᥗzV=gVm/LیM/z4oDxGÞ=ylFK#wp/jM }HT疸T95:zkM߸1[ QxCx qWhdv՛2rS3܁oCD2J|:']ţ(]Z&"trL{u#)d;IMdMj `mX<,(V=L^]At~-Uc+Z޿P +Bu'I]PԺ:#`=:Gq<|hhܩRQ!0rn%?6?%E# .Aֻt1- . BM*dHSi07 Lbv`2phF+5LjgԁiʁuPGukoUQu70d:`[8sݨ! պچ:j4#~.3ѼnY ]H-(1retUU<˹ 7Mi ^衮m6[SG`vVޗx]/P5;1]uF{'cA5Ȭ70d v6+nZWn5FW#8Iem˸zS^ "*N*ʹ;B=ۢTzXWa+84Pk{:>.sܒz |Pzrd?rK5T{Mx"&WZ {Y[sm>J1mUKu(pt`qFhU@2"#=u555%70[9/bӛ9As8-8VNrcrsw - h:(ܫg/e? 1t!_B6Y #PVu(21S|J}8dvkM6;m9<^<v -]3/In]/*2^sj'@]5WNI=O> &pDDN݅V{W9RoZK!"2tr-._SK!r\Fڃ!A4emo6䥄sh`wsQ^̺ڳbw6*]]/U{.j[q] G("F4GzgfY@A/gO$|9)JXش#kWmv֕`a] K 96J mQV]GeĮۗl9\]1+N6RX8sHv!W镎fP[tm#'g?g.s\G;z TE><ƆIx%O>OW:hΛ!Y $o/l 9d2FGfGe Nʹdj=cϟx3$4'y*i|OKw2F&5;"FguVkv)=I˜m bn8^ Nkf gY\Un0) _(s^;~~Y n,ӝ[| n>Yطuָ96@r|S&IR`!O^YQ suأvW'D! Zj…>,-u_4UՎGo/nW,iUS-taW8p,i8sŊՖF?TSZU.&F7eP81b GPŮOwgUHSdVnvZJ]6?$]̲Vs$ .*( '6 mM~y }IX捃ÈS&qrfe5kvix&=h0~+#,*쎐S g\TlTҦj Wh؅6VZc6kn &y{]NM)| EH7d{tBȀ/7Lk56'hOaܟ&I]\"9;5uEFʝAP wҿcZu]Vt:S;'z#`)r{;S#jk@Yǰ7z0vGE+P4]=£\0̲#3E5Pc Hu9ta\ 1%7VYT_!NY0+h릉T1 Gr|8xp;0Zcv@ Cڥam cote5ԺI ,u!vl5KiqִTU6BiThb1j sgۓ#9Uf{);:7XY1^qNtN e_ 5g,Ԣc-v js̒l:pMjH:Bm$ڢc9'-D#L׊ ~Ѯ+j{i8dJ5X]VU3UDG iޜHsgg4=Z(n,Xf]jӒ\WpWuEL+7Fó(v0rhNqED|KyD-ɘVz*^h‰(G7ZllS@mLhE5tJ0>,SchTDRc>+ie|/-6k1hplg=To%i׷⢒'PXbdD6FI1'bGHfp4viLv\ݹK4۩9c:mMnͩt5},} .g>g-6ѷS$v* ~~~~sm__TZ˜.e+AUI@ܳձdHތb[+`FeԳ7 e5Z_G9 Þc{AXL_$`X.!B5UvGHS5f,f&66 J IݢFW&5FAءtoZ[A|5]x}N jڻZ(kt{eȲ,{nrN:C,|X;Ʊ)ON*ZB 'j+Cv2MAߏA9|&]ӣ~/o7Ɣ-'='SP4BhnK+=MA51D6@e$ari5,@oV9 wcGWnNKgei;Pk@aɢ8|ήa63qƇTJ0TWd]cIMT 97/Vsx;矐2Vat:..[b6Lu$#iGk - v[y ։`t;B)FkQ1* tl89wRXS FEZN(GꍆLbl/;َo. #=u r:|MkGu\q=;%Ƨ;_]EI;cAP a]j~LASl`~"3HH~XB6d.GYS:+;S_Umnޯ3qwǘִ]]F+_!hBmvjެ^qlNE5?,֚ɉ,A:s$'Ce4j$K{+=8q (,q\P9 ^*]+E$D/ 6aߵHܱǼs.7~mz3BVHAn[7a̼@9)%!Xc9S)<^F8MMOηѼ6Ѧo-xd@М YOJ_4N_mŝ۽F'uWLoC(.;rvý?;3CۚvMai|Ra&Z]`pnq#I拕M1q~ZL"7.Nc{M.J3iڣjowh5gikpj[vګr8(so{a'Z1%:Kk (,y&fh~LB*ڳ\JlKPYm(O/5S'|2Vv)8Q>/H9ׂuѬj; {Ne3_&vIuZ1690ҡ_/D,:Ykow_ZmY蛝{.t$e/Wqج68978_L;(,g\xTW>W'5p :aG21&55*WZI5+/D,1示nVg6yG:զ^RJUY,9<]t g9@cuVEIe*ie5v)U4y|TX=R`Κ!^?+)([g 2+75plS OlnIpڬV@)`9ZT7z؀WkōcxNDSx,Lbu#eٯ6@2 sU$-}RJ`Br U,=%s'Tyjg*:!<./eNjjq .˝~IOHNz&5w:栲btRհtsEx$9^yF’unDY5]'+<͖Pk=gSP1TDDRQ<үiR()|ܢseűK£Y5^CNmRe=ʕRT=#IS'6.2@ fxX3T(1(e Iq{/n= ȓSmir.q+s(1_bWƾR+W+S騆]Fٕ 4*/ILy’l Ik)P+k.cuh2w\A RoAX八nemJ,\te.%|Ŏl&4ZPOP*J5E] jk?1s/ #4g`uők%?/uBM/u7fBŴVb^!8*q #Ḫ՘q.PŊ<,ԛ׽s( P[M1z+db]w.eAC s`^U)\U Y^5% :.ff^dL?M vрUަfa\Zŝ-ݫ/8RU,(<0iϼ?D;KqeRP*B 7KWvp̰ؕT%؏<T`.4fF,8,/P% ҜkA J^mߖŮW%sy3DQ9sd6a'"+D*[oP"b\FħTlXH qYakp}fSf G )wc7+r)Mڢb KX] ya&4_]՛ aWCGHrLO(7ŸEW2\(VML vA"T45־%LK+9\鮲!AM.x"5):N1 [ v$mEthgXOXd;KL5 r~iZ8*Ð*=ʋ~#DW"3Ys$[kY{"8++;o96)*13/g0MV3̸(xA0h*^tOO"V| pQB3"Ζ ijޓʈO1:W9]MʇyOHRPJ j튅klW8ex`+\yZɘ@yJ `0s6^L ZE0][d8 |Y&^(Pd ʕFM[K0@ıe_뾑^/L`KKG}gP@eiFdeO^vo-CRA F5ӭzC6L|0W>LF%wN" T5B̿X)~%M9Jx7)3q"CeVN.,5qXkuqkVVgbj:7=yh3 +YC뼯H^)@8j$ &Jp۝̰r@qϩ7cqd`&"?MܪTcO#r+;iuR9Ȩ#s0k+5JӆLacmY^X) Zd^͜ʬ0Ua4!tu?MJR#p6`xVT۫VU`0MEsؔN)L P6!a+ \wA/W3B]"U͸|BKKtUrVz?]<@by7ӂ-eߘ5 %5WɼӖ\>qYq~\VzL.Rm(͡4/ >Ie^(XIG%`lRk|GWc_(0:~))7XQ9_ Ls f厠Z5ǘs=w.a)v?' >hf-(-0d;ϤY*لS5uP~aX]3R K--Qfc"8B/ 5|Sfpp&WPNI:L@ԓJr_V9uK{)7U`ԻSq>OUg%LepկzC&WAPe[)T+PJTqcIC65~gβmyk(zV[ΥHq%^-|)Hu,Kʫ-֯1!XcD-c*]^XaGw@Iml[2"lO0 h2xY +YPVեi bnX9ŀo81Yc> =8%W:ћ?iJ^#(XX}lѯ_ĵ74 lֳKjmAhqUĤ~ĺW _&h$e gF#*J kT);DkUn{lI+1/&!J"d/kCNᆉYw;V[FE, WjiL$NzDoE_TRgPjXP@Vc$WiI5a3o2e˄Ae0-k7-+i]I~1[+2Q޶BjRd }9f^:djO']"g7r]FqNaįq/%R70ڇ*@FY6ܔE'(_SY~gJ$ JJa 7^0htt`9 Д2k_XU{3./aYU&,R+Ŵ.IN΍ftT/#﹃'bQҶѓHE&kE\dL2ʘ# b/$Db[=w ѫ.n7B=%VBḶGg9|rtzL^gaz?DjVaFHajU`_Vko#<2KHO,ɞ%а~}ٔؖ` 4(lrvt8@C_NLxZOLjZ06R(&wH-Nzq(t#$f%Rc3C=&`˕N" `(⨚\ fVE N#ec1jK@4%@8d/dC`!fUD (5O2ʇct-8k]حN~Ve"^?k0*c}eLeu W*U˶JRw,MNJE}ϡj*2XT-QymnwfkRbRsq&ȡ `L8~Ɏ1F>F `$Vx.hyc-M~= b=w}q _OHKK!5}'[ P,ɧ~]$iR_kU/,_Z M|4ҵ,'jlgw|Լc5ĵ(^ d^' ^yLh]p^jkeі|lͧU#yr.`ʭ*K:K")PrJ[U؃iOįͫ,&4:^y 紻.W~OȔ}qݒ.D)K*u+!Q,{VeoGL(]T+ Ff(yHmp4h*q7nR*v_],Ϭ'q/ ZiQVz̷[#jj1iBXN Bn^5pJa~HѨ%y>=}Q'Uu'6XuXr`^W>%o_YM)܂]:k E(]*ڡ~f@DU[zb}aԳQQwi01 R^`Žl>DeeY#SjuȯSG,Uhx{|?UH>(U&/'ٳ -u0~F2T#lB-:b6jo,a?k􋮔kqkww:~̲1UoX Y.%z=ҩqat,Z*_ \P@T>42IOIrpG\.+Dcz!^j@]K+F+eDT05ObL -A*9BPA ,rBJVp`G>]8WOEW*Z:NV. G$tm9&$rR``2~:fu-PDq.KPT@s Vʭ˂3N}F@_q *1a3ct9HX~&FWaTqnmǤmu׷,q@ ܗ%t?앇.9r..B[2VuEx0%:qbU&NOU5l26}wnSc'H8l=t6TRhX$K`9E8Ԍd.j'~k ]- %.o2)j"V.xs:rH QXX+tیn!5,Az D@U  p [ PRd yp*/]& #njkʭbWƯdԫKUCFY11'tzs+߬pRJlU]J'*5r&!6JRK*Kxū+ng2P}zCf ӿ00}ADfb,T['P{H>эoL>A C@T)z͝ZV~mX@x2S0xo ua}ɼoQ=.UG)(Zk?[q/:1"$nTŀ^~zW]:ࠚ{D4+E]=%uT8kP4<ĚW*u7ŇM5CN :T̺c JԱk2È%SJ-X@4YtBiw)ŇopV^ПQei2ܪ[2WHX%bxo8ot\J87QoE͸"5`[]EFPk89˗_1,m[̰M싧NUj80,B_bħ. -Ǚ-<@F3 dUxn 6UzBf`t޼·fc6y31+F-+s)Ɇ1p:b`-\6*cQRgDD{?o_ D~+˺+EgJV>ī4/@z*v"!g[Q %-YP+w6gLbqedlѷ04( 3g)ܭpx q6q|6SQ'5~ X&e&7EơRw|8|  mc"ݯx͊ll%>^.RfUjXݼ4,dFxjw_Hr}%ޏ.؃,扏TЃS(HU5WUxDMӋ7)F_\7xf,Yg̩Dd Pj;ITInƉtH(_kbVe)+h]C`qw$7Pٺ\0eƹD`(.syCWbN/Q~56u>u&,Z 5xZ{0^>ﴧl@IuezƱOXa+q.u\b+]5 \Q87>r6p [WKl4+DifP]krXv@ > _ڈi=eJ\CqH\u.NY+*>O̯NL~fw:K*=Pyb^R(/ʽo2ooIҷ>;ݛ+/ ̗s1]bS"cM3wg1pkDQ`\ܳL;le2BKnwToW4\b W07bg\u~f_<ž>x. :p@Qj#U4<3(Tl\ ˵tXb= \Or]7kkyc. ׯˆ(7ȗܹo-}NW>}j-zsW-z?h+9\K6Rm|W[j5t.~!qrcψc(IeGP" % e\GBQaetp,eE.B995.3U7Ի+@ *EZ{+cd,-X1YwOܠuw*WdUBKbq-ŴW!).*U0t.̙ S,V[Kss :L}Ro JT,h}xٌT+/ouk-nlM}c_Joq #0T.epמ˰n vn\ਯɿk 梱%^b!Gw/k(i|5&iԾ7=fAo`㧤jU{̳@ƍ? HJ`ӤB+~PWi*jy֤~HYsu4OǒPPo8 0-)3 kZ;@_v 4cOn엚)U3ؚ1Eem9T\X63.p1po:!g5vM7wz3ԩaR1V헧ya4 n7 Ш,j{6_Z8qbWw,hkdA=)$__'T)NtFu!׃yP? /9GRv>z}'VD-3PUW/7 tǀvnR=}lҁ=Sl<4ޯr$Y1*mOX-~ѝLi~%$" 8`57-U+O4dvC-ea8䶾"Hp`y]0FB2pr F|meUa7䣝U-_O("b?>%sYV#T1SĽ Zp=V6~?| Eec[L܅+0-?+ן,=۫s&jɨM!u\TIXK0bUPxG2,l><be ޥ&}" !c5]nitjZ*tmgGfL%XN+Cn)}-4Vz_1KbR3 h\ʜ# %*(ssDz%ץȱЪgKsi=D\xɄU똬Z#*.s@Qk-l%k LX@*1)Ү/ G pAR@ @B @ `@ $ рE  #\Zd=C'ē8,Z| D/yV H4QpS[?)6#()*kߕ d<LJّ1S ֖'UeFD"Ze)Ѝ ?y43E=/`ҙ挾mw`Њ2`r3QD`4墂|1ITZ fw&:ʐkdj$&Wdfe@`l/Ѭ͊x \+KϥJ:87/NfW\S,iB3:^aAz-ZB)1p(ZЅ2+9cahQ0"Q i!] (Z4:s9,=F 4bx֞a^_}9F3hy͠1/W  d00]4 3v9a+mB% P6/ ೸plw{Qh"&Woǡ _4E=>"H:>V01}W(Ŝ L,ŀ2~ FE!%mC^L|S7p 5iK!`XkPn .C 0- 񨕓QY_`({ PCwW#F+f097a5FCPW([7 1'605!@a,^?߉妀`x`\F/# fm Mn9- ,q30R! W-+.Ee3VEL[FĀlSJw\|_2e&d>xzThz9=LC'U@by@/#/fiD ˜)r -r,珈$6-RkPFAhF?C+j 2#dJ7w/DAd ? һ!Vur^{T*#EGM/t-f~MYhDNG'GCP#9 A}&ǰ Tq8b7'`+z]Z$n܊ > 8 qq7B+D&~5f}Z3 t(&퍑 -@b:y$N#O\ |) ))aZ `z;V$wJ`umCr!{ {WŖS'V\#k@XӜaK4H^ąBɸSűZ o2-_0 p=U7P#:DQ1: 3/A|ElPf$Sncerd,'@Fb_d:y,x1M2JWU; T9\M&Rl.TD/[S Pk`1b4$0\)4nP&+1A1W2 qСm0я0G$?1BfMsо14;RݥK/pҺѸ1`S* Y m? Fa~s~ޖYE@/U/I;ơpM45 z%j 2W͑.yV VB(8E#vi#ă2D(d:2 Xކ R5RHe n^ @1!7QA^>}|N;4Bg#C&7@>â, E$)Z~!Xof} ׁ UlrH2ĀML/̅- 1i-y';$7}_"21IjMa}fm2ގ;LJ?-nWȿ>pZO|2|sw5Fك؅ŘL% ̔@KK| 2(=/lX}_k h H6CR*8x~8Q ]A57=ADi;b3.6MBɣ' (ZauJ U H@TRfS@LA05 .%ȃf e(z&sV4Jf[& 8xZIJ ;kDU2#AW#hRQ& px&onǨOZ=N\ۘ, {y2C+rB A^}e!9*:?V RW d]~bXzBZRU ޥJ'zT Rz(sY7o1$9gHJpJB@nHDg#x+p!JVQ?ߢ#c&#_֢ !K]#C"NEJqBІM_(ad::OC% )x/=t& +P7K;V ) \8YYH3UkT ģl8Ax#C 5`s1 @:?$(\n!_B\;'1#apO'FK4k%`Acm GlQP^ci̘0`[JVj>'ymgS3 eH<L#hAExױD*,~d c * 3-f`{#]cR v 4偩5ɞ.D ۈ dbu4xzP:ŝ6wC]o7Tr3$8J \1J^rFLe`hA`QKf2+G:xq$=#]m+*@827 0LX /Bk ;1-iiL= A&MƫD*ڴ#K"̫&w0kc~ *qTk ~8M- ۆ%H;v 8.?Kv6@Z0AE2hZt@h!T E&nO3etOd^f N@kH8iPyzJa3ZbY␖+’l0`%M S.$gY%B6fA@Vŋ ő J87 :dX!Ì6, { P*UҎk*'ĹqӢ%?Bsi?,%Vq1q]G/(\ϤS328E ?NF5Fb}Qp\MO!dH/J+Y|I.D>e%gX޹xoQFKvxi>$bD(UJkd/|CTڊu&!`U^騾#/ ¡ad_TTegvR #Uxt[T؄5 Q +,|h*W S+ȕ'YBLm{?pK 藭b5L 58Rh*1[C ':l{29p&) dJ @Njٓ *AK Ҵq*g&ZN M 3(rIY |!l;CbJTo#f|D,%[3qQ)h@@GDwyp$?f8fyK}YZL,p(?!N6j2&1^Rsq"Ό-a, fzp**S!U=(I./<ǺvAI>@XAH|gq#kvI]BV50 Fx0#E 00xxNQ5K*_ _-ԈU(F%ec yCd 94fMo >2T6<#d@טjTN qXF%@9FA]#˴U>f b_p25'Cc¡-A >ȵgFGy _16l!cLr*K&֡VLXV~ȽL>D?.,;=gҵEpC. 0^U 9Y*ɐL"r7*JLK gE6sR8oh!a4ՉFv jSP(l$Rҹ+T0T 9ԍoɆ!BP3(002,\nDe.*:u/D+7(#1kV v@9hEE.{Xq6дRa>2Ή1/ T6hP!  McipJHrgz/:/ntiv0!0wN 7^m. [$HW(\V^Ⴀ\FʆIW#,/09(/tH"k&|,N 7xB͹_R0QRt E !`{b_}?@D $4A%CM0@RWB+P ihb"KC e. op >60y*Ѕ_PWemXOd+(0~}3)1yz579rǑ!/B5.ed1Ph"⥉r0;,7Fc*u!AB1f(C˞U@#=NljS.LT9N}zIr;ܩи}'S6죄(ѿ' i 1gs%̧ᣓl"+W;0Oyh)pD\I ǢRaKQc0l{!2}(&_dindmF6ΓHb6wH2kX\`2 bC_ a*1mÔvVZ0b AxB G ?bsFbgw.>R^` ۳Rph> +DT_tC5Y8)"PJ`Qaj8[~1H!>]C(KĶf;%Z͙q~$x*z29 E@[Bx! AY/8hXЀIM L9)Ћ %Q%-9Z2,̖{ Ն!LlįRTE tВ҈W0!]QTц@$7p-6F0?vҵzF^E@^9x"\LE03~"Ҡ@_nVgGl̼#W~}E>R]}4zJƱƠji/= e|[,T[abʳfp*L+Aي$ͩ/b¶TTxFq<ӶyHxQ )Eҗa⡀0jF@D0| 'N-&­yRkd56kIx|SBMWuV&?yQ_N%'Kaw~6Ig{C(ou6 ji ɿR\żs ϔ ?zKQE3'Vb,U'}!']jk4-=*qK5CrXE 6Qu31r˱Ȋ@KT+vL%tq^&B&> #OHAL0K h 0, й#CB4ݲ#mw7[༊~?*r9HspO)~ N >yzTL. ߸ qx@; çdcP80D'غ9;6QnWC6-ziuϖLv>^;tr[H(\(]($J\2+Fr  Cmȳn4i6١$ybzC{eѭ>qcI12t\'$ظY  |ueBy8_ Qʧ,Qp_YIU lqtiU .No aݓ3Ca?҂!`'ȴd3^s2Rr{ pHKe؎n;qG,yTRG<eLJHe]'0q꺬Գ%GD f6$Gd% f7ά{su73rAUظ+55Z|h>?a WTMb-XfSɎʕ kݜԽF!)KtH!d:*(ckvHd= > Default page
  • Your IP: 216.73.216.84
  • Server IP: 13.204.207.56
  • Server: Linux ip-172-31-43-243 5.15.0-1084-aws #91~20.04.1-Ubuntu SMP Fri May 2 06:59:36 UTC 2025 x86_64
  • Server Software: Apache/2.4.41 (Ubuntu)
  • PHP Version: 7.4.33
  • Buat File | Buat Folder
Edit File: CGI.pm
tag # sub textarea { my($self,@p) = self_or_default(@_); my($name,$default,$rows,$cols,$override,$tabindex,@other) = rearrange([NAME,[DEFAULT,VALUE],ROWS,[COLS,COLUMNS],[OVERRIDE,FORCE],TABINDEX],@p); my($current)= $override ? $default : (defined($self->param($name)) ? $self->param($name) : $default); $name = defined($name) ? $self->_maybe_escapeHTML($name) : ''; $current = defined($current) ? $self->_maybe_escapeHTML($current) : ''; my($r) = $rows ? qq/ rows="$rows"/ : ''; my($c) = $cols ? qq/ cols="$cols"/ : ''; my($other) = @other ? " @other" : ''; $tabindex = $self->element_tab($tabindex); return qq{}; } #### Method: button # Create a javascript button. # Parameters: # $name -> (optional) Name for the button. (-name) # $value -> (optional) Value of the button when selected (and visible name) (-value) # $onclick -> (optional) Text of the JavaScript to run when the button is # clicked. # Returns: # A string containing a tag #### sub button { my($self,@p) = self_or_default(@_); my($label,$value,$script,$tabindex,@other) = rearrange([NAME,[VALUE,LABEL], [ONCLICK,SCRIPT],TABINDEX],@p); $label=$self->_maybe_escapeHTML($label); $value=$self->_maybe_escapeHTML($value,1); $script=$self->_maybe_escapeHTML($script); $script ||= ''; my($name) = ''; $name = qq/ name="$label"/ if $label; $value = $value || $label; my($val) = ''; $val = qq/ value="$value"/ if $value; $script = qq/ onclick="$script"/ if $script; my($other) = @other ? " @other" : ''; $tabindex = $self->element_tab($tabindex); return $XHTML ? qq() : qq(); } #### Method: submit # Create a "submit query" button. # Parameters: # $name -> (optional) Name for the button. # $value -> (optional) Value of the button when selected (also doubles as label). # $label -> (optional) Label printed on the button(also doubles as the value). # Returns: # A string containing a tag #### sub submit { my($self,@p) = self_or_default(@_); my($label,$value,$tabindex,@other) = rearrange([NAME,[VALUE,LABEL],TABINDEX],@p); $label=$self->_maybe_escapeHTML($label); $value=$self->_maybe_escapeHTML($value,1); my $name = $NOSTICKY ? '' : 'name=".submit" '; $name = qq/name="$label" / if defined($label); $value = defined($value) ? $value : $label; my $val = ''; $val = qq/value="$value" / if defined($value); $tabindex = $self->element_tab($tabindex); my($other) = @other ? "@other " : ''; return $XHTML ? qq() : qq(); } #### Method: reset # Create a "reset" button. # Parameters: # $name -> (optional) Name for the button. # Returns: # A string containing a tag #### sub reset { my($self,@p) = self_or_default(@_); my($label,$value,$tabindex,@other) = rearrange(['NAME',['VALUE','LABEL'],TABINDEX],@p); $label=$self->_maybe_escapeHTML($label); $value=$self->_maybe_escapeHTML($value,1); my ($name) = ' name=".reset"'; $name = qq/ name="$label"/ if defined($label); $value = defined($value) ? $value : $label; my($val) = ''; $val = qq/ value="$value"/ if defined($value); my($other) = @other ? " @other" : ''; $tabindex = $self->element_tab($tabindex); return $XHTML ? qq() : qq(); } #### Method: defaults # Create a "defaults" button. # Parameters: # $name -> (optional) Name for the button. # Returns: # A string containing a tag # # Note: this button has a special meaning to the initialization script, # and tells it to ERASE the current query string so that your defaults # are used again! #### sub defaults { my($self,@p) = self_or_default(@_); my($label,$tabindex,@other) = rearrange([[NAME,VALUE],TABINDEX],@p); $label=$self->_maybe_escapeHTML($label,1); $label = $label || "Defaults"; my($value) = qq/ value="$label"/; my($other) = @other ? " @other" : ''; $tabindex = $self->element_tab($tabindex); return $XHTML ? qq() : qq//; } #### Method: comment # Create an HTML # Parameters: a string sub comment { my($self,@p) = self_or_CGI(@_); return ""; } #### Method: checkbox # Create a checkbox that is not logically linked to any others. # The field value is "on" when the button is checked. # Parameters: # $name -> Name of the checkbox # $checked -> (optional) turned on by default if true # $value -> (optional) value of the checkbox, 'on' by default # $label -> (optional) a user-readable label printed next to the box. # Otherwise the checkbox name is used. # Returns: # A string containing a field #### sub checkbox { my($self,@p) = self_or_default(@_); my($name,$checked,$value,$label,$labelattributes,$override,$tabindex,@other) = rearrange([NAME,[CHECKED,SELECTED,ON],VALUE,LABEL,LABELATTRIBUTES, [OVERRIDE,FORCE],TABINDEX],@p); $value = defined $value ? $value : 'on'; if (!$override && ($self->{'.fieldnames'}->{$name} || defined $self->param($name))) { $checked = grep($_ eq $value,$self->param($name)) ? $self->_checked(1) : ''; } else { $checked = $self->_checked($checked); } my($the_label) = defined $label ? $label : $name; $name = $self->_maybe_escapeHTML($name); $value = $self->_maybe_escapeHTML($value,1); $the_label = $self->_maybe_escapeHTML($the_label); my($other) = @other ? "@other " : ''; $tabindex = $self->element_tab($tabindex); $self->register_parameter($name); return $XHTML ? CGI::label($labelattributes, qq{$the_label}) : qq{$the_label}; } # Escape HTML sub escapeHTML { require HTML::Entities; # hack to work around earlier hacks push @_,$_[0] if @_==1 && $_[0] eq 'CGI'; my ($self,$toencode,$newlinestoo) = CGI::self_or_default(@_); return undef unless defined($toencode); my $encode_entities = $ENCODE_ENTITIES; $encode_entities .= "\012\015" if ( $encode_entities && $newlinestoo ); return HTML::Entities::encode_entities($toencode,$encode_entities); } # unescape HTML -- used internally sub unescapeHTML { require HTML::Entities; # hack to work around earlier hacks push @_,$_[0] if @_==1 && $_[0] eq 'CGI'; my ($self,$string) = CGI::self_or_default(@_); return undef unless defined($string); return HTML::Entities::decode_entities($string); } # Internal procedure - don't use sub _tableize { my($rows,$columns,$rowheaders,$colheaders,@elements) = @_; my @rowheaders = $rowheaders ? @$rowheaders : (); my @colheaders = $colheaders ? @$colheaders : (); my($result); if (defined($columns)) { $rows = int(0.99 + @elements/$columns) unless defined($rows); } if (defined($rows)) { $columns = int(0.99 + @elements/$rows) unless defined($columns); } # rearrange into a pretty table $result = ""; my($row,$column); unshift(@colheaders,'') if @colheaders && @rowheaders; $result .= "" if @colheaders; for (@colheaders) { $result .= ""; } for ($row=0;$row<$rows;$row++) { $result .= ""; $result .= "" if @rowheaders; for ($column=0;$column<$columns;$column++) { $result .= "" if defined($elements[$column*$rows + $row]); } $result .= ""; } $result .= "
$_
$rowheaders[$row]" . $elements[$column*$rows + $row] . "
"; return $result; } #### Method: radio_group # Create a list of logically-linked radio buttons. # Parameters: # $name -> Common name for all the buttons. # $values -> A pointer to a regular array containing the # values for each button in the group. # $default -> (optional) Value of the button to turn on by default. Pass '-' # to turn _nothing_ on. # $linebreak -> (optional) Set to true to place linebreaks # between the buttons. # $labels -> (optional) # A pointer to a hash of labels to print next to each checkbox # in the form $label{'value'}="Long explanatory label". # Otherwise the provided values are used as the labels. # Returns: # An ARRAY containing a series of fields #### sub radio_group { my($self,@p) = self_or_default(@_); $self->_box_group('radio',@p); } #### Method: checkbox_group # Create a list of logically-linked checkboxes. # Parameters: # $name -> Common name for all the check boxes # $values -> A pointer to a regular array containing the # values for each checkbox in the group. # $defaults -> (optional) # 1. If a pointer to a regular array of checkbox values, # then this will be used to decide which # checkboxes to turn on by default. # 2. If a scalar, will be assumed to hold the # value of a single checkbox in the group to turn on. # $linebreak -> (optional) Set to true to place linebreaks # between the buttons. # $labels -> (optional) # A pointer to a hash of labels to print next to each checkbox # in the form $label{'value'}="Long explanatory label". # Otherwise the provided values are used as the labels. # Returns: # An ARRAY containing a series of fields #### sub checkbox_group { my($self,@p) = self_or_default(@_); $self->_box_group('checkbox',@p); } sub _box_group { my $self = shift; my $box_type = shift; my($name,$values,$defaults,$linebreak,$labels,$labelattributes, $attributes,$rows,$columns,$rowheaders,$colheaders, $override,$nolabels,$tabindex,$disabled,@other) = rearrange([NAME,[VALUES,VALUE],[DEFAULT,DEFAULTS],LINEBREAK,LABELS,LABELATTRIBUTES, ATTRIBUTES,ROWS,[COLUMNS,COLS],[ROWHEADERS,ROWHEADER],[COLHEADERS,COLHEADER], [OVERRIDE,FORCE],NOLABELS,TABINDEX,DISABLED ],@_); my($result,$checked,@elements,@values); @values = $self->_set_values_and_labels($values,\$labels,$name); my %checked = $self->previous_or_default($name,$defaults,$override); # If no check array is specified, check the first by default $checked{$values[0]}++ if $box_type eq 'radio' && !%checked; $name=$self->_maybe_escapeHTML($name); my %tabs = (); if ($TABINDEX && $tabindex) { if (!ref $tabindex) { $self->element_tab($tabindex); } elsif (ref $tabindex eq 'ARRAY') { %tabs = map {$_=>$self->element_tab} @$tabindex; } elsif (ref $tabindex eq 'HASH') { %tabs = %$tabindex; } } %tabs = map {$_=>$self->element_tab} @values unless %tabs; my $other = @other ? "@other " : ''; my $radio_checked; # for disabling groups of radio/checkbox buttons my %disabled; for (@{$disabled}) { $disabled{$_}=1; } for (@values) { my $disable=""; if ($disabled{$_}) { $disable="disabled='1'"; } my $checkit = $self->_checked($box_type eq 'radio' ? ($checked{$_} && !$radio_checked++) : $checked{$_}); my($break); if ($linebreak) { $break = $XHTML ? "
" : "
"; } else { $break = ''; } my($label)=''; unless (defined($nolabels) && $nolabels) { $label = $_; $label = $labels->{$_} if defined($labels) && defined($labels->{$_}); $label = $self->_maybe_escapeHTML($label,1); $label = "$label" if $disabled{$_}; } my $attribs = $self->_set_attributes($_, $attributes); my $tab = $tabs{$_}; $_=$self->_maybe_escapeHTML($_); if ($XHTML) { push @elements, CGI::label($labelattributes, qq($label)).${break}; } else { push(@elements,qq/${label}${break}/); } } $self->register_parameter($name); return wantarray ? @elements : "@elements" unless defined($columns) || defined($rows); return _tableize($rows,$columns,$rowheaders,$colheaders,@elements); } #### Method: popup_menu # Create a popup menu. # Parameters: # $name -> Name for all the menu # $values -> A pointer to a regular array containing the # text of each menu item. # $default -> (optional) Default item to display # $labels -> (optional) # A pointer to a hash of labels to print next to each checkbox # in the form $label{'value'}="Long explanatory label". # Otherwise the provided values are used as the labels. # Returns: # A string containing the definition of a popup menu. #### sub popup_menu { my($self,@p) = self_or_default(@_); my($name,$values,$default,$labels,$attributes,$override,$tabindex,@other) = rearrange([NAME,[VALUES,VALUE],[DEFAULT,DEFAULTS],LABELS, ATTRIBUTES,[OVERRIDE,FORCE],TABINDEX],@p); my($result,%selected); if (!$override && defined($self->param($name))) { $selected{$self->param($name)}++; } elsif (defined $default) { %selected = map {$_=>1} ref($default) eq 'ARRAY' ? @$default : $default; } $name=$self->_maybe_escapeHTML($name); # RT #30057 - ignore -multiple, if you need this # then use scrolling_list @other = grep { $_ !~ /^multiple=/i } @other; my($other) = @other ? " @other" : ''; my(@values); @values = $self->_set_values_and_labels($values,\$labels,$name); $tabindex = $self->element_tab($tabindex); $name = q{} if ! defined $name; $result = qq/"; return $result; } #### Method: optgroup # Create a optgroup. # Parameters: # $name -> Label for the group # $values -> A pointer to a regular array containing the # values for each option line in the group. # $labels -> (optional) # A pointer to a hash of labels to print next to each item # in the form $label{'value'}="Long explanatory label". # Otherwise the provided values are used as the labels. # $labeled -> (optional) # A true value indicates the value should be used as the label attribute # in the option elements. # The label attribute specifies the option label presented to the user. # This defaults to the content of the \n/; for (@values) { if (/_set_attributes($_, $attributes); my($label) = $_; $label = $labels->{$_} if defined($labels) && defined($labels->{$_}); $label=$self->_maybe_escapeHTML($label); my($value)=$self->_maybe_escapeHTML($_,1); $result .= $labeled ? $novals ? "$label\n" : "$label\n" : $novals ? "$label\n" : "$label\n"; } } $result .= ""; return $result; } #### Method: scrolling_list # Create a scrolling list. # Parameters: # $name -> name for the list # $values -> A pointer to a regular array containing the # values for each option line in the list. # $defaults -> (optional) # 1. If a pointer to a regular array of options, # then this will be used to decide which # lines to turn on by default. # 2. Otherwise holds the value of the single line to turn on. # $size -> (optional) Size of the list. # $multiple -> (optional) If set, allow multiple selections. # $labels -> (optional) # A pointer to a hash of labels to print next to each checkbox # in the form $label{'value'}="Long explanatory label". # Otherwise the provided values are used as the labels. # Returns: # A string containing the definition of a scrolling list. #### sub scrolling_list { my($self,@p) = self_or_default(@_); my($name,$values,$defaults,$size,$multiple,$labels,$attributes,$override,$tabindex,@other) = rearrange([NAME,[VALUES,VALUE],[DEFAULTS,DEFAULT], SIZE,MULTIPLE,LABELS,ATTRIBUTES,[OVERRIDE,FORCE],TABINDEX],@p); my($result,@values); @values = $self->_set_values_and_labels($values,\$labels,$name); $size = $size || scalar(@values); my(%selected) = $self->previous_or_default($name,$defaults,$override); my($is_multiple) = $multiple ? qq/ multiple="multiple"/ : ''; my($has_size) = $size ? qq/ size="$size"/: ''; my($other) = @other ? " @other" : ''; $name=$self->_maybe_escapeHTML($name); $tabindex = $self->element_tab($tabindex); $result = qq/"; $self->register_parameter($name); return $result; } #### Method: hidden # Parameters: # $name -> Name of the hidden field # @default -> (optional) Initial values of field (may be an array) # or # $default->[initial values of field] # Returns: # A string containing a #### sub hidden { my($self,@p) = self_or_default(@_); # this is the one place where we departed from our standard # calling scheme, so we have to special-case (darn) my(@result,@value); my($name,$default,$override,@other) = rearrange([NAME,[DEFAULT,VALUE,VALUES],[OVERRIDE,FORCE]],@p); my $do_override = 0; if ( ref($p[0]) || substr($p[0],0,1) eq '-') { @value = ref($default) ? @{$default} : $default; $do_override = $override; } else { for ($default,$override,@other) { push(@value,$_) if defined($_); } undef @other; } # use previous values if override is not set my @prev = $self->param($name); @value = @prev if !$do_override && @prev; $name=$self->_maybe_escapeHTML($name); for (@value) { $_ = defined($_) ? $self->_maybe_escapeHTML($_,1) : ''; push @result,$XHTML ? qq() : qq(); } return wantarray ? @result : join('',@result); } #### Method: image_button # Parameters: # $name -> Name of the button # $src -> URL of the image source # $align -> Alignment style (TOP, BOTTOM or MIDDLE) # Returns: # A string containing a #### sub image_button { my($self,@p) = self_or_default(@_); my($name,$src,$alignment,@other) = rearrange([NAME,SRC,ALIGN],@p); my($align) = $alignment ? " align=\L\"$alignment\"" : ''; my($other) = @other ? " @other" : ''; $name=$self->_maybe_escapeHTML($name); return $XHTML ? qq() : qq//; } #### Method: self_url # Returns a URL containing the current script and all its # param/value pairs arranged as a query. You can use this # to create a link that, when selected, will reinvoke the # script with all its state information preserved. #### sub self_url { my($self,@p) = self_or_default(@_); return $self->url('-path_info'=>1,'-query'=>1,'-full'=>1,@p); } # This is provided as a synonym to self_url() for people unfortunate # enough to have incorporated it into their programs already! sub state { &self_url; } #### Method: url # Like self_url, but doesn't return the query string part of # the URL. #### sub url { my($self,@p) = self_or_default(@_); my ($relative,$absolute,$full,$path_info,$query,$base,$rewrite) = rearrange(['RELATIVE','ABSOLUTE','FULL',['PATH','PATH_INFO'],['QUERY','QUERY_STRING'],'BASE','REWRITE'],@p); my $url = ''; $full++ if $base || !($relative || $absolute); $rewrite++ unless defined $rewrite; my $path = $self->path_info; my $script_name = $self->script_name; my $request_uri = $self->request_uri || ''; my $query_str = $query ? $self->query_string : ''; $script_name =~ s/\?.*$//s; # remove query string $request_uri =~ s/\?.*$//s; # remove query string $request_uri = unescape($request_uri); my $uri = $rewrite && $request_uri ? $request_uri : $script_name; if ( defined( $ENV{PATH_INFO} ) ) { # IIS sometimes sets PATH_INFO to the same value as SCRIPT_NAME so only sub it out # if SCRIPT_NAME isn't defined or isn't the same value as PATH_INFO $uri =~ s/\Q$ENV{PATH_INFO}\E$// if ( ! defined( $ENV{SCRIPT_NAME} ) or $ENV{PATH_INFO} ne $ENV{SCRIPT_NAME} ); # if we're not IIS then keep to spec, the relevant info is here: # https://tools.ietf.org/html/rfc3875#section-4.1.13, namely # "No PATH_INFO segment (see section 4.1.5) is included in the # SCRIPT_NAME value." (see GH #126, GH #152, GH #176) if ( ! $IIS ) { $uri =~ s/\Q$ENV{PATH_INFO}\E$//; } } if ($full) { my $protocol = $self->protocol(); $url = "$protocol://"; my $vh = http('x_forwarded_host') || http('host') || ''; $vh =~ s/^.*,\s*//; # x_forwarded_host may be a comma-separated list (e.g. when the request has # passed through multiple reverse proxies. Take the last one. $vh =~ s/\:\d+$//; # some clients add the port number (incorrectly). Get rid of it. $url .= $vh || server_name(); my $port = $self->virtual_port; # add the port to the url unless it's the protocol's default port $url .= ':' . $port unless (lc($protocol) eq 'http' && $port == 80) or (lc($protocol) eq 'https' && $port == 443); return $url if $base; $url .= $uri; } elsif ($relative) { ($url) = $uri =~ m!([^/]+)$!; } elsif ($absolute) { $url = $uri; } $url .= $path if $path_info and defined $path; $url .= "?$query_str" if $query and $query_str ne ''; $url ||= ''; $url =~ s/([^a-zA-Z0-9_.%;&?\/\\:+=~-])/sprintf("%%%02X",ord($1))/eg; return $url; } #### Method: cookie # Set or read a cookie from the specified name. # Cookie can then be passed to header(). # Usual rules apply to the stickiness of -value. # Parameters: # -name -> name for this cookie (optional) # -value -> value of this cookie (scalar, array or hash) # -path -> paths for which this cookie is valid (optional) # -domain -> internet domain in which this cookie is valid (optional) # -secure -> if true, cookie only passed through secure channel (optional) # -expires -> expiry date in format Wdy, DD-Mon-YYYY HH:MM:SS GMT (optional) #### sub cookie { my($self,@p) = self_or_default(@_); my($name,$value,$path,$domain,$secure,$expires,$httponly,$max_age,$samesite) = rearrange([NAME,[VALUE,VALUES],PATH,DOMAIN,SECURE,EXPIRES,HTTPONLY,'MAX-AGE',SAMESITE],@p); require CGI::Cookie; # if no value is supplied, then we retrieve the # value of the cookie, if any. For efficiency, we cache the parsed # cookies in our state variables. unless ( defined($value) ) { $self->{'.cookies'} = CGI::Cookie->fetch; # If no name is supplied, then retrieve the names of all our cookies. return () unless $self->{'.cookies'}; return keys %{$self->{'.cookies'}} unless $name; return () unless $self->{'.cookies'}->{$name}; return $self->{'.cookies'}->{$name}->value if defined($name) && $name ne ''; } # If we get here, we're creating a new cookie return undef unless defined($name) && $name ne ''; # this is an error my @param; push(@param,'-name'=>$name); push(@param,'-value'=>$value); push(@param,'-domain'=>$domain) if $domain; push(@param,'-path'=>$path) if $path; push(@param,'-expires'=>$expires) if $expires; push(@param,'-secure'=>$secure) if $secure; push(@param,'-httponly'=>$httponly) if $httponly; push(@param,'-max_age'=>$max_age) if $max_age; push(@param,'-samesite'=>$samesite) if $samesite; return CGI::Cookie->new(@param); } sub parse_keywordlist { my($self,$tosplit) = @_; $tosplit = unescape($tosplit); # unescape the keywords $tosplit=~tr/+/ /; # pluses to spaces my(@keywords) = split(/\s+/,$tosplit); return @keywords; } sub param_fetch { my($self,@p) = self_or_default(@_); my($name) = rearrange([NAME],@p); return [] unless defined $name; unless (exists($self->{param}{$name})) { $self->add_parameter($name); $self->{param}{$name} = []; } return $self->{param}{$name}; } ############################################### # OTHER INFORMATION PROVIDED BY THE ENVIRONMENT ############################################### #### Method: path_info # Return the extra virtual path information provided # after the URL (if any) #### sub path_info { my ($self,$info) = self_or_default(@_); if (defined($info)) { $info = "/$info" if $info ne '' && substr($info,0,1) ne '/'; $self->{'.path_info'} = $info; } elsif (! defined($self->{'.path_info'}) ) { my (undef,$path_info) = $self->_name_and_path_from_env; $self->{'.path_info'} = $path_info || ''; } return $self->{'.path_info'}; } # This function returns a potentially modified version of SCRIPT_NAME # and PATH_INFO. Some HTTP servers do sanitise the paths in those # variables. It is the case of at least Apache 2. If for instance the # user requests: /path/./to/script.cgi/x//y/z/../x?y, Apache will set: # REQUEST_URI=/path/./to/script.cgi/x//y/z/../x?y # SCRIPT_NAME=/path/to/env.cgi # PATH_INFO=/x/y/x # # This is all fine except that some bogus CGI scripts expect # PATH_INFO=/http://foo when the user requests # http://xxx/script.cgi/http://foo # # Old versions of this module used to accomodate with those scripts, so # this is why we do this here to keep those scripts backward compatible. # Basically, we accomodate with those scripts but within limits, that is # we only try to preserve the number of / that were provided by the user # if $REQUEST_URI and "$SCRIPT_NAME$PATH_INFO" only differ by the number # of consecutive /. # # So for instance, in: http://foo/x//y/script.cgi/a//b, we'll return a # script_name of /x//y/script.cgi and a path_info of /a//b, but in: # http://foo/./x//z/script.cgi/a/../b//c, we'll return the versions # possibly sanitised by the HTTP server, so in the case of Apache 2: # script_name == /foo/x/z/script.cgi and path_info == /b/c. # # Future versions of this module may no longer do that, so one should # avoid relying on the browser, proxy, server, and CGI.pm preserving the # number of consecutive slashes as no guarantee can be made there. sub _name_and_path_from_env { my $self = shift; my $script_name = $ENV{SCRIPT_NAME} || ''; my $path_info = $ENV{PATH_INFO} || ''; my $uri = $self->request_uri || ''; $uri =~ s/\?.*//s; $uri = unescape($uri); if ( $IIS ) { # IIS doesn't set $ENV{PATH_INFO} correctly. It sets it to # $ENV{SCRIPT_NAME}path_info # IIS also doesn't set $ENV{REQUEST_URI} so we don't want to do # the test below, hence this comes first $path_info =~ s/^\Q$script_name\E(.*)/$1/; } elsif ($uri ne "$script_name$path_info") { my $script_name_pattern = quotemeta($script_name); my $path_info_pattern = quotemeta($path_info); $script_name_pattern =~ s{(?:\\/)+}{/+}g; $path_info_pattern =~ s{(?:\\/)+}{/+}g; if ($uri =~ /^($script_name_pattern)($path_info_pattern)$/s) { # REQUEST_URI and SCRIPT_NAME . PATH_INFO only differ by the # numer of consecutive slashes, so we can extract the info from # REQUEST_URI: ($script_name, $path_info) = ($1, $2); } } return ($script_name,$path_info); } #### Method: request_method # Returns 'POST', 'GET', 'PUT', 'PATCH' or 'HEAD' #### sub request_method { return (defined $ENV{'REQUEST_METHOD'}) ? $ENV{'REQUEST_METHOD'} : undef; } #### Method: content_type # Returns the content_type string #### sub content_type { return (defined $ENV{'CONTENT_TYPE'}) ? $ENV{'CONTENT_TYPE'} : undef; } #### Method: path_translated # Return the physical path information provided # by the URL (if any) #### sub path_translated { return (defined $ENV{'PATH_TRANSLATED'}) ? $ENV{'PATH_TRANSLATED'} : undef; } #### Method: request_uri # Return the literal request URI #### sub request_uri { return (defined $ENV{'REQUEST_URI'}) ? $ENV{'REQUEST_URI'} : undef; } #### Method: query_string # Synthesize a query string from our current # parameters #### sub query_string { my($self) = self_or_default(@_); my($param,$value,@pairs); for $param ($self->param) { my($eparam) = escape($param); for $value ($self->param($param)) { $value = escape($value); next unless defined $value; push(@pairs,"$eparam=$value"); } } for (keys %{$self->{'.fieldnames'}}) { push(@pairs,".cgifields=".escape("$_")); } return join($USE_PARAM_SEMICOLONS ? ';' : '&',@pairs); } sub env_query_string { return (defined $ENV{'QUERY_STRING'}) ? $ENV{'QUERY_STRING'} : undef; } #### Method: accept # Without parameters, returns an array of the # MIME types the browser accepts. # With a single parameter equal to a MIME # type, will return undef if the browser won't # accept it, 1 if the browser accepts it but # doesn't give a preference, or a floating point # value between 0.0 and 1.0 if the browser # declares a quantitative score for it. # This handles MIME type globs correctly. #### sub Accept { my($self,$search) = self_or_CGI(@_); my(%prefs,$type,$pref,$pat); my(@accept) = defined $self->http('accept') ? split(',',$self->http('accept')) : (); for (@accept) { ($pref) = /q=(\d\.\d+|\d+)/; ($type) = m#(\S+/[^;]+)#; next unless $type; $prefs{$type}=$pref || 1; } return keys %prefs unless $search; # if a search type is provided, we may need to # perform a pattern matching operation. # The MIME types use a glob mechanism, which # is easily translated into a perl pattern match # First return the preference for directly supported # types: return $prefs{$search} if $prefs{$search}; # Didn't get it, so try pattern matching. for (keys %prefs) { next unless /\*/; # not a pattern match ($pat = $_) =~ s/([^\w*])/\\$1/g; # escape meta characters $pat =~ s/\*/.*/g; # turn it into a pattern return $prefs{$_} if $search=~/$pat/; } } #### Method: user_agent # If called with no parameters, returns the user agent. # If called with one parameter, does a pattern match (case # insensitive) on the user agent. #### sub user_agent { my($self,$match)=self_or_CGI(@_); my $user_agent = $self->http('user_agent'); return $user_agent unless defined $match && $match && $user_agent; return $user_agent =~ /$match/i; } #### Method: raw_cookie # Returns the magic cookies for the session. # The cookies are not parsed or altered in any way, i.e. # cookies are returned exactly as given in the HTTP # headers. If a cookie name is given, only that cookie's # value is returned, otherwise the entire raw cookie # is returned. #### sub raw_cookie { my($self,$key) = self_or_CGI(@_); require CGI::Cookie; if (defined($key)) { $self->{'.raw_cookies'} = CGI::Cookie->raw_fetch unless $self->{'.raw_cookies'}; return () unless $self->{'.raw_cookies'}; return () unless $self->{'.raw_cookies'}->{$key}; return $self->{'.raw_cookies'}->{$key}; } return $self->http('cookie') || $ENV{'COOKIE'} || ''; } #### Method: virtual_host # Return the name of the virtual_host, which # is not always the same as the server ###### sub virtual_host { my $vh = http('x_forwarded_host') || http('host') || server_name(); $vh =~ s/:\d+$//; # get rid of port number return $vh; } #### Method: remote_host # Return the name of the remote host, or its IP # address if unavailable. If this variable isn't # defined, it returns "localhost" for debugging # purposes. #### sub remote_host { return $ENV{'REMOTE_HOST'} || $ENV{'REMOTE_ADDR'} || 'localhost'; } #### Method: remote_addr # Return the IP addr of the remote host. #### sub remote_addr { return $ENV{'REMOTE_ADDR'} || '127.0.0.1'; } #### Method: script_name # Return the partial URL to this script for # self-referencing scripts. Also see # self_url(), which returns a URL with all state information # preserved. #### sub script_name { my ($self,@p) = self_or_default(@_); if (@p) { $self->{'.script_name'} = shift @p; } elsif (!exists $self->{'.script_name'}) { my ($script_name,$path_info) = $self->_name_and_path_from_env(); $self->{'.script_name'} = $script_name; } return $self->{'.script_name'}; } #### Method: referer # Return the HTTP_REFERER: useful for generating # a GO BACK button. #### sub referer { my($self) = self_or_CGI(@_); return $self->http('referer'); } #### Method: server_name # Return the name of the server #### sub server_name { return $ENV{'SERVER_NAME'} || 'localhost'; } #### Method: server_software # Return the name of the server software #### sub server_software { return $ENV{'SERVER_SOFTWARE'} || 'cmdline'; } #### Method: virtual_port # Return the server port, taking virtual hosts into account #### sub virtual_port { my($self) = self_or_default(@_); my $vh = $self->http('x_forwarded_host') || $self->http('host'); my $protocol = $self->protocol; if ($vh) { return ($vh =~ /:(\d+)$/)[0] || ($protocol eq 'https' ? 443 : 80); } else { return $self->server_port(); } } #### Method: server_port # Return the tcp/ip port the server is running on #### sub server_port { return $ENV{'SERVER_PORT'} || 80; # for debugging } #### Method: server_protocol # Return the protocol (usually HTTP/1.0) #### sub server_protocol { return $ENV{'SERVER_PROTOCOL'} || 'HTTP/1.0'; # for debugging } #### Method: http # Return the value of an HTTP variable, or # the list of variables if none provided #### sub http { my ($self,$parameter) = self_or_CGI(@_); if ( defined($parameter) ) { $parameter =~ tr/-a-z/_A-Z/; if ( $parameter =~ /^HTTP(?:_|$)/ ) { return $ENV{$parameter}; } return $ENV{"HTTP_$parameter"}; } return grep { /^HTTP(?:_|$)/ } keys %ENV; } #### Method: https # Return the value of HTTPS, or # the value of an HTTPS variable, or # the list of variables #### sub https { my ($self,$parameter) = self_or_CGI(@_); if ( defined($parameter) ) { $parameter =~ tr/-a-z/_A-Z/; if ( $parameter =~ /^HTTPS(?:_|$)/ ) { return $ENV{$parameter}; } return $ENV{"HTTPS_$parameter"}; } return wantarray ? grep { /^HTTPS(?:_|$)/ } keys %ENV : $ENV{'HTTPS'}; } #### Method: protocol # Return the protocol (http or https currently) #### sub protocol { local($^W)=0; my $self = shift; return 'https' if uc($self->https()) eq 'ON'; return 'https' if $self->server_port == 443; my $prot = $self->server_protocol; my($protocol,$version) = split('/',$prot); return "\L$protocol\E"; } #### Method: remote_ident # Return the identity of the remote user # (but only if his host is running identd) #### sub remote_ident { return (defined $ENV{'REMOTE_IDENT'}) ? $ENV{'REMOTE_IDENT'} : undef; } #### Method: auth_type # Return the type of use verification/authorization in use, if any. #### sub auth_type { return (defined $ENV{'AUTH_TYPE'}) ? $ENV{'AUTH_TYPE'} : undef; } #### Method: remote_user # Return the authorization name used for user # verification. #### sub remote_user { return (defined $ENV{'REMOTE_USER'}) ? $ENV{'REMOTE_USER'} : undef; } #### Method: user_name # Try to return the remote user's name by hook or by # crook #### sub user_name { my ($self) = self_or_CGI(@_); return $self->http('from') || $ENV{'REMOTE_IDENT'} || $ENV{'REMOTE_USER'}; } #### Method: nosticky # Set or return the NOSTICKY global flag #### sub nosticky { my ($self,$param) = self_or_CGI(@_); $CGI::NOSTICKY = $param if defined($param); return $CGI::NOSTICKY; } #### Method: nph # Set or return the NPH global flag #### sub nph { my ($self,$param) = self_or_CGI(@_); $CGI::NPH = $param if defined($param); return $CGI::NPH; } #### Method: private_tempfiles # Set or return the private_tempfiles global flag #### sub private_tempfiles { warn "private_tempfiles has been deprecated"; return 0; } #### Method: close_upload_files # Set or return the close_upload_files global flag #### sub close_upload_files { my ($self,$param) = self_or_CGI(@_); $CGI::CLOSE_UPLOAD_FILES = $param if defined($param); return $CGI::CLOSE_UPLOAD_FILES; } #### Method: default_dtd # Set or return the default_dtd global #### sub default_dtd { my ($self,$param,$param2) = self_or_CGI(@_); if (defined $param2 && defined $param) { $CGI::DEFAULT_DTD = [ $param, $param2 ]; } elsif (defined $param) { $CGI::DEFAULT_DTD = $param; } return $CGI::DEFAULT_DTD; } # -------------- really private subroutines ----------------- sub _maybe_escapeHTML { # hack to work around earlier hacks push @_,$_[0] if @_==1 && $_[0] eq 'CGI'; my ($self,$toencode,$newlinestoo) = CGI::self_or_default(@_); return undef unless defined($toencode); return $toencode if ref($self) && !$self->{'escape'}; return $self->escapeHTML($toencode, $newlinestoo); } sub previous_or_default { my($self,$name,$defaults,$override) = @_; my(%selected); if (!$override && ($self->{'.fieldnames'}->{$name} || defined($self->param($name)) ) ) { $selected{$_}++ for $self->param($name); } elsif (defined($defaults) && ref($defaults) && (ref($defaults) eq 'ARRAY')) { $selected{$_}++ for @{$defaults}; } else { $selected{$defaults}++ if defined($defaults); } return %selected; } sub register_parameter { my($self,$param) = @_; $self->{'.parametersToAdd'}->{$param}++; } sub get_fields { my($self) = @_; return $self->CGI::hidden('-name'=>'.cgifields', '-values'=>[keys %{$self->{'.parametersToAdd'}}], '-override'=>1); } sub read_from_cmdline { my($input,@words); my($query_string); my($subpath); if ($DEBUG && @ARGV) { @words = @ARGV; } elsif ($DEBUG > 1) { require Text::ParseWords; print STDERR "(offline mode: enter name=value pairs on standard input; press ^D or ^Z when done)\n"; chomp(@lines = ); # remove newlines $input = join(" ",@lines); @words = &Text::ParseWords::old_shellwords($input); } for (@words) { s/\\=/%3D/g; s/\\&/%26/g; } if ("@words"=~/=/) { $query_string = join('&',@words); } else { $query_string = join('+',@words); } if ($query_string =~ /^(.*?)\?(.*)$/) { $query_string = $2; $subpath = $1; } return { 'query_string' => $query_string, 'subpath' => $subpath }; } ##### # subroutine: read_multipart # # Read multipart data and store it into our parameters. # An interesting feature is that if any of the parts is a file, we # create a temporary file and open up a filehandle on it so that the # caller can read from it if necessary. ##### sub read_multipart { my($self,$boundary,$length) = @_; my($buffer) = $self->new_MultipartBuffer($boundary,$length); return unless $buffer; my(%header,$body); my $filenumber = 0; while (!$buffer->eof) { %header = $buffer->readHeader; unless (%header) { $self->cgi_error("400 Bad request (malformed multipart POST)"); return; } $header{'Content-Disposition'} ||= ''; # quench uninit variable warning my $param = _mp_value_parse( $header{'Content-Disposition'},'name' ); $param .= $TAINTED; # See RFC 1867, 2183, 2045 # NB: File content will be loaded into memory should # content-disposition parsing fail. my ($filename) = $header{'Content-Disposition'} =~/ filename=(("[^"]*")|([a-z\d!\#'\*\+,\.^_\`\{\}\|\~]*))/i; $filename ||= ''; # quench uninit variable warning $filename =~ s/^"([^"]*)"$/$1/; # Test for Opera's multiple upload feature my($multipart) = ( defined( $header{'Content-Type'} ) && $header{'Content-Type'} =~ /multipart\/mixed/ ) ? 1 : 0; # add this parameter to our list $self->add_parameter($param); # If no filename specified, then just read the data and assign it # to our parameter list. if ( ( !defined($filename) || $filename eq '' ) && !$multipart ) { my($value) = $buffer->readBody; $value .= $TAINTED; push(@{$self->{param}{$param}},$value); next; } UPLOADS: { # If we get here, then we are dealing with a potentially large # uploaded form. Save the data to a temporary file, then open # the file for reading. # skip the file if uploads disabled if ($DISABLE_UPLOADS) { while (defined($data = $buffer->read)) { } last UPLOADS; } # set the filename to some recognizable value if ( ( !defined($filename) || $filename eq '' ) && $multipart ) { $filename = "multipart/mixed"; } my $tmp_dir = $CGI::OS eq 'WINDOWS' ? ( $ENV{TEMP} || $ENV{TMP} || ( $ENV{WINDIR} ? ( $ENV{WINDIR} . $SL . 'TEMP' ) : undef ) ) : undef; # File::Temp defaults to TMPDIR require CGI::File::Temp; my $filehandle = CGI::File::Temp->new( UNLINK => $UNLINK_TMP_FILES, DIR => $tmp_dir, ); $filehandle->_mp_filename( $filename ); $CGI::DefaultClass->binmode($filehandle) if $CGI::needs_binmode && defined fileno($filehandle); # if this is an multipart/mixed attachment, save the header # together with the body for later parsing with an external # MIME parser module if ( $multipart ) { for ( keys %header ) { print $filehandle "$_: $header{$_}${CRLF}"; } print $filehandle "${CRLF}"; } my ($data); local($\) = ''; my $totalbytes = 0; while (defined($data = $buffer->read)) { if (defined $self->{'.upload_hook'}) { $totalbytes += length($data); &{$self->{'.upload_hook'}}($filename ,$data, $totalbytes, $self->{'.upload_data'}); } print $filehandle $data if ($self->{'use_tempfile'}); } # back up to beginning of file seek($filehandle,0,0); ## Close the filehandle if requested this allows a multipart MIME ## upload to contain many files, and we won't die due to too many ## open file handles. The user can access the files using the hash ## below. close $filehandle if $CLOSE_UPLOAD_FILES; $CGI::DefaultClass->binmode($filehandle) if $CGI::needs_binmode; # Save some information about the uploaded file where we can get # at it later. # Use the typeglob + filename as the key, as this is guaranteed to be # unique for each filehandle. Don't use the file descriptor as # this will be re-used for each filehandle if the # close_upload_files feature is used. $self->{'.tmpfiles'}->{$$filehandle . $filehandle} = { hndl => $filehandle, name => $filehandle->filename, info => {%header}, }; push(@{$self->{param}{$param}},$filehandle); } } } sub _mp_value_parse { my ( $string,$field ) = @_; my $is_quoted = $string =~/[\s;]$field="/ ? 1 : 0; my $param; if ( $is_quoted ) { # a quoted token cannot contain anything but an unescaped quote ($param) = $string =~/[\s;]$field="((?:\\"|[^"])*)"/; } else { # a plain token cannot contain any reserved characters # https://tools.ietf.org/html/rfc2616#section-2.2 # separators = "(" | ")" | "<" | ">" | "@" # | "," | ";" | ":" | "\" | <"> # | "/" | "[" | "]" | "?" | "=" # | "{" | "}" | SP | HT ($param) = $string =~/[\s;]$field=([^\(\)<>\@,;:\\"\/\[\]\?=\{\} \015\n\t]*)/; } return $param; } ##### # subroutine: read_multipart_related # # Read multipart/related data and store it into our parameters. The # first parameter sets the start of the data. The part identified by # this Content-ID will not be stored as a file upload, but will be # returned by this method. All other parts will be available as file # uploads accessible by their Content-ID ##### sub read_multipart_related { my($self,$start,$boundary,$length) = @_; my($buffer) = $self->new_MultipartBuffer($boundary,$length); return unless $buffer; my(%header,$body); my $filenumber = 0; my $returnvalue; while (!$buffer->eof) { %header = $buffer->readHeader; unless (%header) { $self->cgi_error("400 Bad request (malformed multipart POST)"); return; } my($param) = $header{'Content-ID'}=~/\<([^\>]*)\>/; $param .= $TAINTED; # If this is the start part, then just read the data and assign it # to our return variable. if ( $param eq $start ) { $returnvalue = $buffer->readBody; $returnvalue .= $TAINTED; next; } # add this parameter to our list $self->add_parameter($param); UPLOADS: { # If we get here, then we are dealing with a potentially large # uploaded form. Save the data to a temporary file, then open # the file for reading. # skip the file if uploads disabled if ($DISABLE_UPLOADS) { while (defined($data = $buffer->read)) { } last UPLOADS; } my $tmp_dir = $CGI::OS eq 'WINDOWS' ? ( $ENV{TEMP} || $ENV{TMP} || ( $ENV{WINDIR} ? ( $ENV{WINDIR} . $SL . 'TEMP' ) : undef ) ) : undef; # File::Temp defaults to TMPDIR require CGI::File::Temp; my $filehandle = CGI::File::Temp->new( UNLINK => $UNLINK_TMP_FILES, DIR => $tmp_dir, ); $filehandle->_mp_filename( $filehandle->filename ); $CGI::DefaultClass->binmode($filehandle) if $CGI::needs_binmode && defined fileno($filehandle); my ($data); local($\) = ''; my $totalbytes; while (defined($data = $buffer->read)) { if (defined $self->{'.upload_hook'}) { $totalbytes += length($data); &{$self->{'.upload_hook'}}($param ,$data, $totalbytes, $self->{'.upload_data'}); } print $filehandle $data if ($self->{'use_tempfile'}); } # back up to beginning of file seek($filehandle,0,0); ## Close the filehandle if requested this allows a multipart MIME ## upload to contain many files, and we won't die due to too many ## open file handles. The user can access the files using the hash ## below. close $filehandle if $CLOSE_UPLOAD_FILES; $CGI::DefaultClass->binmode($filehandle) if $CGI::needs_binmode; # Save some information about the uploaded file where we can get # at it later. # Use the typeglob + filename as the key, as this is guaranteed to be # unique for each filehandle. Don't use the file descriptor as # this will be re-used for each filehandle if the # close_upload_files feature is used. $self->{'.tmpfiles'}->{$$filehandle . $filehandle} = { hndl => $filehandle, name => $filehandle->filename, info => {%header}, }; push(@{$self->{param}{$param}},$filehandle); } } return $returnvalue; } sub upload { my($self,$param_name) = self_or_default(@_); my @param = grep {ref($_) && defined(fileno($_))} $self->param($param_name); return unless @param; return wantarray ? @param : $param[0]; } sub tmpFileName { my($self,$filename) = self_or_default(@_); # preferred calling convention: $filename came directly from param or upload if (ref $filename) { return $self->{'.tmpfiles'}->{$$filename . $filename}->{name} || ''; } # backwards compatible with older versions: $filename is merely equal to # one of our filenames when compared as strings foreach my $param_name ($self->param) { foreach my $filehandle ($self->multi_param($param_name)) { if ($filehandle eq $filename) { return $self->{'.tmpfiles'}->{$$filehandle . $filehandle}->{name} || ''; } } } return ''; } sub uploadInfo { my($self,$filename) = self_or_default(@_); return if ! defined $$filename; return $self->{'.tmpfiles'}->{$$filename . $filename}->{info}; } # internal routine, don't use sub _set_values_and_labels { my $self = shift; my ($v,$l,$n) = @_; $$l = $v if ref($v) eq 'HASH' && !ref($$l); return $self->param($n) if !defined($v); return $v if !ref($v); return ref($v) eq 'HASH' ? keys %$v : @$v; } # internal routine, don't use sub _set_attributes { my $self = shift; my($element, $attributes) = @_; return '' unless defined($attributes->{$element}); $attribs = ' '; for my $attrib (keys %{$attributes->{$element}}) { (my $clean_attrib = $attrib) =~ s/^-//; $attribs .= "@{[lc($clean_attrib)]}=\"$attributes->{$element}{$attrib}\" "; } $attribs =~ s/ $//; return $attribs; } ######################################################### # Globals and stubs for other packages that we use. ######################################################### ######################## CGI::MultipartBuffer #################### package CGI::MultipartBuffer; $_DEBUG = 0; # how many bytes to read at a time. We use # a 4K buffer by default. $MultipartBuffer::INITIAL_FILLUNIT ||= 1024 * 4; $MultipartBuffer::TIMEOUT ||= 240*60; # 4 hour timeout for big files $MultipartBuffer::SPIN_LOOP_MAX ||= 2000; # bug fix for some Netscape servers $MultipartBuffer::CRLF ||= $CGI::CRLF; $INITIAL_FILLUNIT = $MultipartBuffer::INITIAL_FILLUNIT; $TIMEOUT = $MultipartBuffer::TIMEOUT; $SPIN_LOOP_MAX = $MultipartBuffer::SPIN_LOOP_MAX; $CRLF = $MultipartBuffer::CRLF; sub new { my($package,$interface,$boundary,$length) = @_; $FILLUNIT = $INITIAL_FILLUNIT; $CGI::DefaultClass->binmode($IN); # if $CGI::needs_binmode; # just do it always # If the user types garbage into the file upload field, # then Netscape passes NOTHING to the server (not good). # We may hang on this read in that case. So we implement # a read timeout. If nothing is ready to read # by then, we return. # Netscape seems to be a little bit unreliable # about providing boundary strings. my $boundary_read = 0; if ($boundary) { # Under the MIME spec, the boundary consists of the # characters "--" PLUS the Boundary string # BUG: IE 3.01 on the Macintosh uses just the boundary -- not # the two extra hyphens. We do a special case here on the user-agent!!!! $boundary = "--$boundary" unless CGI::user_agent('MSIE\s+3\.0[12];\s*Mac|DreamPassport'); } else { # otherwise we find it ourselves my($old); ($old,$/) = ($/,$CRLF); # read a CRLF-delimited line $boundary = ; # BUG: This won't work correctly under mod_perl $length -= length($boundary); chomp($boundary); # remove the CRLF $/ = $old; # restore old line separator $boundary_read++; } my $self = {LENGTH=>$length, CHUNKED=>!$length, BOUNDARY=>$boundary, INTERFACE=>$interface, BUFFER=>'', }; $FILLUNIT = length($boundary) if length($boundary) > $FILLUNIT; my $retval = bless $self,ref $package || $package; # Read the preamble and the topmost (boundary) line plus the CRLF. unless ($boundary_read) { while ($self->read(0)) { } } die "Malformed multipart POST: data truncated\n" if $self->eof; return $retval; } sub readHeader { my($self) = @_; my($end); my($ok) = 0; my($bad) = 0; local($CRLF) = "\015\012" if $CGI::OS eq 'VMS' || $CGI::EBCDIC; do { $self->fillBuffer($FILLUNIT); $ok++ if ($end = index($self->{BUFFER},"${CRLF}${CRLF}")) >= 0; $ok++ if $self->{BUFFER} eq ''; $bad++ if !$ok && $self->{LENGTH} <= 0; # this was a bad idea # $FILLUNIT *= 2 if length($self->{BUFFER}) >= $FILLUNIT; } until $ok || $bad; return () if $bad; #EBCDIC NOTE: translate header into EBCDIC, but watch out for continuation lines! my($header) = substr($self->{BUFFER},0,$end+2); substr($self->{BUFFER},0,$end+4) = ''; my %return; if ($CGI::EBCDIC) { warn "untranslated header=$header\n" if $_DEBUG; $header = CGI::Util::ascii2ebcdic($header); warn "translated header=$header\n" if $_DEBUG; } # See RFC 2045 Appendix A and RFC 822 sections 3.4.8 # (Folding Long Header Fields), 3.4.3 (Comments) # and 3.4.5 (Quoted-Strings). my $token = '[-\w!\#$%&\'*+.^_\`|{}~]'; $header=~s/$CRLF\s+/ /og; # merge continuation lines while ($header=~/($token+):\s+([^$CRLF]*)/mgox) { my ($field_name,$field_value) = ($1,$2); $field_name =~ s/\b(\w)/uc($1)/eg; #canonicalize $return{$field_name}=$field_value; } return %return; } # This reads and returns the body as a single scalar value. sub readBody { my($self) = @_; my($data); my($returnval)=''; #EBCDIC NOTE: want to translate returnval into EBCDIC HERE while (defined($data = $self->read)) { $returnval .= $data; } if ($CGI::EBCDIC) { warn "untranslated body=$returnval\n" if $_DEBUG; $returnval = CGI::Util::ascii2ebcdic($returnval); warn "translated body=$returnval\n" if $_DEBUG; } return $returnval; } # This will read $bytes or until the boundary is hit, whichever happens # first. After the boundary is hit, we return undef. The next read will # skip over the boundary and begin reading again; sub read { my($self,$bytes) = @_; # default number of bytes to read $bytes = $bytes || $FILLUNIT; # Fill up our internal buffer in such a way that the boundary # is never split between reads. $self->fillBuffer($bytes); my $boundary_start = $CGI::EBCDIC ? CGI::Util::ebcdic2ascii($self->{BOUNDARY}) : $self->{BOUNDARY}; my $boundary_end = $CGI::EBCDIC ? CGI::Util::ebcdic2ascii($self->{BOUNDARY}.'--') : $self->{BOUNDARY}.'--'; # Find the boundary in the buffer (it may not be there). my $start = index($self->{BUFFER},$boundary_start); warn "boundary=$self->{BOUNDARY} length=$self->{LENGTH} start=$start\n" if $_DEBUG; # protect against malformed multipart POST operations die "Malformed multipart POST\n" unless $self->{CHUNKED} || ($start >= 0 || $self->{LENGTH} > 0); #EBCDIC NOTE: want to translate boundary search into ASCII here. # If the boundary begins the data, then skip past it # and return undef. if ($start == 0) { # clear us out completely if we've hit the last boundary. if (index($self->{BUFFER},$boundary_end)==0) { $self->{BUFFER}=''; $self->{LENGTH}=0; return undef; } # just remove the boundary. substr($self->{BUFFER},0,length($boundary_start))=''; $self->{BUFFER} =~ s/^\012\015?//; return undef; } my $bytesToReturn; if ($start > 0) { # read up to the boundary $bytesToReturn = $start-2 > $bytes ? $bytes : $start; } else { # read the requested number of bytes # leave enough bytes in the buffer to allow us to read # the boundary. Thanks to Kevin Hendrick for finding # this one. $bytesToReturn = $bytes - (length($boundary_start)+1); } my $returnval=substr($self->{BUFFER},0,$bytesToReturn); substr($self->{BUFFER},0,$bytesToReturn)=''; # If we hit the boundary, remove the CRLF from the end. return ($bytesToReturn==$start) ? substr($returnval,0,-2) : $returnval; } # This fills up our internal buffer in such a way that the # boundary is never split between reads sub fillBuffer { my($self,$bytes) = @_; return unless $self->{CHUNKED} || $self->{LENGTH}; my($boundaryLength) = length($self->{BOUNDARY}); my($bufferLength) = length($self->{BUFFER}); my($bytesToRead) = $bytes - $bufferLength + $boundaryLength + 2; $bytesToRead = $self->{LENGTH} if !$self->{CHUNKED} && $self->{LENGTH} < $bytesToRead; # Try to read some data. We may hang here if the browser is screwed up. my $bytesRead = $self->{INTERFACE}->read_from_client(\$self->{BUFFER}, $bytesToRead, $bufferLength); warn "bytesToRead=$bytesToRead, bufferLength=$bufferLength, buffer=$self->{BUFFER}\n" if $_DEBUG; $self->{BUFFER} = '' unless defined $self->{BUFFER}; # An apparent bug in the Apache server causes the read() # to return zero bytes repeatedly without blocking if the # remote user aborts during a file transfer. I don't know how # they manage this, but the workaround is to abort if we get # more than SPIN_LOOP_MAX consecutive zero reads. if ($bytesRead <= 0) { die "CGI.pm: Server closed socket during multipart read (client aborted?).\n" if ($self->{ZERO_LOOP_COUNTER}++ >= $SPIN_LOOP_MAX); } else { $self->{ZERO_LOOP_COUNTER}=0; } $self->{LENGTH} -= $bytesRead if !$self->{CHUNKED} && $bytesRead; } # Return true when we've finished reading sub eof { my($self) = @_; return 1 if (length($self->{BUFFER}) == 0) && ($self->{LENGTH} <= 0); undef; } 1; package CGI; # We get a whole bunch of warnings about "possibly uninitialized variables" # when running with the -w switch. Touch them all once to get rid of the # warnings. This is ugly and I hate it. if ($^W) { $CGI::CGI = ''; $CGI::CGI=<