battery.jpg0000644000175000017500000002506307700515173012531 0ustar davidjdavidjJFIFddDucky<Adobed        !"12BAbQaRr#q3CS$cѢs4DT%& ?-qăO&#{Huĭ.E xdvt8PTF|8ArZ{u(/W@Û;&!!Y"@#wѠyGʈoIo% ĨD9GD>BOZZZ3r}8t%dVܪ?bq@Ӳ;8ߺ6)qO;^d'K%DQ@ejJְ%'%RTABˎ (Wq;33neheP <4 `ZqX'D%KSƔ<4 @~oA÷29SS!6N ycY.bq2 -'kAlm+gn2'"E$X=A>Q>ܑQ:b7t:ZMƘ#jIhAB$hV8 3 #mTG( H4/)e{qȎĠYRa $ zw: Wm6M'dc(2 ׺T&&h:hzt{QlkC"uOnCg19 17 ȍ)SA34rh&D *U4 Zm#8@>DNk! ԗWA{2umغhBr "W |nW*ӑ\/0gapFgc32 &\࢏|3?jwSt,|&knZ*"4v9- !+rh d~6`Cs n!Y%Ff" aeĸLB^U].CkF]E GJ 8D%#"[sK@F.'(C`Ts渄D "naS@=IR̟ܶzu\a"s[v`縠$=BQ~hh3vU|h_)x80HB._YDQPY$&yVPpp 8j*k`L4L4FGާUk6N7[uY]b-XHӍsby#$ NS201?\XOmp%_/>m=چ҆MRy) Ha` e⻓Mk%hiIzGʺK<`$<{!,aɷ.8Fr<|,O!^|u|=dؽ2Dղ[?]̇M6jH!G'0+:E]!~΅D^0T9ZKy[nM49GO X@"P(% ʫdAXXIi:1Üη-΁h3ک] 8r_hɶر-~5=&Q0nd1R7E EU)sf\P=9魴L{k (. hf%^U|t1tbL!ŽrGI.q6jێm{v:3: 9teխi|w;1 n[ & ͡⸮Q|o>jhDG:|)omxPT;ll5; ov]nC! AH2TčQS!}t8tQ0\+J&x6M? \o#!pcj 1 lEX@_y;<ˠ'#&>ŎV[6(*G&1ժ>_ͻw{i&lQ!$ SAZwpT,Otg`T[yˊú V.M@NݨBW .w͠R)Yt@F"_}W][m/N^/ Yba.sVbGKyP =.KVؐ4R^/X[{6ԶHDLbqF+ Fl-A4ĸ1Z1,AhD\è6@ SZ9knⱉ`_w1DUqUKA$2GP12,Hu ÛkE-OCvDy8pG_β<.Ötcm.|D.y~|0}*BO/(6/ +b>ˠj\>տ.btJ5}ˠݛS.1n3f.{1ԗ/K#T OwDo7#.TfnuGL\ -!][j~**@[_?6Db3pgs""xVU r7Ga`c>9."\|shK#aCg;O%8HB^'W@'Y8mf_V T}al2rLԱ["UnSQUB3ngv6SYDphءs/AZZ qf<\m^l2î% Ei#NÙ\]ӟQzpvuc\FdbRIY7EQEW~x`Z~,qGS"Ð=]w@ZJȊpR%VR: ovco?$&ic*;`Kzvkp?fȇ0ŐkJHqRڅ}8|F7{S$N8P5 *ǵJZ%'-dk@B+DJP h#;v͏e9B)CG21R[h!t ]j5,2;yjlhھ2]X ܞ ݹٖ96en=PbL.2KTš e}۔UIlVh;@ @ |nmLX:;3!m&41H5^f+bΎ\8* yV\N`w6rVQP[Tn(}ŕsDvTi\uWRX6&b63cm~+13F/N<κ4\TȺ <-7dw<eptYPVdSƅOVɄQ1ϹS,Lvx҂Qҿ.Bs$On ڼZNH./tWϦWgݸ;ŸLyEˈhCεh; ۼL˭.MϺ/t]U *JO n_Mj[;0r.brc.Bq't۸Y^E?`\;D6{E΋Sè`/@ԖKtnƴ)BSʖ;@ @'u.ZܢtsEwXrMNǼ(YLs <3Z ]=&Nf{u7p.JcmhOZw86F8;dW'ĒMEDRQh>Dx{3f6 v.z@l#]!JtP044K'D*q7%6E٬vcs'D%eBr:wldMC(e2eK2_X1G*9$2"dnu%e#>lIU㠝{Xw6_m2F3Q]'%:Q)٭W/a8&#03n|lĔhʼ h,uL߅qQ3/cKAۮȂ'[k_ad%J"ܩqw)yk_6CU­@$lZoN @݄8^e~pIos)-i3Fܩ65UNCrHyMxZ'x=Dׂ s3jd7S=`b.*m3$ R!^ql~}>OfdQ[MȐ5Q꺋{)Ic1ry.Bvr*1N.' _`p_ce6R{[$rW:%TB ;owroN8-_n-}+8'ț{^]Î3d$R6H&(vG]DLvk.Go?/#9:(p,Jf]h + u\Q4pXȲ ?*XGܘ:M3stу!N"#th![+EGˠOE@u9J_AZRJAT8Z- n/)Ƕxcpi$-uN绽2oX"RXl"Jʸ^[My{j eAS܉zL Q5~DF.+$F=ā=!{(ݤ n̦7lVMdTlPzJ#[B;&SEGЅEUW <8X: gqgsW#l Qd+[Xdl % AoqOD~W(Zp(9-k"I:)64Ngky>yp4ܜϫ(ZKT%,. y30s#&ee~{apCM6&۸D΁ h5OӋ*n-Kd\=Z-GȄ$(*)\grWv:}6Cy:CxmQw FvJK "YZ2m>6- h1.4'E &VOA嘭"Уq!IH>rB;̈W#{tҰyӠ5ݙV5liY *`mޥ#YTf?&,q^y*_h ֈTDqRk@h΢ %ӄdG:C@ڼJ 8\rАGtAqle),P]J9)9$Q ;M;I~J1"i%'zh7}q:BHm'Dqryyh,Ze3TEAN((ACh|~9h3ܔcwO&Ǵ`1$r u$Lz|Z KaBbP[dԅEplA?kr8Pw-UEXl!Ųj@=H])UCdJT{(E{6Dv1aB.ح|I4]eZs vcffw[f`ړUP%"J`h9xD "ⓐ!_ jlqLkuY{vn%qc]BLHU !U)E. ]̐& tsËãrbBzUq r\H~h6Ei O'<4gᇝxd7G0} AX_4z &9F,}!IIT8!cyh ebGjD 'UDNB2SZ0*l~HH-<27r5H;rZu.&Ҋ{X> ;?\1̃kN¾ -R7+ڱLmȽjAh~/'xVr*<>> M_ d'|2xlS(AwQWEV+l}?NNkzml$es,~qļ:Ҫ8Iq{i(w^,arR|HzL=AsUJ& aa6vf:hQZ{񉠆ɛmp qWQtt A 0הDRR|4nLL숔f7Y}lI5hq=c-eyl&l3(e:M+")ʠpC[=؍\q_7oeq b! M9ۛX^"nq4î]C=$0ͽCKОjCOI)hMyDK`aˎ7(Yk'17sۼ!:)<@v?xٌI2"NqS뫇MƻCDx<̴.)-n~:r2I)9Me mCjS*!{33c478-"I'O-)MC xeO';rdJie 3q@c [&ǎׇ k~Id&Mȝ&-eN"Yw۴q}̐~呺ݲdHXE6#4 ̋`V bdCIrDY2_v3$J!4,r AW /Ҕ_}@ΣJ&Ҧ"}oh9stM EK߷ͧHò YҖ? ~ w)mSW۰rqx<8\5#dQFq' qNnUB Rn[c#'a̔lY9eYyǺ*J)P*RTU_Wu7]1fdrn' Bbл^d`ۍH+1$* pQI%FkԱxݑĻ%dL0DfF$FY"Z46w#qw É[2:2 iuhn# O(ˠ{wecXH^n:vI8ÖS⒲s%(ŖD"BLSǩWڄD%Kxjط,c$h4}( TO^ UQRJ">aYtײ8F9#s;n/ x}7h1 ?"69|{ˠd1[ym" q6@VkKA]'Qln l~Eܼq:e vJuXPi{{Rfe" ͹'@aDI*ۈ.v##fR:+BCvKnU*9US,vt1/8Y~TFx:A65H y.-:78E!P Xo13}Oɠv,liʉ!%>Z|4-r#%a\KMJ4VV/$NKŪ!hJڪ!*oAn9 @Be8 _AJi:M:"V@L~ 2TD7 tQsa(rZ#]/PQHG@C%w6QkY9< 2R"WlDfu'#]QSfK&qh$p.C56ĭ"+6fYB6z,vHGh-cbHmc`s*kFWAmʛ܌ܔV RyiűҎȯ1"V@W@ @ @ I6th)m)ƷpT~_eRƚ rR寳]נLkYYPKCznyj7i*dr+wjo+'KRN_I]Eܻh *]򙹠mNK[ V7O}V=ZZberries.jpg0000644000175000017500000006135707216030505012511 0ustar davidjdavidjJFIFddDucky<&Adobed #<b       ! 1"A#023B$4!1AQaq"2BR#b3rCScT0@!P`apq A"!1AQaq𑡱 $^JDU^?iNI-W1l!͍ROjWWNQN&rU|=RlaPG2O 'WErnOd5 HyB*bҭo Yj@mfm!k.ЅgF%(7V1+!a0XM<[u/m)7x=ghɨ#;Т-Râ>Y6yQ@sRB{fKh84<3YTp'}Jq sO]IH 6/tBKD"2xJA;1"hl˰ZnfAlh iVAKUyno Űy\fp@ӋٗN:w;*pl-(+72-{#M=iYFqڃ.^*'!IWШ}'UI}+]@!T+Na i)Da:^YYOQ^3 ca2Ztew9O2˺"8Wsczكd/ˢ tMM|{-VY^>{&΁iϤ[%dm.oLt9{\;w.6Vu.j\v9jLYgY]m8vr8`Aد6ѵ;l{m\[O]^mkpMz蹅ҽ:moUA?Z;6S`u7hVطC=_}G!?קk2#w3ΩBKV,NV}oĻRR}SRWr$E,MzͅlM~5cw_UWRCrUI4[kwAG/Vv=?bYUӶTzKvb֢٩uνR bXI; WysZԸMWu֊cbX Ҋ_kz }WٯdnΌ}U\P0V/\ynUUIM_RRXM]g꼵5=QB[+WOn*ڭhv+IvFa_,>u_yZ}mNX[sPݱ+zEmK3FZ}n vQSYJWr\fY>ɣ{&gbNFuE~YX6m͏N{> a!^뵭M]ܝZlU77m;tfX|2J1 Z2&Iڡ}׵گJwK>c/TJV-U}jܾpkе),]|m}mǰR 뭻J E. u]lج=-6FZYIlWٵFz߼[[K{^=_e2>ljW`b\Ej+'LՓn Xd-miz6]?_xR^(ڍG(M;U{k^7=3VoM޿]+ujߦ֢W閊Y{j?n`^Ng9]}U}ٵ׳rzF2TVj%¢evgpYīSc`~֙˓zmziM Ru*WgvnfXXEu=DTm!nsnsƹu\E[{o}U CU=_P+B͍F9ٳm+cs>z}ST쐴; =zJM SUOSR@mmjz$S>6={_XNu i߶~hݴSGEoڶt)}fZg5fg֖ ,J5iVsu޻ceVtЧ`XLZ)5Nڪge4..ҳfIuzWikjj۫ cVƉja U/yh XNݮtZpkE*nov׺c٥_emhwWWR؃c_oo`&8VR?fCW]AU~YF{ݳ~mluY_oeCjQͫ{cwVްR+MBjV+j1o j)^e]kgUx8]r׌G^}}gA:mhhA׶oP\\*cQ0D&v~c`l8<"Vܩ؞,8 ȍ10~scN?B9 |@/Lf03b!3xx"/|x0<213ghLa8Cf~f9N&y9 z3; `11X&gPpv4 ^g)x>'3,#8cq,>]F4`O1h'h^u$<,33&s~8 XbXDS ΰ/c[| q0<(|s>fgş\Bx0C3 < pO<=h[`\C1i{L3N'0αD"cH~D ~!V B >`'8 ?^yuxrcargOP'LF31"-rx'3n g0p`SOa% D+2˷X3= 3LØ8yBXxV|Fo8b?0G5^l98|p!g%Cg8&~'Z<@& <4`\LC\o&&891/ 6D :00a8ǐ `%bǬk<Ƭ:)-30X?2E9lbwمlA+7ֈyĥ20 323x-o1hvGab`J,{<3 dF,܁;r^-KTT,3>y<nx_Akp@&~c!u@x<>Jp'Xk1_h~!>鎩)3pR푀BR:hQ>ή*wP빔6O4:_+HfQx8<_%)KC(ً %J=f 42&Rm3oٿ~ Br>Z>hshu L8({Rn ),Kz䑋M#Te4}Z//le=Κ1'yF?4=Kr\vZ1'a>Kx?ȅSƑ,[c $v!#/=JG,B= vF m GyBMSb:/^ۓ/oޫjǨ7pr>lGӚ5͵،cY2fU9D ɤ"[-*qHwjТeßL)4ssR`N ܏L~ ),fJ[PU.q.s\ bn{?)ۖE94pwb$G($:` jFN"pCGQ1+a8HT1:Hs dɨ:*q37iTB@8[P2 V^ϖGRy~kuMjxĊry ~CG&<)+suO;۷nQ4 $OF#X) h8H䜹C>#d<%і\6"f1r6Z.b+1dlL:7b\3u_#ہ8l#5ۛ;b]Jݷo^ԑ ?['s؄G qڈj5%R埽J͙DvZ7 #ኢi)Fq>hZ܈v"љ}>IU 1{Lm/Uz#[ Zq4#OЌPp$@W!WrJg &3Q4RfU7z\cGoFGU`d0a,@Yd^:21N…b2u_i4:c@2eDܴN3<04u SnRpðOꖀ#w]2 T'$ӌ?N= 0qC]ڏmQrKDhşx#~qWU&?[yφvV*i5cpZIoW%jܶsl7ag[\"n{q.ekZ#ܡ=T ς#$:r컛_x)JMJ#eC6"˦P Ԫ-#pDv*ۋBH\ )Eò9g*nLDk x kIjGqp$a@F}xY.dF#(KavP&E$3;c܎ؾئ>fu۔AXوFP F:9{}dD -L9ਪ⋓DX^UuW4,3<MMl?^AƭrW'f:˿'DbR%2>F@|GL.ܢKӑDY3V9` &fQn['EC8]VlFt3lT'Mѓ c~b0P"x7 WnSyZDg[qsT-Wl3$Q)Av>7A [ኔ0O))|:Tl HflL y _Rhn܏.9mB.m[vQӹzB!=tFw6a[ͱچ'j|Y {6m]gZRҞo_nK _E!0MgDzrq2ѷ)y41bLP̑+vעm|墐ЍB`Bn"|#cٕ/ R+v~(!+[ oP/я8LF29>7 T_ASfrn M @L$ba=bӜd1'❲U9JjyxȡWWF'?Rr2~?W,H9FM%IR8@!u9CU@H1D f dyG,OG,(!;@:=UK2o*@F\Ml{4̨z #?}XH+>OP{U!N0@x 3#dQ/+eH'(#ܤjF0DE/Rk8F]#H ~䌫> 6슘4%4~i?4{QQ"ppn/U=E-Tژun}xqD)ܚV$c=1U ⎫q[lPQяgr4[Y}!"%#,-AGk EH~bFCQ.kWL9 -1,Ph kٰYrVNr"G4fsmj(ƺeJƋscЦR`0' #V|K%ˊXT̸S,eV'z6Fd,L 6|UNRGrOMa>&9JG-Q<=.NKQr6ڰ8'yF!vQ3: j.v1" zz{stDt*}GA_bæoGK;x&’3dLG%ܙ"Iry<\FTp)4\SV`sŢgMW/yw2Qڦ{vtUڅ14 4Te#yTtB1xFSau Q&ѯs# x؏oUC0ߊؽg>5-Hz5jrߔuMEs0قL`F~ʪ՜{o9(s2*Vf\lb+[n9qR=<4 ?!rjXJU 83eTou) r$yŸejG#w t;Ƭt\`% >+킚"m[a_ 2'=è3WQ>0?Hm;}cPE) =ڮY]e\R㻁^ {ʗޡl]RU"yB9)Av-s@8x" RwCL=PtAJWb7ܼD5Gʻ73Ot (?;;8m&?c /pKYH/ 2u(Vgڵ٣! xUQ=Sj4m j6,ncPjQ=xfmR(P3NZ:seZrA:kvdMgĶL`~VL_j*+"|T"/+ܶ^+Z_r+"ރ` ?*DE5~ 9-[ Zͽ}jN{>5Zt(Ac^`6li~+`3Q<1JqkDr('9p3"M15r^kSe*lR𘩐w[3w#tu|b!hxxjYF:c~:0M[fW 1r1e.kK (lgT<WkT̞Kc#syHg Bb]|LhMTZ.9n/=37ħAaXk|=,N LDݎqE{Y_&&($ )f-UR-`t.ѫ_B9";5} [0i2JH0mp'/xҚgIB7Pb/y& gTBB3T+GAD R  ddned V|Jo+V,902Ll28e ޅ1B:\'v+x19 *o?YKJ,Ud=39/0UsS`pX啖LJGýb@o]qn?lJd>¥Oӵ>s Xⴇk%) lzj̫6xN0\PP59j1ABbʑ`k 2…ǽ2I}#E/Xf3]T ܪ~R64׿0"9y1R֐kujՙܻ Gڽmj F6hk:`75b,%ja~Q[[ SoAAPc 4Ъ5FYK1tju8S 񒡨(r7U,qѽ[1ڷ9zUyla"G)ؐ骛5PigbC7tE|Qv oYKF}u T0Lr`U[MømjkZ")_1p\̹e.1PVJɁAV2AFl2ϖǒ4`h6`PPp3Ƀ.gojSs_܂&UaZú%W1cɜss폦b8-jb)ݍ2G Azڃ五:cqë0;KƤ.Ώ_\WXjxYXjjlI`>%-xCvJ4KP][׊!`\>ew-Ż70iTkzR.υ_eW*n\iNV]&T~3_DY2{~"7x6JGX{$W샣/Bǽ1mA LLg;+ dn0*a5r<{`aPjHE? &s-o(ƴ9gb+) I'$4y)Q/ fd6?Բ,uRpw|em9JwqKy s!<0dqlg=nsf5۠Y1U E+bLutV-s朝{J>2T\Y>"Bh+].qwd٨-;VCNglV Cw,H=@յ-Wo!peYX\π뷙G 潥TVxn)l殢 C򍉞%$+J7*EET)oT=/;8ܹ g\F_&2s~A̾pЙy<\ʲqf+VoR ,SN}[ ʩq[@mb%cLK^>!?MWf&!CKSU ]|Kdk$zvfu?=WohY*.LYd{Ka%(ox>9LhYʥ-K dշGOك{p_ȍ/7 *.bpDMq3 :#qqO6G[5l0u!F>RXx-I~XڧN*C =ҎGޣYT9K- p͚2ĢOd.[(-ZXp0uԌ.a?w cz3Jփ祖-VH^]n?Ͼy\ h҃į@݃/鴡EԨjB@al!HT?-@jVnL);ghlOJ yʅǻr&MXb;Vu|t 5jц͸ L6Zڠ gQEa3~*".QќJ*'nl/Kd(G)8U^ѯ#fM=WMu!?>'L~a-v¨TS)ZB(њ82=9e~!t ɖ:3/+вw\wcr%qe;Kpch~s"&JLU4L|5\v=22B{G< A>/tLBKub}𑻐^s9n@]?2{{ ("*>Z,-50"[Eav\K en[,(Iv:t\m(59-o۹Bgup:U\ [ qE*7t|&i uc&S"tGB%He/6fnV*{@$%?\i@^ _n|s0nP360RZ-]s kMS+qQ$Eٜ~ CI^Nu]rtxus?!g>R//oFVW\P[ Ũ kСe#iqĴhsK Ԭpm}P;YRT*U:"Yj.V"̦}Q7f!pk3!ӏJf<Wek/3KoCLI^y{0Ar+]MnPF*j%a6X<ƼEom\g0 Ij%IQ'ߢla4/ B9\ɘ[ь@MaE^3%WR2`P5*.$ q%Sf>5*1*0WSYBF^7ҟ\Aeu =K}cw9M"JLsU,BC-DBqSe<^"Kcu.F/鬸1s(Td!˼NFeq%hM BJK_ :zKs Y38%~=j[Eշ+nUJǙF%Fp*WPѶkWl VTq;@e@kҗ%ZY2 3mJˁ8\Fރ\J%+Ӊz¨CՕ^!0qC$qck%̞&̯W\Cr+~3oƘZW= 9;.,īD߈ʛUx2˔̩L+!8g pܫc^#lE,N_Z=A:!RD1dc'q8C+r =:1>=U})g?!ylFq:F]Fʁqb}`#EK \OtrTPsq ޠzͱz-B8a[-c1o(QI2\tG0CszԪ燯3s{j!mǧ+R<:48+g7F RF'UVMK5q;z ҽM@NK #1z`$ =@5>gB"Գ n}P`jV=N%屛 ֳ >w}} Wc^Drx{Ɍ[f^`8`_2͎eilV1詂SL/uCXSfEC(cԈv3oJeaOĶq5L3VR8Y qqpKЊJy&: \K.='Iġ c)8-xybgAcuλ9/cE<$ao~GzASy*c" ܂R*;OT0^ˍ1,T{C61%qԳ^ 7;*ŠskLшݮ*?7 2."mfE-K,5_Z:K.\L&7 -#Jα)]Yq-WG9HM07|JGKa, -.f腟E*Z۩JOpe8cI4EED[̳r0E4@ rڧp7髹t@5̯GsijBl-vl]elKj?\N"hʝ#>Z  hRSJEPP=5az~CQTL|30&l R]B3,aUzmB]pJ|K}m({J53 q/Y7 = Y*,B3KYKRPT) twM'Gs3%ybB=:z6̴f ]f8 ~JPR:8  >%n29@kczb WLo륑%j7ꁌ@f*H%=lhуհESg1/NK^7t\j# o*g]O&n PY8n/1n;}[7#@BV 'Qy5P "_woWHKZ?ˇ:@އ3 LAB=ަ-=xEJ[evʻ! rJCH;LxM{%P 8iVQR#PʃŸ j- ,#"]r(\][n&UĪqR}rL92"Jr~|, K^]h%ޥ`ݪ<1!mf`zh f5r>rř) 6L!3s^:NfpNYJydaNY MI Sg|L%t9D- pCq%& ;C~:թzXrZT-Ll3O+6pQ*!\n]:SEll\Bf>ZaZO KxMƱy-px/h#:4| 6A27BҒۧaa[7V˩P-o1E1+FiC*f_PX  mMA;EC PIGa6uZQ,;ax86xтJyj& '/E7eᆧHUlrB &=t˰:0E3ԝF͌p;@B~YL@a2̳Xaի_h@C"|6,I`ln/2 C8p9Sb:'#fZ Ckn!O?vP&܂(s FۆN̾Nk%),V}ݦO4 +m]ղʢ4>fD 9xihM1P]* OR*mXRC5 `:ܵ c5t!DO Z`!}89cASL*:@:mQx ȳ0"y{4:R3s% v7K.ٙB:;2n \\An{;KIDPP̫7YcRnQM%EHwsㆆ (' JUFrWq^9.%O9x30 :.}Ypwd./RW % 2u\t(K!JĮ*oA v "h'B5#¡Z6xP'ǁC!VL! ^gSi)M LRWE/0s 6~¯nŞ&:#CRh1PɈ7JOl=DI۵NTW"Ɖ,R̡Р<.tM6@](YN>#tp4հD+L>0[C˴0H %X #6ԾԃoS `ZEV.;.3/i-RN炨Gu&u@ ZpLq(K,@Cp1:5Z GG%Q)C}WKmqorR7nQz$6 ඳnR0}Qr+kd&6PWa; x\r.p=G %Wx.\9XRzK?k*vYBuvǙqr5X ̣,]gBbM8S|*{mgNBe idxر|e^ =⊫Pq 8 EX tJ4lC 8 ɝ1dla ݞ+ɪ): :,$(h_chdu2(c9503IV[eX>J$tmc7pD5/6# Έ5ׇBh%E%1'xo ip=ѓܤ&Q+1mցcцOH_,@ig3FlSeFG7CD@D9ў#-ߘR דk)3Q }4Rr@Oh4 `F`e9Un{DB!Q6䃞EUgBKhi ,%w*†W[͢K#5@ {P]1$5,Z2賊Fjm@-EVjv1q7#,MXGC<2RQ%: K%T›l%9 Cmf $S"%Cp{`¬t £ paM6 Aܛȕ7@bQհ:[3/'+nb-!EY.ӹk}7km7, *7̧!xl6B+c,^nQ|RbsrMx/ʊ>-r**WK#j!S a #M@( |muS5s@./%a.`JT3LX5kƢVUz3/YLC2G ;U[o PRnGm+|઀")1wnwb?cr ?Zq0 n%<1 3 qf!1@pJu*NfCl6ovTbi o-"n_1f^eQX!ʈ ("Äu9 s+RӘe+$Y XpMMnb%6 S9Aj$0)nbѷqj:ĤL{DJ˒9bm8@i˸]\2VR;8;(^|^;DT 6f kL-͆mb#VMSĩJ\5ɐ*kа6w5PlK`#6F ʍř0f9/dJ/}O-A-zqnJAf $x˭f5b{Re#/r\QrĊ0lyej6ߟ4ؑ.} +~(o%\y/bV_8RUen ~[&.x2vs:)! # SFgOuh>O(|2b "]K MExiq{!jYyX㉬&Fz1@*v~ KMz0~)hqPtPbڜKf#3P4?iv|?05!U|x8-ĺ)k(b^PS2X5J-y^?~,r,_j Ѥxm5Le`lrK{W7&Poj!܂ùT̪Thb91&؜N /XWܷ߿W|J#~CV}HԷQ0jf[Z !M-ѿevR7 b`dP5bf"rcRYbJi7 XQ9Yp ]tDX"1 =dv0-LL+.s<Ũ넠-gO9\em\̽U/$DWBƥ!,7T;8="{1_߾YHֈ[ިxxDF|[wB8fGQjK5\k|8o+jlw6E{(_وcxe>i>t@47:wS*@ cA\K\t-CK}08{7/",DR+n!SYRoXWn^4Eġ"WP e*aM2 Z%?: K`Í>e͸Ԧ]J;,5k|L&|];?D<} e}˖걯TjjZ,pѷ-J2~q6UŻF[m5Ɉls^y\T⾞O?(u4OfdTw?z!` T6iFZ#+},X51S:=F2,j<:.^"qZaz 䀿kb,hEx]nbU?E 1e~J^rKqV`/' X25S-Odw& ʕ_xJ%W+ "iWE]:z"Zd9>sfzUY7\ʻOx[0`q|lP!o$p%#9g1q({"{ķR^&S,C.pE[%r"3p0LLٖ9y!Hn"0܀UV nXRcxw,Q\Om:>;̼5] ъxbQ̗"pC;FƥryRC5-XVy UYE5B 3*K澽ïM'~̈́Atgjץ!7z-Ar^ lkiIAӟ0}wv5ߗч*^.1C+@]5E|G[R?WoD+?<G>м w'N",{HSq;%CTw9cjVFwE;uC?to_yBp5~3 kgx=ƢB ]D[@uu}兼U8JkE5"~*^f` fHH*1j\|ks4mῳl@OAVws@s.7Y*ivQ@o >S AX)-ybYFQr?BTY&=ILqG{Gtpx 7^]߻"P>S(n' oO9D*g\6ਯ{o65< (-wwvԪhAFT9~U+#e>@TU}ʀg?9E0sæ2HɲW 6p|Q˝a9 nl&(Jad+Fhly?S^EyuPfO2#'6l GL8e˗̵9}~bgAS01N #41\"29p0x4\eYGdV>w_Uf_=MDnTJ:?;W gfnFYpO }ĠE(? ~eMh"jϱγܧٸStcanister1.jpg0000644000175000017500000001511207216030507012735 0ustar davidjdavidjJFIFddDucky<&Adobed  &H       i 0!123"#4@A$P5 !1 AQaq"2rBR#0@b30!pQ1a `A"!1AQa q0@ ERCٴX2@@+- IxtD@ ۲m[]yޛ! azp.Mł,>ܕK 7bHAꃏ'nkn"t~R@{ƌyO뼽|gb힗L@X Y`qˣtz,2,ي3rח0T9`2,1 uΟ?O<~, blZ3ҋݏ~v=8׮ \>^N}+ۣ%z c{Oʱ䗽8XV9t?Vg[<$7|%vÔ;s&Թ{kߙz%uκ=A\=gpQ6 t5:sw,e trPX9%MYlEd8;Bmm-YCRS]y?-.@~o)7[e1o΀.ԔW^C z] bHc$0ԔW>C fDD#cy]dh'j*{ɡݮ1g)_ܺo9y$m7=&.2@Mp솂j*ǒwzo;[%wAPXojxjJǒ3Wg%1tYWʌ<:ﷆ9} ;T#ٹw+wfY\"loP'2Jۼ5%sn(U k♙DOnRsgjJH L 0ԕ9\z=A#=$zGnd}yٌa;vwh"A46GlMRWb)RIm. s<mfqB62*Ĭ{b{o0{3)lSi1ʝQ(.hM /\ac^^^p=7(YePuI "WwSDnk0d#7haL2"J'xݺ**** UUWUSJ$0wUUPޔ7 eLd'7jor@"7m60[*O$ r? 蝩Dau*MjT6W?21eE@ʣV/\kVJ$Ra ?`uP\zY=3Qsg"pZ6Q.데=Zߥ =+h3#hk`zonKZHPRtX]qIkhΓ>S:XPg8>S4UԸzc|-HoS.7]}l<8;l4[D>S(g|+|>SlH=n&lB}"/=c01pm>i^Bܻu}!OY"h(#柽#WXyک$ó+]NلЖ{;ϒ~`e7^bRXB 7o*`s91%WXf _jAӗs3&5iZN˯Xtt@v<.U]c劽\*Fy{a"9aD8e 9ҵa{Pך;M>6- /?Gb'dͅ[ {2bd\Wc|i|xN }@p3Q أ]n -Q.e:FY~or-TmJ8[-#d3O:h?9 vx'NryIw?)/:Xo)tt=Eyx! l0P[WA^`j^Xni] s-{cxZcvgrsAw@NzWYg:hyB(/ٮ=%,8Oi-~?0뽦CI 2yRH;CJ_ωsUՋdˇ4?XL+.|P $/_ד7? O??!J*W'v󖈾3`"uw5Z; yv(PVȁRki} HTPx4V~˖K @ϫfm(ǚ4Ec[Y(`,~>O-|fOhTv$uJ/%m3T>>>>9ޝݝgruJIRJ9~J AILH@" KL+`$_H $FdQ١.Ak(L)@q{H>!ߒVdng6@ Z Ը0@*L$ I??ԻhΏ;ǣ"$&׋Ф\ͫj6/A g ׬,"3Wq:vqޛ8 .b?(%{0b,ft-L.䢩:|)t/޸b{1ʤY$ 'jZri2 YtfT\ӝ>Lt>$_O,n,ke:o7KncCk.&7eߘ,hlݟ±P喍GLjOLg3GIƸ*+ F!,5Ax~J߬N7+mb^Y޵j`*E<jp2=X_Ifx=Yf}vm`cF.e]؃foK0wG7~c]uy(z*NN(,W BzTcczqUPcFx~lTn t[m:.D̓0D8~$6(1qMNsuP6:WZ,ܳt3J)Tz3h}"*0J0r{+sS a뚇aQKDu-a]Ggt 3Sx6>e*Ermy3x,ERk\OcIgF cb"ߠ˷1 (U|0׀w"4D`˯nNM>VݞB!AJ5`[lĺtYgL{oWY:Ty P_|7{Ghz|Gwžׇ;`kw=3G=+0&S\9]Ake!E§(.Aŷ@+Y2Ym-zRaY~w%\P4~ҰUQd5={ \K"үBpWt]def6Gv ~+݈.2mj@#Mma@-T)Hpy(-+5{^46r*pضF9,(sU^P?I00 YW^}ILgpAu)P(dJyP(.U`$(JZa@׃]ܼ.qi-[;ٛls_.R8N-(X@LRTUwՠecEA5Mp#AAGˍƅɬ2vVHTY"O_up>Tz~>0tT=0W?y >+fEOYk_F5O;pa< O/`kdx]3+5ԩPxM<&AcX`RA-m\ѣEà G_(0m ]D!:`l*ZyYDeNYu\X6z"u'IҽHK}p<Qb[=rQè4䧬Yǐߋ9re:uRw |$wٔWz{AU|Ͻ3s~ oIڏBv'j2vDfZ0˿Hd4le޿z<n1.ʆ,?:p X "F+~,K<7?Rѕ[.Tae\Eyo*bXf[,6AܡAh@uRs ?}1e1AeIJ0y?WJW-+~wgpn{;eXZWڣe5uR1gЌtbz,&i`4g+~Z yR+)kvFLq֌ѽ5_o+4/EPVؠ7k,N8A.z(GDDk@?^"kFo(,Q 8s+| p/k7ٝ}u紥O*zSB.', ϔ@:'BiyWV-_|{ǪӺ.fV*L qPQQxT|\"J P5-3 ?canplants1.jpg0000664000175000017500000004226107700501760013121 0ustar davidjdavidjJFIFddDucky<Adobed        "!2BRb#1r3AQCaSs$qc4𑣳T%!1A"2QaBRqbr3# ?f W<i]]dio306wΝ֔DkpwfC~>MglUcog xM' =Cdmuʝi~#.Xj"S,ʽ5gҷQ?E wx|m [>vg͙`?.;F8El_%D9mx[vԋJGLU车Gg,7QK=7Vmr yKrFRb 6T;v{9.rT4tD8qiWVE }]D]wud[a&JXǻjf RpVjBٳ9q5R ƷKlUY+ME +ܡ.?1CŎc-򭻉U`Z&/ g9KpF5{"C!Wǽmvif J/(ODQ}yM$2m;coUThw6bx.(XVҳ܊xْ' O#GoC CF\NŷϨ{X] !՗iE# L-"/Eobdֱe5+,!6A¦VrOWYB'k!BK뿪>r{A\`-s4T]xM/oớ٪eCcF ?>K׭ov/˯3i|$kc @/;)Lɂ& l8Vms,lR9mhW>ñP}+ Y L4zS"3])#n3t0bz`Z"山ڶ.7NKӧ(b%;""^-- Ž Ա VE+KvyY\ tǨݿ3O %td-U|^^? =IJDDբVHuß.-:BP}=IsQp6J۵D[XF;tj[vaBgVmAc^Ty,3_̈́{k w\ rC`Ͻog~Y+ Ppwِ"_lۭ1lekįe[?U2S K"2W53khPW3m:5̃[nƮ5Qe37l'ExV vk:j mEh=X|>ν5Xh5eǷj5sb=NRЛUьq,c}£]=lE|BWB\1%dLlϘ!no)tTm7QS4pQps*0+ZWw1ec{{ZW۫c63!I˰9G]LH$6Ł*xӮEdڵ Ekc3hI-+16_Kil?  OJH|hBM ;ͫ!bcTcp >ؿgw֝&Uxi4J dc,DYkM2Ŋ6"vţRbͻ]/}Пa88(MsbRC6.YC_ytpȤx#9[g^&|"'}1S|*- bl_?5_jŬ@0_n;{Z2YT p5 )f/7ҍMczvL;7 Kh[d$,P։!08WW~ ΐU9s i4[z\/skC;0QnfRkA.b&v^ )5N-nyZ v@:i#솰83W(.hD%y kATҐʡBM ܣH\%cj٢Zض<ҳXw7rX%ԭ*$I39No07XLҖ:طp)K(kIJ}[a'~>Ʌ[&-`pb0"8vUjN: %ll]9.PZ^(aaEL#ʽ,a|ic'Rp?bfq˽% kVՐe1fߛ?}kY- 5\Q{fZ-epR eNScbߦW&52P$f$9seqEk:V`|=OA2 !Ù{[sW>n 1 Eqc:P82P lj _cw5^7mmk*OZ SYR;MulC ՗OD]`|12BO!*vp`'1y골ԦZB`m8O^P2־o ʟD*e*,hsP\[%)/ En"6Ln7ld{^mMWLH.&C$#>xV*:{[ݚ3c!湰dyt@/s~ E>EyX4 j/@9OQexd-I0P˧z̟r¶U? YvS _%R4Ej[NklqTd`ϽNe 8⊝"!cac ։{^ GV'DNQ<\ O~iy(h@F̎k%JU7'WILlq{G=e䠧:cDQIܻd%`@A˳ OJceALl;Dg>Ȏ $\e_6NVD>ojB #$9r$<7o}3vQONzSN/ 7 qTYa z]EuE柝{Ef`ʽ2#jBw(D^o{*٩,6#[xkYzg=×8LYx6;(蓇uwp>ٿs-ԅQِmvpP5lxt鄆qڬ{M!ܻYl$$~ާJ`gu>@Kg Aa ŷ2ͭY6ǒǤ]T uGL Q߸`Z".K5Bo}S``mLSlT?)5 XsAPӏ0́ǰf<>{̷ov.A9vz6@F |mZWlZL 0Y4 ;@+تѰB$$Qd[Z;k* s݅C.]"{G"Hg&'VC+lᇑ B0^gZJ qmS{r;Q#?ziBcSZܝluOdӟ-]]C;*\^ ʋkϟ1n_sz_-ݰ;ĹelUvAKZh-vkںmf &'վ6Cѓ{_n_6b9;Co){-&rQ|:1;mݘ;>YU"vq+ O\0ڤZ+rSf˨Oy@L.]Bx\؍zvq~/xIuxk?!;w{JE,k!yf%kb$ΟR2b&bW_aTiRwq\Dp $FQU* TP(sϖs, '@WVĈh W YHjFo;e۰D\τuuJ.uhѫoȮ;y.]始yVJ]j:\",'hh~\.c<-.%*,s+q>8o_uoh|P8Ʈί/JͱfHyɣIJ:&Bf8<۽9&up;^=t1ϩ#˳z1s qF^8Ye,#'Fش+R}+;-_QE<3% ObqEkUu?J؍`#naqoh15QR#մBJ cs# mK]XT4:?AUe0Y N2ثrPs -JV`\ g&8 wͦƇ˷F^Im~=Uz1bWz$O E(u"(bB "xy ]sDl- +<V:nbd5;x1Ůd֖nosϳ>X̎jgj\.6굶=[Ŭ9]Q,dh2/(›1۰k[bvI 5-LTR:e&L+B<W 7$Wt.P5\yEZi1sP6lz~E@ƭ6WU. # [B 5hTi vd96{]NgQ;ҫ6}yZ 4OlDʘjɔ4VVC?U\"C"S#SC&'S2QRM@>)Z;}HX7uOұԔ>ŌgǓ}Lfԑm3߻g3]"]*slI S*j!l݋: pGUsZU˕|_k嗬rj,p`._*N൐QGwlL[tx 1/Yz|qw n&B_WF]ͱDt:Pi}w{<殹4(6YPtEDjAjk<c -\A\&~t' ՉYqd[C=C dwi=1Ɲ߳oeSq;(Ysh>j?a K*@ c q[}G.hXBڦ_m-dY2w7:i݉667X Ů"2}1<|0+:dYEӥv*-l锩 1+ `C09W?4y$tX_?z\e`Mt멪"Uu/,EaB<Ū+Ҹ}{@ܹr91)ʚ4Rz9G<0J 3<`xxs|Z5ʅ! yHcmwvhzddToW^i ]Gwk^q3 0>1úr>Ѽb/!wcffIq; Zs$)$LP"{[>^mLcӈ0r‘zΎ۽8S/bLmY[ vy{:笐*+}tOtO`MhIHzkJfvQ[XieS-ީ-`fņKiS`GqieԵf&Ac`}l{41ݺcz 1m77~ |2R3S˼&Ӗ}+-O#O&գ\NB0wjU2j'}.l%J2b2@9wëW1)MHr$ QjEJ[. U7tҟ)[b6v/Gꌡbкm[uWq걏UZ.GoD o#~n$b\\ @oڢy62%kK.R\OQ,Uo75_.ngǏ%k@njx?X^Ui, n֮ޭr_Tao`C.?ɾ.s(+[jȥVdWĭjYC֮Jf\ڦ`ۿ-[~!7 d܋A>ʲK:F]R >)152R}վ<8?lw] mڸJig ܕ'Z&Gtk-PP -,1wgq@Ֆ@SVx$lXƬ MtUPmnu]^#ͪ'bfPskټzwBr2zoZ>=*VjWh>={@){* -w0rѣFKT%USyO0|l5m7 +/D|67-ޯSvY(Q_7|ьzw& n3)b%H] ]SB{fW`zgV%p 6TvMVٻ]nΊLCD$wsyiY˖Ys;+gsmڤ^]t}?|}`՚!zX(Dȶʏ`<v/ӭV@W똿l*a|g!#z^Nm-q".ذ7._˗z-ˆ36`.nC_ W2U\M5TƬ,cݥX)|1)vSs(jN.^tuZ8<zs<8|:S2 f *~iVUӭȂ6 n'@hUD)85YeEK\mtubE-nݢݼtxN~WOq+QJ3bFVej@+WM8ՕLl}JuW-WFLOHgB=2Y9yy;taz;-GhoiYc_V:*,Y8#!]`ij{y7.vw&UvҪ*s+mwgg' #ntތF.Xs&BC` bgQvjw6]tu1m5+W=VH aD}[Aqͻyr.@f-[=טgQn嵛iÛљ#/^s81L հɥiHGy&:peKr=w3)ޫi2*%~j%!p.>%:h/t/Kͷ˻]:v8nqrR3;8Mg:j]ee#Xv%]?Oo({~P+5yO*/Zc5ci`[QׄŠطM1JD6n7;~? 9>J-Q[qSwJa*ƇqRUeVt-wpz;sGW2Yff̏>՛VqE;_RETGtAR@rܭMF9=ux`L dд- Y 2ONJM-J9GO9Bc:,25->=vcO!&Bs"P|r )^gM[&Il7` ;B~_< lU |=,eUՊֱ tIl1TtSk.IrgYJ6!NY@ӲG< d$1eksZW4fədm.u{g,R鰠@ַ n^mvp\˝ lPSnPamF`V-agˁ~\JlJxc̲/9+.X̠pvo1(6۵:?צ.dvVwno.~i:`%;N5R}՟E %<̲~2mNQX4 Σ (ajE}$=Uq>vYtD|lU4[SH@Em-k%"ӝ,X Ce˛Yѥclz(W6x!c936m|x\vk4,!fŻ\㺈; 6+6*Xp,aV!-)oYI.;fPe?-Gg[HAܷhՈǮO5XW.`Zv},I5v|.-+ݾ'hTt=Ss^"CSwrLdԌЧz5$Rac&aA<.;GwX[yUɦIR2;$9KUn(h_MwдԬ\p Nip٤aɆYCqTu\i *o -olE{7FVͶkvkWO0_+ V D*$+.LCnCn=DxͿfj+8"Ln+vB"GYo0ƾ-VW"[Z uFcq~Õoz1ODٗi"v}^`.J\`ջvГ**<s V8k[\RxY ˻Z h&S['{vwnk"ŊBu!aC% C>mV*ED;X{3,Z v~YC6(|t ʕ54f[E IT]_(taÈIRcra7'gq"e^q8f]8T]}˳Qv* Hv5n,hSlKR۱(Q&A%.`0*ub()1j%Fu_| ̳5Wҳ(K3+RiUg䯩ڞ "m-p/WP<$эpNٿQ0YBy A'c 3EV딗Dڊ V&c"w qX8^aY^b;{U0@c-hȕImz_1>ZSdG=N M5OJ![sEsDw`OyLN&Br$Q ?1bLJmѿe<=~eFʵmnKgq^ĕj/դľf>Im@}VuAǹ@*o#R9o=d.c//-T {q"ݽU6SH˺Igzͦ//o\gy*}W',)22 cЦ'~\xjʠPymKޠ" *5VK[b{ڑ7Dx</(7Za1]ќ%A(| iL\4{cf HC٪6bOsƓjGRbpނy+WY ;E\(g()aށs^-ny"cJ?HwO; 61~ֹ|gw [D݀}P{I6j֪YDAiPZeo)&͒=L!Z('8ܯrr;E|Z"Xjd MpY˛f؍nj( bzf0uTC,ȖknzfvG:SY;ceV.@KUUSC۶~\RS%MQ@Vod<=r cp}!|o(2=_+OÐNR!G'ͦƪ;}u23y7% B*m[&uN -C"A>U,v_eFm^֧^܍dYv.b$X}9l2 n3%jY~۱X<.<5*r_Zo-,be@q̐sQ. {^R-vG^tK5jkgp=2?ͮU'TvB Luw" |n(3t~(2 ٝL\Q ٫B-:酉LaZD]:Ÿ۷5fݖQpۦi^!ϦrYguO@9[pK 4j9 {K0}bEm4ӢXd;G,UNݗ]8$R4PBheqfxk*έ{.\W5H>tp/;V1T]vNz: $UpE;a)G_8bDpud<ڵ"1 }Wn_YLS;Oݼm=nME*IH xM"^;r~W|8lDe|Q!8ejv'z8M>99wD|dB@~P5juE ;=5fjS a MFl {q\A9$ e)a:?Jhfy0Uӹͷ6+ wSp{xt 8􃼋 1XZ$P*–3Ոv^U٦svl-ۙ6Y9ujwQ6)vOxq!\vBI֕%} !cZc3X 0M8pԒq;4La>Jn+փJ!lW p21 45Z`Xa3cUZjJZ,.x}dAv;5BYzQoiYݝ$#nbjPa;#: ')cfj=y1WSceUز-(VJ *VLV߄o0㦞o;\** ]vv-WԘD?DȤUc=9_3nWߛMN;YfTpK;T'nm1@ee^ԘR$<\ß5S/j2jtc.=º{|Glk3etVGc$鞦Y W>vnfn=)Upij,$5ݼgW>aashTODOh1a) \ߩUZw`ri"Aw=U'xF:e[=74Hԓ b?̾rBmX)6.A#ˑ^w$ 6f$ :AQr*]Zr DɎr?S q-l,*pN p*v^m%;]jݹy{M"-ۯ$f8}cNK{Dz-mKi؎ȧvc4QpCsU :n, ܦnj0uZ4 D bSк*W4C*SKFm.lx~ۤ?3it}&M.ӡ,D+VL_V iVdo۸Lݯ{11b>r'n(֫Si\ qfba"p|&뭱OY~Xl.iS[=5:P `5i2L>*c#E8EQm`{R v) ]_0<ro.p-TẂB -.jŽ G?`HK ?BuMw.XP#] Wev axrԷzӦoLR{Y9ezme`*1-tOXG#˫\w_¸Ba}R'E̟nԦ7Ur/j jy܆'O]ڶ =nq _frr۵\cʒv*ĖCoE#sWl qnFn" 7SIRE}`aD U[d}|[d4Oq阳MRPhCBVY3mcqxO o@ 4"zv&fJ%ܣ&N !)dͩ{GmuiJdJZȋL&.%A" ؁SNwHR#ͷ~]C(cN d-Ddq->_)lY@+$| L׶Gن SM;D&Fך=Uqܶ 3qG;wAҲ.W΂p[쐎 zf# 3#yL88ya/ @,es@ xtbAlZ ;#DStmuM"q"˄:${x _w?: 4:iO^_BXIa"ɵa0lYOfWjE)1?}t8~Ζϛtvs>Hǣw~_Knzu6OOMI1NixGj4~*;jl',c΄cql{"eT>faq:xxW-:5Ӫ >teÇt=uF ljPf4UrZ͟ ۴O~]SG}Px͟|iK.\+Z$"e~C6;oB.6oϠjBJeVG;$vz3Z88lã{pgޣk9Ԝb&^JyyG^m4|zzehma-I;Nn˷_=gCtpv]ۧpt:;\} mK_MOfn@͚$Jz=.p>nHjeb>>RotnՑdCo\UxoZcanplants.jpg0000644000175000017500000004376607346643654013070 0ustar davidjdavidjJFIFddDuckyDAdobed       !1A"Qaq2B#R3$rbcCS4%5!1AQaq"2BRbr#3C ?Ծ[ݧEo!D!1H>xvatgCTw0lX FQ߁=.F }v:;fF+}e2BobrݾE1[͸lO+[iQ$MyדA,M.E@\~! >WZ5m K 3 yu~߸T+) 'wr7HK><%wCddHi#?M isYRc+kW<&ܡl9U@m<©0POL(Ķ۪[q5uxdԬ6jܕwDk Krf,{ґ"ba{ ~]'uv">? ڧ*Z82JD7 7v8Xq$]lT;^o",ݝ+}%HPb&9;!#雉Js7)SqФz}#ʹy׹< Yq'V|ǻT|cnZmv` M0e<~LI@Kqdaׁ*)D&Y?1ɏ'[UPVkH`m4\*ϭ 6ƝG ]0P9Cm⵵頉,x ͊$,;E@>"w ]\Y>rʑAg9߬GqT(69GZ5eo|ozvyvcg_ʭW]6Snf4#ATMEVxHC8rTc^'ȍ I_֑db ~Fc.2B]&uIE R՚HWA=xy9+8=N8sǯܻY3V.dPk\J矋~1[hFc!+I]+ŭc H$eeplYO=yH\!pS"_5lu,khZRv%h#)$_-[ ꞲlbrƾX~nNv͂;q@VnšxV?G}b{i;QOᘄ7NO=_IuWܖdO2%k^e~GirP]?x&%yEԤ@Iѐ-0kNjõF7lc?H[8=H`kJ!&?mirP;e>2''#| e43uN21( -UgH r9vr&%NFJ(դfB)ww= ?>68UEMO*ξklXnQ1 Xb9G0(cJwղxk/wb&;\cmǫѝrX;։gߌƶNq$j03EP;1nZz5Kݾ/ɴƦ^WdSA<$FA  Bħz>IO#Py?{/Ntqp8z)8#ba&O7Ag6ťQSq+Gq߱eA:Q~qJo|->M*:uup\}C?Y翕fqUlwSh$䍛{4vzm|YA^14pJX##'׵D> Z5lg7+Ց aFNV X5rbx/~ݻLårBf:׫<ܪ][v3Ǩ(j%A3{/F|kwO"G+?:x=F:pA:`N0U?-3: ZS+twbR ӑfDsN?k޵'A s_:k/Vi^+B~aNFER\W{8<2 z!\p@Dԙ#\׈\N1r̂೽\[s'^x&z9H:v??WZ1,B1֩-4w[yb[/:,r`芠2?Y-JS5$#% <1S.J6B?Dv 3 +EH͇7a=UQ/o/^VL9n-8h;2込p?rΩ܀ I'*GRGOiQI Z<l2ԜR)[VvlQˆOBc6-1\b&,4TH-FOuRAoȯ.{SZ&ʀ DpGfR;=S@,EJoiͪF .=I~mүQS*:~` k块]`[_w3!ïChX.[QF=Qְx6Ǵ[15nB=}mZ{,$+d RI=(* lcvx+#qڃ&=` U`=,p<^FK6$z߀ ՊcVOZx4ل&,2NGN}DWbm zw]Psg<9cN(HZ7GgLB0*&XBvZ!j#_{hvQJsn u۫Ҕ0n5Rx4|䶫m+;iiK^u- `q>n@ZDZik]Yi L=ìl#/2z`ԝiAb|Ӎi^#6>ӥ`[$ 9ͷo|H*M !Þ8Gf8''L | I2B:}b8gTųVkv)8 ?n9'OԿ@VpUKF>_H:܆StM6tޑ6{~P=&G"ii|zJYG:$IiS C$̘H6<;z/(}Pݷ %R1(&Y/.%c7QBv0?.Z Iȷ00ͲpA2ЛhSdǽUm+d62 p_oMs児t:H˽0_3gّʬY=ŎϨ(MobW0VyP}UՊT:y(g>SLW P̱yX$vþIu;b8iH汨н{rIY?F%vnyzh*NEap8\{egV,mxOv9Uveqݽ/Nv d\Ahx|hjW"6GDQ7c`OoBI,ujD?%P1$?e}`Yo]c42`_gU.5@HY6;ۍ4LFpI:M?4vBeWEAM SxKdsĞ!G=q5naם3:z-^tar Y''퟊Q.;G}jzkf;-e.W b2íoЗςj,ax#Wr.ӭ+7P,dQ.0G)݈zJY4NIOR8e v{ʹXR BMo`|z#6EdڀiH~;{q=KmLY2j];S-ID/Rb6E? 0Gz;._o@2-ކasYe9 ]T$\߷ZWO|mF==怆_hkPKwenlCOvVP/9"z@'8LAkExAba-y9Z)^2= }tjB3:Iœ؞`/go[aVRYDf|;VX(ۋ4lU㒃ۮ>gv[_o"@w<jڹ$֌ Tf6*:'Rb)ъI $̄#GR0C+8^ʚQa-^ݘVL%IGݙcOrzf r *|mW&KyI,L!K=ؓ" sܛrUpq;>tpB\Q%ewQl"6+P&Tw`AA;`diJR]h#%*V`= o:ƸNfb[+E$ e7\ۈ9!uxBL"!rsZBT0Z4>7obj{I! $o+noӧD`1EG-0j-5+4kߞ^vR|)Q/k[?7VwE>zA"@^X V>MA˖z\Kc!YL+U3w^351*rpcөPWP-ާ %ucdDX&DCܖ #!ej6t?ӄLzzR*xϚ{|F[aqː̟s?^lUO0ګxcTY|5W.Oԑ7^۶ͥ*PO߻zO)ڹhῴt^!tv+:j5&ȬqxϿ61@>ailX 2L|zjjn>V* E''Argo;hL>1#޹Yu%d>D#w?*憌k9`EWo6̌{䭾ގգ]E8X9KcȲcanv ׫5f{;]#j f:u$?˗fANhݬXpq; ߇~J1:Cs?.j{]zMtǖ: Xk;kLF)h֭02X#<<@=:Dbcy9HYCFO"k (7խ@.'l\ˤ[L{Y%?ObxcJJy~g:V[vшvg3UuI{{xUs#`{|dm t?%Nۏ-]OWb-VXc<ד$s;>jQp\Cmۍr9ȵ: m|nl-,[z6!(WpNPȒF(fb_͵@+Kuwu3BI-nn<ƭ8e`VY?b¨*9n݋@Wsbܧ8 좋,~ }J+5ub1{.Hpñu#V /$$>%%M_g -b)j*G#}.r(cӣ <7UwݰIef+"  oˬ۸Klm^FO?S};uo%cԵ"qs+Uw,2 }{ ű1nWye-VF(O3[g⺋]F)#`YB~uCˉ֥ @nX& oX.+=6!ko(!^1ᕸ J#~X鞩1?QAerFmLNre׿qQL*:'2ګ-:v)Ed`TE`n\O|u^KZ;sB߮&j<YNDHG(9@y/ɰ:\;mGMP^h%W2?%;a'裫28㤓d+;}͙ipF&9H {jc"(:Aqv cGQ5Ӫg-עaw{na'ڣDA !67(s{bS߮''wVev GˤPv<\K:Ŭ E/J؆{fUyܑ0+:lLIoo^?/O7&;F W9P~ ^,M\_sxήc Uyl oJ*(s[y'Qzq"!cl>g!ܖ:f)$q(bW!`t NhlW[S?f )v8QD1NqXRE[KɒJW]03Ur<%UlAF=KCD=rZAL#H|o80ׂ*;34vv#/ n6²ٶOf5;]*{GޯB5DW3d*N[_N0"ؠ}ht\K>ڐrQgyH!x9)*|w]9Acwu_԰RXvufDW1F#PhEimԾ{3AosMZ@p@ CbLAmJYOR)bANtۜf>GbvO>AZ[K<կV'/@>u elzׯ8M4PV=~]gmb{TB@.ʳU#1:]wg9DuDWسn74J_5}:\ݟb U}t- =v 0Þy9sBLk^^"Gq(ٴMۇnt<L n[e)V3bl1,X&rzkfb ^m׫Fϒی>OYQOokOƥio1\;b{&rzHĿERg4_S\7ej{K?0~}-P3Is{{wlڜT Td8rzx PǕ,qϩ!q?RI 8{R &JԋZ+GGD+,=ܮǖ'ƩMӚQJ@Ǽ<dݼ1'\DžW쥧LGn'iJ`ԣQ z' Iđ܎ tRͿCMV<&tse]##uJꡳ,W0G{FFN>SfbQ],|W%}8Kxs1+ul$9"7` niy%ԒJ؞6B"X Ϯ?e_Id2J?q[S(3ģé+ECj1<\E'ql 8?jFד`Er'4[]β:ku#uQ(!^xbzwlq1>+^׷Y?VϞ'nYfXu^@V g:^l.D0 Qvw(C2亥vԆ@dK68Uy.1;1aQK5<*}: 6)Jxh+XͷJua=˶?=Pn!gJoˀ銧S 0M}^I*Z"U$2Po?fd'Svۭ%pG{hT_MeL-UDH"ʳa>Ix|rq W<_vbT6{[ۃ걔ktw$)-EkYmm-O[Udu4z'v!nSEjڸ 7tDZ1SL~H|`G<:W2l䌗q8*~=Z-FcV_ADr'ڲavV5XmPY!i-6[]|4@H>nMpf5uSyX1JEɑsugi.f5!zqG9$ׅq;[6vl;LPMe"i1V=3z~IR8gI[ %B?3+|H^F}:ųP`&i{F=ơJʲ= *$n~qщ'2pgyU)Ut:[W׮ A{PӽObYܵ+ b%`X8ghX`?n텳+3 Rg]ҟyTm"2Ь߱(ǫlBshݧ߱ XTȧ{l$9tbrvjoUwsrdhX w9EH?oI;DT-i{X\ CqW=ذIYXД[^3F98o%|gxzgK7#:ug{ Cwo$n=K#J@l>OK9\~{Ԅu~åo# WMnI0 "8_=tn-գWJji~~ȭկf$*=m۔ID=d?@7" OO,ϐ)wUVx)^-VOb4IZjo[^Iq3V܆j^$ݜr)8F/2XVi2Kr*Jw gd:NNEo/)1 w,ɡN* 3JY"(zs3yQAq~*mxvwhYM|Kb}=r}Cc4VBddxGzu.~]B7x: oaHvEՙCZ`1dFq F}B D$q+<0@ҪF"n]3Sb@5G 5dpAe!Gs1|uRq^eV @'I&i"TŴR Ǹ{HW1+vİJ3Cu:$cݖ5IۥLdS~QGbmls0Cv0[׮'UgvIR+ԍs3cl/tAT܉^.#x%jD2dťw=1~Gjɾ#>!gckDIZz56Y#F[%б uшlVNӳhW} NFZ3kJc%KAi vv.[;L,I۵Zm<Eh5+Rl/CjSJzƆ>V4iشbTǸ R0gt܃0˧m;̢@ $ ʓ *Ӻp* ӯ2=nj0xC))#&?$lfΗ[6ƔGx[\=FYX0ͻw`0ꐘr2,,,?YQH:d1(uAb1$=g8n~P{|TDZU}ٝTxF%q⼹9ǷO0Ke.<#|Sbо´mav Lr+2X#12}=K! 'y^ߛ3>;J+$--X$|D< q%*קw1S6]6`dXFX)G4H3b5թnc ϵ+є.Ζc{s+,Fk` +u[Bl`]=o~MkY-ֿS㭫̓G]Z~݊G*׍aۗ1r\DAkV@6Ƽ|T%8"{u?A$Ԯ XDl[9pp?ڄP<]R,Q+UIeDjD#8 pvM G LI%+/IAJ/6Mq@Gnv[8y >I:k _(LݶuP5ZbH$?|unā/"J\EÇ#ߐtmit_n`扒9>D y+L[4* $`ܡ?+'= PF9lwzTw TN`y$-[Kt-C^JÒLd Dխ乑ǾnԮI-yYtZ/Sk<*EO4<2xdwZ!I Q ^W 8K|.=Ւy@d#KczF7]; 9e!\}Ab u:W:H.]p\?Иʇ餉8NF%W\( N~܃d{\ Àjm>+\2L؈rxdR2s]Q! )H,ot$ $kqaoZJ+1Z!VDNĨN2;Pf af>%3X&$eXuҫrleekEPH9FVlFgE)4eDQ4P.BWOYO0p1 =K]`6>eǮ$fp`W+'̲p>亪ѱ Dd, ANYpU٘ة|Fg,˨"8oHā/&E‡~9h\}qLBTP *Y2@<~T/ڝNS1>Oq puzrDߟv#'DZn_7ǣʨ3AKq齬U+XbѰVpn%W YdgᱫDuxv5{Q2ykk/R}XI JJߎ{YѓN5RɄU㙄!x{?;@OTA_늙jF{v%p;'@Zdj=-%{q@(<Xg>>2`ƞǶ;.SۿdJSI>% 2C >n@vQS*{n{xT`|.A%Ⴉ?X&[)៟rw}wDW~'"=Az*!.W3{C>4ᓌdgE@y>\7ϷǢ/cappack1.jpg0000644000175000017500000003225307346643661012553 0ustar davidjdavidjJFIFddDuckyDAdobed         !1QAa"q2R3SB#b$4t%6rCcsTƒD&dV!1AQa"q2R#Bbr ?jq)lԶ:` zozozozozozozozozozozozozoPr=]s O܈lWh+v c}~_=G{}~_=G{}~_=G{}~_=G{}~_=G{}~_=G{MTQև@8B "mu,e8)6 }2PQg||=Wwj_y;5}|{毼|=Wwj_y;5}|{毼|=Wwj_y;5}|{毼6i7nECNv'09{KNN~WOB[sR(=Z\?LJ`*W 9Z^BNLxq;}%)$pK\H, 2\RǛi U=4dTo"ybVտF?XJ "YB|u,y͛xmD."}L~Uv>ݴep~9ݷ Yi {d?團݂B񶉑O11y;`^?B3͗<(9c=@^(ҴT:S3;/}ʪvvh ԯih%3'6O>6j/clpFGÞ"\F?B/!.[XWЉ͎uE{QZ-R6rJe6)fT.59{&)?44+&8# 뽲wB\Z8"yPBⰨy$V38*e p'ēqXR MHګJ)i8jP͞:f|՗'+?eV=p5 Mp^Q?M\Pg#e/-zǶJpNB}1T:xIeSJg30q8¦EI ˺pfXLtoʫ-OYHm}8LIZ6W8ylfٹS6SVPnB:5AK|1nU֛V B;}9QVu%ֺyg!z-5=34tl f ) ONCDe0s 0#$$e<;::p+n_lss>sD@GC,iu StxLF-uU/c_>UΞj[JUKa@QǂsO?U7QGUxUK]>RH@~Gk`;nu8)I][g៵Z起3r>oWB徝 kK`rp\$'<%7%o?s"EZ[|?#Z~֝o7ݧi b|S5gX=}3䭹Snzt60Ւ@%_@'<˛WX]鎮ߋ5/\Xqe&#肣z3cn9]h{[FG;Ypaj*iXJP+RI2pVm [_&/* 0++#NZ^җ[#19o`h8(AL0Lv<u;W na`IM~~ u}i~IIROJy[+T*Egfs.r)e,If =ɏ8[=1{=ɂΘ3W~X$T׺fdOގXWӾx/wkQeXt4t_R(fž*T8Z(H{w{YtljSwURSB{T!NbYFgF[W}Wu 5ͫy:J̏'IRTT 8cjK xlYn TtH*tzEH (bNC%ROG͔R@L|2(a/ &Vrz <]0 g#&ga ($LqF*da,3<2$))Gc@]wNs`?7妥tuԠ,9JGN2 Zg~o|2AS-URY}%N 9g7 ի({/W&,Uk_ڣjӵ:qEtoJffeR 2«I|حf\[sV}M.Xio!1;Z>y~G\Xq::=-:+*wM< ѴA&, Q~_R~gxUu j4LrGѭ'X17e}ic+OsA4Δ"ѐ*&XJ=yX/ۖ7}ݾ!5j+Hq@)t3JAB7ښJߢ SIBPKR2U)(F 2S2&PUC6Ce"Z28̝[h̹R,rl+}Pp9Ǎjz\wbn"+L;©O)^{! @&r֒6&sCʚ[Orj(vtY\k|~R⒢EFmc<xn{F]As|>)6iP2j (OCD5zZMT)/ Te2#u|L]/zkvmOV=YgPn|B͔߭(V3 řuܸ̻Bh4V%<\"KBQBuN$aL[dT-)lU)2tƕBrjQTҚl5KV̐!IϊpyL/k wq R)#LHdВ_us޶~qt-/jJ }HNю0@c '{` }͡=rSW%ThH jH39luTNӧ<jѣoxGfU n5JjOWincq^*kT9ò>r)RV?D N׉2^k&۹*Xl0LZnkJ^]M1S- V:dQVT"-g d㉹E9 ׸rRȸLuFM}B%5ኦV3Lg'KʹU?ALqX/ˢrNdGJrdEJK%7A#Ɯ~\-mhQ~gYԛ]&A8}.o ')Jk]CeJ(QtzلthJF*R+Wؖ>.ХMzm;C4S-N`NuqW]퓽|hUܱ?Ʃ[uĸSjҤs6)wG5m~*8?YDsZֻ2vRSRS$q~ZTtIbkqJPTN 8 !J Ng:!H$3)&Xx X '9b,Yl 䳎F}8 ";=G[ $X<KZDs}I*r2S29u^,}1feuO&idۊ9&AH="a۴~֗i.4ShH] ɓvթ]Nآ2X K3#6IMB=l9Z,"2E/ʣWV:QYRa7LuH&sEjiVCT߬ibg9qf|򖕥jE(2'1YJ$Y/rA%H:r3g%[A: T䒰j&4!3jIqP#F}TH #}WESڔ-$ua-? ,~tj(xoqsZ;\w9@{dl_7?/y'9c1FDu̙a)gt=m Ѐ 2Dz}qIdN%3?*X Di&D~cH'  g@tYZ{B2g9c/$^Xi3̜]TV1՜lIzP$H<2LXDӁ L!- Z_ psܺdsf8fgJb Ic#ڝ['~Ws>[pFtV623QRQSsf+xlWT *L7~F Bh6Vp*R39"bOَک[.-S۔h\)O e*"<\#MrWY6ێX}3@m"Vo[JvzMqI_n%9 p!M)TjLq#vv mmSe5BjSW.(iimRRBs19/$彝V[&Iy"QHx&" ]⎚aOTRKmˊTXm&8ƐTa$$1E|f}QASR>J&Jt^ S>A%~EYl[Õjq%R4Kt`-; 뽲wB$|@jjzIJ;kyF ֣ I(-!I HP"J d$ JJBJIPīވa4RR6 d C8-Qf1HOdY$ x$q33\x@U8@]wNs`K݇?S5MšAA $cQ=+HH.1EWjH  _>1Z-ǗdT^ ks^KSG:Ґux}q/I:k;tܛr "ɺD>vuj Z)LbʞRYd9N{qǩxp+YN])KU?5ͪJf% %UnT<\7PǩT۪q!&JʉX]:u5;NfisZ. ̠D;^A>矐O80Hx tMlk!ER"xE+!3%i2G!.,'U,%/0ce֯RR$!<ͳ3Rvbf_+)^B% 됊V7V+B_.6A%;`ѷsnRPX+a58PT4 V˭l&,·Mjؼ4ͻӬ$r]RY)/ȍRV͸y^Wu7Uy8KSO)ZdO1N_UV3DK[VJhRT[}۞lJY*su#pe{~ЬG*suxFm-f9cɑǶ ]ݤtEiٍdOtmh:>6V+݋ =5{da79-ɻ&@mt7d[RƗdqz8a(:U$X9,%2-:.64 #Dl8m*RD2Dzh3,LO?i#ثr7ABt!:%(b0jAvU&KeP<(uhqSDUҾڶ=-Qتm.<*N!$$eIwWKV~T-y4*m)*RE^r]^4$7,Vm!~zDSÈ$ RHb^deZ{ د[=>1Bj LJc`ĞZq% *P]@"mڂ9pP9U~OBoe@}ʲsRLzmTqUqz)3bJdSfTF<5Q8CimTGLDz-MJȖ$s=­p_ջ6=V%;%ϥ$']j=q T&gHpi;J{qKs9`Nѵ:4ٝ+ 6֤ ;V|aXcߩG u: yqZS>JdxvaDIg&uhXP.=e1p4L`"HHZu ̒fzmsnDžzdiLɗ|>>bB Lr3H(x_q% .l; ֆ}WHuFVTnFV۬-CPIRe8F8u\ָH{8O՟*vlݦ1ʞ##\4ܩ 'h9 2?x?Sd;GൖK%hV>TҚ%iQ|XG;75]d33Mؠ 25č#DKGl\D~ۮL7i]Uh4γ=N53yrڄs5N\7G**9NJVɧ8GV^T5.AYKl;L*1JQ)2~lՙqZ󬎾S4Cj)MBNdx'{P gq]lΥVm^iyͣ<1-Z=L}[Nt6e6)- RG9m'knMdA J#)=r+I릧eVܛqʻr4VEAaZCFcOz L1 dt) `)DIs@-as9 :3+UcqejORbBPB:#f$Hs#2&~XPUhݵTRRݨ*[5,TU0HNJ@=FM}x׿[E}֦S./]E0e! [eCC9>/6gm"㶥w]u.0/RTLa>&=Su%ݮSQ\)[!%ф+Fi6sX^,K_ U;e !#@*e -3> cD$Sz,ToLLخr=S:Q{J\ ygLÎ]Q[`V$%æ$ f@tJ))Lbdc9p'RQ33=(IcR@D˧X"X1Q s@ڙJUxcmen\-*BߤYRR 3) b \ZյI[Z3/6H0>9$/絿C QL{'-*QPY=ZdQUapN8ß92cjOB=0k&aCGC-.+kG92 D?u]$*<~N>E^BYLQV?`r4^L+5_6vXvTIq2uŧ 茽G&vRP:o{PL2qqC3%?;C~ O~Y)N;(rf`\{&1f>@f%/1@f'?1?Bharfj`a0ig׿hbBq*)y1GO 4?v$6"                                                    capsulepack.jpg0000644000175000017500000001303507216030507013341 0ustar davidjdavidjJFIFddDucky!AdobedL   #%'%#//33//@@@@@@@@@@@@@@@&&0##0+.'''.+550055@@?@@@@@@@@@@@@"!1A @`"B%0P$5  !1A"Qa2qBR3 br#4@s$0PCScT` gm|ޫ|#N)G80]$tp@NĔBrgZz"[a]6g@>FLzmJ H^^9w;?;7S2Z!=6P$ce;byfE3ehˈu &wFf<I.z,iJbQ9;rH/65"E{*%ӳ,@F94A%3Cywn:g:HV'Y8K-fCM & q։힞:\tzVDl/ A6=i7ijT~Θz$^tb%X"Jk׆YQyJĜr$,8qՄFl5Q aɒ[cD{9#UrAk%&+:vZYBW} =Q*߉P\dI}n+uyq}s1n9:ӑ79BND4[g8Lpߴ+%I%2 +֓'5 W sE\$Ā> b@6v^27*o1PGJ ڕ5JL[{ $s L-d Wr0cbLbmMa٨zQ0\ak c}K?}C9i&p.Ԭő]zL12F$ )27 vWQuAy 8X޲nKi2> 4ޞ&ʧ XJ=OwKV~s f;HcvN]+۷ ݏAp$+U`-l6Ͻ3o{%⃵lN*Z$pmG;H)HoqȻhWIN`N~qͤg DgVx6Hg6$QC;tM@ۘ]bٮl! AJ"72{:ip;d8PV(ڍ̛[*if`CVM]7JYP,ٜ-4_gkpO6a?Vb܆20E`+dIA#0ceMi\.hV6l{c\ۺd22ӑY{|N{97*d!I6j{W YHF 周8ޭPw)$kq 1v(vbAY[˙9tbVeuS ¹Eم4sx#ᴐsu2w]b9U:եUJIC|}%tLNE]Cj)[cit + w~OŒΦt `*O=eepC ͎]ߴ*TdW298p)-* +A$ml`}T}Βbc}Ņi2qXB9:8nͨpj[,J0Ժԡf06?P##$f9ӐV6?} ߏu ĪB`Iuf1hAYe,.`TPK$R6;}V,wk@iflpQnneaQ 0~`V0@$&=o[v`2B5F,[xy7Pu$3H:T~˚Ąif!^n h1Į|0 WD /ux Mni.o5+$6ꟈi6#o%ꁦz [gɵ_}@{h$ .fFyRˤ ¯YlE@jYXޕWR WEto8 ͷ_ed0s;*૎dd( jK..@'᫖M#"b*D7j@[ aF4Y NRFPˊe\ȕ9DŽL X X"R])\unQ@1m\U%6X*C0zXBE9Rj9p9sl 1Mh1 @_c!P5D9S\iI_v+*䲛16'mD[o#[[+aJ'eKT8 8oelek,qfe VH6%oqb h2mlu9*F+|1m6lzZXh:z}sdWJ̗aHkҜyΕĀ9;I”Ǫn]f׽QkH0\-j%`M~h7 0,N?&#я:Q"v#B]jRv[l>rFO4K FԒ hFIkMr(NV[PGb"ފIA123H",zpOq$5{Z mFN:xy=QkWُ?䒰C/d7z`bJ:;GQ]E@$^* !bϲ ؏MHH $ײasqB +ziVx+6 V?-ȨBÈ,n.47U@`Ke}v,#Cw:Tf< {Gꮕ?9TO&` рU;ikh,df`I* a\V2>C7;fI^SNNA7ȌϞAd j y̭ܧWm=K[3omDk:ηҮr7$Eia*o6ӬxyIJf}-'h/TG,n OsL 항~DIN+ R6W[ʧOU}HA(U뢕Vaqc˰Z^(D`Xm/dmc ;&49y֢Cu!mKtQI/k #_=,~J^SWom}U^>#3a"/1` kuoFc8i t[ax"~-iɴ7a|.h[[I) p?ʇ/VkYVUeYVUeYVUeYVUM)"&ԅ6Nzo{K߃"abBW/QM"yYdz馚 頏 Cx$>󊺠_6Y2RAj C2+.#mV;H-zw$AW0DG/[|xqJN'oRN5-a)QX4ōȱ=Էy9)Ł}FH0* capsules.jpg0000644000175000017500000000601307216030507012663 0ustar davidjdavidjJFIFddDucky!Adobed    #%'%#//33//@@@@@@@@@@@@@@@&&0##0+.'''.+550055@@?@@@@@@@@@@@@"0 @!1"A!1A"Q20aq# BRr@3bCS0! 1AQ"@aqB mznӢkkMˇٹFy;0XWXrʼz,Yc6ef/餷9dPR3^m.Lq -jG3WmآMQn*&ӈ93gcGgqWc/M=ܥG|??}-DBz"F_mCY<2bDD~_ι5·=ƀ>)AؗeS\7dr[d#\ ,UF3ܑ$V݁lJFp'Hz,缗IԐbV0`22jؕVh2-LGp-Z[WҔ #+d~uK\ñf%M86љR˥wSs>qk%-B"1 HU{B[]gT̪N2ji4q7u;o?u|2CzWJW[m]8| ΣBvN ɓnOM[1݂ᖯ\ 0}I6*_O$QƠDJ5-2t4ME |Vե'Nu ɝX&2b3HB*Zv>p07qRZ?ρo曈ՂcH C9L?Q3b$tmKT}ard"g^T|"D\?%w;Q׍zYZvlvTJ F?Tʠ@_1#EUA;38 jiBZHu1 ||&ܐ1߯MC#!r\/Pah1HE<%K(>l`|5Ǘ>E' 06",6:Dzf E[.# JBpFT8/vSÄ>]+Ԗ٣k53u1`Y\ІVQDg9\,$xLrWmռmltf95VP~..ժ -L Rځ+Ju8iiO\b`yN|ϕQ9Z΂fxo/ DuC*Z "bZ#[j>=N= HzWb$V,5ϤF⤵yPăO>$mydir/wel.log") || &mydie ("Can't open $mydir/wel.log", $!); # flock LOG, 2 || &mydie ("Can't flock $mydir/wel.log", $!); # print LOG "$DATE: $ENV{'HTTP_REFERER'} $ENV{'REMOTE_HOST'}\n"; # close (LOG); # # &unlock_file("$mydir/wel.log"); #------------------------------------------------------------------- # # [1.2] Location of Sendmail # # This should be set to the email address of the person that # that will receive email if anyone attempts CGI security violation. # # The "-t" option is important to form an email header. # # The "-n" turns off local aliasing of email addresses. # # The "-oeq" and "-oi" ignores spurious error messages from sendmail # on different versions of sendmail. "-oeq" is used on Sun Solaris # flavors of Unix and "-oi" is used on BSD flavors of UNIX. Check # your man pages for details. # # If you are having problems, try the different sendmail flags. # All sendmail progs are not created and configured equal. # # You may also try using mailx. # #------------------------------------------------------------------- my ($MAILPROG); # $MAILPROG = '/usr/sbin/sendmail -t'; # $MAILPROG = '/usr/sbin/sendmail -t -oeq -n'; $MAILPROG = '/usr/sbin/sendmail -t -oi -n'; #------------------------------------------------------------------- # # [1.3] WEBMASTER # # This should be set to the email address of the person that # that will receive email if anyone attempts CGI security violation. # # This also identifies the AGENT responsible for the sending # of email, which is shown in the "Sender:" email header, reflecting # RFC 822. Thus, all problems in the sending of email will be # emailed to this person. # # NOTE: PLEASE MAKE YOURSELF THE WEBMASTER. # #------------------------------------------------------------------- my ($WEBMASTER); $WEBMASTER = $ENV{'SERVER_ADMIN'}; # $WEBMASTER = 'journo@bigpond.net.au'; #------------------------------------------------------------------- # # [1.4] Allowed URLs # # Uncomment these lines if you wish to enable authorized access # to the script only from a specific url. Then specify allowed URLS. # You may specify a full path to the document(s) or just the # domain(s). May use '*' wildcard. # #------------------------------------------------------------------- my (@AUTHORIZED_URLS); # @AUTHORIZED_URLS = ( # "http://www.yourdomain.com/admin", # "http://www.gt.ed.net", # "http://*.gt.ed.net" # ); #------------------------------------------------------------------- # # [1.5] Ignore URLs # # Uncomment these lines if you wish to restrict access to the CGI # CGI script from specific URLs. Then specify the URLs you wish to # restrict. Useful if you offer this script as a public service and # someone abuses it. May use '*' wildcard. # #------------------------------------------------------------------- my (@IGNORE_URLS); # @IGNORE_URLS = ( # "http://www.aol.com", # "http://www.msn.com" # ); #------------------------------------------------------------------- # # [1.6] Ignore IP addresses # # Uncomment these lines if you wish to restrict access SUBMITTING # to the CGI script from specific IP addresses. Useful if someone # is harassing the recipients from a form. May use '*' wildcard. # #------------------------------------------------------------------- my (@IGNORE_IP_ADDRESSES); # @IGNORE_IP_ADDRESSES = ( # "192.168.*.*", # "*.dialup.aol.com" # ); #------------------------------------------------------------------- # # [1.7] Reserved Words # # These are the reserved input field names of this software that # will not be automatically parsed in the body of email messages. # #------------------------------------------------------------------- my (@RESERVED_WORDS); @RESERVED_WORDS = ( "to", "cc", "bcc", "subject", "next_url", "required", "required_url", "hidden", "ignore_ip", "autoresponse", "autoresponse_email", "autoresponse_name", "autoresponse_subject", "autoresponse_verbose", "autoresponse_hidden" ); my %FORM; my @VARS; ############################################################################ ## ## ## [2.0] STANDARD SUBROUTINES ## ## ############################################################################ #!!! #!!! [2.1] Print Header #!!! #!!! Prints the content type header for html #!!! in text format. This tells the browser #!!! that whatever text gets printed next is #!!! HTML. #!!! #!!! &print_header; #!!! sub print_header { print "Content-type: text/html\n\n"; } # end print_header #!!! #!!! [2.2] Redirect URL #!!! #!!! This will redirect the person's browser to a new url. #!!! #!!! NOTE: Compuserve's 2.0 browser does not know how to #!!! deal with this and returns an error! #!!! #!!! &redirect_url ($url); #!!! sub redirect_url { my ($url) = @_; print "Location: $url\n\n"; } # end redirect_url #!!! #!!! [2.3] Not Reserved #!!! #!!! A boolean function that returns a nil value if #!!! the word passed to it is a reserved word #!!! in the constant array @RESERVED_WORDS. #!!! #!!! $true_if = ¬_reserved ($form_name); #!!! sub not_reserved { my ($word) = @_; my ($reserved, $retval); $retval = 1; foreach $reserved (@RESERVED_WORDS) { if ($word eq $reserved) { $retval = ""; last; } # end if } # end foreach $retval; } # end not_reserved #!!! #!!! [2.4] Check Required Fields #!!! #!!! Entry Conditions: #!!! #!!! CGI parsing done. #!!! $FORM{'required'} is defined. #!!! $FORM{'required_url'} is defined. #!!! #!!! Description: #!!! #!!! Splits up value of $FORM{'required'} by #!!! using a comma as a delimiter and ignore #!!! spaces after the delimiter. Checks #!!! to make each form value is not null #!!! that is a split value of $FORM{'required'} #!!! #!!! Exit Conditions: #!!! #!!! Redirects browser to url defined by #!!! $FORM{'required_url'} if a required field #!!! is null, otherwise, does nothing. #!!! #!!! Example: #!!! #!!! &check_required_fields; #!!! sub check_required_fields { my ($index, @required_fields); @required_fields = split (/,/, $FORM{'required'}); $index = 1; while ( @required_fields >= $index ) { $required_fields[$index - 1] =~ s/^ +//; if ( $FORM{$required_fields[$index - 1]} eq "" ) { &blank_response; exit; } # end if $index++; } # end while } # end check_required_fields #!!! #!!! [2.5] Print Email Header #!!! sub print_email_header { my ( $recipient, $cc, $bcc, $sender, $sender_name, $replyto, $subject) = @_; my (@senders); if ($sender_name eq "") { $sender_name = $ENV{'REMOTE_HOST'}; } # end if if ($sender_name eq "") { $sender_name = $ENV{'REMOTE_ADDR'}; } # end if if ($replyto !~ /.+@.+\..+/) { $replyto = 'nobody@'."$ENV{'REMOTE_ADDR'}"; $sender = $replyto; $sender_name = "(Unknown Email) $sender_name"; } # end if if (length($recipient) > 255 && length($cc) > 255 && length($bcc) > 255) { &print_header; print "

Security Error

\n"; print "

This CGI script does not allow email recipients\n"; print "to contain a list of email addresses longer than\n"; print "255 characters.

"; exit; } # end if print MAIL "To: $recipient\n"; if ($cc ne "") { print MAIL "Cc: $cc\n"; } # end if if ($bcc ne "") { print MAIL "Bcc: $bcc\n"; } # end if print MAIL "X-Sender: \"Web Email Lite $Version\" <$WEBMASTER>\n"; if ($replyto ne "") { @senders = split (/,/, $sender); print MAIL "From: \"$sender_name\" <$senders[0]>\n"; print MAIL "Reply-To: $replyto\n"; } # end if print MAIL "Subject: $subject\n\n"; } # end print_email_header #!!! #!!! [2.6] Print Automatic Parse #!!! sub print_automatic_parse { my ($pair, $name, $value, $line, @lines, $line_length, @words, $word); foreach $pair (@VARS) { ($name, $value) = split (/\t/, $pair, 2); if (¬_reserved($name)) { if (length($value) + length($name) + 8 >= 62) { @words = ""; @lines = split (/ /, $value); $line_length = 5; foreach $word (@lines) { $line_length = length ($word) + $line_length + 1; if ($line_length >= 57) { push (@words, "\n"); $line_length = 5; } # end if push (@words, "$word "); } # end foreach $value = join ("", @words); } # end if if ($value =~ /\n/) { $value =~ s/^[ \t\n\r]+//; $value =~ s/[ \t\n\r]+$//; @lines = split (/\n/,$value); print MAIL "$name:\n\n"; foreach $line (@lines) { print MAIL " $line\n"; } # end foreach print MAIL "\n"; } else { print MAIL "$name: $value\n"; } # end if else } else { if ($name eq "hidden") { if ($value eq "hr") { print MAIL "----------------------"; print MAIL "----------------------"; print MAIL "----------------------"; print MAIL "--------\n"; } else { print MAIL "$value\n"; } # end if else } # end if } # end if else } # end foreach } # end print_automatic_parse #!!! #!!! [2.7] Print Custom Autoresponse #!!! sub print_custom_autoresponse { my ($pair, $name, $value); foreach $pair (@VARS) { ($name, $value) = split (/\t/, $pair, 2); if ($name eq "autoresponse_hidden") { $value =~ s/\r\n/\n/g; $value =~ s/\r/\n/g; if ($value eq "hr") { print MAIL "----------------------"; print MAIL "----------------------"; print MAIL "----------------------"; print MAIL "--------\n"; } else { print MAIL "$value\n"; } # end if else } # end if } # end foreach } # end print_custom_autoresponse #!!! #!!! [2.8] Analyze Input #!!! #!!! Searches and replaces $value string with all matches #!!! of double pound characters with corresponding value #!!! of the input tag. #!!! #!!! Should I allow one to backslash a # sign and back #!!! slash a back slash? Man, can that get yucky. #!!! sub analyze_input { my ($value) = @_; my (@matches, @parts, $i, $match); if ($value =~ /#/) { @matches = ""; @parts = split (/#/,$value); $i = 0; while (@parts > $i + 1) { $matches[$i] = $parts[$i+1]; $i = $i + 2; } # end while foreach $match (@matches) { $value =~ s/#$match#/$FORM{$match}/ie; } # end foreach } # end if return ($value); } # end analyze_input #!!! #!!! [2.9] Lock File #!!! #!!! It is preferred to use flock on BSD operating systems or any #!!! other means of the operating system to exclusively lock access #!!! on the file without the chance of more than one lock being #!!! due to concurrent access. Also, flock automatically will free #!!! the locks in case of premature termination. #!!! sub lock_file { my ($file) = @_; my ($wait_time, $sys_time); $sys_time = time() || &mydie ("Can't get system time", $!); if (-e "$file.lock" && -M "$file.lock" < 90) { $wait_time = 15 + $sys_time; while (-e "$file.lock" && time < $wait_time) { # Wait for lock. # If after 15 seconds, go ahead and print # error message. } # end while if (-e "$file.lock") { print "Content-type: text/html\n\n"; print "

Web Email Lite Busy

\n"; print "

Web Email Lite is busy\n"; print "processing other requests. Please try again\n"; print "in a few moments.

\n"; exit; } else { open (LOCK, ">$file.lock") || &mydie ("Can't open $file.lock", $!); close (LOCK); } # end if else } else { open (LOCK, ">$file.lock") || &mydie ("Can't open $file.lock", $!); close (LOCK); } # end if else } # end lock file #!!! #!!! [2.10] Unlock File #!!! #!!! It is preferred to use flock on BSD operating systems or any #!!! other means of the operating system to exclusively lock access #!!! on the file without the chance of more than one lock being #!!! due to concurrent access. Also, flock automatically will free #!!! the locks in case of premature termination. #!!! sub unlock_file { my ($file) = @_; unlink ("$file.lock") || &mydie ("Can't unlink $file.lock", $!); } # end unlock file #!!! #!!! [2.11] My Die #!!! #!!! Prints out system error message as HTML. Useful for debugging. #!!! sub mydie { my ($err, $sys_err) = @_; print "Content-type: text/html\n\n"; print "

Web Email Lite Error

\n"; print "

$err

\n"; print "

$sys_err

\n"; exit; } # end mydie #!!! #!!! [2.12] Print Email Footer #!!! sub print_email_footer { print MAIL "----------------------"; print MAIL "----------------------"; print MAIL "----------------------"; print MAIL "--------\n"; print MAIL "HTTP Referer: $ENV{'HTTP_REFERER'}\n"; print MAIL "HTTP User Agent: $ENV{'HTTP_USER_AGENT'}\n"; print MAIL "Remote Host: $ENV{'REMOTE_HOST'}\n"; print MAIL "Remote Address: $ENV{'REMOTE_ADDR'}\n"; print MAIL "----------------------"; print MAIL "----------------------"; print MAIL "----------------------"; print MAIL "--------\n"; } # end print_email_footer ############################################################################ ## ## ## [3.0] RESPONSE SUBROUTINES ## ## ############################################################################ #!!! #!!! [3.1] Print Mail #!!! #!!! This subroutines sends email to the recipient. #!!! sub print_mail { my ($name, $value, $pair, $submitter_email, $submitter_name); my ($subject, $replyto, $cc, $to, $bcc); $to = ""; $cc = $to; $bcc = $cc; # Determine who the submitter is and their email address foreach $pair (@VARS) { ($name, $value) = split (/\t/, $pair); if ($name =~ /^email$/i) { $submitter_email = $value; } if ($name =~ /^e-mail$/i) { $submitter_email = $value; } if ($name =~ /^name$/i) { $submitter_name = $value; } if ($name =~ /^bcc$/i) { if ($bcc eq "") { $bcc = $value; } else { $bcc = "$bcc, $value"; } # end if else } # end if if ($name =~ /^cc$/i) { if ($cc eq "") { $cc = $value; } else { $cc = "$cc, $value"; } # end if else } # end if if ($name =~ /^to$/i) { if ($to eq "") { $to = $value; } else { $to = "$to, $value"; } # end if else } # end if } # end foreach # Send mail to recipient open (MAIL, "|$MAILPROG\n") || &mydie ("Can't open $MAILPROG", $!); if ($FORM{'subject'} ne "") { $subject = $FORM{'subject'}; } # end if else { $subject = "Submission from $ENV{'HTTP_REFERER'}"; } $replyto = $submitter_email; &print_email_header ( $to, $cc, $bcc, $submitter_email, $submitter_name, $replyto, $subject ); # end print_email_header &print_automatic_parse; &print_email_footer; close(MAIL); } # end print_mail #!!! #!!! [3.2] Auto Respond #!!! #!!! This subroutines sends email to the submitter. #!!! sub auto_respond { my ($name, $value, $pair, $submitter_email); my ($submitter_name, $from_email, $subject); my ($from_email_name, $to, $cc, $bcc, $null); $to = ""; $cc = $to; $bcc = $cc; # Determine who the submitter is and their email address foreach $pair (@VARS) { ($name, $value) = split (/\t/, $pair, 2); if ($name =~ /^email$/i) { $submitter_email = $value; } if ($name =~ /^e-mail$/i) { $submitter_email = $value; } if ($name =~ /^name$/i) { $submitter_name = $value; } if ($name =~ /^to$/i) { if ($to eq "") { $to = $value; } else { $to = "$to, $value"; } # end if else } # end if } # end foreach # Determine the reply-to/from email address if ($FORM{'autoresponse_email'} ne "") { $from_email = $FORM{'autoresponse_email'}; } else { $from_email = $to; } # end else if $from_email_name = $FORM{'autoresponse_name'}; # Determine if valid email address was provided if ($submitter_email =~ /.+@.+\..+/) { # Print Email Header open (MAIL, "|$MAILPROG\n") || &mydie ("Can't open $MAILPROG", $!); if ($FORM{'autoresponse_subject'} ne "") { $subject = $FORM{'autoresponse_subject'}; } else { $subject = "Autoresponse Reply from $ENV{'HTTP_REFERER'}"; } # end if else $null = ""; &print_email_header ( $submitter_email, $null, $null, $from_email, $from_email_name, $from_email, $subject ); # end print_email_header if ($FORM{'autoresponse'} eq "custom") { &print_custom_autoresponse; } # end if if ($FORM{'autoresponse'} eq "generic") { print MAIL "$submitter_name\n\n"; print MAIL "Thank you for visiting our website!\n\n"; print MAIL "We have been emailed the information provided\n"; print MAIL "at the end of this message.\n\n"; print MAIL "Thanks Again!\n\n"; } # end if if ($FORM{'autoresponse'} eq "thank_you") { print MAIL "$submitter_name\n\n"; print MAIL "Thank you for visiting our website!\n\n"; print MAIL "Feel free to reply back to this message\n"; print MAIL "if you would like to make corrections,\n"; print MAIL "provide further comments, or make any\n"; print MAIL "inquiries.\n\n"; print MAIL "Thanks Again!\n\n"; } # end if if ($FORM{'autoresponse'} eq "verify") { print MAIL "$submitter_name\n\n"; print MAIL "Thank you for visiting our website!\n\n"; print MAIL "Please reply back to this message to verify\n"; print MAIL "that we have received this information from\n"; print MAIL "the right person! We are unable to process\n"; print MAIL "this information unless we receive a reply.\n\n"; print MAIL "Thanks Again!\n\n"; } # end if if ($FORM{'autoresponse_verbose'} eq "") { &print_automatic_parse; } # end if &print_email_footer; close(MAIL); } # end if } # end auto_respond #!!! #!!! [3.3] &correct_response; #!!! #!!! This delivers the file defined by $RESPONSE #!!! #!!! You may of course use an alternate technique and #!!! hard code the html in this subroutine. This will allow #!!! you to use $FORM variables to generate custom #!!! html output depending on the values submitted by the #!!! form. #!!! sub correct_response { my ($next_url); $next_url = $FORM{'next_url'}; &redirect_url ($next_url); exit; } # end correct_response #!!! #!!! [3.4] &blank_response; #!!! #!!! This delivers the file defined by $BLANK_RESPONSE #!!! You may of course use an alternate technique and #!!! hard code the html in this subroutine. This will allow #!!! you to use $FORM variables to generate custom #!!! html output depending on the values submitted by the #!!! form. #!!! sub blank_response { my ($next_url); $next_url = $FORM{'required_url'}; &redirect_url ($next_url); exit; } # end blank_response ############################################################################ ## ## ## [4.0] CGI PARSE WITH SECURITY CHECKS ## ## ############################################################################ #!!! #!!! [4.1] Authorized Access Only #!!! #!!! Call this subroutine if you wish to restrict access to #!!! allow only a specific domain or document to be able #!!! to use this script. #!!! sub security_check { my ($referer) = $ENV{"HTTP_REFERER"}; my ($url, $passed, @url_parts, @referer_parts, $index, $url_part, $failed, $url_protocol, $referer_protocol, @url_address_parts, @referer_address_parts, $url_address_part); my (@env_list, $final_pass); $final_pass = 0; ($referer_protocol, $referer) = split (/\:/, $referer); $referer = substr($referer, 3, length($referer)); @referer_parts = split (/\//, $referer); @referer_address_parts = split (/\./, $referer_parts[0]); foreach $url (@AUTHORIZED_URLS) { ($url_protocol, $url) = split (/\:/, $url); $url = substr($url, 3, length($url)); @url_parts = split (/\//, $url); @url_address_parts = split (/\./, $url_parts[0]); $failed = 0; $index = 0; foreach $url_address_part (@url_address_parts) { if (!($url_address_part =~ /$referer_address_parts[$index]/i) && $url_address_part ne "*") { $failed++; } # end if $index++; } # end foreach $passed = 0; $index = 0; foreach $url_part (@url_parts) { if ($url_part =~ /$referer_parts[$index]/i || $url_part eq "*") { $passed++; } # end if $index++; } # end foreach if (@url_parts == $passed && $url_protocol eq $referer_protocol && $failed < 1) { $final_pass = 1; } # end if } # end while if ($final_pass == 0) { open (MAIL, "|$MAILPROG") || &mydie ("Can't open $MAILPROG", $!); print MAIL "To: $WEBMASTER\n"; print MAIL "Subject: *** SUBMIT VIOLATION ***\n\n"; while (@env_list = each(%ENV)) { print MAIL "@env_list\n"; } # end while print MAIL "\n"; close (MAIL); &print_header; print "

SECURITY ERROR!

\n\n"; print "Your domain is not configured to use this\n"; print "cgi-bin script.

\n"; exit; } # end if } # end security_check #!!! #!!! [4.2] Check URLS #!!! #!!! Call this subroutine if you wish to restrict access from #!!! specific URLS. #!!! sub check_urls { my ($referer) = $ENV{"HTTP_REFERER"}; my ($url, $passed, @url_parts, @referer_parts, $index, $url_part, $failed, $url_protocol, $referer_protocol, @url_address_parts, @referer_address_parts, $url_address_part); my (@env_list, $final_pass); $final_pass = 0; ($referer_protocol, $referer) = split (/\:/, $referer); $referer = substr($referer, 3, length($referer)); @referer_parts = split (/\//, $referer); @referer_address_parts = split (/\./, $referer_parts[0]); foreach $url (@IGNORE_URLS) { ($url_protocol, $url) = split (/\:/, $url); $url = substr($url, 3, length($url)); @url_parts = split (/\//, $url); @url_address_parts = split (/\./, $url_parts[0]); $failed = 0; $index = 0; foreach $url_address_part (@url_address_parts) { if (!($url_address_part =~ /$referer_address_parts[$index]/i) && $url_address_part ne "*") { $failed++; } # end if $index++; } # end foreach $passed = 0; $index = 0; foreach $url_part (@url_parts) { if ($url_part =~ /$referer_parts[$index]/i || $url_part eq "*") { $passed++; } # end if $index++; } # end foreach if (@url_parts == $passed && $url_protocol eq $referer_protocol && $failed < 1) { $final_pass = 1; } # end if } # end while if ($final_pass == 1) { open (MAIL, "|$MAILPROG") || &mydie ("Can't open $MAILPROG", $!); print MAIL "To: $WEBMASTER\n"; print MAIL "Subject: *** SUBMIT VIOLATION ***\n\n"; while (@env_list = each(%ENV)) { print MAIL "@env_list\n"; } # end while print MAIL "\n"; close (MAIL); &print_header; print "

SECURITY ERROR!

\n\n"; print "Your domain is not configured to use this\n"; print "cgi-bin script.

\n"; exit; } # end if } # end check_urls #!!! #!!! [4.3a] Check IP Addresses #!!! #!!! Call this subroutine if you wish to restrict access from #!!! specific IP addresses. Useful if someone is #!!! abusing service from the CGI script. #!!! sub check_ip_addresses { my ($remote_address) = ($ENV{"REMOTE_ADDR"}); my ($remote_host) = ($ENV{"REMOTE_HOST"}); my ($ip, $failed, @parts, @address_parts, $index); my ($address_failed, $host_failed, $part, @host_parts); foreach $ip (@IGNORE_IP_ADDRESSES) { $address_failed = 0; $host_failed = 0; @parts = split (/\./, $ip); @address_parts = split (/\./, $remote_address); @host_parts = split (/\./, $remote_host); $index = 0; foreach $part (@parts) { if ($part =~ /$address_parts[$index]/i || $part eq "*") { $address_failed++; } # end if if ($part =~ /$host_parts[$index]/i || $part eq "*") { $host_failed++; } # end if $index++; } # end foreach if ($address_failed == @parts || $host_failed == @parts) { &print_header; print "

SECURITY ERROR!

\n\n"; print "Your IP address is not allowed to submit\n"; print "to this cgi-bin script.

\n"; exit; } # end if } # end while } # end check_ip_addresses #!!! #!!! [4.3b] Check More IPs #!!! #!!! Call this subroutine if you wish to restrict access from #!!! specific IP addresses. Useful if someone is harassing #!!! the creator of some form. #!!! sub check_more_ips { my ($remote_address) = ($ENV{"REMOTE_ADDR"}); my ($remote_host) = ($ENV{"REMOTE_HOST"}); my ($ip, $failed, @ip_list, $index); @ip_list = split (/,/, $FORM{'ignore_ips'}); $index = 1; while ( @ip_list >= $index ) { $ip_list[$index - 1] =~ s/^ +//; $index++; } # end while while ($ip = @ip_list) { if ($remote_address =~ /^$ip/i) { $failed = 1; } if ($remote_host =~ /^$ip/i) { $failed = 1; } } # end while if ($failed == 1) { &print_header; print "

SECURITY ERROR!

\n\n"; print "Your IP address is not allowed to submit\n"; print "to this form.

\n"; exit; } # end if } # end check_more_ips #!!! #!!! [4.5] CGI Form Variable Security Check #!!! #!!! Call this subroutine from your cgi-parsing routine to insure #!!! any values that are used directly as a system call do #!!! not contain any special characters that may cause damage to #!!! the server. This includes any form variable that is used in #!!! an open call. #!!! #!!! &name_security_check ($FORM{'Sensitive Input'}); #!!! sub name_security_check { my ($input_var) = @_; my (@env_list); # Some of these may be extreme in certain cases. In those # case you might want to perform further parsing to handle # exceptions. Be very careful if you do this. if ($input_var =~ /[^_0-9a-zA-Z]+/) { open (MAIL, "|$MAILPROG") || mydir ("Can't open $MAILPROG", $!); print MAIL "To: $WEBMASTER\n"; print MAIL "Subject: *** SUBMIT VIOLATION ***\n\n"; while (@env_list = each(%ENV)) { print MAIL "@env_list\n"; } # end while print MAIL "\n"; close (MAIL); &print_header; print "

SECURITY ERROR!

"; print "This web software does not allow certain"; print "special characters to be placed in certain"; print "input fields.\n

"; exit; } # end if } # end name_security_check #!!! #!!! [4.6] Process HTML #!!! #!!! Call this subroutine if you wish to replace #!!! "<" and ">" characters with HTML character #!!! encoding. #!!! #!!! $FORM{'Input'} = &process_html ($FORM{'Input'}); #!!! sub process_html { my ($input_var) = @_; $input_var =~ s//>/g; $input_var; } # end process_html #!!! #!!! [4.7] CGI parse for Get and Post methods #!!! #!!! Parses an input form into name/value pairs. #!!! Strips SSI (Server Side Includes). #!!! Performs authorized access check if configured. #!!! #!!! Add calls to &name_security_check for any input variables #!!! that are used to make system calls such as to the #!!! the open file function. #!!! #!!! This form name value variables are made available in these globals: #!!! #!!! @VARS = ("$name\t$value", "$name\t$value", "$name\t$value") #!!! $FORM{$name} = $value #!!! sub cgi_parse { my ($storage, @variables, $pair, $name, $value, @tmpvars); # Distinguish between POST and GET methods # and get the input if ($ENV{'REQUEST_METHOD'} =~ /post/i) { read(STDIN, $storage, $ENV{'CONTENT_LENGTH'}); } else { $storage = $ENV{'QUERY_STRING'}; } # end if else # Split the name-value @variables = split(/&/, $storage); foreach $pair (@variables) { ($name, $value) = split(/=/, $pair, 2); # Un-Webify plus signs and %-encoding $value =~ s/\+/ /g; $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; $value =~ s/\r\n/\n/g; $value =~ s/\r/\n/g; $name =~ s/\+/ /g; $name =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; $name =~ s/\r\n/\n/g; $name =~ s/\r/\n/g; # SECURITY CHECK: Strip server side includes from all input $name =~ s///g; $value =~ s///g; # Name/Value pairs are made available in @VARS for automatic # parsing of all variables. $FORM{$name} = $value; push (@tmpvars, "$name\t$value"); } # end foreach foreach $pair (@tmpvars) { ($name, $value) = split (/\t/, $pair, 2); $name = &analyze_input($name); $value = &analyze_input($value); $FORM{$name} = $value; push (@VARS, "$name\t$value"); } # end foreach # # Selectively check input variables for special characters that # are used to make system calls. This includes the open call. # # Example: # # &name_security_check ($FORM{'file'}); # } # end cgi_parse ############################################################################ ## ## ## [5.0] Main ## ## ############################################################################ #!!! #!!! [5.1] Parse the submitted form and perform #!!! [5.2] security checks. #!!! if (@IGNORE_IP_ADDRESSES) {&check_ip_addresses; } if (@IGNORE_URLS) { &check_urls; } if (@AUTHORIZED_URLS) { &security_check; } &cgi_parse; if ($FORM{'ignore_ip'}) { &check_more_ips; } #!!! #!!! [5.3] Check for required input. #!!! #!!! Check for required input. Give the html file defined #!!! by $BLANK_RESPONSE if a form item contains no data that #!!! was provide in the $required_fields list. #!!! if ($FORM{'required'} ne "") { &check_required_fields; } # end if #!!! #!!! [5.4] Print EMail to Recipient #!!! if ($FORM{'to'} ne "") { &print_mail; } # end if #!!! #!!! [5.5] Print Email Autoresponse #!!! if ($FORM{'autoresponse'} ne "") { &auto_respond; } # end if #!!! #!!! [5.6] Print HTML Response #!!! if ($FORM{'next_url'} ne "") { &correct_response; } else { &print_header; print "

Web Email Lite

\n"; print "

The form was submitted.

\n"; } # end if else exit; contact.html0000644000175000017500000002210210266470761012671 0ustar davidjdavidj Guarana seed nutritional supplements, energy drinks




Home

About Guarana

When to use

Recipes

Health Information

Sports information

Contact us

Online Purchase



www.guarana.com.au


The Guarana Company
Proprietry Limited

Incorporated 1993

ACN : 059 497 844

ABN : 55 059 497 844

Unit 94, 96 Guildford Rd
Mt Lawley, 6050
Western Australia

Telephone:

Australia (08) 9825 3050
International 618-9825 3050

Facsimile:

Australia (08) 9371 0779
International 618-9371 0779

guarana

Contact The Guarana Company


The Guarana Company Pty Ltd. is the manufacturer of Guarana Naturale ® and Guarana Active ®

Please contact us at any time for any queries you may have regarding guarana or orders for Guarana Naturale ® and Guarana Active ®

We guarantee delivery to you no matter where you are in the world.


Postal address

          The Guarana Company Pty Ltd
          ACN: 059 497 844
          ABN: 55 059 497 844
          Unit 94 / 96 Guildford Rd
          MT LAWLEY
          Western Australia 6050


Telephone: 08 9825 3050

Telephone International: 618 9825 3050

Fax: 08 9371 0779

International Fax: 618 9371 0779



Send us a message:
Your name
Your email address
Message

nutritional supplements

www.guarana.com.au





This website designed by Scribeworks 2003
ec_api/0000755000175000017500000000000010042140645011556 5ustar davidjdavidjec_api/xmlrpc/0000755000175000017500000000000010041103314013052 5ustar davidjdavidjec_api/xmlrpc/doc/0000755000175000017500000000000010041103321013615 5ustar davidjdavidjec_api/xmlrpc/doc/CVS/0000755000175000017500000000000010041103322014251 5ustar davidjdavidjec_api/xmlrpc/doc/CVS/Entries.Extra0000644000175000017500000000062510041103321016671 0ustar davidjdavidj/Makefile/// /apidocs.html/// /arrayuse.html/// /bugs.html/// /custom.dsl/// /debugging.html/// /examples.html/// /helpers.html/// /index.html/// /introduction.html/// /jellyfish.html/// /manifest.html/// /reserved.html/// /support.html/// /sysmethhelp.html/// /sysmethodsig.html/// /xmlrpc-server.html/// /xmlrpc_php.sgml/// /xmlrpcmsg.html/// /xmlrpcresp.html/// /xmlrpcval.html/// ec_api/xmlrpc/doc/CVS/Entries0000644000175000017500000000174410041103321015612 0ustar davidjdavidj/Makefile/1.1/Wed Aug 6 04:03:37 2003// /apidocs.html/1.1/Wed Aug 6 04:03:37 2003// /arrayuse.html/1.1/Wed Aug 6 04:03:37 2003// /bugs.html/1.1/Wed Aug 6 04:03:37 2003// /custom.dsl/1.1/Wed Aug 6 04:03:37 2003// /debugging.html/1.1/Wed Aug 6 04:03:37 2003// /examples.html/1.1/Wed Aug 6 04:03:37 2003// /helpers.html/1.1/Wed Aug 6 04:03:37 2003// /index.html/1.1/Wed Aug 6 04:03:37 2003// /introduction.html/1.1/Wed Aug 6 04:03:37 2003// /jellyfish.html/1.1/Wed Aug 6 04:03:37 2003// /manifest.html/1.1/Wed Aug 6 04:03:37 2003// /reserved.html/1.1/Wed Aug 6 04:03:37 2003// /support.html/1.1/Wed Aug 6 04:03:37 2003// /sysmethhelp.html/1.1/Wed Aug 6 04:03:37 2003// /sysmethodsig.html/1.1/Wed Aug 6 04:03:37 2003// /xmlrpc-server.html/1.1/Wed Aug 6 04:03:37 2003// /xmlrpc_php.sgml/1.1/Wed Aug 6 04:03:38 2003// /xmlrpcmsg.html/1.1/Wed Aug 6 04:03:37 2003// /xmlrpcresp.html/1.1/Wed Aug 6 04:03:37 2003// /xmlrpcval.html/1.1/Wed Aug 6 04:03:37 2003// D ec_api/xmlrpc/doc/CVS/Repository0000644000175000017500000000002210041103322016345 0ustar davidjdavidjozman/xmlrpc/doc ec_api/xmlrpc/doc/CVS/Root0000644000175000017500000000006510041103322015120 0ustar davidjdavidj:pserver:ozman@gooble.teragen.com.au:2401:/home/cvs ec_api/xmlrpc/doc/apidocs.html0000644000175000017500000002143510041103315016135 0ustar davidjdavidjClass documentation

Chapter 5. Class documentation

xmlrpc_client

This is the basic class used to represent a client of an XML-RPC server.

Creation

The constructor has the following syntax:

$client=new xmlrpc_client($server_path, $server_hostname, $server_port);

Here's an example client set up to query Userland's XML-RPC server at betty.userland.com:

$client=new xmlrpc_client("/RPC2", "betty.userland.com", 80);

The server_port parameter is optional, and if omitted will default to 80 when using HTTP and 443 when using HTTPS (see the "send" method below.)

Methods

This class supports the following methods.

send

This method takes the form:

$response=$client->send($xmlrpc_message, $timeout, $server_method);

Where $xmlrpc_message is an instance of xmlrpcmsg (see xmlrpcmsg), and $response is an instance of xmlrpcresp (see xmlrpcresp).

The $timeout is optional, and will be set to 0 (wait forever) if omitted. This timeout value is passed to fsockopen().

The server_method parameter is optional, and if omitted will default to 'http'. The only other valid value is 'https', which will use an SSL HTTP connection to connect to the remote server. Note that your PHP must have the "curl" extensions compiled in in order to use this feature. Note that when using SSL you should normally set your port number to 443, unless the SSL server you are contacting runs at any other port.

Warning

PHP 4.0.2 or greater is required for SSL functionality. PHP 4.0.6 has a bug which prevents SSL working.

If the value of $response is 0 rather than an xmlrpcresp object, then this signifies an I/O error has occured. You can find out what the I/O error was from the values $client->errno and $client->errstring.

In addition to low-level errors, the XML-RPC server you were querying may return an error in the xmlrpcresp object. See xmlrpcresp for details of how to handle these errors.

setCredentials

$client->setCredentials($username, $password);

This method sets the username and password for authorizing the client to a server. With the default (HTTP) transport, this information is used for HTTP Basic authorization.

setCertificate

$client->setCertificate($certificate, $passphrase);

This method sets the optional certificate and passphrase used in SSL-enabled communication with a remote server (when the server_method is set to 'https' in the client's construction).

The certificate parameter must be the filename of a PEM formatted certificate. The passphrase parameter must contain the password required to use the certificate.

This requires the "curl" extensions to be compiled into your installation of PHP.

setSSLVerifyPeer

$client->setSSLVerifyPeer($i);

This method defines whether connections made to XMLRPC backends via HTTPS should verify the remote host's SSL certificate, and cause the connection to fail if the cert verification fails. $i should be a boolean value.

setSSLVerifyHost

$client->setSSLVerifyHost($i);

This method defines whether connections made to XMLRPC backends via HTTPS should verify the remote host's SSL certificate's common name (CN). By default, only the existence of a CN is checked. $i should be an integer value; 0 to not check the CN at all, 1 to merely check for its existence, and 2 to check that the CN on the certificate matches the hostname that is being connected to.

setDebug

$client->setDebug($debugOn);

$debugOn is either 0 or 1 depending on whether you require the client to print debugging information to the browser. The default is not to output this information.

The debugging information includes the raw data returned from the XML-RPC server it was querying, and the PHP value the client attempts to create to represent the value returned by the server. This option can be very useful when debugging servers as it allows you to see exactly what the server returns.

ec_api/xmlrpc/doc/Makefile0000644000175000017500000000033710041103315015263 0ustar davidjdavidjWEB=/var/www/xmlrpc/doc all: index.html index.html: xmlrpc_php.sgml jade -t sgml -d custom.dsl xmlrpc_php.sgml clean: rm -f *.html install: mkdir -p ${WEB} cp *.html ${WEB} web: mkdir -p ${WEB} cp *.html ${WEB} ec_api/xmlrpc/doc/introduction.html0000644000175000017500000001006510041103316017232 0ustar davidjdavidjIntroduction

Chapter 1. Introduction

XML-RPC is a format devised by Userland Software for achieving remote procedure call via XML. XML-RPC has its own web site, www.XmlRpc.com

The most common implementations of XML-RPC available at the moment use HTTP as the transport. A list of implementations for other languages such as Perl and Python can be found on the www.xmlrpc.com.

This collection of PHP classes provides a framework for writing XML-RPC clients and servers in PHP.

Warning

The server code works only with versions of PHP3 >= 3.0.12. The code is also known to work with PHP4.

If you wish to use SSL to communicate with remote servers, you need the "curl" extension compiled into your PHP installation, this is available in PHP 4.0.2 and greater, although 4.0.6 has a bug preventing SSL working.

Acknowledgements

Jim Winstead

Peter Kocks

Nicolay Mausz

Ben Margolin

Dan Libby

Gaetano Giunta

Idan Sofer

Giancarlo Pinerolo

Justin Miller

ec_api/xmlrpc/doc/arrayuse.html0000644000175000017500000000673110041103315016350 0ustar davidjdavidjEasy use with PHP arrays

Easy use with PHP arrays

Dan Libby was kind enough to contribute two helper functions that make it easier to translate to and from PHP arrays. This makes it easier to deal with complex structures. At the moment support is limited to int, double, string, array and struct datatypes; note also that all PHP arrays are encoded as structs due to PHP not being able to tell the difference between a hash and a linear array.

These functions reside in xmlrpc.inc.

xmlrpc_decode

$arr=xmlrpc_decode($xmlrpc_val);

Returns a PHP array stuffed with the values found in the xmlrpcval $xmlrpc_val, translated into native PHP types.

xmlrpc_encode

$xmlrpc_val=xmlrpc_encode($phpval);

Returns an xmlrpcval populated with the PHP values in $phpval. Works recursively on arrays and structs. Note that there's no support for non-base types like base-64 values or date-times.

ec_api/xmlrpc/doc/bugs.html0000644000175000017500000000513310041103315015450 0ustar davidjdavidjBugs

Chapter 3. Bugs

This is a bare framework. The "nice" bits haven't been put in yet. Specifically, no HTTP response checking is performed, and no type validation or coercion has been put in. PHP being a loosely-typed language, this is going to have to be done explicitly.

dateTime.iso8601 is supported opaquely. It can't be done natively as the XML-RPC specification explictly forbids passing of timezone specifiers in ISO8601 format dates. You can, however, use the iso8601_encode() and iso8601_decode() functions to do the encoding and decoding for you.

If alternative character set encoding is sent in HTTP header than it will be ignored for the moment. We speak only UTF-8...

If more than 32k of HTTP headers are encountered (like, why?) then the response parsing code will break.

ec_api/xmlrpc/doc/custom.dsl0000644000175000017500000000102610041103315015635 0ustar davidjdavidj ]> (define %link-mailto-url% "edd@usefulinc.com") (define %html-ext% ".html") (define %use-id-as-filename% #t) (define %root-filename% "index") ec_api/xmlrpc/doc/debugging.html0000644000175000017500000000476210041103316016453 0ustar davidjdavidjDebugging aids

Debugging aids

xmlrpc_debugmsg

xmlrpc_debugmsg($debugstring);

Sends the contents of $debugstring in XML comments in the server return payload. If a PHP client has debugging turned on, the user will be able to see server debug information.

Use this function in your methods so you can pass back diagnostic information. It is only available from xmlrpcs.inc.

ec_api/xmlrpc/doc/examples.html0000644000175000017500000000505410041103316016331 0ustar davidjdavidjExamples

Chapter 8. Examples

The best examples are to be found in the sample files included with the distribution. Some are included here.

XML-RPC client: state name query

Code to get the corresponding state name from a number (1-50) from Dave Winer's server

  $f=new xmlrpcmsg('examples.getStateName',
				   array(new xmlrpcval($HTTP_POST_VARS["stateno"], "int")));
  $c=new xmlrpc_client("/RPC2", "betty.userland.com", 80);
  $r=$c->send($f);
  $v=$r->value();
  if (!$r->faultCode()) {
      print "State number ". $HTTP_POST_VARS["stateno"] . " is " .
      $v->scalarval() . "<BR>";
      print "<HR>I got this value back<BR><PRE>" .
      htmlentities($r->serialize()). "</PRE><HR>\n";
  } else {
      print "Fault: ";
      print "Code: " . $r->faultCode() . 
            " Reason '" .$r->faultString()."'<BR>";
  }
		
ec_api/xmlrpc/doc/helpers.html0000644000175000017500000001164510041103316016160 0ustar davidjdavidjHelper functions

Chapter 6. Helper functions

XML-RPC for PHP contains some helper functions which you can use to make processing of XML-RPC requests easier.

Date functions

The XML-RPC specification has this to say on dates:

Don't assume a timezone. It should be specified by the server in its documentation what assumptions it makes about timezones.

Unfortunately, this means that date processing isn't straightforward. Although XML-RPC uses ISO 8601 format dates, it doesn't use the timezone specifier.

We strongly recommend that in every case where you pass dates in XML-RPC calls, you use UTC (GMT) as your timezone. Most computer languages include routines for handling GMT times natively, and you won't have to translate between timezones.

For more information about dates, see ISO 8601: The Right Format for Dates, which has a handy link to a PDF of the ISO 8601 specification. Note that XML-RPC uses exactly one of the available representations: CCYYMMDDTHH:MM:SS.

iso8601_encode

$isoString=iso8601_encode($time_t, $utc=0);

Returns an ISO 8601 formatted date generated from the UNIX timestamp $time_t, as returned by the PHP function time().

The argument $utc can be omitted, in which case it defaults to 0. If it is set to 1, then the function corrects the time passed in for UTC. Example: if you're in the GMT-6:00 timezone and set $utc, you will receive a date representation six hours ahead of your local time.

The included demo program vardemo.php includes a demonstration of this function.

iso8601_decode

$time_t=iso8601_decode($isoString, $utc=0);

Returns a UNIX timestamp from an ISO 8601 encoded time and date string passed in. If $utc is 1 then $isoString is assumed to be in the UTC timezone, and thus the $time_t result is also UTC: otherwise, the timezone is assumed to be your local timezone and you receive a local timestamp.

ec_api/xmlrpc/doc/index.html0000644000175000017500000001427410041103316015626 0ustar davidjdavidjXML-RPC for PHP

XML-RPC for PHP

version 1.1

Edd Dumbill

All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  • Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

  • Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

  • Neither the name of the "XML-RPC for PHP" nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


ec_api/xmlrpc/doc/sysmethodsig.html0000644000175000017500000000631510041103320017231 0ustar davidjdavidjsystem.methodSignature

system.methodSignature

This method takes one parameter, the name of a method implemented by the XML-RPC server.

It returns an array of possible signatures for this method. A signature is an array of types. The first of these types is the return type of the method, the rest are parameters.

Multiple signatures (ie. overloading) are permitted: this is the reason that an array of signatures are returned by this method.

Signatures themselves are restricted to the top level parameters expected by a method. For instance if a method expects one array of structs as a parameter, and it returns a string, its signature is simply "string, array". If it expects three integers, its signature is "string, int, int, int".

If no signature is defined for the method, a none-array value is returned. Therefore this is the way to test for a non-signature, if $resp below is the response object from a method call to system.methodSignature:

$v=$resp->value();
if ($v->kindOf()!="array") {
  // then the method did not have a signature defined
}
			

See the introspect.php demo included in this distribution for an example of using this method.

ec_api/xmlrpc/doc/jellyfish.html0000644000175000017500000000524710041103317016511 0ustar davidjdavidjThe Jellyfish Book

The Jellyfish Book

Together with Simon St.Laurent and Joe Johnston, I wrote a book on XML-RPC for O'Reilly and Associates on XML-RPC. It features a rather fetching jellyfish on the cover.

Complete details of the book are available from O'Reilly's web site.

I'm responsible for the chapter on PHP, which includes a worked example of creating a forum server, and hooking it up the O'Reilly's Meerkat service in order to allow commenting on news stories from around the Web.

If you've benefitted from the effort I've put into writing this software, then please consider buying the book!

ec_api/xmlrpc/doc/manifest.html0000644000175000017500000001063110041103317016317 0ustar davidjdavidjFiles in the distribution

Chapter 2. Files in the distribution

xmlrpc.inc

the XML-RPC classes. include() this in your PHP files to use the classes.

xmlrpcs.inc

the XML-RPC server class. include() this in addition to xmlrpc.inc to get server functionality

bettydemo.php

demo which retrieves a state name from a number using Dave Winer's XML-RPC server at betty.userland.com

server.php

a sample server hosting three functions: a US State lookup tool, a struct sorter and an echo function.

client.php, agesort.php, echotest.php

client code to exercise the various functions in server.php

base64test.php, stringtest.php

Tests to verify that encoding and decoding of base 64 and entities is functioning correctly.

vardemo.php

examples of how to construct xmlrpcval types

demo1.txt, demo2.txt, demo3.txt

XML-RPC responses captured in a file for testing purposes (you can use these to test the xmlrpcmsg->parseResponse() method).

httptest.php

Testing that HTTP response detection works OK.

test.pl, test.py

Perl and Python programs to exercise server.php to test that some of the methods work. Make sure you point these at your server, not mine!

workspace.testPhpServer.fttb

Frontier scripts to exercise the demo server. Thanks to Dave Winer for permission to include these. See Dave's announcement of these.

phpunit.php

Fred Yankowski's unit test framework for PHP.

testsuite.php

Start of a unit test suite for this software package. If you do development on this software, please consider submitting tests for this suite.

which.php

A demo of the interopEchoTests.whichToolkit method.

discuss.php, comment.php

Software used in the PHP chapter of The Jellyfish Book to provide a comment server and allow the attachment of comments to stories from Meerkat's data store.

rsakey.pem

A test certificate for the SSL support. It has the passphrase "test."

ec_api/xmlrpc/doc/reserved.html0000644000175000017500000000537510041103317016341 0ustar davidjdavidjReserved methods

Chapter 7. Reserved methods

In order to extend the functionality offered by XML-RPC servers without impacting on the protocol, I've included experimental support in this release for reserved methods.

All methods starting with system. are considered reserved by the server. PHP for XML-RPC itself provides three special methods, detailed in this chapter.

system.listMethods

This method may be used to enumerate the methods implemented by the XML-RPC server.

The system.listMethods method requires no parameters. It returns an array of strings, each of which is the name of a method implemented by the server.

ec_api/xmlrpc/doc/support.html0000644000175000017500000000546710041103317016240 0ustar davidjdavidjSupport

Chapter 4. Support

Online Support

XML-RPC for PHP is offered "as-is" without any warranty or commitment to support. However, informal advice and help is available via the XML-RPC for PHP mailing list and XML-RPC.com.

  • The PHP XML-RPC interest mailing list is run by the author. More details can be found here.

  • For more general XML-RPC questions, there is a Yahoo! Groups XML-RPC mailing list.

  • The XML-RPC.com discussion group is a useful place to get help with using XML-RPC. This group is also gatewayed into the Yahoo! Groups mailing list.

ec_api/xmlrpc/doc/sysmethhelp.html0000644000175000017500000000424510041103317017062 0ustar davidjdavidjsystem.methodHelp

system.methodHelp

This method takes one parameter, the name of a method implemented by the XML-RPC server.

It returns a documentation string describing the use of that method. If no such string is available, an empty string is returned.

The documentation string may contain HTML markup.

ec_api/xmlrpc/doc/xmlrpc-server.html0000644000175000017500000002200410041103320017311 0ustar davidjdavidjxmlrpc_server

xmlrpc_server

The current implementation of this class has been kept as simple as possible. The constructor for the server basically does all the work. Here's a minimal example:

  function foo ($params) {
     ...
  }

  $s=new xmlrpc_server( array("examples.myFunc" =>
				array("function" => "foo")));
	  

This performs everything you need to do with a server. The single argument is an associative array from method names to function names. The request is parsed and despatched to the relevant function, which is reponsible for returning a xmlrpcresp object, which gets serialized back to the caller. See server.php in this distribution for examples of how to do this.

Here is a more detailed look at what the handler function foo may do:


  function foo ($params) {
        global $xmlrpcerruser; // import user errcode value

        // $params is an Array of xmlrpcval objects

        if ($err) {
          // this is an error condition
          return new xmlrpcresp(0, $xmlrpcerruser+1, // user error 1
           "There's a problem, Captain");
        } else {
          // this is a successful value being returned
          return new xmlrpcresp(new xmlrpcval("All's fine!", "string"));
        }
  }
		

The dispatch map

The first argument to the xmlrpc_server constructor is an array, called the dispatch map. In this array is the information the server needs to service the XML-RPC methods you define.

The dispatch map takes the form of an associative array of associative arrays: the outer array has one entry for each method, the key being the method name. The corresponding value is another associative array, which can have the following members:

  • function - this entry is mandatory. It must be a name of a function in the global scope which services the XML-RPC method.

  • signature - this entry is an array containg the possible signatures (see Signatures) for the method. If this entry is present then the server will check that the correct number and type of parameters have been sent for this method before dispatching it.

  • docstring - this entry is a string containing documentation for the method. The documentation may contain HTML markup.

Look at the server.php example in the distribution to see what a dispatch map looks like.

Method signatures

A signature is a description of a method's return type and its parameter types. A method may have more than one signature.

Within a server's dispatch map, each method has an array of possible signatures. Each signature is an array of types. The first entry is the return type. For instance, the method

string examples.getStateName(int)
has the signature
array($xmlrpcString, $xmlrpcInt)
and, assuming that it the only possible signature for the method, might be used like this in server creation:
$findstate_sig=array(array($xmlrpcString, $xmlrpcInt));

$findstate_doc='When passed an integer between 1 and 51 returns the
name of a US state, where the integer is the index of that state name
in an alphabetic order.';

$s=new xmlrpc_server( array( "examples.getStateName" => 
						array("function" => "findstate",
						"signature" => $findstate_sig,
						"docstring" => $findstate_doc)));

For convenience the strings representing the XML-RPC types have been encoded as global variables:

$xmlrpcI4="i4";
$xmlrpcInt="int";
$xmlrpcBoolean="boolean";
$xmlrpcDouble="double";
$xmlrpcString="string";
$xmlrpcDateTime="dateTime.iso8601";
$xmlrpcBase64="base64";
$xmlrpcArray="array";
$xmlrpcStruct="struct";

Delaying the server response

You may want to construct the server, but for some reason not fulfill the request immediately (security verification, for instance). If you pass the constructor a second argument of 0 this will have the desired effect. You can then use the service() method of the server class to service the request. For example:

$s=new xmlrpc_server($myDispMap, 0);

// ... some code that does other stuff here

$s->service();

Fault reporting

Fault codes for your servers should start at the value indicated by the global $xmlrpcerruser + 1.

Standard errors returned by the server include:

1 Unknown method

Returned if the server was asked to dispatch a method it didn't know about

2 Invalid return payload

This error is actually generated by the client, not server, code, but signifies that a server returned something it couldn't understand.

3 Incorrect parameters

This error is generated when the server has signature(s) defined for a method, and the parameters passed by the client do not match any of signatures.

4 Can't introspect: method unknown

This error is generated by the builtin system.* methods when any kind of introspection is attempted on a method undefined by the server.

5 Didn't receive 200 OK from remote server

This error is generated by the client when a remote server doesn't return HTTP/1.1 200 OK in response to a request. A more detailed error report is added onto the end of the phrase above.

100- XML parse errors

Returns 100 plus the XML parser error code for the fault that occurred. The faultString returned explains where the parse error was in the incoming XML stream.

ec_api/xmlrpc/doc/xmlrpc_php.sgml0000644000175000017500000015152610041103320016666 0ustar davidjdavidj XML-RPC for PHP version 1.1 December 17, 2002 Edd Dumbill Useful Information Company
edd@usefulinc.com
1999,2000,2001 Edd Dumbill, Useful Information Company All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of the "XML-RPC for PHP" nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Introduction XML-RPC is a format devised by Userland Software for achieving remote procedure call via XML. XML-RPC has its own web site, www.XmlRpc.com The most common implementations of XML-RPC available at the moment use HTTP as the transport. A list of implementations for other languages such as Perl and Python can be found on the www.xmlrpc.com. This collection of PHP classes provides a framework for writing XML-RPC clients and servers in PHP. The server code works only with versions of PHP3 >= 3.0.12. The code is also known to work with PHP4. If you wish to use SSL to communicate with remote servers, you need the "curl" extension compiled into your PHP installation, this is available in PHP 4.0.2 and greater, although 4.0.6 has a bug preventing SSL working. Acknowledgements Jim Winstead jimw@php.net Peter Kocks peter.kocks@baygate.com Nicolay Mausz mausz@flying-dog.com Ben Margolin ben@wendy.auctionwatch.com Dan Libby dan@libby.com Gaetano Giunta g.giunta@libero.it Idan Sofer i_sofer@yahoo.com Giancarlo Pinerolo ping@alt.it Justin Miller justin@voxel.net Files in the distribution xmlrpc.inc the XML-RPC classes. include() this in your PHP files to use the classes. xmlrpcs.inc the XML-RPC server class. include() this in addition to xmlrpc.inc to get server functionality bettydemo.php demo which retrieves a state name from a number using Dave Winer's XML-RPC server at betty.userland.com server.php a sample server hosting three functions: a US State lookup tool, a struct sorter and an echo function. client.php, agesort.php, echotest.php client code to exercise the various functions in server.php base64test.php, stringtest.php Tests to verify that encoding and decoding of base 64 and entities is functioning correctly. vardemo.php examples of how to construct xmlrpcval types demo1.txt, demo2.txt, demo3.txt XML-RPC responses captured in a file for testing purposes (you can use these to test the xmlrpcmsg->parseResponse() method). httptest.php Testing that HTTP response detection works OK. test.pl, test.py Perl and Python programs to exercise server.php to test that some of the methods work. Make sure you point these at your server, not mine! workspace.testPhpServer.fttb Frontier scripts to exercise the demo server. Thanks to Dave Winer for permission to include these. See Dave's announcement of these. phpunit.php Fred Yankowski's unit test framework for PHP. testsuite.php Start of a unit test suite for this software package. If you do development on this software, please consider submitting tests for this suite. which.php A demo of the interopEchoTests.whichToolkit method. discuss.php, comment.php Software used in the PHP chapter of to provide a comment server and allow the attachment of comments to stories from Meerkat's data store. rsakey.pem A test certificate for the SSL support. It has the passphrase "test." Bugs This is a bare framework. The "nice" bits haven't been put in yet. Specifically, no HTTP response checking is performed, and no type validation or coercion has been put in. PHP being a loosely-typed language, this is going to have to be done explicitly. dateTime.iso8601 is supported opaquely. It can't be done natively as the XML-RPC specification explictly forbids passing of timezone specifiers in ISO8601 format dates. You can, however, use the and functions to do the encoding and decoding for you. If alternative character set encoding is sent in HTTP header than it will be ignored for the moment. We speak only UTF-8... If more than 32k of HTTP headers are encountered (like, why?) then the response parsing code will break. Support Online Support XML-RPC for PHP is offered "as-is" without any warranty or commitment to support. However, informal advice and help is available via the XML-RPC for PHP mailing list and XML-RPC.com. The PHP XML-RPC interest mailing list is run by the author. More details can be found here. For more general XML-RPC questions, there is a Yahoo! Groups XML-RPC mailing list. The XML-RPC.com discussion group is a useful place to get help with using XML-RPC. This group is also gatewayed into the Yahoo! Groups mailing list. The Jellyfish Book Together with Simon St.Laurent and Joe Johnston, I wrote a book on XML-RPC for O'Reilly and Associates on XML-RPC. It features a rather fetching jellyfish on the cover. Complete details of the book are available from O'Reilly's web site. I'm responsible for the chapter on PHP, which includes a worked example of creating a forum server, and hooking it up the O'Reilly's Meerkat service in order to allow commenting on news stories from around the Web. If you've benefitted from the effort I've put into writing this software, then please consider buying the book! Class documentation xmlrpc_client This is the basic class used to represent a client of an XML-RPC server. Creation The constructor has the following syntax: $client=new xmlrpc_client $server_path $server_hostname $server_port Here's an example client set up to query Userland's XML-RPC server at betty.userland.com: $client=new xmlrpc_client("/RPC2", "betty.userland.com", 80); The server_port parameter is optional, and if omitted will default to 80 when using HTTP and 443 when using HTTPS (see the "send" method below.) Methods This class supports the following methods. send This method takes the form: $response=$client->send $xmlrpc_message $timeout $server_method Where $xmlrpc_message is an instance of xmlrpcmsg (see ), and $response is an instance of xmlrpcresp (see ). The $timeout is optional, and will be set to 0 (wait forever) if omitted. This timeout value is passed to fsockopen(). The server_method parameter is optional, and if omitted will default to 'http'. The only other valid value is 'https', which will use an SSL HTTP connection to connect to the remote server. Note that your PHP must have the "curl" extensions compiled in in order to use this feature. Note that when using SSL you should normally set your port number to 443, unless the SSL server you are contacting runs at any other port. PHP 4.0.2 or greater is required for SSL functionality. PHP 4.0.6 has a bug which prevents SSL working. If the value of $response is 0 rather than an xmlrpcresp object, then this signifies an I/O error has occured. You can find out what the I/O error was from the values $client->errno and $client->errstring. In addition to low-level errors, the XML-RPC server you were querying may return an error in the xmlrpcresp object. See for details of how to handle these errors. setCredentials $client->setCredentials $username $password This method sets the username and password for authorizing the client to a server. With the default (HTTP) transport, this information is used for HTTP Basic authorization. setCertificate $client->setCertificate $certificate $passphrase This method sets the optional certificate and passphrase used in SSL-enabled communication with a remote server (when the server_method is set to 'https' in the client's construction). The certificate parameter must be the filename of a PEM formatted certificate. The passphrase parameter must contain the password required to use the certificate. This requires the "curl" extensions to be compiled into your installation of PHP. setSSLVerifyPeer $client->setSSLVerifyPeer $i This method defines whether connections made to XMLRPC backends via HTTPS should verify the remote host's SSL certificate, and cause the connection to fail if the cert verification fails. $i should be a boolean value. setSSLVerifyHost $client->setSSLVerifyHost $i This method defines whether connections made to XMLRPC backends via HTTPS should verify the remote host's SSL certificate's common name (CN). By default, only the existence of a CN is checked. $i should be an integer value; 0 to not check the CN at all, 1 to merely check for its existence, and 2 to check that the CN on the certificate matches the hostname that is being connected to. setDebug $client->setDebug $debugOn $debugOn is either 0 or 1 depending on whether you require the client to print debugging information to the browser. The default is not to output this information. The debugging information includes the raw data returned from the XML-RPC server it was querying, and the PHP value the client attempts to create to represent the value returned by the server. This option can be very useful when debugging servers as it allows you to see exactly what the server returns. xmlrpcmsg This class provides a representation for a request to an XML-RPC server. A client sends an xmlrpcmsg to a server, and receives back an xmlrpcresp (see ). Creation The constructor takes the following form: $msg=new xmlrpcmsg $methodName $parameterArray Where $methodName is a string indicating the name of the method you wish to invoke, and $parameterArray is a simple Array of xmlrpcval objects. Here's an example message to the US state name server: $msg=new xmlrpcmsg("examples.getStateName", array(new xmlrpcval(23, "int"))); This example requests the name of state number 23. For more information on xmlrpcval objects, see . Methods serialize $outString=$msg->serialize Returns the an XML string representing the XML-RPC message. addParam $msg->addParam $xmlrpcVal Adds the xmlrpcval $xmlrpcVal to the parameter list for this method call. getParam $xmlrpcVal=$msg->getParam $n Gets the $nth parameter in the message. Use this method in server implementations. Returns the undef value if no such parameter exists. getNumParams $n=$msg->getNumParams Returns the number of parameters attached to this message. method $methName=$msg->method $msg->method $methName Gets or sets the method contained in the XML-RPC message. parseResponse $response=$msg->parseResponse $xmlString Given an incoming XML-RPC server response contained in the string $xmlString, this method constructs an xmlrpcresp response object and returns it, setting error codes as appropriate (see ). This method processes any HTTP/MIME headers it finds. parseResponseFile $response=$msg->parseResponseFile $fileHandle Given an incoming XML-RPC server response on the file handle $fileHandle, this method reads the data and passes it to parseResponse This method is useful to construct responses from pre-prepared files (see files demo1.txt, demo2.txt, demo3.txt in this distribution). It processes any HTTP headers it finds. xmlrpcresp This class is used to contain responses to XML-RPC requests. A server method handler will construct an xmlrpcresp and pass it as a return value. This same value will be returned by the result of an invocation of the send method of the xmlrpc_client class. Creation $resp=new xmlrpcresp $xmlrpcval $resp=new xmlrpcresp 0 $errcode $errstring The first instance is used when execution has happened without difficulty: $xmlrpcval is an xmlrpcval value with the result of the method execution contained in it. The second type of constructor is used in case of failure. $errcode and $errstring are used to provide indication of what has gone wrong. See for more information on passing error codes. Methods faultCode $fn=$resp->faultCode Returns the integer fault code return from the XML-RPC response $resp. A zero value indicates success, any other value indicates a failure response. faultString $fs=$resp->faultString Returns the human readable explanation of the fault indicated by $resp->faultCode. value $xmlrpcVal=$resp->value Returns an xmlrpcval object containing the return value sent by the server. If the response's faultCode is non-zero then the value returned by this method should not be used (it may not even be an object). serialize $outString=$resp->serialize Returns an XML string representation of the response. xmlrpcval This is where a lot of the hard work gets done. This class enables the creation and encapsulation of values for XML-RPC. Ensure you've read the XML-RPC spec at http://www.xmlrpc.com/stories/storyReader$7 before reading on as it will make things clearer. The xmlrpcval class can store arbitrarily complicated values using the following types: i4 int boolean string double dateTime.iso8601 base64 array struct. You should refer to the spec for more information on what each of these types mean. Notes on types int The type i4 is accepted as a synonym for int. The value parsing code will always convert i4 to int: int is regarded by this implementation as the canonical name for this type. base64 Base 64 encoding is performed transparently to the caller when using this type. Therefore you ought to consider it as a "binary" data type, for use when you want to pass none 7-bit clean data. Decoding is also transparent. boolean The values true and 1 map to true. All other values (including the empty string) are converted to false. string The characters < > " and & are converted to their entity equivalents &lt; &gt; &quot; and &amp; for transport through XML-RPC. The current XML-RPC spec recommends only encoding < & but this implementation goes further, for reasons explained by the XML 1.0 recommendation. TODO: &apos; entity not yet supported Creation The constructor is the normal way to create an xmlrpcval. The constructor can take these forms: $myVal=new xmlrpcval $myVal=new xmlrpcval $stringVal $myVal=new xmlrpcval $scalarVal "int" | "boolean" | "string" | "double" | "dateTime.iso8601" | "base64" $myVal=new xmlrpcval $arrayVal "array" | "struct" The first constructor creates an empty value, which must be altered using the methods addScalar, addArray or addStruct before it can be used. The second constructor creates a simple string value. The third constructor is used to create a scalar value. The second parameter must be a name of an XML-RPC type. Examples: $myInt=new xmlrpcvalue(1267, "int"); $myString=new xmlrpcvalue("Hello, World!", "string"); $myBool=new xmlrpcvalue(1, "boolean"); The fourth constructor form can be used to compose complex XML-RPC values. The first argument is either a simple array in the case of an XML-RPC array or an associative array in the case of a struct. The elements of the array must be xmlrpcval objects themselves. Examples: $myArray=new xmlrpcval(array( new xmlrpcval("Tom"), new xmlrpcval("Dick"), new xmlrpcval("Harry")), "array"); $myStruct=new xmlrpcval(array( "name" => new xmlrpcval("Tom"), "age" => new xmlrpcval(34, "int"), "geek" => new xmlrpcval(1, "boolean")), "struct"); See the file vardemo.php in this distribution for more examples. Methods addScalar $ok=$val->addScalar $stringVal $ok=$val->addScalar $scalarVal "int" | "boolean" | "string" | "double" | "dateTime.iso8601" | "base64" If $val is an empty xmlrpcval this method makes it a scalar value, and sets that value. If $val is already a scalar value, then no more scalars can be added and 0 is returned. If all went OK, 1 is returned. There is a special case if $val is an array: the scalar value passed is appended to the array. addArray $ok=$val->addArray $arrayVal Turns an empty xmlrpcval into an array with contents as specified by $arrayVal. See the fourth constructor form for more information. addStruct $ok=$val->addArray $assocArrayVal Turns an empty xmlrpcval into a struct with contents as specified by $assocArrayVal. See the fourth constructor form for more information. kindOf $kind=$val->kindOf Returns a string containing "struct", "array" or "scalar" describing the base type of the value. If it returns "undef" it means that the value hasn't been initialised. serialize $outString=$val->serialize Returns a string containing the XML-RPC representation of this value. scalarval $scalarVal=$val->scalarval If $val->kindOf()=="scalar", this method returns the actual PHP-language value of the scalar (base 64 decoding is automatically handled here). scalartyp $typeName=$val->scalartyp If $val->kindOf()=="scalar", this method returns a string denoting the type of the scalar. As mentioned before, i4 is always coerced to int. arraymem $xmlrpcVal=$val->arraymem $n Returns the $nth element in the array represented by the value $val. The value returned is an xmlrpcval object. arraysize $len=$val->arraysize If $val is an array, returns the number of elements in that array. structmem $xmlrpcVal=$val->structmem $memberName Returns the element called $memberName from the struct represented by the value $val. The value returned is an xmlrpcval object. structeach list($key,$value)=$val->structeach Returns the next (key,value) pair from the struct, when $val is a struct. See also . structreset $val->structreset Resets the internal pointer for structeach() to the beginning of the struct, where $val is a struct. xmlrpc_server The current implementation of this class has been kept as simple as possible. The constructor for the server basically does all the work. Here's a minimal example: function foo ($params) { ... } $s=new xmlrpc_server( array("examples.myFunc" => array("function" => "foo"))); This performs everything you need to do with a server. The single argument is an associative array from method names to function names. The request is parsed and despatched to the relevant function, which is reponsible for returning a xmlrpcresp object, which gets serialized back to the caller. See server.php in this distribution for examples of how to do this. Here is a more detailed look at what the handler function foo may do: function foo ($params) { global $xmlrpcerruser; // import user errcode value // $params is an Array of xmlrpcval objects if ($err) { // this is an error condition return new xmlrpcresp(0, $xmlrpcerruser+1, // user error 1 "There's a problem, Captain"); } else { // this is a successful value being returned return new xmlrpcresp(new xmlrpcval("All's fine!", "string")); } } The dispatch map The first argument to the xmlrpc_server constructor is an array, called the dispatch map. In this array is the information the server needs to service the XML-RPC methods you define. The dispatch map takes the form of an associative array of associative arrays: the outer array has one entry for each method, the key being the method name. The corresponding value is another associative array, which can have the following members: function - this entry is mandatory. It must be a name of a function in the global scope which services the XML-RPC method. signature - this entry is an array containg the possible signatures (see ) for the method. If this entry is present then the server will check that the correct number and type of parameters have been sent for this method before dispatching it. docstring - this entry is a string containing documentation for the method. The documentation may contain HTML markup. Look at the server.php example in the distribution to see what a dispatch map looks like. Method signatures A signature is a description of a method's return type and its parameter types. A method may have more than one signature. Within a server's dispatch map, each method has an array of possible signatures. Each signature is an array of types. The first entry is the return type. For instance, the method string examples.getStateName(int) has the signature array($xmlrpcString, $xmlrpcInt) and, assuming that it the only possible signature for the method, might be used like this in server creation: $findstate_sig=array(array($xmlrpcString, $xmlrpcInt)); $findstate_doc='When passed an integer between 1 and 51 returns the name of a US state, where the integer is the index of that state name in an alphabetic order.'; $s=new xmlrpc_server( array( "examples.getStateName" => array("function" => "findstate", "signature" => $findstate_sig, "docstring" => $findstate_doc))); For convenience the strings representing the XML-RPC types have been encoded as global variables: $xmlrpcI4="i4"; $xmlrpcInt="int"; $xmlrpcBoolean="boolean"; $xmlrpcDouble="double"; $xmlrpcString="string"; $xmlrpcDateTime="dateTime.iso8601"; $xmlrpcBase64="base64"; $xmlrpcArray="array"; $xmlrpcStruct="struct"; Delaying the server response You may want to construct the server, but for some reason not fulfill the request immediately (security verification, for instance). If you pass the constructor a second argument of 0 this will have the desired effect. You can then use the service() method of the server class to service the request. For example: $s=new xmlrpc_server($myDispMap, 0); // ... some code that does other stuff here $s->service(); Fault reporting Fault codes for your servers should start at the value indicated by the global $xmlrpcerruser + 1. Standard errors returned by the server include: 1 Unknown method Returned if the server was asked to dispatch a method it didn't know about 2 Invalid return payload This error is actually generated by the client, not server, code, but signifies that a server returned something it couldn't understand. 3 Incorrect parameters This error is generated when the server has signature(s) defined for a method, and the parameters passed by the client do not match any of signatures. 4 Can't introspect: method unknown This error is generated by the builtin system.* methods when any kind of introspection is attempted on a method undefined by the server. 5 Didn't receive 200 OK from remote server This error is generated by the client when a remote server doesn't return HTTP/1.1 200 OK in response to a request. A more detailed error report is added onto the end of the phrase above. 100- XML parse errors Returns 100 plus the XML parser error code for the fault that occurred. The faultString returned explains where the parse error was in the incoming XML stream. Helper functions XML-RPC for PHP contains some helper functions which you can use to make processing of XML-RPC requests easier. Date functions The XML-RPC specification has this to say on dates:
Don't assume a timezone. It should be specified by the server in its documentation what assumptions it makes about timezones.
Unfortunately, this means that date processing isn't straightforward. Although XML-RPC uses ISO 8601 format dates, it doesn't use the timezone specifier. We strongly recommend that in every case where you pass dates in XML-RPC calls, you use UTC (GMT) as your timezone. Most computer languages include routines for handling GMT times natively, and you won't have to translate between timezones. For more information about dates, see ISO 8601: The Right Format for Dates, which has a handy link to a PDF of the ISO 8601 specification. Note that XML-RPC uses exactly one of the available representations: CCYYMMDDTHH:MM:SS. iso8601_encode $isoString=iso8601_encode $time_t$utc=0 Returns an ISO 8601 formatted date generated from the UNIX timestamp $time_t, as returned by the PHP function time(). The argument $utc can be omitted, in which case it defaults to 0. If it is set to 1, then the function corrects the time passed in for UTC. Example: if you're in the GMT-6:00 timezone and set $utc, you will receive a date representation six hours ahead of your local time. The included demo program vardemo.php includes a demonstration of this function. iso8601_decode $time_t=iso8601_decode $isoString$utc=0 Returns a UNIX timestamp from an ISO 8601 encoded time and date string passed in. If $utc is 1 then $isoString is assumed to be in the UTC timezone, and thus the $time_t result is also UTC: otherwise, the timezone is assumed to be your local timezone and you receive a local timestamp.
Easy use with PHP arrays Dan Libby was kind enough to contribute two helper functions that make it easier to translate to and from PHP arrays. This makes it easier to deal with complex structures. At the moment support is limited to int, double, string, array and struct datatypes; note also that all PHP arrays are encoded as structs due to PHP not being able to tell the difference between a hash and a linear array. These functions reside in xmlrpc.inc. xmlrpc_decode $arr=xmlrpc_decode $xmlrpc_val Returns a PHP array stuffed with the values found in the xmlrpcval $xmlrpc_val, translated into native PHP types. xmlrpc_encode $xmlrpc_val=xmlrpc_encode $phpval Returns an xmlrpcval populated with the PHP values in $phpval. Works recursively on arrays and structs. Note that there's no support for non-base types like base-64 values or date-times. Debugging aids xmlrpc_debugmsg xmlrpc_debugmsg $debugstring Sends the contents of $debugstring in XML comments in the server return payload. If a PHP client has debugging turned on, the user will be able to see server debug information. Use this function in your methods so you can pass back diagnostic information. It is only available from xmlrpcs.inc.
Reserved methods In order to extend the functionality offered by XML-RPC servers without impacting on the protocol, I've included experimental support in this release for reserved methods. All methods starting with system. are considered reserved by the server. PHP for XML-RPC itself provides three special methods, detailed in this chapter. system.listMethods This method may be used to enumerate the methods implemented by the XML-RPC server. The system.listMethods method requires no parameters. It returns an array of strings, each of which is the name of a method implemented by the server. system.methodSignature This method takes one parameter, the name of a method implemented by the XML-RPC server. It returns an array of possible signatures for this method. A signature is an array of types. The first of these types is the return type of the method, the rest are parameters. Multiple signatures (ie. overloading) are permitted: this is the reason that an array of signatures are returned by this method. Signatures themselves are restricted to the top level parameters expected by a method. For instance if a method expects one array of structs as a parameter, and it returns a string, its signature is simply "string, array". If it expects three integers, its signature is "string, int, int, int". If no signature is defined for the method, a none-array value is returned. Therefore this is the way to test for a non-signature, if $resp below is the response object from a method call to system.methodSignature: $v=$resp->value(); if ($v->kindOf()!="array") { // then the method did not have a signature defined } See the introspect.php demo included in this distribution for an example of using this method. system.methodHelp This method takes one parameter, the name of a method implemented by the XML-RPC server. It returns a documentation string describing the use of that method. If no such string is available, an empty string is returned. The documentation string may contain HTML markup. Examples The best examples are to be found in the sample files included with the distribution. Some are included here. XML-RPC client: state name query Code to get the corresponding state name from a number (1-50) from Dave Winer's server $f=new xmlrpcmsg('examples.getStateName', array(new xmlrpcval($HTTP_POST_VARS["stateno"], "int"))); $c=new xmlrpc_client("/RPC2", "betty.userland.com", 80); $r=$c->send($f); $v=$r->value(); if (!$r->faultCode()) { print "State number ". $HTTP_POST_VARS["stateno"] . " is " . $v->scalarval() . "<BR>"; print "<HR>I got this value back<BR><PRE>" . htmlentities($r->serialize()). "</PRE><HR>\n"; } else { print "Fault: "; print "Code: " . $r->faultCode() . " Reason '" .$r->faultString()."'<BR>"; }
ec_api/xmlrpc/doc/xmlrpcmsg.html0000644000175000017500000001470310041103320016523 0ustar davidjdavidjxmlrpcmsg

xmlrpcmsg

This class provides a representation for a request to an XML-RPC server. A client sends an xmlrpcmsg to a server, and receives back an xmlrpcresp (see xmlrpc_client->send).

Creation

The constructor takes the following form:

$msg=new xmlrpcmsg($methodName, $parameterArray);

Where $methodName is a string indicating the name of the method you wish to invoke, and $parameterArray is a simple Array of xmlrpcval objects. Here's an example message to the US state name server:

$msg=new xmlrpcmsg("examples.getStateName",
		  array(new xmlrpcval(23, "int")));
		

This example requests the name of state number 23. For more information on xmlrpcval objects, see xmlrpcval.

Methods

serialize

$outString=$msg->serialize();

Returns the an XML string representing the XML-RPC message.

addParam

$msg->addParam($xmlrpcVal);

Adds the xmlrpcval $xmlrpcVal to the parameter list for this method call.

getParam

$xmlrpcVal=$msg->getParam($n);

Gets the $nth parameter in the message. Use this method in server implementations. Returns the undef value if no such parameter exists.

getNumParams

$n=$msg->getNumParams();

Returns the number of parameters attached to this message.

method

$methName=$msg->method();

$msg->method($methName);

Gets or sets the method contained in the XML-RPC message.

parseResponse

$response=$msg->parseResponse($xmlString);

Given an incoming XML-RPC server response contained in the string $xmlString, this method constructs an xmlrpcresp response object and returns it, setting error codes as appropriate (see xmlrpc_client->send).

This method processes any HTTP/MIME headers it finds.

parseResponseFile

$response=$msg->parseResponseFile($fileHandle);

Given an incoming XML-RPC server response on the file handle $fileHandle, this method reads the data and passes it to parseResponse

This method is useful to construct responses from pre-prepared files (see files demo1.txt, demo2.txt, demo3.txt in this distribution). It processes any HTTP headers it finds.

ec_api/xmlrpc/doc/xmlrpcresp.html0000644000175000017500000001146610041103321016712 0ustar davidjdavidjxmlrpcresp

xmlrpcresp

This class is used to contain responses to XML-RPC requests. A server method handler will construct an xmlrpcresp and pass it as a return value. This same value will be returned by the result of an invocation of the send method of the xmlrpc_client class.

Creation

$resp=new xmlrpcresp($xmlrpcval);

$resp=new xmlrpcresp(0, $errcode, $errstring);

The first instance is used when execution has happened without difficulty: $xmlrpcval is an xmlrpcval value with the result of the method execution contained in it.

The second type of constructor is used in case of failure. $errcode and $errstring are used to provide indication of what has gone wrong. See xmlrpc_server for more information on passing error codes.

Methods

faultCode

$fn=$resp->faultCode();

Returns the integer fault code return from the XML-RPC response $resp. A zero value indicates success, any other value indicates a failure response.

faultString

$fs=$resp->faultString();

Returns the human readable explanation of the fault indicated by $resp->faultCode.

value

$xmlrpcVal=$resp->value();

Returns an xmlrpcval object containing the return value sent by the server. If the response's faultCode is non-zero then the value returned by this method should not be used (it may not even be an object).

serialize

$outString=$resp->serialize();

Returns an XML string representation of the response.

ec_api/xmlrpc/doc/xmlrpcval.html0000644000175000017500000003117710041103321016524 0ustar davidjdavidjxmlrpcval

xmlrpcval

This is where a lot of the hard work gets done. This class enables the creation and encapsulation of values for XML-RPC.

Ensure you've read the XML-RPC spec at http://www.xmlrpc.com/stories/storyReader$7 before reading on as it will make things clearer.

The xmlrpcval class can store arbitrarily complicated values using the following types: i4 int boolean string double dateTime.iso8601 base64 array struct. You should refer to the spec for more information on what each of these types mean.

Notes on types

int

The type i4 is accepted as a synonym for int. The value parsing code will always convert i4 to int: int is regarded by this implementation as the canonical name for this type.

base64

Base 64 encoding is performed transparently to the caller when using this type. Therefore you ought to consider it as a "binary" data type, for use when you want to pass none 7-bit clean data. Decoding is also transparent.

boolean

The values true and 1 map to true. All other values (including the empty string) are converted to false.

string

The characters < > " and & are converted to their entity equivalents &lt; &gt; &quot; and &amp; for transport through XML-RPC. The current XML-RPC spec recommends only encoding < & but this implementation goes further, for reasons explained by the XML 1.0 recommendation.

TODO: &apos; entity not yet supported

Creation

The constructor is the normal way to create an xmlrpcval. The constructor can take these forms:

$myVal=new xmlrpcval();

$myVal=new xmlrpcval($stringVal);

$myVal=new xmlrpcval($scalarVal, "int" | "boolean" | "string" | "double" | "dateTime.iso8601" | "base64");

$myVal=new xmlrpcval($arrayVal, "array" | "struct");

The first constructor creates an empty value, which must be altered using the methods addScalar, addArray or addStruct before it can be used.

The second constructor creates a simple string value.

The third constructor is used to create a scalar value. The second parameter must be a name of an XML-RPC type. Examples:

		  $myInt=new xmlrpcvalue(1267, "int");
		  $myString=new xmlrpcvalue("Hello, World!", "string");
		  $myBool=new xmlrpcvalue(1, "boolean");
		

The fourth constructor form can be used to compose complex XML-RPC values. The first argument is either a simple array in the case of an XML-RPC array or an associative array in the case of a struct. The elements of the array must be xmlrpcval objects themselves. Examples:

		  $myArray=new xmlrpcval(array(
		      new xmlrpcval("Tom"), new xmlrpcval("Dick"),
		      new xmlrpcval("Harry")), "array");

		  $myStruct=new xmlrpcval(array(
		      "name" => new xmlrpcval("Tom"),
		      "age" => new xmlrpcval(34, "int"),
		      "geek" => new xmlrpcval(1, "boolean")), "struct");
		

See the file vardemo.php in this distribution for more examples.

Methods

addScalar

$ok=$val->addScalar($stringVal);

$ok=$val->addScalar($scalarVal, "int" | "boolean" | "string" | "double" | "dateTime.iso8601" | "base64");

If $val is an empty xmlrpcval this method makes it a scalar value, and sets that value. If $val is already a scalar value, then no more scalars can be added and 0 is returned. If all went OK, 1 is returned.

There is a special case if $val is an array: the scalar value passed is appended to the array.

addArray

$ok=$val->addArray($arrayVal);

Turns an empty xmlrpcval into an array with contents as specified by $arrayVal. See the fourth constructor form for more information.

addStruct

$ok=$val->addArray($assocArrayVal);

Turns an empty xmlrpcval into a struct with contents as specified by $assocArrayVal. See the fourth constructor form for more information.

kindOf

$kind=$val->kindOf();

Returns a string containing "struct", "array" or "scalar" describing the base type of the value. If it returns "undef" it means that the value hasn't been initialised.

serialize

$outString=$val->serialize();

Returns a string containing the XML-RPC representation of this value.

scalarval

$scalarVal=$val->scalarval();

If $val->kindOf()=="scalar", this method returns the actual PHP-language value of the scalar (base 64 decoding is automatically handled here).

scalartyp

$typeName=$val->scalartyp();

If $val->kindOf()=="scalar", this method returns a string denoting the type of the scalar. As mentioned before, i4 is always coerced to int.

arraymem

$xmlrpcVal=$val->arraymem($n);

Returns the $nth element in the array represented by the value $val. The value returned is an xmlrpcval object.

arraysize

$len=$val->arraysize();

If $val is an array, returns the number of elements in that array.

structmem

$xmlrpcVal=$val->structmem($memberName);

Returns the element called $memberName from the struct represented by the value $val. The value returned is an xmlrpcval object.

structeach

list($key,$value)=$val->structeach();

Returns the next (key,value) pair from the struct, when $val is a struct. See also structreset().

structreset

$val->structreset();

Resets the internal pointer for structeach() to the beginning of the struct, where $val is a struct.

ec_api/xmlrpc/bettydemo.php0000644000175000017500000000152110041103310015552 0ustar davidjdavidj xmlrpc send($f); $v=$r->value(); if (!$r->faultCode()) { print "State number ". $HTTP_POST_VARS["stateno"] . " is " . $v->scalarval() . "
"; print "
I got this value back
" .
	  htmlentities($r->serialize()). "

\n"; } else { print "Fault: "; print "Code: " . $r->faultCode() . " Reason '" .$r->faultString()."'
"; } } print "

enter a state number to query its name"; ?> ec_api/xmlrpc/ChangeLog0000644000175000017500000002220110041103307014623 0ustar davidjdavidj2003-01-12 Andres Salomon * released 1.0.99.2. * Makefile: separate doc/Makefile a bit more from Makefile, and add clean rules. 2003-01-10 Andres Salomon * xmlrpc.inc: xmlrpcresp and parseResponse cleanups; variable name renames ('xv' to 'val', for example), type checking, and stricter default values. * xmlrpc.inc: fix xmlrpcresp's faultcode; return -1 for FAULT responses from the server whose faultcodes don't reflect any errors. 2003-01-08 Andres Salomon * xmlrpc.inc: rename $_xh[$parser]['ha'] to $_xh[$parser]['headers']. * xmlrpc.inc: fix bugs related to $_xh[$parser]['headers]; some places treated this as an array, others as a scalar. Treat unconditionally as an array. Also wrap header debugging output in PRE tags. 2002-12-17 Andres Salomon * released 1.0.99. * Makefile: changed the tarball format/dist rule to a more conventional form, as well as normal release updates. * xmlrpc.inc: added setSSLVerifyPeer and setSSLVerifyHost; as of curl 7.10, various certificate checks are done (by default). The default for CURLOPT_SSL_VERIFYHOST is to ensure the common name on the cert matches the provided hostname. This breaks a lot of stuff, so allow users to override it. * doc/xmlrpc_php.sgml: updated documentation accordingly. 2002-09-06 Geoffrey T. Dairiki Add support for system.multicall() to both the client and the server. * testsuite.php: Add new tests 'testServerMulticall', and 'testClientMulticall'. * xmlrpc.inc: Added new error messages for system.multicall(). * xmlrpcs.inc: Added new procedure call system.multicall(). See http://www.xmlrpc.com/discuss/msgReader$1208 for details. * xmlrpc.inc: Added system.multicall functionality to xmlrpc_client. xmlrpc_client::send can now take an array of xmlrpcmsg's as an argument. In that case it will attempt to execute the whole array of procure calls in a single HTTP request using system.multicall(). (If that attempt fails, then the calls will be excuted one at a time.) The return value will be an array of xmlrpcresp's (or 0 upon transport failure.) 2001-11-29 Edd Dumbill * xmlrpc.inc: fixed problem with processing HTTP headers that broke any payload with more than one consecutive newline in it. also initialise the 'ac' array member to empty string at start. * testsuite.php: added unit test to exercise above bug * xmlrpcs.inc: fixed uninitialized variable $plist 2001-09-25 Edd Dumbill * xmlrpc.inc: applied urgent security fixes as identified by Dan Libby 2001-08-27 Edd Dumbill * xmlrpc.inc: Merged in HTTPS support from Justin Miller, with a few additions for better traceability of failure conditions. Added small fix from Giancarlo Pinerolo. Bumped rev to 1.0. Changed license to BSD license. 2001-06-15 Edd Dumbill * xmlrpcs.inc: Added \r into return MIME headers for server class 2001-04-25 Edd Dumbill * server.php: Added interop suite of methods. 2001-04-24 Edd Dumbill * testsuite.php: added in test case for string handling bug. * xmlrpc.inc: merged in minor fixes from G Giunta to fix noninitialization. Created new method, getval(), which includes experimental support for recreating nested arrays, from Giunta and Sofer. Fixed string handling bug where characters after but before weren't ignored. Added in support for native boolean type into xmlrpc_encode (Giunta). * xmlrpcs.inc: updated copyright notice 2001-01-15 Edd Dumbill * xmlrpc.inc: fixed bug with creation of booleans. Put checks in to ensure that numbers were really numeric. Fixed bug with non-escaping of dollar signs in strings. * testsuite.php: created test suite. 2000-08-26 Edd Dumbill * xmlrpcs.inc: added xmlrpc_debugmsg() function which outputs debug information in comments inside the return payload XML * xmlrpc.inc: merged in some changes from Dan Libby which fix up whitespace handling. * xmlrpcs.inc: added Content-length header on response (bug from Jan Varga . This means you can no longer print during processing * xmlrpc.inc: changed ereg_replace to str_replace in several places (thanks to Dan Libby for this). * xmlrpc.inc: added xmlrpc_encode() and xmlrpc_decode() from Dan Libby--these helper routines make it easier to work in native PHP data structures. 2000-07-21 Edd Dumbill * xmlrpc.inc: added xmlrpc_client::setCredentials method to pass in authorization information, and modified sendPayload* methods to send this OK. Thanks to Grant Rauscher for the impetus to do this. Also, made the client send empty if there are no parameters set by the user. * doc/xmlrpc_php.sgml: updated documentation to reflect recent changes 2000-07-18 Edd Dumbill * server.php: added examples.invertBooleans method to server as a useful test method for boolean values. * xmlrpc.inc: rearranged the way booleans are handled to fix outstanding problems. Fixed calling addScalar() on arrays so it works. Finally fixed backslashification issues to remove the problem will dollar signs disappearing. * booltest.php: really fixed booleans this time. 2000-06-03 Edd Dumbill * xmlrpcs.inc: made signature verification more useful - now returns what it found was wrong * xmlrpc.inc: fixed bug with decoding dateTimes. Also fixed a bug which meant a PHP syntax error happened when attempting to receive empty arrays or structs. Also fixed bug with booleans always being interpreted as 'true'. * server.php: Added validator1 suite of tests to test against validator.xmlrpc.com 2000-05-06 Edd Dumbill * released 1.0b6 * added test.pl and test.py, Perl and Python scripts that exercise server.php somewhat (but not a lot) * added extra fault condition for a non 200 OK response from the remote server. * added iso8601_encode() and iso8601_decode() to give some support for passing dates around. They translate to and from UNIX timestamps. Updated documentation accordingly. * fixed string backslashification -- was previously a little overzealous! new behavior is '\' --> '\\' and '"' --> '\"'. Everything else gets left alone. 2000-04-12 Edd Dumbill * updated and bugfixed the documentation * fixed base 64 encoding to only happen at serialize() time, rather than when a base64 value is created. This fixes the double encoding bug reported by Nicolay Mausz . The same approach ought to be taken with encoding XML entities in the data - this is a TODO. * integrated further code from Peter Kocks: used his new code for send(), adding a second, optional, parameter which is a timeout parameter to fsockopen() 1999-10-11 Edd Dumbill * added bug fixes from Peter Kocks 1999-10-10 Edd Dumbill * updated the documentation 1999-10-08 Edd Dumbill * added system.* methods and dispatcher, plus documentation * fixed bug which meant request::getNumParams was returning an incorrect value * added signatures into the dispatch map. This BREAKS COMPATIBILITY with previous releases of this code 1999-08-18 Edd Dumbill * made entity encoding and decoding transparent now on string passing. * de-globalised the globals in the parse routines, using an associative array to hold all parser state $_xh * changed default input encoding to be UTF-8 to match expectation * separated out parseResponse into parseResponse and parseResponseFile so that you can call parseResponse on a string if you have one handy 1999-07-20 Edd Dumbill * Moved documentation into Docbook format 1999-07-19 Edd Dumbill * Added an echo server into server.php and echotest.php, a client which will exercise the new echo routine. * Added test for no valid value returned: in this case will now throw the error "invalid payload" * Added serialize() method to xmlrpcresp to return a string with the response serialized as XML * Added automatic encoding and decoding for base64 types * Added setDebug() method to client to enable HTML output debugging in the client 1999-07-08 Edd Dumbill * Improved XML parse error reporting on the server side to send it back in a faultCode packet. expat errors now begin at 100 1999-07-07 Edd Dumbill * Changed the structmem and arraymem methods of xmlrpcval to always return xmlrpc vals whether they referred to scalars or complex types. * Added the server class and demonstrations * Fixed bugs in the XML parsing and reworked it $Id: ChangeLog,v 1.1 2003/09/24 03:43:48 ozman Exp $ ec_api/xmlrpc/README0000644000175000017500000000032210041103307013731 0ustar davidjdavidjHTML documentation can be found in the doc/ directory. Recent changes in the ChangeLog Use of this software is subject to the terms in doc/index.html The passphrase for the rsakey.pem certificate is 'test'. ec_api/xmlrpc/agesort.php0000644000175000017500000000266310041103310015232 0ustar davidjdavidj xmlrpc 24, "Edd" => 45, "Joe" => 37, "Fred" => 27); include("xmlrpc.inc"); reset($inAr); print "
This was the input data

";
while (list($key, $val)=each($inAr)) {
print $key . ", " . $val . "\n";
}
// create parameters from the input array
$p=array();
reset($inAr);
while (list($key, $val)=each($inAr)) {
  $p[]=new xmlrpcval(array("name" => new xmlrpcval($key), 
						   "age" => new xmlrpcval($val, "int")), "struct");
}
$v=new xmlrpcval($p, "array");
// print "Output values look like this: 
\n" .  htmlentities($v->serialize()). "
\n"; $f=new xmlrpcmsg('examples.sortByAge', array($v)); $c=new xmlrpc_client("/server.php", "xmlrpc.heddley.com", 80); $c->setDebug(1); print "\nAnd I sent\n\n"; print htmlspecialchars($f->serialize()); print "
"; print "Sending request...
\n"; $r=$c->send($f); if (!$r) { die("send failed"); } $v=$r->value(); if (!$r->faultCode()) { print "The server gave me these results:
";
  $max=$v->arraysize(); 
  for($i=0; $i<$max; $i++) {
	$rec=$v->arraymem($i);
	$n=$rec->structmem("name");
	$a=$rec->structmem("age");
	print $n->scalarval() . ", " . $a->scalarval() . "\n";
  }

  print "

For nerds: I got this value back
" .
	htmlentities($r->serialize()). "

\n"; } else { print "Fault: "; print "Code: " . $r->faultCode() . " Reason '" .$r->faultString()."'
"; } ?> ec_api/xmlrpc/bug_whitespace.xml0000644000175000017500000000070710041103310016565 0ustar davidjdavidjuserid311127 dateCreated20011126T09:17:52contenthello world. 2 newlines follow and there they were.postid7414222 ec_api/xmlrpc/bug_string.xml0000644000175000017500000000071310041103310015734 0ustar davidjdavidj success 1 sessionID S300510007I ec_api/xmlrpc/introspect.php0000644000175000017500000000343010041103311015752 0ustar davidjdavidj xmlrpc send($msg); if (!$r) { print "
ERROR: couldn't send message
\n"; return 0; } else { if (!$r->faultCode()) { return $r->value(); } else { print "Fault: "; print "Code: " . $r->faultCode() . " Reason '" .$r->faultString()."'
"; return 0; } } } $f=new xmlrpcmsg('system.listMethods'); $c=new xmlrpc_client("/server.php", "xmlrpc.heddley.com", 80); $c->setDebug(0); $v=rpc_call($c, $f); print "

methods available at http://" . $c->server . ":" . $c->port . $c->path . "

\n"; if ($v) { for($i=0; $i<$v->arraysize(); $i++) { $mname=$v->arraymem($i); print "

" . $mname->scalarval() . "

\n"; $f=new xmlrpcmsg('system.methodHelp'); $f->addParam(new xmlrpcval($mname->scalarval(), "string")); $w=rpc_call($c, $f); if ($w) { $txt=$w->scalarval(); if ($txt!="") { print "

Documentation

${txt}

\n"; } else { print "

No documentation available.

\n"; } } $f=new xmlrpcmsg('system.methodSignature'); $f->addParam(new xmlrpcval($mname->scalarval(), "string")); $w=rpc_call($c, $f); if ($w) { print "

Signature

\n"; if ($w->kindOf()=="array") { for($j=0; $j<$w->arraysize(); $j++) { $x=$w->arraymem($j); $ret=$x->arraymem(0); print "" . $ret->scalarval() . " " . $mname->scalarval() ."("; if ($x->arraysize()>1) { for($k=1; $k<$x->arraysize(); $k++) { $y=$x->arraymem($k); print $y->scalarval(); if ($k<$x->arraysize()-1) { print ", "; } } } print ")
\n"; } } else { print "Signature unknown\n"; } print "

\n"; } } } ?> ec_api/xmlrpc/client.php0000644000175000017500000000174610041103310015045 0ustar davidjdavidj xmlrpc " . htmlentities($f->serialize()) . "
\n"; $c=new xmlrpc_client("/server.php", "xmlrpc.heddley.com", 80); $c->setDebug(1); $r=$c->send($f); if (!$r) { die("send failed"); } $v=$r->value(); if (!$r->faultCode()) { print "State number ". $HTTP_POST_VARS["stateno"] . " is " . $v->scalarval() . "
"; // print "
I got this value back
" .
	//  htmlentities($r->serialize()). "

\n"; } else { print "Fault: "; print "Code: " . $r->faultCode() . " Reason '" .$r->faultString()."'
"; } } print "

enter a state number to query its name"; ?> ec_api/xmlrpc/comment.php0000644000175000017500000001234610041103311015230 0ustar davidjdavidj"; exit(); } function dispatch($client, $method, $args) { $msg=new xmlrpcmsg($method, $args); $resp=$client->send($msg); if (!$resp) { print "

IO error: ".$client->errstring."

"; bomb(); } if ($resp->faultCode()) { print "

There was an error: " . $resp->faultCode() . " " . $resp->faultString() . "

"; bomb(); } return xmlrpc_decode($resp->value()); } // create client for discussion server $dclient=new xmlrpc_client("${mydir}/discuss.php", "xmlrpc.usefulinc.com", 80); // check if we're posting a comment, and send it if so $storyid=$HTTP_POST_VARS["storyid"]; if ($storyid) { // print "Returning to " . $HTTP_POST_VARS["returnto"]; $res=dispatch($dclient, "discuss.addComment", array(new xmlrpcval($storyid), new xmlrpcval(stripslashes ($HTTP_POST_VARS["name"])), new xmlrpcval(stripslashes ($HTTP_POST_VARS["commenttext"])))); // send the browser back to the originating page Header("Location: ${mydir}/comment.php?catid=" . $HTTP_POST_VARS["catid"] . "&chanid=" . $HTTP_POST_VARS["chanid"] . "&oc=" . $HTTP_POST_VARS["catid"]); exit(0); } // now we've got here, we're exploring the story store ?> meerkat browser

Meerkat integration

Make a comment on the story

Your name:

Your comment:

" />
new xmlrpcval($chanid, "int"), "ids" => new xmlrpcval(1, "int"), "descriptions" => new xmlrpcval(200, "int"), "num_items" => new xmlrpcval(5, "int"), "dates" => new xmlrpcval(0, "int") ), "struct"))); } ?>

Subject area:

News source:

Stories available

"; print ""; print "\n"; // now look for existing comments $res=dispatch($dclient, "discuss.getComments", array(new xmlrpcval($v['id']))); if (sizeof($res)>0) { print "\n"; } print "\n"; } ?>
" . $v['title'] . "
"; print $v['description'] . "
"; print "Read full story "; print "Comment on this story"; print ""; print "

" . "Comments on this story:

"; for($i=0; $iFrom: " . htmlentities($s['name']) . "
"; print "Comment: " . htmlentities($s['comment']) . "

"; } print "


Meerkat powered, yeah! $Id: comment.php,v 1.1 2003/09/24 03:43:48 ozman Exp $

ec_api/xmlrpc/demo1.txt0000644000175000017500000000142410041103311014616 0ustar davidjdavidj thearray ABCDEFHIJ 1234 1 theint 23 thestring foobarwhizz thestruct one 1 two 2 ec_api/xmlrpc/demo2.txt0000644000175000017500000000037210041103311014620 0ustar davidjdavidj South Dakota's own ec_api/xmlrpc/demo3.txt0000644000175000017500000000112710041103311014620 0ustar davidjdavidj faultCode 4 faultString Too many parameters. ec_api/xmlrpc/discuss.php0000644000175000017500000000544110041103311015241 0ustar davidjdavidjgetParam(0)); $name=xmlrpc_decode($m->getParam(1)); $comment=xmlrpc_decode($m->getParam(2)); $dbh=dba_open("/tmp/comments.db", "c", "db2"); if ($dbh) { $countID="${msgID}_count"; if (dba_exists($countID, $dbh)) $count=dba_fetch($countID, $dbh); else $count=0; // add the new comment in dba_insert($msgID . "_comment_${count}", $comment, $dbh); dba_insert($msgID . "_name_${count}", $name, $dbh); $count++; dba_replace($countID, $count, $dbh); dba_close($dbh); } else { $err="Unable to open comments database."; } // if we generated an error, create an error return response if ($err) { return new xmlrpcresp(0, $xmlrpcerruser, $err); } else { // otherwise, we create the right response // with the state name return new xmlrpcresp(new xmlrpcval($count, "int")); } } $getcomments_sig=array(array($xmlrpcArray, $xmlrpcString)); $getcomments_doc='Returns an array of comments for a given ID, which is the sole argument. Each array item is a struct containing name and comment text.'; function getcomments($m) { global $xmlrpcerruser; $err=""; $ra=array(); // get the first param $msgID=xmlrpc_decode($m->getParam(0)); $dbh=dba_open("/tmp/comments.db", "r", "db2"); if ($dbh) { $countID="${msgID}_count"; if (dba_exists($countID, $dbh)) { $count=dba_fetch($countID, $dbh); for($i=0; $i<$count; $i++) { $name=dba_fetch("${msgID}_name_${i}", $dbh); $comment=dba_fetch("${msgID}_comment_${i}", $dbh); // push a new struct onto the return array $ra[]=new xmlrpcval(array( "name" => new xmlrpcval($name), "comment" => new xmlrpcval($comment) ), "struct"); } } } // if we generated an error, create an error return response if ($err) { return new xmlrpcresp(0, $xmlrpcerruser, $err); } else { // otherwise, we create the right response // with the state name return new xmlrpcresp(new xmlrpcval($ra, "array")); } } $s=new xmlrpc_server( array( "discuss.addComment" => array("function" => "addcomment", "signature" => $addcomment_sig, "docstring" => $addcomment_doc), "discuss.getComments" => array("function" => "getcomments", "signature" => $getcomments_sig, "docstring" => $getcomments_doc)) ); ?> ec_api/xmlrpc/workspace.testPhpServer.fttb0000644000175000017500000001367310041103314020560 0ustar davidjdavidj ec_api/xmlrpc/introspect_demo.php0000644000175000017500000000343710041103312016766 0ustar davidjdavidj xmlrpc send($msg); if (!$r) { print "
ERROR: couldn't send message
\n"; return 0; } else { if (!$r->faultCode()) { return $r->value(); } else { print "Fault: "; print "Code: " . $r->faultCode() . " Reason '" .$r->faultString()."'
"; return 0; } } } $f=new xmlrpcmsg('system.listMethods'); $c=new xmlrpc_client("/demo/server.php", "xmlrpc.usefulinc.com", 80); $c->setDebug(0); $v=rpc_call($c, $f); print "

methods available at http://" . $c->server . ":" . $c->port . $c->path . "

\n"; if ($v) { for($i=0; $i<$v->arraysize(); $i++) { $mname=$v->arraymem($i); print "

" . $mname->scalarval() . "

\n"; $f=new xmlrpcmsg('system.methodHelp'); $f->addParam(new xmlrpcval($mname->scalarval(), "string")); $w=rpc_call($c, $f); if ($w) { $txt=$w->scalarval(); if ($txt!="") { print "

Documentation

${txt}

\n"; } else { print "

No documentation available.

\n"; } } $f=new xmlrpcmsg('system.methodSignature'); $f->addParam(new xmlrpcval($mname->scalarval(), "string")); $w=rpc_call($c, $f); if ($w) { print "

Signature

\n"; if ($w->kindOf()=="array") { for($j=0; $j<$w->arraysize(); $j++) { $x=$w->arraymem($j); $ret=$x->arraymem(0); print "" . $ret->scalarval() . " " . $mname->scalarval() ."("; if ($x->arraysize()>1) { for($k=1; $k<$x->arraysize(); $k++) { $y=$x->arraymem($k); print $y->scalarval(); if ($k<$x->arraysize()-1) { print ", "; } } } print ")
\n"; } } else { print "Signature unknown\n"; } print "

\n"; } } } ?> ec_api/xmlrpc/mail.php0000644000175000017500000000407510041103312014511 0ustar davidjdavidj xmlrpc addParam(new xmlrpcval($mailto)); $f->addParam(new xmlrpcval($mailsub)); $f->addParam(new xmlrpcval($mailmsg)); $f->addParam(new xmlrpcval($mailfrom)); $f->addParam(new xmlrpcval($mailcc)); $f->addParam(new xmlrpcval($mailbcc)); $f->addParam(new xmlrpcval("text/plain")); $c=new xmlrpc_client($XP, $XS, 80); $c->setDebug(1); $r=$c->send($f); if (!$r) { die("send to ${XS}${XP} port 80 failed: network OK?"); } $v=$r->value(); if (!$r->faultCode()) { print "Mail sent OK
\n"; } else { print ""; print "Mail send failed
\n"; print "Fault: "; print "Code: " . $r->faultCode() . " Reason '" .$r->faultString()."'
"; print "
"; } } ?>

Mail demo

This form enables you to send mail via an XML-RPC server. For public use only the "Userland" server will work (see Dave Winer's message). When you press send this page will reload showing you the XML-RPC message received from the host server, and the internal evaluation done by the PHP implementation.

You can find the source to this page here: mail.php
And the source to the UsefulInc mail-by-XML-RPC server (look for the 'mail_send' method): server.php

Server
Subject
To
Cc
Bcc

From

Body
ec_api/xmlrpc/phpunit.php0000644000175000017500000002474610041103312015265 0ustar davidjdavidj // OntoSys, Inc // // $Id: phpunit.php,v 1.1 2003/09/24 03:43:48 ozman Exp $ // Copyright (c) 2000 Fred Yankowski // Permission is hereby granted, free of charge, to any person // obtaining a copy of this software and associated documentation // files (the "Software"), to deal in the Software without // restriction, including without limitation the rights to use, copy, // modify, merge, publish, distribute, sublicense, and/or sell copies // of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS // BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // error_reporting(63); /* interface Test { function run(&$aTestResult); function countTestCases(); } */ function trace($msg) { return; print($msg); flush(); } class Exception { /* Emulate a Java exception, sort of... */ var $message; function Exception($message) { $this->message = $message; } function getMessage() { return $this->message; } } class Assert { function assert($boolean, $message=0) { if (! $boolean) $this->fail($message); } function assertEquals($expected, $actual, $message=0) { if ($expected != $actual) { $this->failNotEquals($expected, $actual, "expected", $message); } } function assertRegexp($regexp, $actual, $message=false) { if (! preg_match($regexp, $actual)) { $this->failNotEquals($regexp, $actual, "pattern", $message); } } function failNotEquals($expected, $actual, $expected_label, $message=0) { // Private function for reporting failure to match. $str = $message ? ($message . ' ') : ''; $str .= "($expected_label/actual)
"; $htmlExpected = htmlspecialchars($expected); $htmlActual = htmlspecialchars($actual); $str .= sprintf("
%s\n--------\n%s
", $htmlExpected, $htmlActual); $this->fail($str); } } class TestCase extends Assert /* implements Test */ { /* Defines context for running tests. Specific context -- such as instance variables, global variables, global state -- is defined by creating a subclass that specializes the setUp() and tearDown() methods. A specific test is defined by a subclass that specializes the runTest() method. */ var $fName; var $fResult; var $fExceptions = array(); function TestCase($name) { $this->fName = $name; } function run($testResult=0) { /* Run this single test, by calling the run() method of the TestResult object which will in turn call the runBare() method of this object. That complication allows the TestResult object to do various kinds of progress reporting as it invokes each test. Create/obtain a TestResult object if none was passed in. Note that if a TestResult object was passed in, it must be by reference. */ if (! $testResult) $testResult = $this->_createResult(); $this->fResult = $testResult; $testResult->run(&$this); $this->fResult = 0; return $testResult; } function countTestCases() { return 1; } function runTest() { $name = $this->name(); // Since isset($this->$name) is false, no way to run defensive checks $this->$name(); } function setUp() /* expect override */ { //print("TestCase::setUp()
\n"); } function tearDown() /* possible override */ { //print("TestCase::tearDown()
\n"); } //////////////////////////////////////////////////////////////// function _createResult() /* protected */ { /* override this to use specialized subclass of TestResult */ return new TestResult; } function fail($message=0) { //printf("TestCase::fail(%s)
\n", ($message) ? $message : ''); /* JUnit throws AssertionFailedError here. We just record the failure and carry on */ $this->fExceptions[] = new Exception(&$message); } function error($message) { /* report error that requires correction in the test script itself, or (heaven forbid) in this testing infrastructure */ printf('ERROR: ' . $message . '
'); $this->fResult->stop(); } function failed() { return count($this->fExceptions); } function getExceptions() { return $this->fExceptions; } function name() { return $this->fName; } function runBare() { $this->setup(); $this->runTest(); $this->tearDown(); } } class TestSuite /* implements Test */ { /* Compose a set of Tests (instances of TestCase or TestSuite), and run them all. */ var $fTests = array(); function TestSuite($classname=false) { if ($classname) { // Find all methods of the given class whose name starts with // "test" and add them to the test suite. We are just _barely_ // able to do this with PHP's limited introspection... Note // that PHP seems to store method names in lower case, and we // have to avoid the constructor function for the TestCase class // superclass. This will fail when $classname starts with // "Test" since that will have a constructor method that will // get matched below and then treated (incorrectly) as a test // method. So don't name any TestCase subclasses as "Test..."! if (floor(phpversion()) >= 4) { // PHP4 introspection, submitted by Dylan Kuhn $names = get_class_methods($classname); while (list($key, $method) = each($names)) { if (preg_match('/^test/', $method) && $method != "testcase") { $this->addTest(new $classname($method)); } } } else { $dummy = new $classname("dummy"); $names = (array) $dummy; while (list($key, $value) = each($names)) { $type = gettype($value); if ($type == "user function" && preg_match('/^test/', $key) && $key != "testcase") { $this->addTest(new $classname($key)); } } } } } function addTest($test) { /* Add TestCase or TestSuite to this TestSuite */ $this->fTests[] = $test; } function run(&$testResult) { /* Run all TestCases and TestSuites comprising this TestSuite, accumulating results in the given TestResult object. */ reset($this->fTests); while (list($na, $test) = each($this->fTests)) { if ($testResult->shouldStop()) break; $test->run(&$testResult); } } function countTestCases() { /* Number of TestCases comprising this TestSuite (including those in any constituent TestSuites) */ $count = 0; reset($fTests); while (list($na, $test_case) = each($this->fTests)) { $count += $test_case->countTestCases(); } return $count; } } class TestFailure { /* Record failure of a single TestCase, associating it with the exception(s) that occurred */ var $fFailedTestName; var $fExceptions; function TestFailure(&$test, &$exceptions) { $this->fFailedTestName = $test->name(); $this->fExceptions = $exceptions; } function getExceptions() { return $this->fExceptions; } function getTestName() { return $this->fFailedTestName; } } class TestResult { /* Collect the results of running a set of TestCases. */ var $fFailures = array(); var $fRunTests = 0; var $fStop = false; function TestResult() { } function _endTest($test) /* protected */ { /* specialize this for end-of-test action, such as progress reports */ } function getFailures() { return $this->fFailures; } function run($test) { /* Run a single TestCase in the context of this TestResult */ $this->_startTest($test); $this->fRunTests++; $test->runBare(); /* this is where JUnit would catch AssertionFailedError */ $exceptions = $test->getExceptions(); if ($exceptions) $this->fFailures[] = new TestFailure(&$test, &$exceptions); $this->_endTest($test); } function countTests() { return $this->fRunTests; } function shouldStop() { return $this->fStop; } function _startTest($test) /* protected */ { /* specialize this for start-of-test actions */ } function stop() { /* set indication that the test sequence should halt */ $fStop = true; } function countFailures() { return count($this->fFailures); } } class TextTestResult extends TestResult { /* Specialize TestResult to produce text/html report */ function TextTestResult() { $this->TestResult(); // call superclass constructor } function report() { /* report result of test run */ $nRun = $this->countTests(); $nFailures = $this->countFailures(); printf("

%s test%s run
", $nRun, ($nRun == 1) ? '' : 's'); printf("%s failure%s.
\n", $nFailures, ($nFailures == 1) ? '' : 's'); if ($nFailures == 0) return; print("

    \n"); $failures = $this->getFailures(); while (list($i, $failure) = each($failures)) { $failedTestName = $failure->getTestName(); printf("
  1. %s\n", $failedTestName); $exceptions = $failure->getExceptions(); print("
      "); while (list($na, $exception) = each($exceptions)) printf("
    • %s\n", $exception->getMessage()); print("
    "); } print("
\n"); } function _startTest($test) { printf("%s ", $test->name()); flush(); } function _endTest($test) { $outcome = $test->failed() ? "FAIL" : "ok"; printf("$outcome
\n"); flush(); } } class TestRunner { /* Run a suite of tests and report results. */ function run($suite) { $result = new TextTestResult; $suite->run($result); $result->report(); } } ?> ec_api/xmlrpc/rsakey.pem0000644000175000017500000000075510041103312015060 0ustar davidjdavidj-----BEGIN RSA PRIVATE KEY----- MIIBOgIBAAJBAM12w6/J20HMj0V9VC24xPFQG9RKSDt8vmviM+tnc1BgCrzPyF1v 3/rWGoWDjkJrE9WFOeqIjJHeEWWT4uKq2ZkCAwEAAQJAZZYJ7Nld+et9DvuHak/H uBRGnjDYA+mKcObXitWMUzk2ZodL8UoCP1J9kKqV8Zp/l2cBZkLo0aWTN94sWkHy rQIhAOhxWxRXSZ4kArIQqZnDG9JgtOAeaaFso/zpxIHpN6OrAiEA4klzl+rUc32/ 7SDcJYa9j5vehp1jCTnkN+n0rujTM8sCIAGwMRUovSQk5tAcRt8TB7SzdxzZm7LM czR3DjJTW1AZAiEAlYN+svPgJ+cAdwdtLgZXHZoZb8xx8Vik6CTXHPKNCf0CIBQL zF4Qp8/C+gjsXtEZJvhxY7i1luHn6iNwNnE932r3 -----END RSA PRIVATE KEY----- ec_api/xmlrpc/server.php0000644000175000017500000005362610041103312015103 0ustar davidjdavidjgetParam(0); // if it's there and the correct type if (isset($sno) && ($sno->scalartyp()=="int")) { // extract the value of the state number $snv=$sno->scalarval(); // look it up in our array (zero-based) if (isset($stateNames[$snv-1])) { $sname=$stateNames[$snv-1]; } else { // not, there so complain $err="I don't have a state for the index '" . $snv . "'"; } } else { // parameter mismatch, complain $err="One integer parameter required"; } // if we generated an error, create an error return response if ($err) { return new xmlrpcresp(0, $xmlrpcerruser, $err); } else { // otherwise, we create the right response // with the state name return new xmlrpcresp(new xmlrpcval($sname)); } } $addtwo_sig=array(array($xmlrpcInt, $xmlrpcInt, $xmlrpcInt)); $addtwo_doc='Add two integers together and return the result'; function addtwo($m) { $s=$m->getParam(0); $t=$m->getParam(1); return new xmlrpcresp(new xmlrpcval($s->scalarval()+$t->scalarval(), "int")); } $addtwodouble_sig=array(array($xmlrpcDouble, $xmlrpcDouble, $xmlrpcDouble)); $addtwodouble_doc='Add two doubles together and return the result'; function addtwodouble($m) { $s=$m->getParam(0); $t=$m->getParam(1); return new xmlrpcresp(new xmlrpcval($s->scalarval()+$t->scalarval(), "double")); } $stringecho_sig=array(array($xmlrpcString, $xmlrpcString)); $stringecho_doc='Accepts a string parameter, returns the string.'; function stringecho($m) { // just sends back a string $s=$m->getParam(0); return new xmlrpcresp(new xmlrpcval($s->scalarval())); } $echoback_sig=array(array($xmlrpcString, $xmlrpcString)); $echoback_doc='Accepts a string parameter, returns the entire incoming payload'; function echoback($m) { // just sends back a string with what i got // send to me, just escaped, that's all // // $m is an incoming message $s="I got the following message:\n" . $m->serialize(); return new xmlrpcresp(new xmlrpcval($s)); } $echosixtyfour_sig=array(array($xmlrpcString, $xmlrpcBase64)); $echosixtyfour_doc='Accepts a base64 parameter and returns it decoded as a string'; function echosixtyfour($m) { // accepts an encoded value, but sends it back // as a normal string. this is to test base64 encoding // is working as expected $incoming=$m->getParam(0); return new xmlrpcresp(new xmlrpcval($incoming->scalarval(), "string")); } $bitflipper_sig=array(array($xmlrpcArray, $xmlrpcArray)); $bitflipper_doc='Accepts an array of booleans, and returns them inverted'; function bitflipper($m) { global $xmlrpcArray; $v=$m->getParam(0); $sz=$v->arraysize(); $rv=new xmlrpcval(array(), $xmlrpcArray); for($j=0; $j<$sz; $j++) { $b=$v->arraymem($j); if ($b->scalarval()) { $rv->addScalar(false, "boolean"); } else { $rv->addScalar(true, "boolean"); } } return new xmlrpcresp($rv); } // Sorting demo // // send me an array of structs thus: // // Dave 35 // Edd 45 // Fred 23 // Barney 37 // // and I'll return it to you in sorted order function agesorter_compare($a, $b) { global $agesorter_arr; // don't even ask me _why_ these come padded with // hyphens, I couldn't tell you :p $a=ereg_replace("-", "", $a); $b=ereg_replace("-", "", $b); if ($agesorter_arr[$a]==$agesorter[$b]) return 0; return ($agesorter_arr[$a] > $agesorter_arr[$b]) ? -1 : 1; } $agesorter_sig=array(array($xmlrpcArray, $xmlrpcArray)); $agesorter_doc='Send this method an array of [string, int] structs, eg:
 Dave   35
 Edd    45
 Fred   23
 Barney 37
And the array will be returned with the entries sorted by their numbers. '; function agesorter($m) { global $agesorter_arr, $xmlrpcerruser, $s; xmlrpc_debugmsg("Entering 'agesorter'"); // get the parameter $sno=$m->getParam(0); // error string for [if|when] things go wrong $err=""; // create the output value $v=new xmlrpcval(); $agar=array(); if (isset($sno) && $sno->kindOf()=="array") { $max=$sno->arraysize(); // TODO: create debug method to print can work once more // print "\n"; for($i=0; $i<$max; $i++) { $rec=$sno->arraymem($i); if ($rec->kindOf()!="struct") { $err="Found non-struct in array at element $i"; break; } // extract name and age from struct $n=$rec->structmem("name"); $a=$rec->structmem("age"); // $n and $a are xmlrpcvals, // so get the scalarval from them $agar[$n->scalarval()]=$a->scalarval(); } $agesorter_arr=$agar; // hack, must make global as uksort() won't // allow us to pass any other auxilliary information uksort($agesorter_arr, agesorter_compare); $outAr=array(); while (list( $key, $val ) = each( $agesorter_arr ) ) { // recreate each struct element $outAr[]=new xmlrpcval(array("name" => new xmlrpcval($key), "age" => new xmlrpcval($val, "int")), "struct"); } // add this array to the output value $v->addArray($outAr); } else { $err="Must be one parameter, an array of structs"; } if ($err) { return new xmlrpcresp(0, $xmlrpcerruser, $err); } else { return new xmlrpcresp($v); } } // signature and instructions, place these in the dispatch // map $mail_send_sig=array(array($xmlrpcBoolean, $xmlrpcString, $xmlrpcString, $xmlrpcString, $xmlrpcString, $xmlrpcString, $xmlrpcString, $xmlrpcString)); $mail_send_doc='mail.send(recipient, subject, text, sender, cc, bcc, mimetype)
recipient, cc, and bcc are strings, comma-separated lists of email addresses, as described above.
subject is a string, the subject of the message.
sender is a string, it\'s the email address of the person sending the message. This string can not be a comma-separated list, it must contain a single email address only. text is a string, it contains the body of the message.
mimetype, a string, is a standard MIME type, for example, text/plain. '; // WARNING; this functionality depends on the sendmail -t option // it may not work with Windows machines properly; particularly // the Bcc option. Sneak on your friends at your own risk! function mail_send($m) { global $xmlrpcerruser, $xmlrpcBoolean; $err=""; $mTo=$m->getParam(0); $mSub=$m->getParam(1); $mBody=$m->getParam(2); $mFrom=$m->getParam(3); $mCc=$m->getParam(4); $mBcc=$m->getParam(5); $mMime=$m->getParam(6); if ($mTo->scalarval()=="") $err="Error, no 'To' field specified"; if ($mFrom->scalarval()=="") $err="Error, no 'From' field specified"; $msghdr="From: " . $mFrom->scalarval() . "\n"; $msghdr.="To: ". $mTo->scalarval() . "\n"; if ($mCc->scalarval()!="") $msghdr.="Cc: " . $mCc->scalarval(). "\n"; if ($mBcc->scalarval()!="") $msghdr.="Bcc: " . $mBcc->scalarval(). "\n"; if ($mMime->scalarval()!="") $msghdr.="Content-type: " . $mMime->scalarval() . "\n"; $msghdr.="X-Mailer: XML-RPC for PHP mailer 1.0"; if ($err=="") { if (!mail("", $mSub->scalarval(), $mBody->scalarval(), $msghdr)) { $err="Error, could not send the mail."; } } if ($err) { return new xmlrpcresp(0, $xmlrpcerruser, $err); } else { return new xmlrpcresp(new xmlrpcval("true", $xmlrpcBoolean)); } } $v1_arrayOfStructs_sig=array(array($xmlrpcInt, $xmlrpcArray)); $v1_arrayOfStructs_doc='This handler takes a single parameter, an array of structs, each of which contains at least three elements named moe, larry and curly, all s. Your handler must add all the struct elements named curly and return the result.'; function v1_arrayOfStructs($m) { $sno=$m->getParam(0); $numcurly=0; for($i=0; $i<$sno->arraysize(); $i++) { $str=$sno->arraymem($i); $str->structreset(); while(list($key,$val)=$str->structeach()) if ($key=="curly") $numcurly+=$val->scalarval(); } return new xmlrpcresp(new xmlrpcval($numcurly, "int")); } $v1_easyStruct_sig=array(array($xmlrpcInt, $xmlrpcStruct)); $v1_easyStruct_doc='This handler takes a single parameter, a struct, containing at least three elements named moe, larry and curly, all <i4>s. Your handler must add the three numbers and return the result.'; function v1_easyStruct($m) { $sno=$m->getParam(0); $moe=$sno->structmem("moe"); $larry=$sno->structmem("larry"); $curly=$sno->structmem("curly"); $num=$moe->scalarval()+ $larry->scalarval()+ $curly->scalarval(); return new xmlrpcresp(new xmlrpcval($num, "int")); } $v1_echoStruct_sig=array(array($xmlrpcStruct, $xmlrpcStruct)); $v1_echoStruct_doc='This handler takes a single parameter, a struct. Your handler must return the struct.'; function v1_echoStruct($m) { $sno=$m->getParam(0); return new xmlrpcresp($sno); } $v1_manyTypes_sig=array(array($xmlrpcArray, $xmlrpcInt, $xmlrpcBoolean, $xmlrpcString, $xmlrpcDouble, $xmlrpcDateTime, $xmlrpcBase64)); $v1_manyTypes_doc='This handler takes six parameters, and returns an array containing all the parameters.'; function v1_manyTypes($m) { return new xmlrpcresp(new xmlrpcval(array( $m->getParam(0), $m->getParam(1), $m->getParam(2), $m->getParam(3), $m->getParam(4), $m->getParam(5)), "array")); } $v1_moderateSizeArrayCheck_sig=array(array($xmlrpcString, $xmlrpcArray)); $v1_moderateSizeArrayCheck_doc='This handler takes a single parameter, which is an array containing between 100 and 200 elements. Each of the items is a string, your handler must return a string containing the concatenated text of the first and last elements.'; function v1_moderateSizeArrayCheck($m) { $ar=$m->getParam(0); $sz=$ar->arraysize(); $first=$ar->arraymem(0); $last=$ar->arraymem($sz-1); return new xmlrpcresp(new xmlrpcval($first->scalarval() . $last->scalarval(), "string")); } $v1_simpleStructReturn_sig=array(array($xmlrpcStruct, $xmlrpcInt)); $v1_simpleStructReturn_doc='This handler takes one parameter, and returns a struct containing three elements, times10, times100 and times1000, the result of multiplying the number by 10, 100 and 1000.'; function v1_simpleStructReturn($m) { $sno=$m->getParam(0); $v=$sno->scalarval(); return new xmlrpcresp(new xmlrpcval(array( "times10" => new xmlrpcval($v*10, "int"), "times100" => new xmlrpcval($v*100, "int"), "times1000" => new xmlrpcval($v*1000, "int")), "struct")); } $v1_nestedStruct_sig=array(array($xmlrpcInt, $xmlrpcStruct)); $v1_nestedStruct_doc='This handler takes a single parameter, a struct, that models a daily calendar. At the top level, there is one struct for each year. Each year is broken down into months, and months into days. Most of the days are empty in the struct you receive, but the entry for April 1, 2000 contains a least three elements named moe, larry and curly, all <i4>s. Your handler must add the three numbers and return the result.'; function v1_nestedStruct($m) { $sno=$m->getParam(0); $twoK=$sno->structmem("2000"); $april=$twoK->structmem("04"); $fools=$april->structmem("01"); $curly=$fools->structmem("curly"); $larry=$fools->structmem("larry"); $moe=$fools->structmem("moe"); return new xmlrpcresp(new xmlrpcval($curly->scalarval()+ $larry->scalarval()+ $moe->scalarval(), "int")); } $v1_countTheEntities_sig=array(array($xmlrpcStruct, $xmlrpcString)); $v1_countTheEntities_doc='This handler takes a single parameter, a string, that contains any number of predefined entities, namely <, >, & \' and ".
Your handler must return a struct that contains five fields, all numbers: ctLeftAngleBrackets, ctRightAngleBrackets, ctAmpersands, ctApostrophes, ctQuotes.'; function v1_countTheEntities($m) { $sno=$m->getParam(0); $str=$sno->scalarval(); $gt=0; $lt=0; $ap=0; $qu=0; $amp=0; for($i=0; $i": $gt++; break; case "<": $lt++; break; case "\"": $qu++; break; case "'": $ap++; break; case "&": $amp++; break; default: break; } } return new xmlrpcresp(new xmlrpcval(array("ctLeftAngleBrackets" => new xmlrpcval($lt, "int"), "ctRightAngleBrackets" => new xmlrpcval($gt, "int"), "ctAmpersands" => new xmlrpcval($amp, "int"), "ctApostrophes" => new xmlrpcval($ap, "int"), "ctQuotes" => new xmlrpcval($qu, "int")), "struct")); } // trivial interop tests // http://www.xmlrpc.com/stories/storyReader$1636 $i_echoString_sig=array(array($xmlrpcString, $xmlrpcString)); $i_echoString_doc="Echoes string."; $i_echoStringArray_sig=array(array($xmlrpcArray, $xmlrpcArray)); $i_echoStringArray_doc="Echoes string array."; $i_echoInteger_sig=array(array($xmlrpcInt, $xmlrpcInt)); $i_echoInteger_doc="Echoes integer."; $i_echoIntegerArray_sig=array(array($xmlrpcArray, $xmlrpcArray)); $i_echoIntegerArray_doc="Echoes integer array."; $i_echoFloat_sig=array(array($xmlrpcDouble, $xmlrpcDouble)); $i_echoFloat_doc="Echoes float."; $i_echoFloatArray_sig=array(array($xmlrpcArray, $xmlrpcArray)); $i_echoFloatArray_doc="Echoes float array."; $i_echoStruct_sig=array(array($xmlrpcStruct, $xmlrpcStruct)); $i_echoStruct_doc="Echoes struct."; $i_echoStructArray_sig=array(array($xmlrpcArray, $xmlrpcArray)); $i_echoStructArray_doc="Echoes struct array."; $i_echoValue_doc="Echoes any value back."; $i_echoBase64_sig=array(array($xmlrpcBase64, $xmlrpcBase64)); $i_echoBase64_doc="Echoes base64."; $i_echoDate_sig=array(array($xmlrpcDateTime, $xmlrpcDateTime)); $i_echoDate_doc="Echoes dateTime."; function i_echoParam($m) { $s=$m->getParam(0); return new xmlrpcresp($s); } function i_echoString($m) { return i_echoParam($m); } function i_echoInteger($m) { return i_echoParam($m); } function i_echoFloat($m) { return i_echoParam($m); } function i_echoStruct($m) { return i_echoParam($m); } function i_echoStringArray($m) { return i_echoParam($m); } function i_echoIntegerArray($m) { return i_echoParam($m); } function i_echoFloatArray($m) { return i_echoParam($m); } function i_echoStructArray($m) { return i_echoParam($m); } function i_echoValue($m) { return i_echoParam($m); } function i_echoBase64($m) { return i_echoParam($m); } function i_echoDate($m) { return i_echoParam($m); } $i_whichToolkit_doc="Returns a struct containing the following strings: toolkitDocsUrl, toolkitName, toolkitVersion, toolkitOperatingSystem."; function i_whichToolkit($m) { global $xmlrpcName, $xmlrpcVersion,$SERVER_SOFTWARE, $xmlrpcStruct; $ret=array( "toolkitDocsUrl" => "http://xmlrpc.usefulinc.com/php.html", "toolkitName" => $xmlrpcName, "toolkitVersion" => $xmlrpcVersion, "toolkitOperatingSystem" => $SERVER_SOFTWARE); return new xmlrpcresp ( xmlrpc_encode($ret)); } $s=new xmlrpc_server( array( "examples.getStateName" => array("function" => "findstate", "signature" => $findstate_sig, "docstring" => $findstate_doc), "examples.sortByAge" => array("function" => "agesorter", "signature" => $agesorter_sig, "docstring" => $agesorter_doc), "examples.addtwo" => array("function" => "addtwo", "signature" => $addtwo_sig, "docstring" => $addtwo_doc), "examples.addtwodouble" => array("function" => "addtwodouble", "signature" => $addtwodouble_sig, "docstring" => $addtwodouble_doc), "examples.stringecho" => array("function" => "stringecho", "signature" => $stringecho_sig, "docstring" => $stringecho_doc), "examples.echo" => array("function" => "echoback", "signature" => $echoback_sig, "docstring" => $echoback_doc), "examples.decode64" => array("function" => "echosixtyfour", "signature" => $echosixtyfour_sig, "docstring" => $echosixtyfour_doc), "examples.invertBooleans" => array("function" => "bitflipper", "signature" => $bitflipper_sig, "docstring" => $bitflipper_doc), "mail.send" => array("function" => "mail_send", "signature" => $mail_send_sig, "docstring" => $mail_send_doc), "validator1.arrayOfStructsTest" => array("function" => "v1_arrayOfStructs", "signature" => $v1_arrayOfStructs_sig, "docstring" => $v1_arrayOfStructs_doc), "validator1.easyStructTest" => array("function" => "v1_easyStruct", "signature" => $v1_easyStruct_sig, "docstring" => $v1_easyStruct_doc), "validator1.echoStructTest" => array("function" => "v1_echoStruct", "signature" => $v1_echoStruct_sig, "docstring" => $v1_echoStruct_doc), "validator1.manyTypesTest" => array("function" => "v1_manyTypes", "signature" => $v1_manyTypes_sig, "docstring" => $v1_manyTypes_doc), "validator1.moderateSizeArrayCheck" => array("function" => "v1_moderateSizeArrayCheck", "signature" => $v1_moderateSizeArrayCheck_sig, "docstring" => $v1_moderateSizeArrayCheck_doc), "validator1.simpleStructReturnTest" => array("function" => "v1_simpleStructReturn", "signature" => $v1_simpleStructReturn_sig, "docstring" => $v1_simpleStructReturn_doc), "validator1.nestedStructTest" => array("function" => "v1_nestedStruct", "signature" => $v1_nestedStruct_sig, "docstring" => $v1_nestedStruct_doc), "validator1.countTheEntities" => array("function" => "v1_countTheEntities", "signature" => $v1_countTheEntities_sig, "docstring" => $v1_countTheEntities_doc), "interopEchoTests.echoString" => array("function" => "i_echoString", "signature" => $i_echoString_sig, "docstring" => $i_echoString_doc), "interopEchoTests.echoStringArray" => array("function" => "i_echoStringArray", "signature" => $i_echoStringArray_sig, "docstring" => $i_echoStringArray_doc), "interopEchoTests.echoInteger" => array("function" => "i_echoInteger", "signature" => $i_echoInteger_sig, "docstring" => $i_echoInteger_doc), "interopEchoTests.echoIntegerArray" => array("function" => "i_echoIntegerArray", "signature" => $i_echoIntegerArray_sig, "docstring" => $i_echoIntegerArray_doc), "interopEchoTests.echoFloat" => array("function" => "i_echoFloat", "signature" => $i_echoFloat_sig, "docstring" => $i_echoFloat_doc), "interopEchoTests.echoFloatArray" => array("function" => "i_echoFloatArray", "signature" => $i_echoFloatArray_sig, "docstring" => $i_echoFloatArray_doc), "interopEchoTests.echoStruct" => array("function" => "i_echoStruct", "signature" => $i_echoStruct_sig, "docstring" => $i_echoStruct_doc), "interopEchoTests.echoStructArray" => array("function" => "i_echoStructArray", "signature" => $i_echoStructArray_sig, "docstring" => $i_echoStructArray_doc), "interopEchoTests.echoValue" => array("function" => "i_echoValue", // no sig as takes anytype "docstring" => $i_echoValue_doc), "interopEchoTests.echoBase64" => array("function" => "i_echoBase64", "signature" => $i_echoBase64_sig, "docstring" => $i_echoBase64_doc), "interopEchoTests.echoDate" => array("function" => "i_echoDate", "signature" => $i_echoDate_sig, "docstring" => $i_echoDate_doc), "interopEchoTests.whichToolkit" => array("function" => "i_whichToolkit", // no sig as no parameters "docstring" => $i_whichToolkit_doc), )); // that should do all we need! ?> ec_api/xmlrpc/test.pl0000644000175000017500000000247010041103313014370 0ustar davidjdavidj#!/usr/local/bin/perl use Frontier::Client; my $serverURL='http://xmlrpc.heddley.com/server.php'; # try the simplest example my $client = Frontier::Client->new( 'url' => $serverURL, 'debug' => 0, 'encoding' => 'iso-8859-1' ); my $resp = $client->call("examples.getStateName", 32); print "Got '${resp}'\n"; # now send a mail to nobody in particular $resp = $client->call("mail.send", ("edd", "Test", "Bonjour. Je m'appelle Grard. Maana. ", "freddy", "", "", 'text/plain; charset="iso-8859-1"')); if ($resp->value()) { print "Mail sent OK.\n"; } else { print "Error sending mail.\n"; } # test echoing of characters works fine $resp = $client->call("examples.echo", 'Three "blind" mice - ' . "See 'how' they run"); print $resp . "\n"; # test name and age example. this exercises structs and arrays $resp = $client->call("examples.sortByAge", [ { 'name' => 'Dave', 'age' => 35}, { 'name' => 'Edd', 'age' => 45 }, { 'name' => 'Fred', 'age' => 23 }, { 'name' => 'Barney', 'age' => 36 } ] ); my $e; foreach $e (@$resp) { print $$e{'name'} . ", " . $$e{'age'} . "\n"; } # test base64 $resp = $client->call("examples.decode64", $client->base64("TWFyeSBoYWQgYSBsaXR0bGUgbGFtYiBTaGUgd" . "GllZCBpdCB0byBhIHB5bG9u")); print $resp . "\n"; ec_api/xmlrpc/test.py0000644000175000017500000000175110041103313014406 0ustar davidjdavidj#!/usr/local/bin/python from xmlrpclib import * import sys server = Server("http://xmlrpc.heddley.com/server.php") try: print "Got '" + server.examples.getStateName(32) + "'" r = server.mail.send("edd", "Test", "Bonjour. Je m'appelle Grard. Maana. ", "freddy", "", "", 'text/plain; charset="iso-8859-1"') if r: print "Mail sent OK" else: print "Error sending mail" r = server.examples.echo('Three "blind" mice - ' + "See 'how' they run") print r # name/age example. this exercises structs and arrays a = [ {'name': 'Dave', 'age': 35}, {'name': 'Edd', 'age': 45 }, {'name': 'Fred', 'age': 23}, {'name': 'Barney', 'age': 36 }] r = server.examples.sortByAge(a) print r # test base 64 b = Binary("Mary had a little lamb She tied it to a pylon") b.encode(sys.stdout) r = server.examples.decode64(b) print r except Error, v: print "XML-RPC Error:",v ec_api/xmlrpc/testsuite.php0000644000175000017500000002324710041103313015623 0ustar davidjdavidjTestCase($name); } function setUp() { global $DEBUG, $LOCALSERVER, $URI; $this->client=new xmlrpc_client($URI, $LOCALSERVER, 80); if ($DEBUG) { $this->client->setDebug(1); } } function stringTest() { $sendstring="here are some \"entities\" < > & and " . "here's a dollar sign \$pretendvarname and a backslash too " . chr(92) . " - isn't that great? \\\"hackery\\\" at it's best " . " also don't want to miss out on \$item[0]"; $f=new xmlrpcmsg('examples.stringecho', array( new xmlrpcval($sendstring, 'string') )); $r=$this->client->send($f); $v=$r->value(); $this->assertEquals($sendstring, $v->scalarval()); } function addingDoublesTest() { // note that rounding errors mean i // keep precision to sensible levels here ;-) $a=12.13; $b=-23.98; $f=new xmlrpcmsg('examples.addtwodouble',array( new xmlrpcval($a, 'double'), new xmlrpcval($b, 'double') )); $r=$this->client->send($f); $v=$r->value(); $this->assertEquals($a+$b,$v->scalarval()); } function addingTest() { $f=new xmlrpcmsg('examples.addtwo',array( new xmlrpcval(12, 'int'), new xmlrpcval(-23, 'int') )); $r=$this->client->send($f); $v=$r->value(); $this->assertEquals(12-23, $v->scalarval()); } function invalidNumber() { $f=new xmlrpcmsg('examples.addtwo',array( new xmlrpcval('fred', 'int'), new xmlrpcval("\"; exec('ls')", 'int') )); $r=$this->client->send($f); $v=$r->value(); // TODO: a fault condition should be generated here // by the server, which we pick up on $this->assertEquals(0, $v->scalarval()); } function booleanTest() { $f=new xmlrpcmsg('examples.invertBooleans', array( new xmlrpcval(array( new xmlrpcval(true, 'boolean'), new xmlrpcval(false, 'boolean'), new xmlrpcval(1, 'boolean'), new xmlrpcval(0, 'boolean'), new xmlrpcval('true', 'boolean'), new xmlrpcval('false', 'boolean') ), 'array' ))); $answer='010101'; $r=$this->client->send($f); $this->assert(!$r->faultCode()); $v=$r->value(); $sz=$v->arraysize(); $got=''; for($i=0; $i<$sz; $i++) { $b=$v->arraymem($i); if($b->scalarval()) { $got.='1'; } else { $got.='0'; } } $this->assertEquals($answer, $got); } function base64Test() { $sendstring='Mary had a little lamb, Whose fleece was white as snow, And everywhere that Mary went the lamb was sure to go. Mary had a little lamb She tied it to a pylon Ten thousand volts went down its back And turned it into nylon'; $f=new xmlrpcmsg('examples.decode64',array( new xmlrpcval($sendstring, 'base64') )); $r=$this->client->send($f); $v=$r->value(); $this->assertEquals($sendstring, $v->scalarval()); } function countEntities() { $sendstring = "h'fd>onc>>l>>rw&bpu>q>eclient->send($f); $v = $r->value(); $got = ''; $expected = '37210'; $expect_array = array('ctLeftAngleBrackets','ctRightAngleBrackets','ctAmpersands','ctApostrophes','ctQuotes'); while(list(,$val) = each($expect_array)) { $b = $v->structmem($val); $got .= $b->me['int']; } $this->assertEquals($expected, $got); } function _multicall_msg($method, $params) { $struct['methodName'] = new xmlrpcval($method, 'string'); $struct['params'] = new xmlrpcval($params, 'array'); return new xmlrpcval($struct, 'struct'); } function testServerMulticall() { // We manually construct a system.multicall() call to ensure // that the server supports it. // Based on http://xmlrpc-c.sourceforge.net/hacks/test_multicall.py $good1 = $this->_multicall_msg( 'system.methodHelp', array(xmlrpc_encode('system.listMethods'))); $bad = $this->_multicall_msg( 'test.nosuch', array(xmlrpc_encode(1), xmlrpc_encode(2))); $recursive = $this->_multicall_msg( 'system.multicall', array(new xmlrpcval(array(), 'array'))); $good2 = $this->_multicall_msg( 'system.methodSignature', array(xmlrpc_encode('system.listMethods'))); $arg = new xmlrpcval( array($good1, $bad, $recursive, $good2), 'array'); $f = new xmlrpcmsg('system.multicall', array($arg)); $r = $this->client->send($f); $this->assert($r->faultCode() == 0, "fault from system.multicall"); $v = $r->value(); $this->assert($v->arraysize() == 4, "bad number of return values"); $r1 = $v->arraymem(0); $this->assert( $r1->kindOf() == 'array' && $r1->arraysize() == 1, "did not get array of size 1 from good1"); $r2 = $v->arraymem(1); $this->assert( $r2->kindOf() == 'struct', "no fault from bad"); $r3 = $v->arraymem(2); $this->assert( $r3->kindOf() == 'struct', "recursive system.multicall did not fail"); $r4 = $v->arraymem(3); $this->assert( $r4->kindOf() == 'array' && $r4->arraysize() == 1, "did not get array of size 1 from good2"); } function testClientMulticall() { $good1 = new xmlrpcmsg('system.methodHelp', array(xmlrpc_encode('system.listMethods'))); $bad = new xmlrpcmsg('test.nosuch', array(xmlrpc_encode(1), xmlrpc_encode(2))); $recursive = new xmlrpcmsg('system.multicall', array(new xmlrpcval(array(), 'array'))); $good2 = new xmlrpcmsg('system.methodSignature', array(xmlrpc_encode('system.listMethods'))); $r = $this->client->send(array($good1, $bad, $recursive, $good2)); $this->assert(count($r) == 4, "wrong number of return values"); $this->assert($r[0]->faultCode() == 0, "fault from good1"); $val = $r[0]->value(); $this->assert( $val->kindOf() == 'scalar' && $val->scalartyp() == 'string', "good1 did not return string"); $this->assert($r[1]->faultCode() != 0, "no fault from bad"); $this->assert($r[2]->faultCode() != 0, "no fault from recursive system.multicall"); $this->assert($r[3]->faultCode() == 0, "fault from good2"); $val = $r[3]->value(); $this->assert($val->kindOf() == 'array', "good2 did not return array"); // This is the only assert in this test which should fail // if the test server does not support system.multicall. $this->assert($this->client->no_multicall == false, "server does not support system.multicall"); } } class TestFileCases extends TestCase { function TestFileCases($name, $base='') { if(!$base) { global $PATH; $base = $PATH; } $this->TestCase($name); $this->root=$base; } function stringBug () { $m=new xmlrpcmsg('dummy'); $fp=fopen($this->root.'/bug_string.xml', 'r'); $r=$m->parseResponseFile($fp); $v=$r->value(); fclose($fp); $s=$v->structmem('sessionID'); $this->assertEquals('S300510007I', $s->scalarval()); } function whiteSpace () { $m=new xmlrpcmsg('dummy'); $fp=fopen($this->root.'/bug_whitespace.xml', 'r'); $r=$m->parseResponseFile($fp); $v=$r->value(); fclose($fp); $s=$v->structmem('content'); $this->assertEquals("hello world. 2 newlines follow\n\n\nand there they were.", $s->scalarval()); } } class TestInvalidHost extends TestCase { function TestInvalidHost($name) { $this->TestCase($name); } function setUp() { global $DEBUG,$LOCALSERVER; $this->client=new xmlrpc_client('/NOTEXIST.php', $LOCALSERVER, 80); if($DEBUG) { $this->client->setDebug(1); } } function test404() { $f=new xmlrpcmsg('examples.echo',array( new xmlrpcval('hello', 'string') )); $r=$this->client->send($f); $this->assertEquals(5, $r->faultCode()); } } class TestHTTPSConnection extends TestCase { function TestInvalidHost($name) { $this->TestCase($name); } function setUp() { global $DEBUG,$HTTPSSERVER,$URI; $this->client=new xmlrpc_client($URI,$HTTPSSERVER); //$this->client->setCertificate('/var/www/xmlrpc/rsakey.pem', // 'test'); if ($DEBUG || 1) { $this->client->setDebug(1); } } function addingTest() { $f=new xmlrpcmsg('examples.getStateName',array( new xmlrpcval(23, 'int') )); $r=$this->client->send($f, 180, 'https'); if($r->faultCode()) { // create dummy value so assert fails $v=new xmlrpcval('SSL send failed.'); print '
Fault: ' . $r->faultString() . '
'; } else { $v=$r->value(); } $this->assertEquals('Michigan', $v->scalarval()); } } $suite->addTest(new TestLocalhost('stringTest')); $suite->addTest(new TestLocalhost('addingTest')); $suite->addTest(new TestLocalhost('addingDoublesTest')); $suite->addTest(new TestLocalhost('invalidNumber')); $suite->addTest(new TestLocalhost('booleanTest')); $suite->addTest(new TestLocalhost('countEntities')); $suite->addTest(new TestLocalhost('base64Test')); $suite->addTest(new TestLocalhost('testServerMulticall')); $suite->addTest(new TestLocalhost('testClientMulticall')); $suite->addTest(new TestInvalidHost('test404')); $suite->addTest(new TestFileCases('stringBug')); $suite->addTest(new TestFileCases('whiteSpace')); $suite->addTest(new TestHTTPSConnection('addingTest')); $title = 'XML-RPC Unit Tests'; ?> <?php echo $title; ?>

Note, tests beginning with 'f_' should fail.

run($suite); ?> ec_api/xmlrpc/vardemo.php0000644000175000017500000000457510041103313015232 0ustar davidjdavidj xmlrpc Testing value serialization\n"; $v=new xmlrpcval(23, "int"); print "

" . htmlentities($v->serialize()) . "
"; $v=new xmlrpcval("What are you saying? >> << &&"); print "
" . htmlentities($v->serialize()) . "
"; $v=new xmlrpcval(array(new xmlrpcval("ABCDEFHIJ"), new xmlrpcval(1234, 'int'), new xmlrpcval(1, 'boolean')), "array"); print "
" . htmlentities($v->serialize()) . "
"; $v=new xmlrpcval(array("thearray" => new xmlrpcval(array(new xmlrpcval("ABCDEFHIJ"), new xmlrpcval(1234, 'int'), new xmlrpcval(1, 'boolean'), new xmlrpcval(0, 'boolean'), new xmlrpcval(true, 'boolean'), new xmlrpcval(false, 'boolean')), "array"), "theint" => new xmlrpcval(23, 'int'), "thestring" => new xmlrpcval("foobarwhizz"), "thestruct" => new xmlrpcval(array("one" => new xmlrpcval(1, 'int'), "two" => new xmlrpcval(2, 'int')), "struct") ), "struct"); print "
" . htmlentities($v->serialize()) . "
"; $w=new xmlrpcval(array($v, new xmlrpcval("That was the struct!")), "array"); print "
" . htmlentities($w->serialize()) . "
"; $w=new xmlrpcval("Mary had a little lamb, Whose fleece was white as snow, And everywhere that Mary went the lamb was sure to go. Mary had a little lamb She tied it to a pylon Ten thousand volts went down its back And turned it into nylon", "base64"); print "
" . htmlentities($w->serialize()) . "
"; print "
Value of base64 string is: '" . $w->scalarval() . "'
"; $f->method(''); $f->addParam(new xmlrpcval("41", "int")); print "

Testing request serialization

\n"; $op=$f->serialize(); print "
" . htmlentities($op) . "
"; print "

Testing ISO date format

\n";

$t=time();
$date=iso8601_encode($t);
print "Now is $t --> $date\n";
print "Or in UTC, that is " . iso8601_encode($t, 1) . "\n";
$tb=iso8601_decode($date);
print "That is to say $date --> $tb\n";
print "Which comes out at " . iso8601_encode($tb) . "\n";
print "Which was the time in UTC at " . iso8601_decode($date, 1) . "\n";

print "
\n"; ?> ec_api/xmlrpc/which.php0000644000175000017500000000130110041103313014657 0ustar davidjdavidj xmlrpc setDebug(0); $r=$c->send($f); if (!$r) { die("send failed"); } $v=xmlrpc_decode($r->value()); if (!$r->faultCode()) { print "
";
	print "name: " . $v["toolkitName"] . "\n";
	print "version: " . $v["toolkitVersion"] . "\n";
	print "docs: " . $v["toolkitDocsUrl"] . "\n";
	print "os: " . $v["toolkitOperatingSystem"] . "\n";
	print "
"; } else { print "Fault: "; print "Code: " . $r->faultCode() . " Reason '" .$r->faultString()."'
"; } ?> ec_api/xmlrpc/xmlrpc.inc0000644000175000017500000010760410041103314015062 0ustar davidjdavidj // $Id: xmlrpc.inc,v 1.1 2003/09/24 03:43:49 ozman Exp $ // Copyright (c) 1999,2000,2002 Edd Dumbill. // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions // are met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following // disclaimer in the documentation and/or other materials provided // with the distribution. // // * Neither the name of the "XML-RPC for PHP" nor the names of its // contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE // REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED // OF THE POSSIBILITY OF SUCH DAMAGE. if (!function_exists('xml_parser_create')) { // Win 32 fix. From: 'Leo West' if($WINDIR) { dl('php3_xml.dll'); } else { dl('xml.so'); } } $xmlrpcI4='i4'; $xmlrpcInt='int'; $xmlrpcBoolean='boolean'; $xmlrpcDouble='double'; $xmlrpcString='string'; $xmlrpcDateTime='dateTime.iso8601'; $xmlrpcBase64='base64'; $xmlrpcArray='array'; $xmlrpcStruct='struct'; $xmlrpcTypes=array( $xmlrpcI4 => 1, $xmlrpcInt => 1, $xmlrpcBoolean => 1, $xmlrpcString => 1, $xmlrpcDouble => 1, $xmlrpcDateTime => 1, $xmlrpcBase64 => 1, $xmlrpcArray => 2, $xmlrpcStruct => 3 ); $xmlEntities=array( 'amp' => '&', 'quot' => '"', 'lt' => '<', 'gt' => '>', 'apos' => "'" ); $xmlrpcerr['unknown_method']=1; $xmlrpcstr['unknown_method']='Unknown method'; $xmlrpcerr['invalid_return']=2; $xmlrpcstr['invalid_return']='Invalid return payload: enabling debugging to examine incoming payload'; $xmlrpcerr['incorrect_params']=3; $xmlrpcstr['incorrect_params']='Incorrect parameters passed to method'; $xmlrpcerr['introspect_unknown']=4; $xmlrpcstr['introspect_unknown']="Can't introspect: method unknown"; $xmlrpcerr['http_error']=5; $xmlrpcstr['http_error']="Didn't receive 200 OK from remote server."; $xmlrpcerr['no_data']=6; $xmlrpcstr['no_data']='No data received from server.'; $xmlrpcerr['no_ssl']=7; $xmlrpcstr['no_ssl']='No SSL support compiled in.'; $xmlrpcerr['curl_fail']=8; $xmlrpcstr['curl_fail']='CURL error'; $xmlrpcerr['multicall_notstruct'] = 9; $xmlrpcstr['multicall_notstruct'] = 'system.multicall expected struct'; $xmlrpcerr['multicall_nomethod'] = 10; $xmlrpcstr['multicall_nomethod'] = 'missing methodName'; $xmlrpcerr['multicall_notstring'] = 11; $xmlrpcstr['multicall_notstring'] = 'methodName is not a string'; $xmlrpcerr['multicall_recursion'] = 12; $xmlrpcstr['multicall_recursion'] = 'recursive system.multicall forbidden'; $xmlrpcerr['multicall_noparams'] = 13; $xmlrpcstr['multicall_noparams'] = 'missing params'; $xmlrpcerr['multicall_notarray'] = 14; $xmlrpcstr['multicall_notarray'] = 'params is not an array'; $xmlrpc_defencoding='UTF-8'; $xmlrpcName='XML-RPC for PHP'; $xmlrpcVersion='1.0.99'; // let user errors start at 800 $xmlrpcerruser=800; // let XML parse errors start at 100 $xmlrpcerrxml=100; // formulate backslashes for escaping regexp $xmlrpc_backslash=chr(92).chr(92); // used to store state during parsing // quick explanation of components: // st - used to build up a string for evaluation // ac - used to accumulate values // qt - used to decide if quotes are needed for evaluation // cm - used to denote struct or array (comma needed) // isf - used to indicate a fault // lv - used to indicate "looking for a value": implements // the logic to allow values with no types to be strings // params - used to store parameters in method calls // method - used to store method name $_xh=array(); function xmlrpc_entity_decode($string) { $top=split('&', $string); $op=''; $i=0; while($i "; break; case 'BOOLEAN': // special case here: we translate boolean 1 or 0 into PHP // constants true or false if ($_xh[$parser]['ac']=='1') { $_xh[$parser]['ac']='true'; } else { $_xh[$parser]['ac']='false'; $_xh[$parser]['vt']=strtolower($name); // Drop through intentionally. } case 'I4': case 'INT': case 'STRING': case 'DOUBLE': case 'DATETIME.ISO8601': case 'BASE64': if ($_xh[$parser]['qt']==1) { // we use double quotes rather than single so backslashification works OK $_xh[$parser]['st'].='"'. $_xh[$parser]['ac'] . '"'; } elseif ($_xh[$parser]['qt']==2) { $_xh[$parser]['st'].="base64_decode('". $_xh[$parser]['ac'] . "')"; } elseif ($name=='BOOLEAN') { $_xh[$parser]['st'].=$_xh[$parser]['ac']; } else { // we have an I4, INT or a DOUBLE // we must check that only 0123456789-. are characters here if (!ereg("^\-?[0123456789 \t\.]+$", $_xh[$parser]['ac'])) { // TODO: find a better way of throwing an error // than this! error_log('XML-RPC: non numeric value received in INT or DOUBLE'); $_xh[$parser]['st'].='ERROR_NON_NUMERIC_FOUND'; } else { // it's ok, add it on $_xh[$parser]['st'].=$_xh[$parser]['ac']; } } $_xh[$parser]['ac']=''; $_xh[$parser]['qt']=0; $_xh[$parser]['lv']=3; // indicate we've found a value break; case 'VALUE': // deal with a string value if (strlen($_xh[$parser]['ac'])>0 && $_xh[$parser]['vt']==$xmlrpcString) { $_xh[$parser]['st'].='"'. $_xh[$parser]['ac'] . '"'; } // This if() detects if no scalar was inside // and pads an empty ''. if($_xh[$parser]['st'][strlen($_xh[$parser]['st'])-1] == '(') { $_xh[$parser]['st'].= '""'; } $_xh[$parser]['st'].=", '" . $_xh[$parser]['vt'] . "')"; if ($_xh[$parser]['cm']) { $_xh[$parser]['st'].=','; } break; case 'MEMBER': $_xh[$parser]['ac']=''; $_xh[$parser]['qt']=0; break; case 'DATA': $_xh[$parser]['ac']=''; $_xh[$parser]['qt']=0; break; case 'PARAM': $_xh[$parser]['params'][]=$_xh[$parser]['st']; break; case 'METHODNAME': $_xh[$parser]['method']=ereg_replace("^[\n\r\t ]+", '', $_xh[$parser]['ac']); break; case 'BOOLEAN': // special case here: we translate boolean 1 or 0 into PHP // constants true or false if ($_xh[$parser]['ac']=='1') { $_xh[$parser]['ac']='true'; } else { $_xh[$parser]['ac']='false'; $_xh[$parser]['vt']=strtolower($name); } break; default: break; } // if it's a valid type name, set the type if (isset($xmlrpcTypes[strtolower($name)])) { $_xh[$parser]['vt']=strtolower($name); } } function xmlrpc_cd($parser, $data) { global $_xh, $xmlrpc_backslash; //if (ereg("^[\n\r \t]+$", $data)) return; // print "adding [${data}]\n"; if ($_xh[$parser]['lv']!=3) { // "lookforvalue==3" means that we've found an entire value // and should discard any further character data if ($_xh[$parser]['lv']==1) { // if we've found text and we're just in a then // turn quoting on, as this will be a string $_xh[$parser]['qt']=1; // and say we've found a value $_xh[$parser]['lv']=2; } if(!@isset($_xh[$parser]['ac'])) { $_xh[$parser]['ac'] = ''; } $_xh[$parser]['ac'].=str_replace('$', '\$', str_replace('"', '\"', str_replace(chr(92),$xmlrpc_backslash, $data))); } } function xmlrpc_dh($parser, $data) { global $_xh; if (substr($data, 0, 1) == '&' && substr($data, -1, 1) == ';') { if ($_xh[$parser]['lv']==1) { $_xh[$parser]['qt']=1; $_xh[$parser]['lv']=2; } $_xh[$parser]['ac'].=str_replace('$', '\$', str_replace('"', '\"', str_replace(chr(92),$xmlrpc_backslash, $data))); } } class xmlrpc_client { var $path; var $server; var $port; var $errno; var $errstring; var $debug=0; var $username=''; var $password=''; var $cert=''; var $certpass=''; var $verifypeer=1; var $verifyhost=1; var $no_multicall=false; function xmlrpc_client($path, $server, $port=0) { $this->port=$port; $this->server=$server; $this->path=$path; } function setDebug($in) { if ($in) { $this->debug=1; } else { $this->debug=0; } } function setCredentials($u, $p) { $this->username=$u; $this->password=$p; } function setCertificate($cert, $certpass) { $this->cert = $cert; $this->certpass = $certpass; } function setSSLVerifyPeer($i) { $this->verifypeer = $i; } function setSSLVerifyHost($i) { $this->verifyhost = $i; } function send($msg, $timeout=0, $method='http') { if (is_array($msg)) { // $msg is an array of xmlrpcmsg's return $this->multicall($msg, $timeout, $method); } // where msg is an xmlrpcmsg $msg->debug=$this->debug; if ($method == 'https') { return $this->sendPayloadHTTPS($msg, $this->server, $this->port, $timeout, $this->username, $this->password, $this->cert, $this->certpass); } else { return $this->sendPayloadHTTP10($msg, $this->server, $this->port, $timeout, $this->username, $this->password); } } function sendPayloadHTTP10($msg, $server, $port, $timeout=0,$username='', $password='') { global $xmlrpcerr, $xmlrpcstr; if ($port==0) { $port=80; } if($timeout>0) { $fp=fsockopen($server, $port,$this->errno, $this->errstr, $timeout); } else { $fp=fsockopen($server, $port,$this->errno, $this->errstr); } if (!$fp) { $this->errstr='Connect error'; $r=new xmlrpcresp(0, $xmlrpcerr['http_error'],$xmlrpcstr['http_error']); return $r; } // Only create the payload if it was not created previously if(empty($msg->payload)) { $msg->createPayload(); } // thanks to Grant Rauscher // for this $credentials=''; if ($username!='') { $credentials='Authorization: Basic ' . base64_encode($username . ':' . $password) . "\r\n"; } $op= "POST " . $this->path. " HTTP/1.0\r\nUser-Agent: PHP XMLRPC 1.0\r\n" . "Host: ". $this->server . "\r\n" . $credentials . "Content-Type: text/xml\r\nContent-Length: " . strlen($msg->payload) . "\r\n\r\n" . $msg->payload; if (!fputs($fp, $op, strlen($op))) { $this->errstr='Write error'; $r=new xmlrpcresp(0, $xmlrpcerr['http_error'], $xmlrpcstr['http_error']); return $r; } $resp=$msg->parseResponseFile($fp); fclose($fp); return $resp; } // contributed by Justin Miller // requires curl to be built into PHP function sendPayloadHTTPS($msg, $server, $port, $timeout=0,$username='', $password='', $cert='',$certpass='') { global $xmlrpcerr, $xmlrpcstr; if ($port == 0) { $port = 443; } // Only create the payload if it was not created previously if(empty($msg->payload)) { $msg->createPayload(); } if (!function_exists('curl_init')) { $this->errstr='SSL unavailable on this install'; $r=new xmlrpcresp(0, $xmlrpcerr['no_ssl'], $xmlrpcstr['no_ssl']); return $r; } $curl = curl_init('https://' . $server . ':' . $port . $this->path); curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); // results into variable if ($this->debug) { curl_setopt($curl, CURLOPT_VERBOSE, 1); } curl_setopt($curl, CURLOPT_USERAGENT, 'PHP XMLRPC 1.0'); // required for XMLRPC curl_setopt($curl, CURLOPT_POST, 1); // post the data curl_setopt($curl, CURLOPT_POSTFIELDS, $msg->payload); // the data curl_setopt($curl, CURLOPT_HEADER, 1); // return the header too curl_setopt($curl, CURLOPT_HTTPHEADER, array('Content-Type: text/xml')); // whether to verify remote host's cert curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, $this->verifypeer); // whether to verify cert's common name (CN); 0 for no, 1 to verify that it exists, and 2 to verify that it matches the hostname used curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, $this->verifyhost); // required for XMLRPC if ($timeout) { curl_setopt($curl, CURLOPT_TIMEOUT, $timeout == 1 ? 1 : $timeout - 1); } // timeout is borked if ($username && $password) { curl_setopt($curl, CURLOPT_USERPWD,"$username:$password"); } // set auth stuff if ($cert) { curl_setopt($curl, CURLOPT_SSLCERT, $cert); } // set cert file if ($certpass) { curl_setopt($curl, CURLOPT_SSLCERTPASSWD,$certpass); } // set cert password $result = curl_exec($curl); if (!$result) { $this->errstr='no response'; $resp=new xmlrpcresp(0, $xmlrpcerr['curl_fail'], $xmlrpcstr['curl_fail']. ': '. curl_error($curl)); } else { $resp = $msg->parseResponse($result); } curl_close($curl); return $resp; } function multicall($msgs, $timeout=0, $method='http') { $results = false; if (! $this->no_multicall) { $results = $this->_try_multicall($msgs, $timeout, $method); /* TODO - this is not php3-friendly */ // if($results !== false) if($results != false) { // Either the system.multicall succeeded, or the send // failed (e.g. due to HTTP timeout). In either case, // we're done for now. return $results; } else { // system.multicall unsupported by server, // don't try it next time... $this->no_multicall = true; } } // system.multicall is unupported by server: // Emulate multicall via multiple requests $results = array(); //foreach($msgs as $msg) @reset($msgs); while(list(,$msg) = @each($msgs)) { $results[] = $this->send($msg, $timeout, $method); } return $results; } // Attempt to boxcar $msgs via system.multicall. function _try_multicall($msgs, $timeout, $method) { // Construct multicall message $calls = array(); //foreach($msgs as $msg) @reset($msgs); while(list(,$msg) = @each($msgs)) { $call['methodName'] = new xmlrpcval($msg->method(),'string'); $numParams = $msg->getNumParams(); $params = array(); for ($i = 0; $i < $numParams; $i++) { $params[$i] = $msg->getParam($i); } $call['params'] = new xmlrpcval($params, 'array'); $calls[] = new xmlrpcval($call, 'struct'); } $multicall = new xmlrpcmsg('system.multicall'); $multicall->addParam(new xmlrpcval($calls, 'array')); // Attempt RPC call $result = $this->send($multicall, $timeout, $method); if (!is_object($result)) return ($result || 0); // transport failed if ($result->faultCode() != 0) return false; // system.multicall failed // Unpack responses. $rets = $result->value(); if ($rets->kindOf() != 'array') return false; // bad return type from system.multicall $numRets = $rets->arraysize(); if ($numRets != count($msgs)) return false; // wrong number of return values. $response = array(); for ($i = 0; $i < $numRets; $i++) { $val = $rets->arraymem($i); switch ($val->kindOf()) { case 'array': if ($val->arraysize() != 1) return false; // Bad value // Normal return value $response[$i] = new xmlrpcresp($val->arraymem(0)); break; case 'struct': $code = $val->structmem('faultCode'); if ($code->kindOf() != 'scalar' || $code->scalartyp() != 'int') return false; $str = $val->structmem('faultString'); if ($str->kindOf() != 'scalar' || $str->scalartyp() != 'string') return false; $response[$i] = new xmlrpcresp(0, $code->scalarval(), $str->scalarval()); break; default: return false; } } return $response; } } // end class xmlrpc_client class xmlrpcresp { var $val = 0; var $errno = 0; var $errstr = ''; var $hdrs = array(); function xmlrpcresp($val, $fcode = 0, $fstr = '') { if ($fcode != 0) { // error $this->errno = $fcode; $this->errstr = htmlspecialchars($fstr); // XXX: encoding probably shouldn't be done here; fix later. } else if (!is_object($val)) { // programmer error error_log("Invalid type '" . gettype($val) . "' (value: $val) passed to xmlrpcresp. Defaulting to empty value."); $this->val = new xmlrpcval(); } else { // success $this->val = $val; } } function faultCode() { return $this->errno; } function faultString() { return $this->errstr; } function value() { return $this->val; } function serialize() { $result = "\n"; if ($this->errno) { $result .= ' faultCode ' . $this->errno . ' faultString ' . $this->errstr . ' '; } else { $result .= "\n\n" . $this->val->serialize() . "\n"; } $result .= "\n"; return $result; } } class xmlrpcmsg { var $payload; var $methodname; var $params=array(); var $debug=0; function xmlrpcmsg($meth, $pars=0) { $this->methodname=$meth; if (is_array($pars) && sizeof($pars)>0) { for($i=0; $iaddParam($pars[$i]); } } } function xml_header() { return "\n\n"; } function xml_footer() { return "\n"; } function createPayload() { $this->payload=$this->xml_header(); $this->payload.='' . $this->methodname . "\n"; // if (sizeof($this->params)) { $this->payload.="\n"; for($i=0; $iparams); $i++) { $p=$this->params[$i]; $this->payload.="\n" . $p->serialize() . "\n"; } $this->payload.="\n"; // } $this->payload.=$this->xml_footer(); $this->payload=str_replace("\n", "\r\n", $this->payload); } function method($meth='') { if ($meth!='') { $this->methodname=$meth; } return $this->methodname; } function serialize() { $this->createPayload(); return $this->payload; } function addParam($par) { $this->params[]=$par; } function getParam($i) { return $this->params[$i]; } function getNumParams() { return sizeof($this->params); } function parseResponseFile($fp) { $ipd=''; while($data=fread($fp, 32768)) { $ipd.=$data; } return $this->parseResponse($ipd); } function parseResponse($data='') { global $_xh,$xmlrpcerr,$xmlrpcstr; global $xmlrpc_defencoding; $parser = xml_parser_create($xmlrpc_defencoding); $_xh[$parser]=array(); $_xh[$parser]['st']=''; $_xh[$parser]['cm']=0; $_xh[$parser]['isf']=0; $_xh[$parser]['ac']=''; $_xh[$parser]['qt']=''; $_xh[$parser]['headers'] = array(); xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, true); xml_set_element_handler($parser, 'xmlrpc_se', 'xmlrpc_ee'); xml_set_character_data_handler($parser, 'xmlrpc_cd'); xml_set_default_handler($parser, 'xmlrpc_dh'); $xmlrpc_value=new xmlrpcval; $hdrfnd = 0; if($this->debug) { //by maHo, replaced htmlspecialchars with htmlentities print "
---GOT---\n" . htmlentities($data) . "\n---END---\n
"; } if($data == '') { error_log('No response received from server.'); $r = new xmlrpcresp(0, $xmlrpcerr['no_data'], $xmlrpcstr['no_data']); xml_parser_free($parser); return $r; } // see if we got an HTTP 200 OK, else bomb // but only do this if we're using the HTTP protocol. if(ereg("^HTTP",$data) && !ereg("^HTTP/[0-9\.]+ 200 ", $data)) { $errstr= substr($data, 0, strpos($data, "\n")-1); error_log('HTTP error, got response: ' .$errstr); $r=new xmlrpcresp(0, $xmlrpcerr['http_error'], $xmlrpcstr['http_error']. ' (' . $errstr . ')'); xml_parser_free($parser); return $r; } // separate HTTP headers from data if (ereg("^HTTP", $data)) { $ar = split("\r\n", $data); while (($line = array_shift($ar))) { if (strlen($line) < 1) { break; } $_xh[$parser]['headers'][] = $line; } $data = join("\r\n", $ar); } if ($this->debug && count($_xh[$parser]['headers'])) { print "
";
				foreach ($_xh[$parser]['headers'] as $header)
				{
					print "HEADER: $header\n";
				}
				print "
\n"; } if (!xml_parse($parser, $data, sizeof($data))) { // thanks to Peter Kocks if((xml_get_current_line_number($parser)) == 1) { $errstr = 'XML error at line 1, check URL'; } else { $errstr = sprintf('XML error: %s at line %d', xml_error_string(xml_get_error_code($parser)), xml_get_current_line_number($parser)); error_log($errstr); $r=new xmlrpcresp(0, $xmlrpcerr['invalid_return'], $xmlrpcstr['invalid_return']); xml_parser_free($parser); echo $errstr; return $r; } } xml_parser_free($parser); if ($this->debug) { print "
---EVALING---[" . 
				strlen($_xh[$parser]['st']) . " chars]---\n" . 
				htmlspecialchars($_xh[$parser]['st']) . ";\n---END---
"; } if (strlen($_xh[$parser]['st'])==0) { // then something odd has happened // and it's time to generate a client side error // indicating something odd went on $r=new xmlrpcresp(0, $xmlrpcerr['invalid_return'], $xmlrpcstr['invalid_return']); } else { eval('$v=' . $_xh[$parser]['st'] . '; $allOK=1;'); if ($_xh[$parser]['isf']) { $errno_v = $v->structmem('faultCode'); $errstr_v = $v->structmem('faultString'); $errno = $errno_v->scalarval(); if ($errno == 0) { // FAULT returned, errno needs to reflect that $errno = -1; } $r = new xmlrpcresp($v, $errno, $errstr_v->scalarval()); } else { $r=new xmlrpcresp($v); } } $r->hdrs = $_xh[$parser]['headers']; return $r; } } class xmlrpcval { var $me=array(); var $mytype=0; function xmlrpcval($val=-1, $type='') { global $xmlrpcTypes; $this->me=array(); $this->mytype=0; if ($val!=-1 || $type!='') { if ($type=='') { $type='string'; } if ($xmlrpcTypes[$type]==1) { $this->addScalar($val,$type); } elseif ($xmlrpcTypes[$type]==2) { $this->addArray($val); } elseif ($xmlrpcTypes[$type]==3) { $this->addStruct($val); } } } function addScalar($val, $type='string') { global $xmlrpcTypes, $xmlrpcBoolean; if ($this->mytype==1) { echo 'xmlrpcval: scalar can have only one value
'; return 0; } $typeof=$xmlrpcTypes[$type]; if ($typeof!=1) { echo 'xmlrpcval: not a scalar type (${typeof})
'; return 0; } if ($type==$xmlrpcBoolean) { if (strcasecmp($val,'true')==0 || $val==1 || ($val==true && strcasecmp($val,'false'))) { $val=1; } else { $val=0; } } if ($this->mytype==2) { // we're adding to an array here $ar=$this->me['array']; $ar[]=new xmlrpcval($val, $type); $this->me['array']=$ar; } else { // a scalar, so set the value and remember we're scalar $this->me[$type]=$val; $this->mytype=$typeof; } return 1; } function addArray($vals) { global $xmlrpcTypes; if ($this->mytype!=0) { echo 'xmlrpcval: already initialized as a [' . $this->kindOf() . ']
'; return 0; } $this->mytype=$xmlrpcTypes['array']; $this->me['array']=$vals; return 1; } function addStruct($vals) { global $xmlrpcTypes; if ($this->mytype!=0) { echo 'xmlrpcval: already initialized as a [' . $this->kindOf() . ']
'; return 0; } $this->mytype=$xmlrpcTypes['struct']; $this->me['struct']=$vals; return 1; } function dump($ar) { reset($ar); while ( list( $key, $val ) = each( $ar ) ) { echo "$key => $val
"; if ($key == 'array') { while ( list( $key2, $val2 ) = each( $val ) ) { echo "-- $key2 => $val2
"; } } } } function kindOf() { switch($this->mytype) { case 3: return 'struct'; break; case 2: return 'array'; break; case 1: return 'scalar'; break; default: return 'undef'; } } function serializedata($typ, $val) { $rs=''; global $xmlrpcTypes, $xmlrpcBase64, $xmlrpcString, $xmlrpcBoolean; switch($xmlrpcTypes[$typ]) { case 3: // struct $rs.="\n"; reset($val); while(list($key2, $val2)=each($val)) { $rs.="${key2}\n"; $rs.=$this->serializeval($val2); $rs.="\n"; } $rs.=''; break; case 2: // array $rs.="\n\n"; for($i=0; $iserializeval($val[$i]); } $rs.="\n"; break; case 1: switch ($typ) { case $xmlrpcBase64: $rs.="<${typ}>" . base64_encode($val) . ""; break; case $xmlrpcBoolean: $rs.="<${typ}>" . ($val ? '1' : '0') . ""; break; case $xmlrpcString: $rs.="<${typ}>" . htmlspecialchars($val). ""; break; default: $rs.="<${typ}>${val}"; } break; default: break; } return $rs; } function serialize() { return $this->serializeval($this); } function serializeval($o) { global $xmlrpcTypes; $rs=''; $ar=$o->me; reset($ar); list($typ, $val) = each($ar); $rs.=''; $rs.=$this->serializedata($typ, $val); $rs.="\n"; return $rs; } function structmem($m) { $nv=$this->me['struct'][$m]; return $nv; } function structreset() { reset($this->me['struct']); } function structeach() { return each($this->me['struct']); } function getval() { // UNSTABLE global $xmlrpcBoolean, $xmlrpcBase64; reset($this->me); list($a,$b)=each($this->me); // contributed by I Sofer, 2001-03-24 // add support for nested arrays to scalarval // i've created a new method here, so as to // preserve back compatibility if (is_array($b)) { @reset($b); while(list($id,$cont) = @each($b)) { $b[$id] = $cont->scalarval(); } } // add support for structures directly encoding php objects if (is_object($b)) { $t = get_object_vars($b); @reset($t); while(list($id,$cont) = @each($t)) { $t[$id] = $cont->scalarval(); } @reset($t); while(list($id,$cont) = @each($t)) { eval('$b->'.$id.' = $cont;'); } } // end contrib return $b; } function scalarval() { global $xmlrpcBoolean, $xmlrpcBase64; reset($this->me); list($a,$b)=each($this->me); return $b; } function scalartyp() { global $xmlrpcI4, $xmlrpcInt; reset($this->me); list($a,$b)=each($this->me); if ($a==$xmlrpcI4) { $a=$xmlrpcInt; } return $a; } function arraymem($m) { $nv=$this->me['array'][$m]; return $nv; } function arraysize() { reset($this->me); list($a,$b)=each($this->me); return sizeof($b); } } // date helpers function iso8601_encode($timet, $utc=0) { // return an ISO8601 encoded string // really, timezones ought to be supported // but the XML-RPC spec says: // // "Don't assume a timezone. It should be specified by the server in its // documentation what assumptions it makes about timezones." // // these routines always assume localtime unless // $utc is set to 1, in which case UTC is assumed // and an adjustment for locale is made when encoding if (!$utc) { $t=strftime("%Y%m%dT%H:%M:%S", $timet); } else { if (function_exists('gmstrftime')) { // gmstrftime doesn't exist in some versions // of PHP $t=gmstrftime("%Y%m%dT%H:%M:%S", $timet); } else { $t=strftime("%Y%m%dT%H:%M:%S", $timet-date('Z')); } } return $t; } function iso8601_decode($idate, $utc=0) { // return a timet in the localtime, or UTC $t=0; if (ereg("([0-9]{4})([0-9]{2})([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})", $idate, $regs)) { if ($utc) { $t=gmmktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]); } else { $t=mktime($regs[4], $regs[5], $regs[6], $regs[2], $regs[3], $regs[1]); } } return $t; } /**************************************************************** * xmlrpc_decode takes a message in PHP xmlrpc object format and * * tranlates it into native PHP types. * * * * author: Dan Libby (dan@libby.com) * ****************************************************************/ function xmlrpc_decode($xmlrpc_val) { $kind = $xmlrpc_val->kindOf(); if($kind == 'scalar') { return $xmlrpc_val->scalarval(); } elseif($kind == 'array') { $size = $xmlrpc_val->arraysize(); $arr = array(); for($i = 0; $i < $size; $i++) { $arr[]=xmlrpc_decode($xmlrpc_val->arraymem($i)); } return $arr; } elseif($kind == 'struct') { $xmlrpc_val->structreset(); $arr = array(); while(list($key,$value)=$xmlrpc_val->structeach()) { $arr[$key] = xmlrpc_decode($value); } return $arr; } } /**************************************************************** * xmlrpc_encode takes native php types and encodes them into * * xmlrpc PHP object format. * * BUG: All sequential arrays are turned into structs. I don't * * know of a good way to determine if an array is sequential * * only. * * * * feature creep -- could support more types via optional type * * argument. * * * * author: Dan Libby (dan@libby.com) * ****************************************************************/ function xmlrpc_encode($php_val) { global $xmlrpcInt; global $xmlrpcDouble; global $xmlrpcString; global $xmlrpcArray; global $xmlrpcStruct; global $xmlrpcBoolean; $type = gettype($php_val); $xmlrpc_val = new xmlrpcval; switch($type) { case 'array': case 'object': $arr = array(); while (list($k,$v) = each($php_val)) { $arr[$k] = xmlrpc_encode($v); } $xmlrpc_val->addStruct($arr); break; case 'integer': $xmlrpc_val->addScalar($php_val, $xmlrpcInt); break; case 'double': $xmlrpc_val->addScalar($php_val, $xmlrpcDouble); break; case 'string': $xmlrpc_val->addScalar($php_val, $xmlrpcString); break; // // Add support for encoding/decoding of booleans, since they are supported in PHP case 'boolean': $xmlrpc_val->addScalar($php_val, $xmlrpcBoolean); break; // case 'unknown type': default: // giancarlo pinerolo // it has to return // an empty object in case (which is already // at this point), not a boolean. break; } return $xmlrpc_val; } ?> ec_api/xmlrpc/xmlrpcs.inc0000644000175000017500000003131310041103314015236 0ustar davidjdavidj // $Id: xmlrpcs.inc,v 1.1 2003/09/24 03:43:49 ozman Exp $ // Copyright (c) 1999,2000,2002 Edd Dumbill. // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions // are met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following // disclaimer in the documentation and/or other materials provided // with the distribution. // // * Neither the name of the "XML-RPC for PHP" nor the names of its // contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE // REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED // OF THE POSSIBILITY OF SUCH DAMAGE. // XML RPC Server class // requires: xmlrpc.inc // listMethods: either a string, or nothing $_xmlrpcs_listMethods_sig=array(array($xmlrpcArray, $xmlrpcString), array($xmlrpcArray)); $_xmlrpcs_listMethods_doc='This method lists all the methods that the XML-RPC server knows how to dispatch'; function _xmlrpcs_listMethods($server, $m) { global $xmlrpcerr, $xmlrpcstr, $_xmlrpcs_dmap; $v=new xmlrpcval(); $dmap=$server->dmap; $outAr=array(); for(reset($dmap); list($key, $val)=each($dmap); ) { $outAr[]=new xmlrpcval($key, 'string'); } $dmap=$_xmlrpcs_dmap; for(reset($dmap); list($key, $val)=each($dmap); ) { $outAr[]=new xmlrpcval($key, 'string'); } $v->addArray($outAr); return new xmlrpcresp($v); } $_xmlrpcs_methodSignature_sig=array(array($xmlrpcArray, $xmlrpcString)); $_xmlrpcs_methodSignature_doc='Returns an array of known signatures (an array of arrays) for the method name passed. If no signatures are known, returns a none-array (test for type != array to detect missing signature)'; function _xmlrpcs_methodSignature($server, $m) { global $xmlrpcerr, $xmlrpcstr, $_xmlrpcs_dmap; $methName=$m->getParam(0); $methName=$methName->scalarval(); if (ereg("^system\.", $methName)) { $dmap=$_xmlrpcs_dmap; $sysCall=1; } else { $dmap=$server->dmap; $sysCall=0; } // print "\n"; if (isset($dmap[$methName])) { if ($dmap[$methName]['signature']) { $sigs=array(); $thesigs=$dmap[$methName]['signature']; for($i=0; $igetParam(0); $methName=$methName->scalarval(); if (ereg("^system\.", $methName)) { $dmap=$_xmlrpcs_dmap; $sysCall=1; } else { $dmap=$server->dmap; $sysCall=0; } // print "\n"; if (isset($dmap[$methName])) { if ($dmap[$methName]['docstring']) { $r=new xmlrpcresp(new xmlrpcval($dmap[$methName]['docstring']), 'string'); } else { $r=new xmlrpcresp(new xmlrpcval('', 'string')); } } else { $r=new xmlrpcresp(0, $xmlrpcerr['introspect_unknown'], $xmlrpcstr['introspect_unknown']); } return $r; } $_xmlrpcs_multicall_sig = array(array($xmlrpcArray, $xmlrpcArray)); $_xmlrpcs_multicall_doc = 'Boxcar multiple RPC calls in one request. See http://www.xmlrpc.com/discuss/msgReader$1208 for details'; function _xmlrpcs_multicall_error($err) { if (is_string($err)) { global $xmlrpcerr, $xmlrpcstr; $str = $xmlrpcstr["multicall_${err}"]; $code = $xmlrpcerr["multicall_${err}"]; } else { $code = $err->faultCode(); $str = $err->faultString(); } $struct['faultCode'] = new xmlrpcval($code, 'int'); $struct['faultString'] = new xmlrpcval($str, 'string'); return new xmlrpcval($struct, 'struct'); } function _xmlrpcs_multicall_do_call($server, $call) { if ($call->kindOf() != 'struct') { return _xmlrpcs_multicall_error('notstruct'); } $methName = $call->structmem('methodName'); if (!$methName) { return _xmlrpcs_multicall_error('nomethod'); } if ($methName->kindOf() != 'scalar' || $methName->scalartyp() != 'string') { return _xmlrpcs_multicall_error('notstring'); } if ($methName->scalarval() == 'system.multicall') { return _xmlrpcs_multicall_error('recursion'); } $params = $call->structmem('params'); if (!$params) { return _xmlrpcs_multicall_error('noparams'); } if ($params->kindOf() != 'array') { return _xmlrpcs_multicall_error('notarray'); } $numParams = $params->arraysize(); $msg = new xmlrpcmsg($methName->scalarval()); for ($i = 0; $i < $numParams; $i++) { $msg->addParam($params->arraymem($i)); } $result = $server->execute($msg); if ($result->faultCode() != 0) { return _xmlrpcs_multicall_error($result); // Method returned fault. } return new xmlrpcval(array($result->value()), 'array'); } function _xmlrpcs_multicall($server, $m) { $calls = $m->getParam(0); $numCalls = $calls->arraysize(); $result = array(); for ($i = 0; $i < $numCalls; $i++) { $call = $calls->arraymem($i); $result[$i] = _xmlrpcs_multicall_do_call($server, $call); } return new xmlrpcresp(new xmlrpcval($result, 'array')); } $_xmlrpcs_dmap=array( 'system.listMethods' => array( 'function' => '_xmlrpcs_listMethods', 'signature' => $_xmlrpcs_listMethods_sig, 'docstring' => $_xmlrpcs_listMethods_doc), 'system.methodHelp' => array( 'function' => '_xmlrpcs_methodHelp', 'signature' => $_xmlrpcs_methodHelp_sig, 'docstring' => $_xmlrpcs_methodHelp_doc), 'system.methodSignature' => array( 'function' => '_xmlrpcs_methodSignature', 'signature' => $_xmlrpcs_methodSignature_sig, 'docstring' => $_xmlrpcs_methodSignature_doc), 'system.multicall' => array( 'function' => '_xmlrpcs_multicall', 'signature' => $_xmlrpcs_multicall_sig, 'docstring' => $_xmlrpcs_multicall_doc ) ); $_xmlrpc_debuginfo=''; function xmlrpc_debugmsg($m) { global $_xmlrpc_debuginfo; $_xmlrpc_debuginfo=$_xmlrpc_debuginfo . $m . "\n"; } class xmlrpc_server { var $dmap=array(); function xmlrpc_server($dispMap='', $serviceNow=1) { global $HTTP_RAW_POST_DATA; // dispMap is a dispatch array of methods // mapped to function names and signatures // if a method // doesn't appear in the map then an unknown // method error is generated /* milosch - changed to make passing dispMap optional. * instead, you can use the class add_to_map() function * to add functions manually (borrowed from SOAPX4) */ if($dispMap) { $this->dmap = $dispMap; if($serviceNow) { $this->service(); } } } function serializeDebug() { global $_xmlrpc_debuginfo; if ($_xmlrpc_debuginfo!='') { return "\n"; } else { return ''; } } function service() { global $xmlrpc_defencoding; $r=$this->parseRequest(); $payload='' . "\n" . $this->serializeDebug() . $r->serialize(); Header("Content-type: text/xml\r\nContent-length: " . strlen($payload)); print $payload; } /* add a method to the dispatch map */ function add_to_map($methodname,$function,$sig,$doc) { $this->dmap[$methodname] = array( 'function' => $function, 'signature' => $sig, 'docstring' => $doc ); } function verifySignature($in, $sig) { for($i=0; $igetNumParams()+1) { $itsOK=1; for($n=0; $n<$in->getNumParams(); $n++) { $p=$in->getParam($n); // print "\n"; if ($p->kindOf() == 'scalar') { $pt=$p->scalartyp(); } else { $pt=$p->kindOf(); } // $n+1 as first type of sig is return type if ($pt != $cursig[$n+1]) { $itsOK=0; $pno=$n+1; $wanted=$cursig[$n+1]; $got=$pt; break; } } if ($itsOK) { return array(1); } } } return array(0, "Wanted ${wanted}, got ${got} at param ${pno})"); } function parseRequest($data='') { global $_xh,$HTTP_RAW_POST_DATA; global $xmlrpcerr, $xmlrpcstr, $xmlrpcerrxml, $xmlrpc_defencoding, $_xmlrpcs_dmap; if ($data=='') { $data=$HTTP_RAW_POST_DATA; } $parser = xml_parser_create($xmlrpc_defencoding); $_xh[$parser]=array(); $_xh[$parser]['st']=''; $_xh[$parser]['cm']=0; $_xh[$parser]['isf']=0; $_xh[$parser]['params']=array(); $_xh[$parser]['method']=''; // decompose incoming XML into request structure xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, true); xml_set_element_handler($parser, 'xmlrpc_se', 'xmlrpc_ee'); xml_set_character_data_handler($parser, 'xmlrpc_cd'); xml_set_default_handler($parser, 'xmlrpc_dh'); if (!xml_parse($parser, $data, 1)) { // return XML error as a faultCode $r=new xmlrpcresp(0, $xmlrpcerrxml+xml_get_error_code($parser), sprintf('XML error: %s at line %d', xml_error_string(xml_get_error_code($parser)), xml_get_current_line_number($parser))); xml_parser_free($parser); } else { xml_parser_free($parser); $m=new xmlrpcmsg($_xh[$parser]['method']); // now add parameters in $plist=''; for($i=0; $i\n"; $plist.="$i - " . $_xh[$parser]['params'][$i]. " \n"; eval('$m->addParam(' . $_xh[$parser]['params'][$i]. ');'); } // uncomment this to really see what the server's getting! // xmlrpc_debugmsg($plist); $r = $this->execute($m); } return $r; } function execute ($m) { global $xmlrpcerr, $xmlrpcstr, $_xmlrpcs_dmap; // now to deal with the method $methName = $m->method(); $sysCall = ereg("^system\.", $methName); $dmap = $sysCall ? $_xmlrpcs_dmap : $this->dmap; if (!isset($dmap[$methName]['function'])) { // No such method return new xmlrpcresp(0, $xmlrpcerr['unknown_method'], $xmlrpcstr['unknown_method']); } // Check signature. if (isset($dmap[$methName]['signature'])) { $sig = $dmap[$methName]['signature']; list ($ok, $errstr) = $this->verifySignature($m, $sig); if (!$ok) { // Didn't match. return new xmlrpcresp(0, $xmlrpcerr['incorrect_params'], $xmlrpcstr['incorrect_params'] . ": ${errstr}"); } } $func = $dmap[$methName]['function']; if ($sysCall) { return call_user_func($func, $this, $m); } else { return call_user_func($func, $m); } } function echoInput() { global $HTTP_RAW_POST_DATA; // a debugging routine: just echos back the input // packet as a string value $r=new xmlrpcresp; $r->xv=new xmlrpcval( "'Aha said I: '" . $HTTP_RAW_POST_DATA, 'string'); print $r->serialize(); } } ?> ec_api/xmlrpc/zopetest.php0000644000175000017500000000127510041103314015445 0ustar davidjdavidj zope test \n" . htmlspecialchars($f->serialize()) . "
"; $c=new xmlrpc_client("/index_html", "pingu.heddley.com", 9080); $c->setCredentials("username", "password"); $c->setDebug(1); $r=$c->send($f); if (!$r) { die("send failed"); } $v=$r->value(); if (!$r->faultCode()) { print "I received:" . $v->scalarval() . "
"; print "
I got this value back
" .
		htmlentities($r->serialize()). "

\n"; } else { print "Fault: "; print "Code: " . $r->faultCode() . " Reason '" .$r->faultString()."'
"; } ?> ec_api/ec_client.php0000644000175000017500000001622610063752140014225 0ustar davidjdavidjprocess_charge ( array ( // "card_name" => "John Doe", // "card_number" => "4564456445644564", // "card_expiry" => "0405", // "card_cvc2" => "123", // "transaction_comment" => "Test Transaction", // Optional // "transaction_client_ref" => "Invoice XXXXXXXX", // Optional // "terminal_type" => "internet", // Optional // "total_amount" => "1.00") // ); // // list ($ret, $msgs) = $chargeObj->process_refund ( array ( // "card_name" => "John Doe", // "card_number" => "4564456445644564", // "card_expiry" => "0405", // "card_cvc2" => "123", // "transaction_orig_ref" => "11XX000000XXXXXX", // Original bank transaction reference // "transaction_comment" => "Test Transaction", // Optional // "transaction_client_ref" => "Invoice XXXXXXXX", // Optional // "terminal_type" => "internet", // Optional // "total_amount" => "1.00") // ); // // foreach ($msgs AS $key => $value) { // print $key."\t".$value."\n"; // } /* -------------------------------------------------------------------------- */ class ec_class { var $ec_id; var $ec_password; var $xmlrpc_client; var $xmlrpc_method; // Constructor function ec_class ($ec_id, // EC merchant identifier $ec_password, // EC merchant password $ec_host = "ec.teragen.com.au", // EC host $ec_port = "443", // EC port - 80 or 443 $ec_path = "/ec/ec_xmlrpc.php", // EC XMLRPC path $ec_method = "http" // XMLRPC transmission method - http/https ) { $this->ec_id = $ec_id; $this->ec_password = $ec_password; $this->xmlrpc_method = $ec_method; $this->xmlrpc_client = new xmlrpc_client($ec_path, $ec_host, $ec_port); $this->xmlrpc_client->setDebug(FALSE); $this->xmlrpc_client->setSSLVerifyHost(0); $this->xmlrpc_client->setSSLVerifyPeer(FALSE); } function set_debug ($debug) { $this->xmlrpc_client->setDebug($debug); } function perform_check ($data) { $d = new xmlrpcval( array ( "ec_id" => new xmlrpcval($this->ec_id, "string"), "ec_password" => new xmlrpcval($this->ec_password, "string"), "data" => new xmlrpcval ( array ( "card_name" => new xmlrpcval($data['card_name'], "string"), "card_type" => new xmlrpcval($data['card_type'], "string"), "card_number" => new xmlrpcval($data['card_number'], "string"), "card_expiry" => new xmlrpcval($data['card_expiry'], "string"), "card_cvc2" => new xmlrpcval($data['card_cvc2'], "string"), "transaction_comment" => new xmlrpcval($data['transaction_comment'], "string"), "transaction_client_ref" => new xmlrpcval($data['transaction_client_ref'], "string"), "terminal_type" => new xmlrpcval($data['terminal_type'], "string"), "total_amount" => new xmlrpcval($data['total_amount'], "string") ), "struct") ), "struct"); $f = new xmlrpcmsg("payment.perform_check", array($d)); $response = $this->xmlrpc_client->send ($f, 0, $this->xmlrpc_method); // Send if (!$response) die("Send failed"); // Failure // Prepare return vals if (!$response->faultCode()) { $response_val = $response->value(); while (list($key,$value) = $response_val->structeach()) { $_response[$key] = $value->scalarval(); } return array (TRUE, $_response); } else { return array (FALSE, array ("code" => $response->faultCode(), "reason" => $response->faultString())); } } function process_charge ($data) { $d = new xmlrpcval( array ( "ec_id" => new xmlrpcval($this->ec_id, "string"), "ec_password" => new xmlrpcval($this->ec_password, "string"), "data" => new xmlrpcval ( array ( "card_name" => new xmlrpcval($data['card_name'], "string"), "card_type" => new xmlrpcval($data['card_type'], "string"), "card_number" => new xmlrpcval($data['card_number'], "string"), "card_expiry" => new xmlrpcval($data['card_expiry'], "string"), "card_cvc2" => new xmlrpcval($data['card_cvc2'], "string"), "transaction_comment" => new xmlrpcval($data['transaction_comment'], "string"), "transaction_client_ref" => new xmlrpcval($data['transaction_client_ref'], "string"), "terminal_type" => new xmlrpcval($data['terminal_type'], "string"), "total_amount" => new xmlrpcval($data['total_amount'], "string") ), "struct") ), "struct"); $f = new xmlrpcmsg("payment.process_charge", array($d)); $response = $this->xmlrpc_client->send ($f, 0, $this->xmlrpc_method); // Send if (!$response) die("Send failed"); // Failure // Prepare return vals if (!$response->faultCode()) { $response_val = $response->value(); while (list($key,$value) = $response_val->structeach()) { $_response[$key] = $value->scalarval(); } return array (TRUE, $_response); } else { return array (FALSE, array ("code" => $response->faultCode(), "reason" => $response->faultString())); } } function process_refund ($data) { $d = new xmlrpcval( array ( "ec_id" => new xmlrpcval($this->ec_id, "string"), "ec_password" => new xmlrpcval($this->ec_password, "string"), "data" => new xmlrpcval ( array ( "card_name" => new xmlrpcval($data['card_name'], "string"), "card_type" => new xmlrpcval($data['card_type'], "string"), "card_number" => new xmlrpcval($data['card_number'], "string"), "card_expiry" => new xmlrpcval($data['card_expiry'], "string"), "card_cvc2" => new xmlrpcval($data['card_cvc2'], "string"), "transaction_comment" => new xmlrpcval($data['transaction_comment'], "string"), "transaction_client_ref" => new xmlrpcval($data['transaction_client_ref'], "string"), "transaction_orig_ref" => new xmlrpcval($data['transaction_orig_ref'], "string"), "terminal_type" => new xmlrpcval($data['terminal_type'], "string"), "total_amount" => new xmlrpcval($data['total_amount'], "string") ), "struct") ), "struct"); $f = new xmlrpcmsg("payment.process_refund", array($d)); $response = $this->xmlrpc_client->send ($f, 0, $this->xmlrpc_method); // Send if (!$response) die("Send failed"); // Failure // Prepare return vals if (!$response->faultCode()) { $response_val = $response->value(); while (list($key,$value) = $response_val->structeach()) { $_response[$key] = $value->scalarval(); } return array (TRUE, $_response); } else { return array (FALSE, array ("code" => $response->faultCode(), "reason" => $response->faultString())); } } } ?>ec_api/example.php0000644000175000017500000000431410063752631013733 0ustar davidjdavidjset_debug (FALSE); // process_charge list ($ret, $msgs) = $chargeObj->process_charge ( array ( "card_name" => "Joe Citizen", "card_type" => "VISA", "card_number" => "4564456445644564", "card_expiry" => "0405", "card_cvc2" => "123", "transaction_comment" => "Test Transaction", "transaction_client_ref" => "Test Comment", "terminal_type" => "internet", "total_amount" => "1.00") ); print "
";
	foreach ($msgs AS $key => $value) {
		print strtoupper($key).": ".$value."\n";
	}
	print "
"; // process_refund list ($ret, $msgs) = $chargeObj->process_refund ( array ( "card_name" => "Joe Citizen", "card_type" => "VISA", "card_number" => "4564456445644564", "card_expiry" => "0405", "card_cvc2" => "123", "transaction_comment" => "Test Transaction", "transaction_client_ref" => "Test Comment", "transaction_orig_ref" => "11XX000000XXXXXX", "terminal_type" => "internet", "total_amount" => "1.00") ); print "
";
	foreach ($msgs AS $key => $value) {
		print strtoupper($key).": ".$value."\n";
	}
	print "
"; // perform_check list ($ret, $msgs) = $chargeObj->perform_check ( array ( "card_name" => "Joe Citizen", "card_type" => "VISA", "card_number" => "4564456445644564", "card_expiry" => "0405", "card_cvc2" => "123", "transaction_comment" => "Test Transaction", "transaction_client_ref" => "Test Comment", "transaction_orig_ref" => "11XX000000XXXXXX", "terminal_type" => "internet", "total_amount" => "1.00") ); print "
";
	foreach ($msgs AS $key => $value) {
		print strtoupper($key).": ".$value."\n";
	}
	print "
"; exit; ?>ec_api/ec_client_return_values.txt0000644000175000017500000000067210042140634017225 0ustar davidjdavidjSystem Level Errors 3xx 300 Query execution failure 301 Service Offline 302 Failed Loading Dependency Application Level 4xx 400 Protocol Not Supported 401 Invalid Packet 402 Invalid ECID specified 403 Forbidden 404 - 405 Class Initialization Failure 406 Transmission Failure 407 ECID Authentication Failed Transaction Level 5xx 500 Card failed LUHN check 501 Card expired 502 Invalid Total Amount 200 Data OK food.jpg0000644000175000017500000005146307216030510011776 0ustar davidjdavidjJFIFddDucky<&Adobed ]2<S1       H !01A"2@B#P3$%5C!1AQaq"2 #BRr3bSs04C$@cT@pq!1AQaq 0@ $<٩ΣiK,*́!fK^ CT@Tp Po\Cg/dQ\^Q:P+[jPկ%f̵)s:z(IPZrK0*,ݱbKM}RSƘ 'r0*C?=G@p LgI 2ƘUM--r cmIMtaGδjV63畄[]2L~ 1~XtwT+/؇xi?Qrs(X'ø^uc4'֠@6Q[A硠 tcMS[xcG"$5a[IjzVT:wMqSI'T׋ӛRyˤQY3}2Az>yYMjR2 ˧=m,3(=:9;hIk" +7 gN{dsma b{ܞd#=3pNꂖ}Nl*0(GVk4i7DTSS; VH`gE*N㠩 V%q;mr2AzvD -(/-ǟCӈאr6@8r[x$1ņtmAW2ZNן~~~ԩ:\qLC2eeѣףsimf6/Nrъí}1ϝ gaSƚIjnl+&ev*{ce7Z<{cҤl'yJcqH+M< ӝ.;s^eⴣjVי: 5ѓ \7ۗ:'Ԯ{& fVLTg9L^|/YvG;7Tz3@i93YjFѕ/Qp0iaL든vbn04JWjӞ5 mcz6$iX`Dz[v ˣ[+vp.ܐ̍̀Wg7Y㦮:%ٓvZ9Sjex#!d&3gJdMEBu&yVzN5=Zvi Z$F*JR8M T U@! X#J*fjCT?t|?("BRF2mY6 1qWxBepIp\Wɓh(V!$sU]׏\̪1k'&kPȂqUNQ v$Q` n*)v{&3/Ƹȅ$Ȭu3,Wzd׎!D?EL.ƏQD,ox8$r"""J(6xQ KIĀ^!!Ýҍ0!QEIQ}e#{ (EWV6LQEQό q_Vyv?,ԪӡeOu?g^l"=ܪqnmG,̱ )PQjҥ|y1V6ɉ! VY^~}sxTY0beg:Eqjh@H,qE~Ee7]]?zpi3(Qxc*Ӣ )uRUj~Oz_\%{IF(֎<EW Fq"b|; 嫄g6JK\FH:rkK.,."Ϲ(%ݚQ=4!|໖bAԫobWp"|opSAGۧa*5eŕd <~L+rwTOQG ;)by.S4P/ "+x%FuGL6i"fOS P2 A v  ,,)i]B0 27*9MK#*Í!c)ư.甭r9yM@e4)ecVl1lIU+!:p|y2#S.~DiuQVMv*ʁuEkfeq3uȨrĪclAvVeXrAXc`G#6 j#9Js xNTx\NvSEq2ƌ׃<3XN"U)ی>%jl'ۧ**3 1KC@BM{Lm-͔ʒSͳMET`!iepra( &:s[dd`%i]ȕlXYFȅ*wGBFKXsy5!gpxe*x F)6tQI|n<"WQS)yLZs.XcH #}΀D.+*B) ʹxگɾ>w2F@3#,9Q̷Rr6D%ǜl]%])XR3u0< {}-&D)r-^]n&' ;IA:ev9ͣ$]MwC=+WFEn3/W((B#UUy4dBuBF.iM;j46%IecâA J'LN~dT|"] ԇ1NbR B2`&*W12WL}(r\.RRLȰ\Bi"tu؄ӧO" SIN >QM2&\B.'aޫ&շHIMђdd'Ѷ2⸣}Amd(vr\m'A &P KAo5u̮es(Ql:2mاAKq _meM$"S};G}Wq\.?R =u~= FGOP˪ci6nqaGK'&D SCNhN:}c(УmvF2m9JRe.6^;ƒ PrN5u>Y2; Ʋ)wEr)A?r\"lt mu>Q "z &LGNK(ɓ YrErIm8!blalkh%sQ))*}G\) #G\Ꝕt#XkCGED!)uԍA"ȩ##PCi9 .JGq&\%r\5dɓmo:t?ё?a?rRsﱯ,"¸:@j1s!>P9R1`Y(pO%o qUIʸqmwψ,Qt(_ E.ֱ`6ugi cNCQ7Ҷ{SgldrC b ݄~'O֌,ԪRc/lÓ0MHƛKqRi мJ/<ѡPPKs8%1+P"(JYCxճڢ t-9 D,.^?q`ݡъяIT6굑ϳ9Q9[5jfRͱ1.ַ+SeA+b- v{\H 7rжTʘYDԞuyBLBw5Q@*'W4M[>LW^zy8gZAp Mg>ݳQ_C-˝(2ExnBr@yb ÿΦpE?qҰ C|uM-p1_*7˩Q i->#a@r:T 6$Gp;ͶHo G(,"c "o7lPu{C'=q+ʦg}6aJ!m4Ҝ 1qBݨĉKJ O0M{qG8" c8bq(5|i2s63DJ))cn Ň5SSR?8'2n,QړuȔʮ{H"lU3сüf8yd[1'TJ8)Plf9dӣ՜LsωU') r0äB^F6iŸ{bjݕmU-(ݬ/DzTp~] wԼm7ܲ8YvFi_5PkO E&X⸁rV*5W<iD@xZӵd΀3T.KitzT]ԟd?0&PĿЇmֵgw`|9 CJWZ":鵇b|ph6eLd [8ý@)5d5E:3pymA1#nhtrO{Qa|:f6\f|3#XFn&f"5qU>eS3<+BaLzqW%7I[)O,;ιֈ7P@et'TN3Q 0@ bLGְx$NpNeDĂ"NhmoAh4)FdkNobFWUmq^7M.o c{fzT^DMZ.fX?n$'<; 3VuO;lQ&eՅqsENtw!#|c]*٠#.Zp'|;S zc |WnzR 5dDa mk#s-vӣnԢgU-0DT٘OPe` #Xh<6L&#*-x䭺BdN|&k$ ]qS4z=ksLZf -pHB;81 YJQ۸)07\E΄̈́E!ڜ6a]( Sb!9NY2M6ֶn הj92D&V 0u#DI l"rPq52uM̃q4B6c=it7:e;8Jt:(j@0ڛVqԡfg]*OքHvhR1} fҾa_?|:Sx??!z/ُ1]%J詙R+}Wnr6ţ ./Y"t0вԮkR h?>YexF)v0J1h 75oҦϤzl,8ck;AS5yʌ JW~sH"S0d7ļ0Ĝ2̻rz#in _CAFYw(_,UoCݟoTy?0tE)KY3<_-~2D)`?tU(<&kpV[{wctX8Y@CDrS;Aސ3}E|t)T\(v4j|toP.y(6 _„‚S?/:|@|(S(O3Yc%ٝ1=eP˜/|@KX")-pXs/} d{&/5%/y-/,D'6'f/1^`}(sTesq 0E_7zV d߭Fƒa/D4?.czL`8CŰ)M,}@kЕTCjo흜2}8t;—G*0waч'dĮ1EQWDoj~r8ݕ N[.>&rF ˓U~#¯cj~F g֪?$ TK~*0ZP``tjhU.Q ]?Ի*91[DՓ `-ԧ8|Q"t-\R02b@!q4ձByUZiXZPp܅aCXסW. *bm0Q7=]*4Gys<(UX8S*̨@C PvWh]1 .m|KFBe- x<'#iWqfHe8JV53ev4PE;%hk8q%\v(2uҒR7m_G}'i?zc"s}TȱU>״b^K~ny_~XD߾~ v?yy D`uSai0ֽ;֭4FZ.=cWO%em+,FF`3p|R `2X/ ()\Xxd Me|1l]XUU5WdVZD;ha3({VeŔwT4NZ0l41%]Ӛ\c::K+BjPExcAvtPG%mw><(,)b7E XJ/Yy,yKD؅͵>fhրy.u4g)C8\悻[ˆ(imcˀiL^Ul j54'=|ʥϋe{ZtkcdLXͩr5/ C]+˺KTkcKn1eVv2e5+gJuP-h6%aa9@@Iz 5>aeĸ@PQnp0320hұ*E] _!\mLBS2in`XDV;6a(84j^** 6O]pmY*g`ЬGxp`QmG _ ؆|Ʒƚ68*mP+\ W%A9sR: 9yG;=,|\M70֡*l y@2]||bQvsUL PL'[p*;N&֊:m=#RM>7d`Z$>J5 h[aوgL.۶գ^&ס-ZL:W/q4śṟWK +0~g%2;NU)ۡ3}_^V`◳nm>pX;!gY&v,W ?T"@@qv=[?3$C7A:gb/T5DnsM)71,~G2-Z[s+k ptD 0\(<0=4|L ^-?:E?rÐm} lTo-)};1k9VnZ8X< OFiGVdX|2 Y߅CQ^wQ"ҙK&|- jd(`QN\ŒX2AF;ς^(.;uVwL{em\R/K3/I| 3\ mZ~[{??!u JB*\QtJ;@_QKw.3ofYR/ z?Ita5G1Wov4?XiV7.*΋gnum8q/rݱzK:}!w(G0:S1kE~71rPz\&~f]込SteQDu7%ыF:ՊVGNHWF:>_AlѤcL%G^S>`(ytռ^ѹd+b Υ+*8^@a!}/D]lzTҮT}j;+ @e%tBzJa78K71z]6t&2Ĩ* .@uXsXQ f$>r ^jzz+qMF= KO'M:lO=tˡ//2/Yo=ma p RV0AeC\a(W@cG_E#rb_Mj?H r.?Mr%D?!e!7E!gFk}˗/X@,zքNWӮ-AGg:a\]y[_Q}Oӿ?w?^+?s{G1w-1CH#5C<w\ ̭ẉ]v<~^bFt:m1Ƃ6ڿkcFq:Ҧt%Nqb*MI~s9ZfW@K!!%ts3MCe@1ZbQ_2);$s"VYчStI]F:]˾Pq_c1]6wt+DX;}/>rp %'e@%WĻ7a tnr$ P_f=.x !(G ss\1R,q8ZfZ#TyeOh/0k3O 1]Us,z8wՑ=-ne%Q&[ f"k=*0: #]Jbԩ^:%0S;b10]KKR Ա1q`=>N(R_C_RDqtORS*W@?eJfek ͤܨa>(vh_KE5#H/E~2o642t _nev2c*% bO+&W # xC9MW Prےe‡Y :!#FC^;{"O4Kǣ6*ϭ#8"d.#7?ںA2'FzGa1m|Lűsw]uE5!^1T]yEW䢪Jgyg ` N6*'GڒbRiR+w8bdXnPqR'Ԇb?M5`"* #~+FMo e:0r8FHRN&8?PD! #/Xx qYc$)-"鸫X_BrQsx ܤM_NÈ!Au2(UʪkX搫;1qQN@;,uX-ڞ;K ;a% j^-kBPL)[7nZTy 97pA"Krl;S|Tlj|RdRpQe2E+;0 )d۸Y )x[,תF1[oby2胰$NC&^øؤU67.i(aRw(eJyC=T DʴR%sQ@iz*,# %]+TI߶*8Ud wC1X؅͇%c TaɊ<LWk/Gi:&쳰ӼKF?()=tc;.T6-Vnf8+`ue nKAQ-lTX]wVi*˽glp`.ڗJɀZ+!6¥lBM`RȚ \fp Z6PVu be,#,=f!ì#b]p@6,Noһ/4^JB2 pDb*􍑶y!JbΜz,!aU5Y3&P%p 7 H-N)^f{0S~5+R MZk}KveW'=/wxii!c@*@ ͫh7"(M%zcތLJ~<_Ar"@@6^T hlgFgk rKgb {T[{6OF&6P,+c }H, .& ?%s )j/<} v[4פ p@3}D y0qHJkayCa,T,\12>·! uBYZ 3\ėwBHOPB Cs0GHZPaÙr wU١Of&D`ayFib=Ư]8mxH)TsG7-ƥ XbIX fIn BN˷GB+ K՗q5 V]o(x% D[1 O/ଞFC =`a 99u{KY} ea򈈴P+pFoE%ópoEtn pnĵ1JlWSQ ]UN .lk]mvӃ]!9bl7oX͋ʭĸl [|fbkyOQtfpMcr8N%M`r͵`~kEua{bAI@smʙb]uYMrơM=!2kCSSQW x儉x:PV%Qlx}KW`pgݙ9R9T Pvdp-DlE> NvZ^ CĖ))Š)@¯ːh-0v[`IzZpj4| udv-^sOh P??@7^ST\Bky%$N!7 WRtb+T RqQ :*s vw wͫ<`(l@B )S)VV46È͠ 1P$\ F@"ۡyd)M.Pjh4@n9-_($ R $c5VkN.^hI~Eg/ {AV*9\6Ne;otdKgbV#7-U)V%T,HQ eBμ=a;4,*u XTe+'5˭.V} A!8DUHX@[uQd(/*V 0=T>-BVBN+@Bc ؝x kibP+B(,5@2!/]4(L_3l$]J}GTʓ I5$d I2̆HQ)kR"@XՀ;}LS >LNțuP2!Hlм h#|Eik`mOV{J*l^Uoin^f(^@-mTX!UTsm,MKXі;{Yl<=!LZ@m3%TYb6ȼPh(]`)̈́*TQbfo}^eӈp2N8A8cPcE\ZZkuNZP3 sL |eelSLd4PC#]&βL旺L:pm[y E+e%:E%lXf rix*xv Z+YBsfyp@kÔ[y̵-DȤ60jfPjnXwTe ֗̉b'WzfOp{v8j/v)C;`22+>{Ho88<794͖aZ.k*J~SRޔ{RUc܏yqa՟B(hr{(Tؖ- *|Cւ[Cpc?#a|!BymwU?y}}.\r˃|t辘/#Ә+C4:WF?MˌcEJFTĢcyFaeOg ~>b"}wbS LJ@Tz3Rrr$2s`:&oġy|@P+X:5wf,.C /N3!گ{?DǴ,XǙzV^6< }K%{ǒ,i5z"XLrB=ZTpYMfdVV=sAetkĿ5-Fyx\E.< 0U5MKߟmwo8_AK 5* Er\q(3{.Rw@s(>X%c0~ ƥK{b?)6|>͟C1bѸQg?!4"BF1i1lAljp5Y"c' Rbp!= Bii8?sA!4Ltodϴ3E˗,<p 1)Nu)= %%G&8F-Ep:^/GseQi#{%7,LjRcQet?<߬ISa]'=pєz$Nc]KfP"49f ]DD#0x^UECmTDpL[DF˗/K<@ i垟~J;Ys&M,_y Di&q9~U-^(YAu."ķ03 xm=#ZQ=?e|/cUqjگ{r+9g(/]1m`F~_!Ig2ZEnRwi ijc3-iK9]qT;yhk ܳFz5;9 +q0U/ƹ}8a?GPv>z"@DI@.i+q:y4$=D1y Ww3uϊM$Ns_)?ru]p}3myT0Dn 2ϡCyϿ~"EphRT1ܢyk&U Dn;b4V 0 !Gј+`IP(w>t,!5 Hr8})GVT),iۤW>F?_xW}$B8Z<pe߈7'dߧzk6\ 0{js*8QPk?D{>biXނ+R#=Wl}u(SϬṯ: 8R9Y_|J /_hqdg,Õ~"033z kʸW" eQ酄=0tXy]B(nY:,b O0Q+N7yJXnqt}ʿ"[%L`hԾYCS㷦_RǷߘ`UYs0V3T,1G1V+؄D?Pm % p> (uY6?W?ѣg 4)k8uqaN-wıW)҅U{B_fYɎ)*}VDgSM-~3/?;EAB+NbqCGwX\z[!)e)//0UE_%޾ n*T>|=&Bf%tJY3 q*nچQjRa%4LaWU`*Q)PP0-FXxFvU` +C.%}J1E9bYb(spE6{)'-: