From 060a665843799a01d856ef1f4e65f3f7e0e4c330 Mon Sep 17 00:00:00 2001 From: Yessiest Date: Sun, 17 Sep 2023 01:32:55 +0400 Subject: [PATCH] Hardened header parsing, basic cookie functionality, restructuring of Util files, proper encoding handling in formdata parser, other minor changes --- .gitignore | 1 + .yardoc/checksums | 21 +- .yardoc/object_types | Bin 8236 -> 9047 bytes .yardoc/objects/root.dat | Bin 148477 -> 159880 bytes LICENSE.md | 660 ++++++++++++++++++ doc/Hyde.html | 6 +- doc/Hyde/Cookie.html | 98 ++- doc/Hyde/DSL.html | 2 +- doc/Hyde/DSL/CommonMethods.html | 2 +- doc/Hyde/DSL/PathConstructors.html | 4 +- doc/Hyde/DSL/PathMethods.html | 14 +- doc/Hyde/DSL/ProbeConstructors.html | 2 +- doc/Hyde/DSL/ProbeMethods.html | 222 +++++- doc/Hyde/DSL/TemplateMethods.html | 2 +- doc/Hyde/Error.html | 138 ++++ doc/Hyde/Handlers.html | 2 +- doc/Hyde/Handlers/CONNECT.html | 2 +- doc/Hyde/Handlers/DELETE.html | 2 +- doc/Hyde/Handlers/GET.html | 2 +- doc/Hyde/Handlers/HEAD.html | 2 +- doc/Hyde/Handlers/Handler.html | 2 +- doc/Hyde/Handlers/OPTIONS.html | 2 +- doc/Hyde/Handlers/PATCH.html | 2 +- doc/Hyde/Handlers/POST.html | 2 +- doc/Hyde/Handlers/PUT.html | 2 +- doc/Hyde/Handlers/Serve.html | 2 +- doc/Hyde/Handlers/TRACE.html | 2 +- doc/Hyde/Node.html | 2 +- doc/Hyde/ParsingError.html | 142 ++++ doc/Hyde/Path.html | 2 +- doc/Hyde/PathContext.html | 2 +- doc/Hyde/Pattern.html | 2 +- doc/Hyde/PatternMatching.html | 2 +- doc/Hyde/PatternMatching/Glob.html | 2 +- doc/Hyde/PatternMatching/ReMatch.html | 2 +- doc/Hyde/Probe.html | 2 +- doc/Hyde/ProbeContext.html | 4 +- doc/Hyde/ProcessorContext.html | 2 +- doc/Hyde/Request.html | 2 +- doc/Hyde/Response.html | 2 +- doc/Hyde/Server.html | 2 +- doc/Hyde/ServerContext.html | 2 +- doc/Hyde/Template.html | 2 +- doc/Hyde/TemplateContext.html | 4 +- doc/Hyde/Templates.html | 2 +- doc/Hyde/Templates/ERB.html | 2 +- doc/Hyde/Templates/Erubi.html | 2 +- doc/Hyde/Util.html | 287 +------- doc/Hyde/Util/FormPart.html | 247 +++++-- doc/Hyde/Util/HeaderRegexp.html | 274 ++++++++ doc/Hyde/Util/Lookup.html | 2 +- doc/Hyde/Util/MultipartParser.html | 50 +- doc/Hyde/Util/ParserCommon.html | 372 +++++++++- doc/Hyde/Util/ParserSorting.html | 120 ++++ doc/Hyde/Util/Query.html | 31 +- doc/_index.html | 30 +- doc/class_list.html | 2 +- doc/file.README.html | 2 +- doc/index.html | 2 +- doc/method_list.html | 46 +- doc/top-level-namespace.html | 2 +- examples/cookies/cookie.ru | 19 + examples/cookies/lib | 1 + examples/cookies/readme.txt | 3 + examples/form2/index.rhtml | 2 +- hyde-0.9.gem | Bin 0 -> 35840 bytes hyde.gemspec | 17 + lib/hyde.rb | 2 +- lib/hyde/dsl/constructors_path.rb | 2 +- lib/hyde/dsl/methods_path.rb | 8 +- lib/hyde/dsl/methods_probe.rb | 41 +- lib/hyde/node.rb | 2 +- lib/hyde/util/cookie.rb | 23 +- lib/hyde/util/errors.rb | 11 + lib/hyde/util/header.rb | 46 -- lib/hyde/util/html.rb | 8 +- lib/hyde/util/multipart.rb | 18 +- lib/hyde/util/{sorting.rb => parsesorting.rb} | 2 +- lib/hyde/util/parseutils.rb | 113 +++ lib/hyde/util/query.rb | 5 +- 80 files changed, 2577 insertions(+), 593 deletions(-) create mode 100644 .gitignore create mode 100644 LICENSE.md create mode 100644 doc/Hyde/Error.html create mode 100644 doc/Hyde/ParsingError.html create mode 100644 doc/Hyde/Util/HeaderRegexp.html create mode 100644 doc/Hyde/Util/ParserSorting.html create mode 100644 examples/cookies/cookie.ru create mode 120000 examples/cookies/lib create mode 100644 examples/cookies/readme.txt create mode 100644 hyde-0.9.gem create mode 100644 hyde.gemspec create mode 100644 lib/hyde/util/errors.rb delete mode 100644 lib/hyde/util/header.rb rename lib/hyde/util/{sorting.rb => parsesorting.rb} (97%) create mode 100644 lib/hyde/util/parseutils.rb diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8966b04 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/*.gem diff --git a/.yardoc/checksums b/.yardoc/checksums index 4cc44b3..55daed0 100644 --- a/.yardoc/checksums +++ b/.yardoc/checksums @@ -1,29 +1,30 @@ lib/hyde.rb 632d043ed6e7484bca963c37e1629e1d88c89b69 -lib/hyde/node.rb 93d92492390abb2b55649c6d027fa3a2cb8f2014 +lib/hyde/node.rb 6f1292fd88ba86988aaae75fb3d94c473b667654 lib/hyde/path.rb 42c04e42ea879ad73f639f2086b3ab5ca40489dc lib/hyde/probe.rb 673b55e9aa8c8286a770bd23ae00bff1ac2047fe lib/hyde/server.rb b68b932a689a2f8dbebebc2dc7eef5972c13d974 lib/hyde/request.rb 792427908273455eb694e13d3517cdae43af3454 lib/hyde/response.rb a82971ec8f86201de804e61b4dd277b48d28e8b2 lib/hyde/template.rb 69e46f8095e5e06dd69129ed975a5ce1e6d3de1f -lib/hyde/util/html.rb a29b85ecfdcbd294efff67a12c509262ac9c49be -lib/hyde/util/query.rb 212e9bd776eac429c2b2a821586ccce97f996df9 -lib/hyde/util/cookie.rb d36b33ae00c2ae8eb7c007bdf40a232c46bda24f -lib/hyde/util/header.rb 3af726c08bdf54a348238e76279e82d75bbaacc1 +lib/hyde/util/html.rb 258126a18f9e73068f2becc09a29bd8461941400 +lib/hyde/util/query.rb b1e7b6da88a5d29198fba7188660a13ad47ac499 +lib/hyde/util/cookie.rb 9a2eb0fdf1f716380c5314cf8fd21df2a60c8237 +lib/hyde/util/errors.rb c275348668ee17c6478e60f3ecfb0c3b9d614480 lib/hyde/util/lookup.rb 5b8e28a8471bb786f4e0a0d616885d238c806661 lib/hyde/template/erb.rb d8c808a1fafaa65b400780204063bae6046e2f54 -lib/hyde/util/sorting.rb af30f01ce32880f7e3e2b0ad6c2324c2fded9431 lib/hyde/probe/handler.rb 9b3eb4ce702c40920c04ed0cb3b93984683de447 lib/hyde/template/erubi.rb 74793cda58f79d6be3ce4ca25dc231296485bd4c -lib/hyde/util/multipart.rb 37abad9fbe044855d02d36185be645f07cb417c0 -lib/hyde/dsl/methods_path.rb 57411e500d5ac565164a1f7250a7141f0f206e60 +lib/hyde/util/multipart.rb 4c29eea87eb2093bad9cea228032121021aa38b9 +lib/hyde/util/parseutils.rb 14376e92c85209d4ba46f4659652bb87a28e3a82 +lib/hyde/dsl/methods_path.rb a695d61027389799893e15db5ae29de2a7511992 lib/hyde/pattern_matching.rb fe86f6529a2d22c128d9d3a74217862e9fa59c15 -lib/hyde/dsl/methods_probe.rb c18bb7f712f14e4d08416f77969eb0af07a4f3a7 +lib/hyde/dsl/methods_probe.rb c9fa8a2f095f6c99d356851f65786d70a288f690 lib/hyde/probe/http_method.rb 26ba5c9dabff5508dbd6efcba1711d6417cf957b +lib/hyde/util/parsesorting.rb a5eb853e443944ff678d70652fce86998c6b02c6 lib/hyde/dsl/methods_common.rb ea06161f4324127fbbacd51918eadc4e059e4fcf lib/hyde/probe/serve_handler.rb 6c1d2a7b949700468342ced4c23c7fe60df4d3f0 lib/hyde/dsl/methods_template.rb 5ea0be9ad7411ed0d5bd50435edf2b7be9bd0b6f -lib/hyde/dsl/constructors_path.rb d9fafcfae4d6aab9bd85a7de955eb74f8d4d5937 +lib/hyde/dsl/constructors_path.rb 9609d3470a1b0f3c30b83a15ef7683ab8c751960 lib/hyde/pattern_matching/glob.rb 16595083bc8d6f7b98f4adda502a05bd411bc1ca lib/hyde/pattern_matching/util.rb 188dc7d5d9a9a6538a01943a83eb132c385dc092 lib/hyde/dsl/constructors_probe.rb c447b87b77ddb476584a3d77c5bf58f8122492ee diff --git a/.yardoc/object_types b/.yardoc/object_types index 2e75a58a8fba1b206779ee53339822822d1652c8..79149b8257323f98728099bdaaf5082c96a385f7 100644 GIT binary patch delta 906 zcma)*J5Rz;6vuN>FuoF%H;G0-4T3R26NkE3YY~D{s6`i(rYToJC?#!CnMj-+)I0hO zjDw^fz{J&oO}~m80+*qUl(?SrJOA^3-TO{Fc!BHDcQ%uRLIEl)yo9&4$MgH(QnFEi z1d-QD6@thLiVBqzSOULd+A{AOht$+^!PzND&1Kye|M&2k!5FC@HGdOD1n#$!F zjy}Lx#2U-8sSHOYN!Cm@`*oW0_EB>Gh{kBlT9jg#bc*H~ile#z!l&d>exMvpHqh1l zl;50wAeteMX(Y@$n?t=>GBxOAM0r_~C69UIfJRKa4-aYL&?0|VdI*g-PRWuaR|$em zQ1STIHh(~Jv>voI0Ep<%V>@7V4$qWU6j{M4UTo>bcW&o zxjFzWqnyWWFF;W)JULyc?Vi*xJTf?4H?GN8#%8T|h5WTpsY9I0bS5TL63QlW9mQP! zX|V{!GT)UEB)#o@w}A+1`F#se6@?n)i<(ryy3w9_wq7+jP|z00hii%~LRH07_-~5kx^~)Emkwe&DQK{OpNTlFUuahR970ln|NhD*B00Y4SZu z2_EI*{Gt+wa#fH3yK-7iVo6ErW&<%crpYeiVw>|M@);*Dl9Je5Dz%VlavF=sVD>#c_s-S`}=+0_s8-_p6Tl9 z>Zgtmp?g&_WAs|@izm$Z59MI%+wm4<;W?T5?tzx(2&sqbe?}A%`pYS1o}i$E?sDfpjm-|9qUaQ?rZ~@ z29}u0|FOgtnTzOEX_UYik$gJ9LoT}CH^9PPh6pNUPKI#WlKB=ZH4_6Z&$<_4J1%Dx zL9b{_vLl8L&$$Fi9sP5238Zw)9PlvEvSb~rx(fmVFN0-Jg(bDfOrD{MgDMA%Us7Y2 z|Vay*7Ft}st;BO#y05Y9#8R+;Qi`hy%4BiR!yqrSs7&`6$7njtS zKZ-srZ-JtYQcDuVj6+7pcViS@p=*YvvcW4LC?>1H*3@jDb+c_jt36%OoxQQu-pnRG z3kmehu;&doT8tHzAUUAc*|d@bzM`a9LL+yyIPA?d+iGG5=0IF#g=IjFCEiLNBdW;`>ZyR<+HAK= z?BE=T9baLIQ;Q^wwIn(%N&XhoSc~MSMb74SlYDrMy;jmNnBHPv+#-ZTYEgY$Nqc>b zufKi1er0| z9yUA&j2*X*Yz6Wup%-p5(b&-g`Ok)NQPe)V3_hiYM_-^7<3m}}ZD53tI}VST4{(W^ z#~mViA9`-wQu?6N5=CJe;##-hFCV?rOF zZ|?YWQ8~b#(LW z1T1`T_Lm5{$MzERqg8V_xm)HeH34 z521~9vD9u)gHpQQo(RPq`|NW7rcuM(c2qaiMbjtemWp3B__wz19sE1Y!4aoB=7LnL z^J;NSYU?l73A33-qvkUxrU&OgmK0R!XC;@2BY=Mg`#22zi*2pv-)VHwf_u?eBNp~0 z3CCSbTJ0{Mg8iNR88B?y^FFZ55K|8PbJFVjMc%=JLN0f_-g=ghyk4T) z{$cS=kVm7Ie9Hm0%5eQKRou%RnYSz_q_`I?OG~AvmZhUEzgxBe|K71YQGZ$IGV2la z%JQg=W6Ps~)VWcWpQZe>JMC_$qaiC3;c`dT%1R)2y0PF+dUn-T{OerJb2qF`1>@dc z+(w7z2!T6}-Wq})#|!k{ni3!{pxh{7;q6vp=3Gsg%uvzf4_b)fFIivMWU#6gG~H<>9P%*_$R16 z2}xzP2NGiTRlOjXsdHR3eQ47S9C&rp!~C;-Gsk>pb23P%F0G@(q}F0D^9GOa?Pq~y z=>A48^|Oq#64b=_9AC@W3JWavKs$QT+51iOCc2Yc*0KAFA-*I0?s6crec9>tkV&r` zkHX)At=G0B!fZu?pSBEz*>vdEX*}<%6A3W04Q z9yP)h!7TfFJVesZAClk-_1ild{}%5}giCbZ-p38!`8x6}#7sKb;t>-F2vW%s3QEhQE_ql ziNVhTx6`YI(e(cPOHtCK0~h>m^y3|J3iUm>Q#akuk>qYtQmwF#AI>_qCqg>z-8G8v zoXzLM^@%EfQ|KEVhfP!byjm8MTK1;U&3_mK)9Kl#){`lIH2uK@sG_S6^})Z79!fXd z#<{@eLw`Awet5`aKwdIjEpP{IY|8e#M=-jT795^p+Ui{uCbd;O4`))&Q$!*Dt^_2~hoL*5l*Qd{vM zSH&Mb-42K7!eH^8qOvnIN#G= zEzjljT*nYi9ofcjL?ze!xGqxY$|F&9z#j@snASQlOlqsU=Bn<*AMS>0wCaec?#O_i zs~fJZ&OgGRD>=-cZrl+`bB>NTh538cgh_2pVRX+6+(6zq%F-~)yE|C0J6Ke6Q(7~D zN=KvV?&q)5m?OP-86bGcRd}hN?Z<+L6MtIt!cbGLH&d9@GL`FM>d!AQ$feE~x%eAi z9N4q?5g`dzC^w*A41@wg< z1BDwd$nc}`ANADvS{9COyIwm^&IwlO^4A9%&iT8Y?;hcNFTZ{ZoTKyJNQ8E}_KnM? zc5ek@QmeprRA53Bx>(KVgu>wHbi$v#xdkUK+vsO+ZsZC;2j_A-adh*^NP6&3L1;UT z55^5aABSySYdsmPbTaggw{rth;$B>_@kZD<8Tv)v;z7XZUJZ;B*ycb;U^&So z7QUlL-hP5$P0lmZ^Dg;1Eg8`kKOFVdxnz+ylbF=%smM(!nr6K-6N>1DcP8prs2V>; zKYzz$s0eU`xvX+2A6yKx0ytYU0=R2r!5J7D+TOiihiFl>vDr*B{@g<51&D~8hvD-A zSm<%+i+10O#+*%rc{Km*G7epS5=~%6$LX^p2vl^0yoaIl#sJoM2GVHh`)1fk8{hvC z|2BM(2s3E=2OpcTMs1s8QY*_0w=A(Zv3>c&SFzNl^HcS!RcS8ME9cLU=aDmvnLqew zFit2VFZ98`!!IPlk&em>bA8}Aed$w*f%1)?jmN*cKf?@q^s|o;e&6SJ1zvU8M3H$6 zeev_YwDzyb7)P)DE63XV*VTxX@x@c5m!6*gVkv%D&_R^&2wn2!63n{x<*Ot{kCO`y zdjD@72-@`3OQg4+CeGlXri+gtDE{jQjRWILs}Wd*|bUfE%+^n#aOU4!tqukJ9`dBKT8hAXwF-jPdjC4OurtSY+ zV%+D274hvISo+>?*BK9c!SlYG&r6S0OmRNP=?C4-dg*di4i~@v;}< z+#h-Kh5WRGKp;KvQz`%a@Dn!dp*VOA;5hpnU?YhR6azs!fk^z#9wIQ6!$Mbvvk)H$ zfFqr8J^%#K6MDfmlH);#9QJ81sKQU7FQCD7&hmvMj<~`fZY4zuVq5@(LlMggfC>1y zB|sqF699h!(-H5^gGp`YIl?=SspekKd;yJ6opp?f`0v?LdFz5-0+Q%f`$ z9E?usmD%MZ9&|JT%($#qMyccMZw9ynHQr=|5-{Ex=;@uS*y};CLTeyv+0e&8R2xZSl;OgQk_C^G3!0gIM*bHy8uOnfK>1|PyW?FB?kT69Z zzgBhHqTmuISr!9~e2$gh7R7#wfr0Qj8yE|V@pB*+ZD&>IYq5Y!muuPJczBoS3@W0T zjZ1(84p=qh8T>FQ5k1|i&f$rG=DU^^Cc*8-RD)LBGf7bIt*BBH+y}Yrk_qg_Trb?& z65MH68|YBOxBEqBpEU|Q`?|kRFplyR-!-_lAeaDpVAPd;jhY(bzuW zeP988R`wAZ-Gw6#SJBK2SWoUX@bRucTmA(E!@aCeUzp8d6F%YEWPAIAFfP}qEx2ab(}@LYq^ z7c^ZGm5mu?u8ey;B~VqE;WQ|nWGy?D32O+}`QZYo^X4ol0P>llhBvbz8$M${W$z?--T#p3c{7V$B%i?5)_pRjxKpji6E;3XC&wPJl@K(Pj5(V+uiDZ$kF zk_5jC24&8AH5Ve-*8?C3E_8lB0IWcI8->wU41z-S_klq$MEu4!!Y&ui4om`n7Ml-A z(3=g(hpqVeQ$8e;RAc9*e0UXfc}9oKPRkI*-Wmc6p@Lb5qJR}_~oZ9)XA9|^A`JC;$fG3d{t zpyJt3;IpHksMkv(JUpLGV%Uh$V9@rXJT`SS+z+3!??%JipijLE*N=f4C9F#u>S0oA z0;xuB*?eko%wvzp1KQ)KMq!U!73nTj9Oa8RY|279c@E>cngS z{$@XALni<7Z53h&>TE26H;F+Ca-@2xb!IWPejeLZjQTVLu_MJ$YBB_Qag0eV#|CzK zG7KV85VMuQ@30WJTDZUrrO+2YXsU@g)j6t$swl5#s)M$z)deQClrQk^kR9#Mr#2{b zF#ldPC`{#nRM-trXFmv;NH-Kq<_e2g{de%$ zgfa`lO)JeTs5VHQKLvZwpK4jjWatxj-RV;;Yo84LuQPk96-|Ni_*KAdq&A3^MUr^7 zuL4GLv-r9KZie{%L0!7E0Xu?`^Z97!im6a-2z)%qaf719YOY0C$PCrcmz7mRCD_>h zYV2?lyIcdYxW3OR7Nwdp1b%1-l&R3!Y_-HLzSMv^M?Ot_+Mp z28vWoKN{rfiEZpYJ0whRZfTN+OQXgWWhq;985>VQS$nwHQtcF+MGrfXdZ zO-DjA+=Mz;&V#kRsBTLNJGCD5HlYDbBsN&>&*5xq14sy9Pd7jY&)9rE-iJg7GiZeE zIw{x@&F*W2dteY7(gf4l_C*jH9EmkVsWl+#lTGk4M@{Qo+Kht^eLm zlii9ZWU|HK4)(?}Xd^p=(Jv*y4mN)|yhu;|=ohb?q4BCIrFMa>rNv&)b}xejA1ir~ zEm#3d;UN2B1x6A(I(^$!}VP9;78B$D07vqdY({OLnBL({=c!X03-VCeBzz`81 z?b{3kU?BToGtA>4+`AV?op^>{&5 z*kuz2-=%khFFSWPj0~FPT`*${R7mH&+YFQ1HapMN9UqB<{5zgO9_zyaz$jOpD=hyoc|0>hqeo>DP3bBH;Eo!$=1LS5X7LV^*Nu?^#`4IvIi9dbauZ6VDa z7r~y~2IGUb{ue4_>JF&VafPtMJK*l%2mT9Huy-fSR#c$h121I4jIh5p3@Mfa&f-}69^u=$UGEV@?bzDK}F z(vTJJMWav*(xO%Il+ZMm+gSv={3zVVi{|mo$r5({F&LQ8H?+$%909IrczS2{e$asd zOBA@Gp4UaxQ+@#IpnyGh04A9VLc3Hy9IGGUA<^$3+~vzFUBX^`3Lb}Y)_4eRF_m{M zb~6@R>@LRihv6Y!s*QCV2075KVd~q%a1nZ89;|0yJ`Jmro4eA2bw~{EbW`j6-7|2T zQsizH^&G58zO`GCyLb`mF2bIB4!$z-N_MzJ|3HX-{13P`NkheSo1V9X0A;I>z%9wN z8^L=x!R>B>ory_j z?C6V-!v6IFJR(k{v-@9!2{q}VURH`p#Y)|iG=%)RNty*tiHTVd-!I2Gkh)`>9?IuG zyk&CO+CRe61OZ>($R{haUV^dg(JTmL-&TMzOX^{+#%FEKSxs}eGjfC~UQRDAHt>bD z`Ta`x9R_=|)KX`c%Eyk%%g@c{!Lp;?(c-L^oV8MmbH2Ua*v04l%3>N1_;Dys;Un8H zX#Xgb&)iZyQFo8mF0klW9^4NRykeA|U74J@qId7gnduqBMp*k>XX-Tl_`8YNyqTW#}Ntqwz54dYg$Xf^HnT2&-a)moq= zxn9kcRf}`P$`aSEvdT21YVjNd<<`!a(Ko$dSaMZWhS{avId1jl|JI^P$!nr-zvJoEz+op;^wY8{aIv<0)GhQ3x*mq{H5DFykb>n zscr^^IaV#sbFIg9F}s*-)M!t6ZpmZC@gFoV*ExMfbbeDvSeUzapE)>wr zHL#$S&n0_Q!mli*Q3sm3_c#`X<`mWDv8P{B4`*3bPrCcM6IV~lQn$92_se#6QBlIr z=DYQzswr0Kg`uM+PMlCQX4b?}6=N!drUt1-;&Na4dHI7>_vPw~HmTa_obRw_I;&gk zEnN-gHxtlkFFLHPi~3?veK8*G^}Bv=)Jmc%aDW!5nwYZY%X_A|zon`Q`=iFoO{}n> zn@bh;_Opl;xD?jKv&AU~IaHlaEi9fgh93}WOV#+3UzR|WQzfvtBoII@L3)9lvQ%+3 z%a^4r)e5*gqnlOq=lI3Hwm>(D;uVQPT(y)ImM8*cYXs{1i%r6dsq(~VNTE1lC=v#M zT2}^9k?O`tl47q4C%$85l@%5Vr`A^S%l7KB)&`rSDN8@f)_@C7I6UK8k6lX7&5&&M zHIlenC#C0QWOX&?U!XsN59q(C0wJF=hmphB6A^g$!#&vivM#yYsxC*lbje}dG^z^I z-3GyRzC2^N2Otb+c}CUrZZk9j67l zn^klaY|sL1QBUvp3jiUSpqTY*TUFbfeC_zRw`WgP zwe@I0zr8L^OICDkN#&G~yLWm8*lpvzE;b9js7;tJQa!faFSJ;e(iOE0cAXM^1>@b? zskisI?ryI6{U$fqs~xqDYTJSZOQeOaIf~-BJnQ=1!oXiyOru2`p2hzxye&=B1Fqiz z+#U>FhN`_HEwD!=dOCm$RfI2I&TXFH2nQ9pyGDOSahGz%y*r!-XctW|sa-VrNK7T8 zG4c#{sa|UsqNj{YuXna!q=_yMRo~Er?a*w`knm1?>jGO7W+14!8H1~43HLXoW+x6z zIC0dt$L6jpWWR-E>UC&p+6;xjjmIg+ezla-W-< zH(=K2!U`q+wxpoTuWfOlWbQpAoCiy;^)?<+HlbcxgxtI+-x#uY8;t)$GQR!zWLp_? zS9R^={m@1}`Cly%P1Bra>o29%NNIDVw40=~@p4*; zoHkh+S5l$e=0yAZIUKg~6vRP>sL`Ok_z@=QaMcxQp$w=r6W7CD41ycN>?@TCU6eaEie0dZ~3S^A1PN3(?6cy-Pi zX^~^W0;vTDWghO&u{XC!d~#>pz0;DzMi0gP-S=+A4dV7Nar7g84IW2m4`bkf=)sP993ZeexV9Rb zC0n!9;Ka@`2YZeZ9tg7yLRnpk;Ay&YUlgZ@=QhhzoXB0119vTjr75nCPw*7osKo;{ z$pKtKZkf4*#FEs<$cvI_ewP(nJ!- zPOgEZHU;L=jZ`_(>0Ds9)z6TAg=STPie{urMIrrB6*HutSE}CX>d2Ms(i$-JMKyWP zf^)EwxldzYM{_+FvYoKOw}HtlOK9Q!@!bN&v?XEG{ar04OUMDMx(!%>qt*puS9e47 zUA2C`3a_qYFQa`yGvc`Ds7P^IkBsLzH1Vlgkez%I7u&zz4F)6{-UGgh*oLe28CfmP zS+>gDtgIPgXIhy&440~H_@IeM2rz%-g#TMqXRnvcX5KNdcQm!yyXWE%69QVS^X8Jp+4t4|O0{0}1A*yydb zc;Y@?cq8V#14_9InZjPY16Ovd*q_!yT3(iZH1F|ZghI1xZIPO7xUA{8#n!^t25MUQ z>Kyv=di3_K)f`5pEq&NuZpEWW$9eZOv8J_nGUK??rT9TF4(C@yclihPiB?7)-mj3( zn5HIYliCT7yE`5n_)mFZ4T&U*>DNJO85*p3ex|Z}jWa}*%3y)DdfV|twBrhO9ou8a z53@PzAUnv}+QM}*96K9+S^GMOs*<`Gllp*-XfB0`?mYCD%!+!^QS;cr)?DpyVB~g|gZhrq_(iA}qZ|2gp_gs{1HLL__o zP6&zT$JzulF7Epcm()`1wL4(~drgOD>*pT^BOAH`jDec!*o95dKNPKGzH4@_!?Ae=nk;=eZy+21>LqmN7{VZ-mO31i zTq<#m3U+mHac>?f6}SCAs99quSGyWYn*%LnUUy4L0p1}sI_%P9uA?xhGUY9#f-d$W zW(L1xJJ{rHwuM6PDNB*ma#d-lOpIW}j)d5RPz?zQx++#Y4?5fezEG;9#AKl>BU&%o z13yzeT^NnoHL+JrQ4p0k3KB#3{Z}&V>unDe+B*u_ex$)&&@=pqw}-LgPZ8(~RQ_9Ufl&@y*E<_dM}fop|2_kDQ7$p5GJip5SI!2tO>1Iy3U`gTamf*7q3fBaa60 zOAPFrWAG@oek5)82W~*WBYVq&*MGEwjM0{y3d2T zrgIa<+|+|fd0CXpWL!&c+|*?>DQ@ek6}fD>4G)%ioAEEzE>_BZWBsBA&Vgdo8Xnk} z$6=RlNVvnq)}Mg8NC{qkItf)$NqCp$ z3DyPe_s%Ul1?zck7CV0m{z#q)R}O)f9tXW&1%D9?A?HrRTK<*afrLP}iQ%1>JKutl zV&}L-JAG0fN#kce`YY`yB70eLEV1hbsdmW$i?9#gf-{^cnH_o?o+W1lQx^U`cn0p2 z&Il2%cf|!8VcI)zL_$WjeG-$}KG`>%pSBp~Z-E~?2P3d8@jU0!aOK+3Po4~H70lSK zWDo#P&i-)zN(X1w$PT>=w?$z+-j0JG);F-SKf?%q6tuVUsFp6mi#becnbSoeb9w!X z%n9Bb;9@9tZ+`0)->MgpnsBNG(M29sLV#B$XPj1G=)1ZjpAHC`237%V7CLVD$$Lm_mX_nB{TjsW)}`6$#)`u~80 zTWFn?Ad}}y;$!^zibfAnHP=8?2>OT-Bts(DzQ4eCq$EP=z_0p}WK4+r2R`q^ zF#O$F_&$VyR1(p}K*yqima47~2{wKJs|3Nne+XyDLWLkdr^0{3M>r(mcmGGCcJ}H= z@G-Ca;03%sxt4uoAeCfo1bggbJa>&>es>U=WR6c@14sMz6Ihq9yDJCv$U!|{1j5>P zehSe(9QE*D@sj5OMS_REfC+{JZcV`R?8Cp};UUYHcx&-M1UvbqqKePI#H0AUZ1q?0 z8aat&@h0imi#Sf6bgAW;ix6ix=^=ESZTbq&$0vRb)Qn*7o(pS`FStXanzz1&A4CPG zFT-i_m5b;rS8yVQiN9Pyq49gmRa{>|QMO-&cR|Ybwr5Og?KxZ6GxFSd{2IJ%z+zi= zkOaTX2yQzP`~8Oyf9M>+29AkT8dl8OFtLmfCdEW{VOftH8&!rgBRU88kbRJtAKB%6 z76M%7vj#@u>k-i`q!+o@I6Tr}5}nFI{6U;ldaV~ZOKx!0EKKT#NMTYsKQaO{I!F8A ztEwDhnm6h;z&liCtaw(6dXbVTykD)`-@XjT_NipVQM(fBe%IQt-iq+*)+ zM3M>oizhx3$cac+A4!gr=OS5t6uFmt8p+N?kqZ9hw^w=o_-L})h@@Rt54{@oAKFI! zhtjC#SW;m4hs#!q%2NO z!QZ@{rKqSbraB1QX^7fRCn7uFPUNP9;`}v4bN)VN$E_6H2pT z`3cG7f4TStX0iuT*#$F+X~Wl4#9c&u7DR;G_;iB^zwuhPdRI||-58dO@EpT(k&olL z_~LjY@GyKAU%b;O;P{M-g6|6FF^m`WU>Gmz$pwL5iE=3be56!VgzvtHf*9uug7^-i z_~H{f0*>z?iVE;OL;*(=5??eS@tvkqzGzCXrI4urQJv;AVgoqF*7PRH(@(m4`VHLDGgW_w@mPc zCo}aSaX!VpWGbzQj?=^OYks&{;JOyA|K@lfxC@cnl&I*qe}$;QWRSFl?**u`xDZn2IBS+*Lj|Zzs&KJTWyR-RWY@-#n$ghOTsNy3BQlgUMz1!$ zJJ;EuX7*;LzSsx@Sz%w21chuiKDY^`Y&lO=@=uJu0dL=7*Tl71v6_OQOIybU&PwJ)(1m@S{u z%kf_N;+ADsP(JP^o0>Nqt)89gM|$fZP)??&e~e%+0Qh5khHssm?nffo6&(bjHRj+m z2E;VAC{Q)3a(iaAva$Znfin9l z1mYwszM$c00~abDO2vg2PUi)7xj$Yxc#4=^BDdMh^eXKFB5sp`RL6 z&&97_;vHwE&mjpu`-5cuRCRQ+6>lo5#?CRk@3>jXS!T;2=D2G?YS-gPvOK9355Hj3 z2g~dUCnWMHJ(wNMAt^qC5HztFVvyzW?57-j#;TIV=90dpi-OfA(~wA++vIF*u#!81 z)!si!9LKZb+JK6#KvMXbV~4ASaiL~&E{VLMBwSU43kf$fn(IXWnjNnCKUWs6r&vjS zxGIMSimi&P30HDSRQ!f;wdc9A#S#c2Y!7G2c_fCNZwbMi<^zcb;g8x+$d zE73)$eUC5FsWT})iD5_cNR;k;Fn0Ug0VJG#l}8Ld7f@UE8z2g;k%Y)`JSP%07sIj! zka*p|5IIRfo;`s0>&io9lRN3_hiuIXkz*Nu7X!Kdko!qw;l`3_Lb8x8OI(Uh{S*Ai;wraQocH*y^Wn8NK#=x|HL!X zAQIs7jIYc>OGV!?1R?q!M--bmh!}N2L9&Ub^eIiP^*E9RAzS=@1B$YSZO%t|jt(Mv z-R)c+e!oV~53KRa7q0ZyeBz6b;|NSsKGDMy%I~b*;Eyzpu#$YV{a0CIJ~8Reg~-Y+ zodD&Qj+I>G*k;}LJcB>-j>4sf%KiB71f$k&`Z5@d9;_f#P7|4aXfNm(%US7QlA*JQ z$~gke*kBT>Yj!73ViIoUWljFv9~R(~;f${nwjB)Bb{_AQ7=G$n_Bv}LgeRitwbLt|MCG&aT*0X zde69R%3_r4T5=Mrjp)$ATo9BOH(5~TI$+)ne)4ae% zC))!>4?*oRnw8kXyc=3sf@cLQz!$Bp4JnDe#t84T7C7XE$;z3CFzuoqZp4Sl%I;(+ z?v2cM{f7uv+(1;&V}mu@Dj2DRb!HDCBOod>LXCCG_}PXg>})3@Wp*_j-!r%J+05>t zBzDlJk*XVWVWo&|4=YYN)N#!#1ML|n7MF&S1YK^FoPhuPL3C%DjPG_R{}V!}=06AE z|EYo8m;e6={k$IDx00hotyGO7WqI6~3Vc%6Po{wf6Z%-mjehF7r5B1PI;{3X$HCvG z#ZJGK9kpOga}WE-g2Em0!=NK~S_Cb9ntOy!mM{!s;4?@%>)C`}R`M?XYCFP5^=p3k ze`4U9u5A4%hYsKLX-%91kRi-VgP?RmH*`Lpz3`FAj+nfxHm5I zm(6tZOOdPu;kt5vSv(DjRGf&CeJrWXN*evuXh!L{ib=TMR^Xc%^7UU&NNE;>)XreJ zx3=$lkx(B;u#$tps_~7UTrA6cV1%B zd~g$gw)>-}-+VHPWsks-VG|efB#$YcWwS?+RNV<7D`xK+flmG$W^XL!J~Yb*ef*~4 zIK4Zz$YGa95D9OLg%pqsp9&wD?w*|FD}qZqn_Ymeb&(J1^K@~7|6Cr%tP>c?NNWd& z@0pzF--_S^KFALu>Fe;^p_M%EgX0n=*)S^(M5le^be^Isr~4s~pK!V+wgKlZ@D2Yh z>G^p-OzK>CPt5Y;@rRDt3P@5QK5uce-AelDa4g{KJy>^k1olq+oxq9x6HX>3Ql_WpCn6KYIvjI;9Es!C94jeh{~k&D_}Fz=NhmR4 y_P|Ivi&KwgD|-=t<`{*H=0~D45U@?7NHAX)+A|7=sd9DX1s|ZN7`>nu&1&IpEAaq$Sp8-FEMr~h$ma9e|K|_^u!#&oS2kSnl2do zWM$`=O3kHZ(`Uw)%`lJEr_L%%N>B3FL<~SB@}E!w z+UTo&I;heb${U+3li%f&TKsSkhe=_O4&B(Hp2IlUgLH#E{0B!F73hO)@RBe$1T7VoP zH11m+-JRc!SJQWf(TDj%;Se1>@G4!FovHuKz?Y~_6DrZ`Tl3Q<{O37!2(IQ?kweB}Lf zVGp1`4ho{m0V-y1dya%j)u80;Qi{BMM0m8{Vfc^S0>j4}1bSh-O*z^J!IqphOt=1-w_ zN`6AjOT#xp1)V;E)2{QG?FH_jRZ32iyn2>@N_s}q-@YcnTV#J}Nm*Q7$M{XXdn zU5=+NA*f5ZU6+Km+qKo8PmsCA8a7>(Nsvjg)`jkiR{oDzmSNU_krv zGRVuSMEyE^@F)G6tZ6XM;Et8w*?M8F_CU*_6DQVza zZ!A0Eqt9#0OK$d+Q?XJoF>k^O_IK}L-Q-Ji5_g;=X*-V z5l_(PTMqhuDb?GurW65OMVp1w^IvDt&TGSLt-=dk$W<9Vx+0vGPR-=i}|q`%CGLSW$BHHmqiaz>(@F;B=b z(V%(ZbbNxW?fbbs0V+j?m9%dD%ZZhq9L6Ds2~Tk7Mvo+hwdF1F234y_k(D9Zw5?=e zAgGqQfu3Dd2xOhNXu^LkHo-a?@#1d$d;CQ{r0-tb53rBE`cj2%pQr2=ls&~EyRF00 z-2kWP=gUg)Z_;v3oxgl4kgJ^c5am^G`{oYQY3_=2I%ws{I0U+{XD|zyLaUSOv6Tu! ze)o2)`B>#^zH#-_9J+Azo4m+a6N}P@tce3XddqcT$ENG-I3Y$`bO&GK3%K0(o9=7O zHxV?^zziSbfD$82cS4uavo9Y+uFF^*fWXczhj7|(GSyaHrSqqUPijG+TT>&LGK~b& zx7T*S=GnEQ@Nd$(80giOzb+i0KOMJzmAcA@4^(nPFCTjQ??dUHIA8YfG7`del@TA> zutATgD>uAOnta%c09tzcw49c1?5ApSC8awzu0_23O^Z|u+~5~CEtmE8u_n>+)1%rJ zZ@vLQxoULz)_#Qgh&KCa>j0o_U0&@+u)750XSlly2o9JtV z*-qo&a0Z%CQ_*T>CwzeqJ@>K>6-;=OK`(mf%^2uGFTU9sa~=6xGJum#JDwEPo4Bnj zoTQt!_0ydchvTxfrC=na+5_P@h3&hQpZQq37*zv|YV;J{>m5wbY=2gD)5Q+QY2n*1 z$q-H8cA&q${S`cPnBgHegFqhov`yNn1T5+EyR>k;?d4rzpgZnv1Z=t*;kamoKp(dV zxhW2oD<$ovMwlmN3BC7BhvRhLo~yo@zI@uH(<2ANXxrX}vTnZCSf_)s=(!!GkVVsW zhEwjFtTx!!kB}^1y7k%MkQn>W(|v7=7|f+xUJIx5+t=t2<{IVLbk#PUW*+Je-Dv%L zOGvseJ^ZE?y%2ObjHbUIgtgrF6IFA$Vm*BLVvONfbTn3px+U3cJ6{~e^1~6zMsWZQ zzD7eqJiA&=V(9(%yXvs*Djl1y(i?2jTR&I^4K!ds4}sMEnaTxDk?C~kQ5~0<<|)y4 zr6_SZJ+*(hZn?W0Y`V%>Zj;k;U|C1(a$iSy@TAx+h~q^4Bk8&QA+*oI@ITe0>l2#n z5jEL`&KsuN<*o@fT{YQd)1>;)5{~JvNhWHN>Cgn-knyMTdp#llj41y!-F4WkJMAtX zo38Rt+vJZuvYKPM%kP2md$`Cy+VxN6XL-t3-4x|t7k%G>>W_ufe;m!#VcXUB*mTw8 zx=oYH4;i`cOCNp6BQNS$w?7q`=PA-xCFe1spnH#o(ZdzKEW=9V^vbc`dWGCgDmGoE zD&$hm(~Z`B#5!W9y9G>W0h7Z5Vk~IsQ5;Y1$A2CR`A;a8A}W?lkNz!;4jJ!FuY8=P zPj=S{o31(~J9KLNWTg%}-F51ZI`wzxL}O1#e6#-c=Nb)uLZd;VMg^$RD1CvuCfIb< zq`;v`-SL%Bu<8V#kg+Gaxcj8^#P2YX9{pO{{BsNLpPslS2oG5p*9U?(`VPWs#c5RmA1PYdh72q!=LlXFe=PGJlS-?p2Jsh<_CEG z;M?728!E_Fx!d-q-s;LqPIqk_#a1MNFB|_B=2pM|qn8ZXMT@Kburu38o1|;V*=~4t zEjZhbf8iWgQ+Mu`zQ)~7*mSj1jcBLnP+a6)b~+ph?sQw}*iU1iiO%?RIjND`R#fgZ z(`UiMaCsS05Fa@3`AUl*3~j8RVDT|3ON>J^_&zlcBU;Xv+2cOO;tXR#iJ0{wiR&!F zrmH4B_->OCD`x7ry99Bg;oh#`B`%s`*+lPt_71M#xL4@u-#%}GW>SuywJ9=SUL zY`ObAIUAow!s(4K3Jmw$ErCr*zTBR&-;>iV(Fyd0FO{KqHpXP#*}(^^Ww})(yrXlR z6@21K$vs}BxZ%@_Uv`d|>g>k@i#_>AmH0Uubuann9)dD`&X4JO+^61{6mImztipag zH~6`k3!ARyGWc;WUFgT>$KpLghp&eC&#)VP6P@{$PLbwk)kxtv%f7Su_V(i%^`zf? zbr90%hOgI>ettBwR~W?;1@xn>7Ygz3KQ7F`zk@EuK+iVo#cTp;ZEt;pIh~arxjYd6 z`dxX9f8|%PpC(-Wm%gXFp4fENv!|$MHxyU)ual&wpXj=RYu)`@?dqPOi>`f6*80)e zYm@1sZ#&?zdgr&D5WuvvK?|(y?zh!mu!Wkxr0Lj0W9!9toD*IH~*D6VvTj z&S>+^HpEQ4b&Nda?>Lq->VNw!1h?IOO{MndqNGKD(aJkZ5&rX?^{Qw$_`09DhB0^7 zs1n`ahwsipc=mtRtGc_vzx&TTgircqy(-TQ9(->R!Z+M|Q#Hg5KJ?d@5Ps>`<*HIQ z`1AK05q|ytVwKemKIcI#!f!oTteWBmpZ{A6!XN(jnrf~ae8MsK08X8|KH4EMYFoqk~BBUvmc{SXm;jj`Mo0f5!_>0iwUd7DDBX#PAG2H!aI`~Tb3!o~ZBVNO?8uM07yKgwrF?z7$gg+hksTPetClD4A=7$?ybbCS&WP++pG`lpJ&8$)bvs~kA0=ub! zw_y@n7YvhCliYAaLf~C8LBZ~HfmGUxaib1}T$ss*hC&e*uZBWCnW>>n-NZ!}}AQEnJ!tbMj!4cM~ zg+W;ST?2bL=w|}7r zFOKz994t^hb{kb~Jk+^c;JbL(OcY9%x&pK;)&M?in*s7v3Z-)uG_(5#cu_e)VYMf8 zTWHayt~BocAXSVTUSvmTbZ5^x!mE(XgaoKoCA;D7N^s}-vVE;#&vk!UCEZD!(c4Vr;B~}(%}QW zS>d+ZW7Bo+niYJvj~?%5gq_a##qh-Voum*0yVMCsry*RSlmgym-l4g%BiYvRvXcVM zg&k-QJ`*OenoO97k#Hjuhd>TX1P1vF(a(@CK+) zDe-z<{M;dLY^f8o-FrhIM|VlIvCDE9h4C3>U&Rixj2OcBJi@9hZ&AWx}yw!^OXf?RvOe{bl8 zbygILaki~DB*0VbY;SlOi)sO4$YW*u0s&3}C|OY-SU}QL{8o4gH=2l0u@6hOte%qi%BEiDi3Cc9Q6HQ){rBSEYE>JQJuR(7htg!5y6_#SomYyd1* z?-hkh%OtATkPiiLlzos7iLR4Q$!_N3Y(B(R4uop;A$J;|0vM@(?CyGOy1E`Y^EnWP zN0kqqk&Ci1au2C^uKgQ4&er{Xc|E5&Rys~bzg*pVVogBYv29tZ51F&e;mdO~#9d4Ww=A^Sz+q=(D- zaS6>3z)PHm;s+0uJaHK8LH^OjFrQmVYa7;w=-O!Z-fl=>H;N$;^4Q~INYLe}JtENU zi<^n{ErGo0H5a}~dEv`VVqe(=2*D)|aDO;?z4x+Jn9SO`~nG+$HywxNm+znx(0tyumWCq;POzQ*&by+iHbpxt7D| z;l7<%#zbgNO7w8ZE% zYn}{ky3?MFMj<1!gHii{I#}e5!EvSu%Hb|2jSy#NX*15wyJ|M88IoA1ItU5~Lc$IR@Gag;e?gjZuHq3-~_2r)Uy%4{b6TiJ- z7L0+!JMI$=o6n%#IV^FBtqx7*${$;^2ZO2ed{MruCL>h5aVeW%^ncN z>gC`K@glyrp{c&HnQ7;MHVAptS|_B};B`=XOS82$ovm+$7ub6tegVQ*`9hd=o#&86j&LtZfGCs?Yjb#>f^^~gqi=Hx(1G{>dR zN=Pt|%`&Exm32#Hw#-w$|5`*XqS5Hx7c* zF%ld8mrk1OZ!w@7D<$cuhKQFwH7TvReq6aZGcB!5gqTrgias$>YE!|EJ-Z&GIWYd;I?vo2lRxQj#?oaqS(=!5X#P8 zhQ6|60agPGzXBaFo$PZ3Iy(J$Ie-=A;UWFu6(qUr;>a7-5TLm1aAc)O#~mrK`V^u_ z!Y=oFbpD7d-aglzuAR=N{R`4xXsN5UG&Si(Rh&LM_cvbs8yo8zQ}pp7Q+-u^BR==iTe%Q4u(dKC zNzf_&Sk$XpnlZ>RibPzNbPeL<^%ATztG@<`?3HWKiS_&|sMzUiAm>dr`}P_HdLauI z@GT5tqnjXxPQK^M_AG!a#5a)6zL3lgEQENhvnvbnAc7@NqKJ1O&nT@3xPxV#wCRYZEc7&a13AlvvNw36&VOwHq2?h@Q!v)R}sP=>{UB@*U^ zCGZxT@+RC&TUSB8UzjZmd{^AVuC0R7fE|H+wcj2n zz6u-(?8>X~IeW;U!B1Q=D$AS8S<4!zmbC?1!`PKIP!(|CzaW|M3Y1DD7hi$Z0Vni zw*D>HKr}(D_cka?)C76Ve4dWz9Q1*<94~_%+6J*M9=fp&I_l7gt_KEey2jIX9#6V* z$K^nV{pJk5cE%UF)_%)&!3jn0*`XJmP;`(TddUgB#`bOpK{Rgrt?hVb;%d3CW&t>+ zQ2UtXvFM%fKF3cK&lZ|pke^@*@;F-{z;?FC4zj-!Sj>WU;~mucUGNPj@6KlKhPg1D z;klZZXW=$+n?P7t9&?FJ@ajc-B82Y+hLln#*?fTb**#AW<%eD*Ev-Sd+;6z zis??Z+Oyw>>)?%jFq@_ChdFVJJ$b|K>vFVY8BE*q} z8XQ@u5zg9EPrisb(!1;+DZRsSC$LK&!Fwb-h$fEG+Rk7!H%H69_!#r3Ax7e@$NkY& zI5u6U!8|clOsALYy;E=x5#I&kiAMa|i0Pwve8w$ZY|jdi-Vvf)HOu+|wqaJxZf4?^ zF8>j>Gs8mMu&@6JJ&0N(ZdlL!1n~;B#-5-hG5vka5?1^KQHUXKU;avsEqu#a&l|Wa zKDZ8BWl6aUnZ+1A4N?o4DttYv|yMTB6jjT{mGxAfnrD+fU#IykM2VC6TF50z*@x& z2k&B$_nrU105t+#dH4SX=5pS8_V-_4v3i+%Z^b=W!h1WipYOqkWW7ebq`mMf1Yn(A z{}mP`trsa=mg#s5>JIjpcK8V1WE<~8UXWYjEbfTH1NfZ#0blm;N8aI8jrEhqG58fj zBl|a4M*b=Zz55%4VVx-+!i)O9dJ64~LLE=P&}91_!a+R>ca2_bx<)Tz@hh=Bxe;zS z*IaZQW(8#$>H7DUvjeS;9W!hlcbpx|H2eiy5RO18jX=@2zJ8L`BHp3(d<5k@*f6Px zX8RsN&walTd`b2rj!Arg8q87&iN!W6B%}w2p9>5Qal9n`-X7xcZ+0b{M74iH$Uq=> z@HY%H@__gRi~h*)CUIgNKb1`MCVjBp{+2fh1gx+Re2A8OuW3K;LuQMVW;ywsBm}ce zKhm9-+HPct6z5(#@nl5^xWn@OWc0M=P<6xqbft5Fg%M3XN;k6OA07B*c23r8#7 zS>DmKr^b-alpNiC*H+_j1-o&-)3oc8$r2#vB+>jtgLQVTGhw`(k%9w6q;)A|8^qkQ zncMNy8UeQL{06QbX{1egFPN`MIG2p@3(L(VKM}7GXPodl`mQ#EWFx$NM+VUVR@jkD z6muiE{hLgJzqCOf$u=JM#8qvkoVyo^Q%a6 zp*yJ~*^)dSD_Cc5_8@C`ncY(q$4YyWTHTFcH+y2!)t)!l%HJ_LIr0%il6%4JvAxI( zz%||6o6iN5{)0fYKHrIv!}in@9ZNQnWIY00orFzSrr(K9N@ssDk`&DyjkGzP50*-l za@9z*fMyr3ESM&X%#}5V;4ukr*?O~2vq%A%6T%-|u>5S&0qbmXHpV5Ec+06Jts(4u zHu;1!hp;U z#}F5W8wZk)K;IhTW@K!-8W{&9&IDx8{xtcSo8|36WGlu|;b0Q+0=`rduiEjojhHLo zI|eaVz^|S5t9bD@2$)ldxdrAFVm~gmVjFV{vBca%#K$Xpd#)k2@dcDbr;^~9dx-op zLBub{fk}ezfmGsCDUkqw^dK_8hhkzIfBYaa#veb3aEuqR#CQ?QE;6aaxXCFbBLKYG zUw(#^gV!dRK(&oAq5KySLV)eP7yif%Kb&LFtd7lQtgpZ;3j9#vEkwD&Esm}PwrePfj>z*C!dx)$nAL1)G?7Ynbts8| zR`wgRk6G$1M7tp2H^*@jB8phYGK)x@*FJB7USAp?g}QnRp)SbQx=PDb6Ta7HD~m{Q z`tROCu+%#cf8$qcX+#!1d>q>IcW#cs-IELS_s?U)Oys^?aD55K-@OU+nYY5dOynes z8%CnZO&^v$jD&kV#E#6bLeQE9KOsnxRBUOS!Uxrp%^pUgaBglGMzpE@{Olu_`VAH3 zwY4VvDT|Ti!AJ4RDOZ8>t7 z!Zw$XIGD?hB3hRf{+=V_3h*Z$p5z>!g*O>YF`Pup4xr*pJ)DI3Anb&{z-sUpkunp1 z`%^od==p=5Wy48FFMox=E-4^768)pFN5U{mQ$u}S6OP}E-7|wA$_LXcg`i`zf*?|c z94E0(BS@^*OzaK}hX_4}ox*7;iP|9El$AFlo1Lr`(eb6m<`E=L_KiY_VwXpdP}JwK zf?XOxq6CG~9?^E;28YFBluxj>^`R!oEOR7@i0PvgBJGZ{iRU4J2n9-kUsUtZG_%%` zs0LbiB+=t@gd-zKh<3ly?y3^YWbOiIyqnnVktE(LPc5)@B@l-;8m_k6$c~IdKmk{J zLX;R(wWIL!T^hq8&ac{n?i{QV46J?>iIJi2tJycBh#_*C%I;J<7XwaV6IsEcMw2Mn zR+SLV4(CG{a@@;`MiXt&A(ee4;@9o?b%M=8uxx=^h^D%}Vd$RKY9WS=XApt)cho{W z?R+T)Kfj+=+b0z#6ojG{OCwGUyw(pKL-byEacG$>pvS2Z5FoJfnRwMNUjLd%Jht`I zAQYA8&X$fL$zDYgM#R%5QXXKR%TnHqE1SmdBne`$)>)gacvm_N{jb*8wXk*aa~u9- zLc_FX68S=&#=b(?y75?MB11ii5Q_dR)7Xt?Ll$qmO{899pAoKbB$(mZfded`jN+GR zgibtW*t=#l#d?jv&OX2|VkSJX*n`pYpFJSQU+wWKtxg20uMKbPSJVUfJnD9bU zz{}Q`@C)d{1>lcSj+eLNN=c+FF%XHdBNXkO9Vjr;>P2TB!>r~T{zqi!w=WcnjnGgeGfXKGp1wp$_J2w z)x}a3)8>QVhf>yZVx3&P?=+EZEUuiS;7w8c?-e8)Lci14cc-D;(v4_pOqhmPFcstZ zrG-R#Z440@KEDaUIM^M+wpvI8!E9|mqR4&>!Bv{Sv2Op_g6oDofbBc6AmsNi!D-?v z=%uyItwRtB`dZ?T6-;^u>dD0b6Y)@v=d3 zA%>0}z<1>XEN3FBe2A4#Bpqbm5@ectnlC{$_>COnbD|tI%sm~8vR9*Q(`h~SZDoOz zkWHIh(9xZRQP?N<66lV?c&zzjZA&$eiWDy)iEgcqX75iT3R#+$(2=*~25CRdVfx8L z@6Xd66Djh-@p9CRTH+(Cu%Rjp4fQYg`NHj(#!m19470 z?zjnTd_9gX5!2|Pi3aps4%<~v46+-+LK RLL`M^*|P3Lg+j7J{x^jjeop`Z diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..c6f01c6 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,660 @@ +# GNU AFFERO GENERAL PUBLIC LICENSE + +Version 3, 19 November 2007 + +Copyright (C) 2007 Free Software Foundation, Inc. + + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + +## Preamble + +The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + +The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains +free software for all its users. + +When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + +Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + +A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + +The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + +An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing +under this license. + +The precise terms and conditions for copying, distribution and +modification follow. + +## TERMS AND CONDITIONS + +### 0. Definitions. + +"This License" refers to version 3 of the GNU Affero General Public +License. + +"Copyright" also means copyright-like laws that apply to other kinds +of works, such as semiconductor masks. + +"The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + +To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of +an exact copy. The resulting work is called a "modified version" of +the earlier work or a work "based on" the earlier work. + +A "covered work" means either the unmodified Program or a work based +on the Program. + +To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + +To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user +through a computer network, with no transfer of a copy, is not +conveying. + +An interactive user interface displays "Appropriate Legal Notices" to +the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + +### 1. Source Code. + +The "source code" for a work means the preferred form of the work for +making modifications to it. "Object code" means any non-source form of +a work. + +A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + +The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + +The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + +The Corresponding Source need not include anything that users can +regenerate automatically from other parts of the Corresponding Source. + +The Corresponding Source for a work in source code form is that same +work. + +### 2. Basic Permissions. + +All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + +You may make, run and propagate covered works that you do not convey, +without conditions so long as your license otherwise remains in force. +You may convey covered works to others for the sole purpose of having +them make modifications exclusively for you, or provide you with +facilities for running those works, provided that you comply with the +terms of this License in conveying all material for which you do not +control copyright. Those thus making or running the covered works for +you must do so exclusively on your behalf, under your direction and +control, on terms that prohibit them from making any copies of your +copyrighted material outside their relationship with you. + +Conveying under any other circumstances is permitted solely under the +conditions stated below. Sublicensing is not allowed; section 10 makes +it unnecessary. + +### 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + +No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + +When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such +circumvention is effected by exercising rights under this License with +respect to the covered work, and you disclaim any intention to limit +operation or modification of the work as a means of enforcing, against +the work's users, your or third parties' legal rights to forbid +circumvention of technological measures. + +### 4. Conveying Verbatim Copies. + +You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + +You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + +### 5. Conveying Modified Source Versions. + +You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these +conditions: + +- a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. +- b) The work must carry prominent notices stating that it is + released under this License and any conditions added under + section 7. This requirement modifies the requirement in section 4 + to "keep intact all notices". +- c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. +- d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + +A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + +### 6. Conveying Non-Source Forms. + +You may convey a covered work in object code form under the terms of +sections 4 and 5, provided that you also convey the machine-readable +Corresponding Source under the terms of this License, in one of these +ways: + +- a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. +- b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the Corresponding + Source from a network server at no charge. +- c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. +- d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. +- e) Convey the object code using peer-to-peer transmission, + provided you inform other peers where the object code and + Corresponding Source of the work are being offered to the general + public at no charge under subsection 6d. + +A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + +A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, +family, or household purposes, or (2) anything designed or sold for +incorporation into a dwelling. In determining whether a product is a +consumer product, doubtful cases shall be resolved in favor of +coverage. For a particular product received by a particular user, +"normally used" refers to a typical or common use of that class of +product, regardless of the status of the particular user or of the way +in which the particular user actually uses, or expects or is expected +to use, the product. A product is a consumer product regardless of +whether the product has substantial commercial, industrial or +non-consumer uses, unless such uses represent the only significant +mode of use of the product. + +"Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to +install and execute modified versions of a covered work in that User +Product from a modified version of its Corresponding Source. The +information must suffice to ensure that the continued functioning of +the modified object code is in no case prevented or interfered with +solely because modification has been made. + +If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + +The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or +updates for a work that has been modified or installed by the +recipient, or for the User Product in which it has been modified or +installed. Access to a network may be denied when the modification +itself materially and adversely affects the operation of the network +or violates the rules and protocols for communication across the +network. + +Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + +### 7. Additional Terms. + +"Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + +When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + +Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders +of that material) supplement the terms of this License with terms: + +- a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or +- b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or +- c) Prohibiting misrepresentation of the origin of that material, + or requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or +- d) Limiting the use for publicity purposes of names of licensors + or authors of the material; or +- e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or +- f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions + of it) with contractual assumptions of liability to the recipient, + for any liability that these contractual assumptions directly + impose on those licensors and authors. + +All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + +If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + +Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; the +above requirements apply either way. + +### 8. Termination. + +You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + +However, if you cease all violation of this License, then your license +from a particular copyright holder is reinstated (a) provisionally, +unless and until the copyright holder explicitly and finally +terminates your license, and (b) permanently, if the copyright holder +fails to notify you of the violation by some reasonable means prior to +60 days after the cessation. + +Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + +Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + +### 9. Acceptance Not Required for Having Copies. + +You are not required to accept this License in order to receive or run +a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + +### 10. Automatic Licensing of Downstream Recipients. + +Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + +An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + +You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + +### 11. Patents. + +A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + +A contributor's "essential patent claims" are all patent claims owned +or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + +Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + +In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + +If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + +If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + +A patent license is "discriminatory" if it does not include within the +scope of its coverage, prohibits the exercise of, or is conditioned on +the non-exercise of one or more of the rights that are specifically +granted under this License. You may not convey a covered work if you +are a party to an arrangement with a third party that is in the +business of distributing software, under which you make payment to the +third party based on the extent of your activity of conveying the +work, and under which the third party grants, to any of the parties +who would receive the covered work from you, a discriminatory patent +license (a) in connection with copies of the covered work conveyed by +you (or copies made from those copies), or (b) primarily for and in +connection with specific products or compilations that contain the +covered work, unless you entered into that arrangement, or that patent +license was granted, prior to 28 March 2007. + +Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + +### 12. No Surrender of Others' Freedom. + +If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under +this License and any other pertinent obligations, then as a +consequence you may not convey it at all. For example, if you agree to +terms that obligate you to collect a royalty for further conveying +from those to whom you convey the Program, the only way you could +satisfy both those terms and this License would be to refrain entirely +from conveying the Program. + +### 13. Remote Network Interaction; Use with the GNU General Public License. + +Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your +version supports such interaction) an opportunity to receive the +Corresponding Source of your version by providing access to the +Corresponding Source from a network server at no charge, through some +standard or customary means of facilitating copying of software. This +Corresponding Source shall include the Corresponding Source for any +work covered by version 3 of the GNU General Public License that is +incorporated pursuant to the following paragraph. + +Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + +### 14. Revised Versions of this License. + +The Free Software Foundation may publish revised and/or new versions +of the GNU Affero General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever +published by the Free Software Foundation. + +If the Program specifies that a proxy can decide which future versions +of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + +Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + +### 15. Disclaimer of Warranty. + +THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT +WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND +PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE +DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR +CORRECTION. + +### 16. Limitation of Liability. + +IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR +CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT +NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR +LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM +TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER +PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +### 17. Interpretation of Sections 15 and 16. + +If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + +END OF TERMS AND CONDITIONS + +## How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these +terms. + +To do so, attach the following notices to the program. It is safest to +attach them to the start of each source file to most effectively state +the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper +mail. + +If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for +the specific requirements. + +You should also get your employer (if you work as a programmer) or +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. For more information on this, and how to apply and follow +the GNU AGPL, see . diff --git a/doc/Hyde.html b/doc/Hyde.html index 120d380..ad6dd8e 100644 --- a/doc/Hyde.html +++ b/doc/Hyde.html @@ -79,7 +79,7 @@
Defined in:
lib/hyde.rb,
- lib/hyde/node.rb,
lib/hyde/path.rb,
lib/hyde/probe.rb,
lib/hyde/server.rb,
lib/hyde/request.rb,
lib/hyde/response.rb,
lib/hyde/template.rb,
lib/hyde/util/html.rb,
lib/hyde/util/query.rb,
lib/hyde/util/cookie.rb,
lib/hyde/util/header.rb,
lib/hyde/util/lookup.rb,
lib/hyde/template/erb.rb,
lib/hyde/util/sorting.rb,
lib/hyde/probe/handler.rb,
lib/hyde/template/erubi.rb,
lib/hyde/util/multipart.rb,
lib/hyde/dsl/methods_path.rb,
lib/hyde/pattern_matching.rb,
lib/hyde/dsl/methods_probe.rb,
lib/hyde/probe/http_method.rb,
lib/hyde/dsl/methods_common.rb,
lib/hyde/probe/serve_handler.rb,
lib/hyde/dsl/methods_template.rb,
lib/hyde/dsl/constructors_path.rb,
lib/hyde/pattern_matching/glob.rb,
lib/hyde/pattern_matching/util.rb,
lib/hyde/dsl/constructors_probe.rb,
lib/hyde/pattern_matching/rematch.rb
+ lib/hyde/node.rb,
lib/hyde/path.rb,
lib/hyde/probe.rb,
lib/hyde/server.rb,
lib/hyde/request.rb,
lib/hyde/response.rb,
lib/hyde/template.rb,
lib/hyde/util/html.rb,
lib/hyde/util/query.rb,
lib/hyde/util/cookie.rb,
lib/hyde/util/errors.rb,
lib/hyde/util/lookup.rb,
lib/hyde/template/erb.rb,
lib/hyde/probe/handler.rb,
lib/hyde/template/erubi.rb,
lib/hyde/util/multipart.rb,
lib/hyde/util/parseutils.rb,
lib/hyde/dsl/methods_path.rb,
lib/hyde/pattern_matching.rb,
lib/hyde/dsl/methods_probe.rb,
lib/hyde/probe/http_method.rb,
lib/hyde/util/parsesorting.rb,
lib/hyde/dsl/methods_common.rb,
lib/hyde/probe/serve_handler.rb,
lib/hyde/dsl/methods_template.rb,
lib/hyde/dsl/constructors_path.rb,
lib/hyde/pattern_matching/glob.rb,
lib/hyde/pattern_matching/util.rb,
lib/hyde/dsl/constructors_probe.rb,
lib/hyde/pattern_matching/rematch.rb
@@ -103,7 +103,7 @@ - Classes: Cookie, Node, Path, PathContext, Pattern, Probe, ProbeContext, ProcessorContext, Request, Response, Server, ServerContext, Template, TemplateContext + Classes: Cookie, Error, Node, ParsingError, Path, PathContext, Pattern, Probe, ProbeContext, ProcessorContext, Request, Response, Server, ServerContext, Template, TemplateContext

@@ -175,7 +175,7 @@ diff --git a/doc/Hyde/Cookie.html b/doc/Hyde/Cookie.html index 26c28c7..717cf83 100644 --- a/doc/Hyde/Cookie.html +++ b/doc/Hyde/Cookie.html @@ -385,16 +385,32 @@
 
 
-9
 10
 11
 12
-13
+13 +14 +15 +16 +17 +18 +19 +20 +21 +22 -
# File 'lib/hyde/util/cookie.rb', line 9
+      
# File 'lib/hyde/util/cookie.rb', line 10
 
 def initialize(key, value, params = {})
+  unless key.match? Hyde::Util::HeaderRegexp::COOKIE_KEY
+    raise Hyde::ParsingError, "invalid cookie key: #{key}"
+  end
+
+  unless value.match? Hyde::Util::HeaderRegexp::COOKIE_VALUE
+    raise Hyde::ParsingError, "invalid cookie value: #{value}"
+  end
+
   @key = key
   @value = value
   @params = params
@@ -436,12 +452,12 @@
       
 
 
-26
-27
-28
+35 +36 +37
-
# File 'lib/hyde/util/cookie.rb', line 26
+      
# File 'lib/hyde/util/cookie.rb', line 35
 
 def key
   @key
@@ -478,12 +494,12 @@
       
 
 
-27
-28
-29
+36 +37 +38
-
# File 'lib/hyde/util/cookie.rb', line 27
+      
# File 'lib/hyde/util/cookie.rb', line 36
 
 def params
   @params
@@ -520,12 +536,12 @@
       
 
 
-26
-27
-28
+35 +36 +37
-
# File 'lib/hyde/util/cookie.rb', line 26
+      
# File 'lib/hyde/util/cookie.rb', line 35
 
 def value
   @value
@@ -599,14 +615,14 @@
       
 
 
-41
-42
-43
-44
-45
+53 +54 +55 +56 +57
-
# File 'lib/hyde/util/cookie.rb', line 41
+      
# File 'lib/hyde/util/cookie.rb', line 53
 
 def self.from_cookie_string(data)
   data.split(";").map do |cookiestr|
@@ -675,17 +691,23 @@
       
 
 
-32
-33
-34
-35
-36
+41 +42 +43 +44 +45 +46 +47 +48
-
# File 'lib/hyde/util/cookie.rb', line 32
+      
# File 'lib/hyde/util/cookie.rb', line 41
 
 def self.from_setcookie_string(data)
-  kvpair, params = Hyde::Util.parse_value(data)
+  kvpair, params = Hyde::Util::ParserCommon.parse_value(
+    data,
+    regexp: Hyde::Util::HeaderRegexp::COOKIE_PARAM
+  )
   key, value = kvpair.split("=").map(&:strip)
   Cookie.new(key, value, params)
 end
@@ -738,15 +760,15 @@
 
 
-17
-18
-19
+26 +27 +28
-
# File 'lib/hyde/util/cookie.rb', line 17
+      
# File 'lib/hyde/util/cookie.rb', line 26
 
 def to_s
-  Hyde::Util.make_value(to_short, @params)
+  Hyde::Util.make_value(to_short, @params)
 end
@@ -778,12 +800,12 @@
 
 
-22
-23
-24
+31 +32 +33
-
# File 'lib/hyde/util/cookie.rb', line 22
+      
# File 'lib/hyde/util/cookie.rb', line 31
 
 def to_short
   "#{key.to_s.strip}=#{value.to_s.strip}"
@@ -798,7 +820,7 @@
 
 
       
diff --git a/doc/Hyde/DSL.html b/doc/Hyde/DSL.html
index 84165ad..b8ed01d 100644
--- a/doc/Hyde/DSL.html
+++ b/doc/Hyde/DSL.html
@@ -117,7 +117,7 @@
 
 
       
diff --git a/doc/Hyde/DSL/CommonMethods.html b/doc/Hyde/DSL/CommonMethods.html
index 1435c78..506563a 100644
--- a/doc/Hyde/DSL/CommonMethods.html
+++ b/doc/Hyde/DSL/CommonMethods.html
@@ -327,7 +327,7 @@
 
 
       
diff --git a/doc/Hyde/DSL/PathConstructors.html b/doc/Hyde/DSL/PathConstructors.html
index dda3a22..85beb8b 100644
--- a/doc/Hyde/DSL/PathConstructors.html
+++ b/doc/Hyde/DSL/PathConstructors.html
@@ -859,7 +859,7 @@
 
 def register(obj)
   unless obj.is_a? Hyde::Node
-    raise StandardError, "register accepts node children only"
+    raise ArgumentError, "register accepts node children only"
   end
 
   @origin.children.append(obj)
@@ -954,7 +954,7 @@
 
 
       
diff --git a/doc/Hyde/DSL/PathMethods.html b/doc/Hyde/DSL/PathMethods.html
index 8899da7..ab46ee9 100644
--- a/doc/Hyde/DSL/PathMethods.html
+++ b/doc/Hyde/DSL/PathMethods.html
@@ -417,7 +417,7 @@ Blocks path access if a filter returns false.

when String @origin.properties['index'] = [index] else - raise StandardError, "index should be an Array or a String" + raise ArgumentError, "index should be an Array or a String" end end
@@ -483,7 +483,7 @@ Blocks path access if a filter returns false.

def plugin(filename) self.define_singleton_method(:run) do |object| unless object.is_a? Hyde::Node - raise StandardError, "not a node instance or subclass instance" + raise ArgumentError, "not a node instance or subclass instance" end object @@ -681,10 +681,6 @@ Does not modify path execution.

- — -

ath [String

-
- @@ -740,10 +736,6 @@ Does not modify path execution.

- — -

ath [String

-
- @@ -775,7 +767,7 @@ Does not modify path execution.

diff --git a/doc/Hyde/DSL/ProbeConstructors.html b/doc/Hyde/DSL/ProbeConstructors.html index bf51d60..d0da29a 100644 --- a/doc/Hyde/DSL/ProbeConstructors.html +++ b/doc/Hyde/DSL/ProbeConstructors.html @@ -317,7 +317,7 @@ diff --git a/doc/Hyde/DSL/ProbeMethods.html b/doc/Hyde/DSL/ProbeMethods.html index db97c8d..b6447e4 100644 --- a/doc/Hyde/DSL/ProbeMethods.html +++ b/doc/Hyde/DSL/ProbeMethods.html @@ -116,6 +116,29 @@
  • + #escape_html(text) ⇒ Object + + + + + + + + + + + + + +

    Escape HTML entities.

    +
    + +
  • + + +
  • + + #file(path, mode = "r", *all, &block) ⇒ Object @@ -273,6 +296,29 @@

    Set response status (generate response if one doesn't exist yet).

    +
  • + + +
  • + + + #unescape_html(text) ⇒ Object + + + + + + + + + + + + + +

    Unescape HTML entities.

    +
    +
  • @@ -286,7 +332,54 @@
    -

    +

    + + #escape_html(text) ⇒ Object + + + + + +

    +
    +

    Escape HTML entities

    + + +
    +
    +
    + + +

    See Also:

    + + +
    + + + + +
    +
    +
    +
    +99
    +100
    +101
    +
    +
    # File 'lib/hyde/dsl/methods_probe.rb', line 99
    +
    +def escape_html(text)
    +  Hyde::Util.escape_html(text)
    +end
    +
    +
    + +
    +

    #file(path, mode = "r", *all, &block) ⇒ Object @@ -307,7 +400,7 @@

    See Also:

      -
    • File#open
    • +
    • File.open
    @@ -317,12 +410,12 @@
     
     
    -88
    -89
    -90
    +93 +94 +95

    -
    # File 'lib/hyde/dsl/methods_probe.rb', line 88
    +      
    # File 'lib/hyde/dsl/methods_probe.rb', line 93
     
     def file(path, mode = "r", *all, &block)
       File.open("#{request.filepath}/#{path}", mode, *all, &block)
    @@ -370,18 +463,22 @@
           
     
     
    -79
    -80
    -81
     82
     83
    -84
    +84 +85 +86 +87 +88 +89
    -
    # File 'lib/hyde/dsl/methods_probe.rb', line 79
    +      
    # File 'lib/hyde/dsl/methods_probe.rb', line 82
     
     def form
    -  _, opts = Hyde::Util.parse_value(request.headers["content-type"])
    +  _, opts = Hyde::Util::ParserCommon.parse_value(
    +    request.headers["content-type"]
    +  )
       Hyde::Util::MultipartParser.new(
         request.input, opts["boundary"]
       ).to_h
    @@ -429,7 +526,6 @@
           
     
     
    -67
     68
     69
     70
    @@ -437,13 +533,18 @@
     72
     73
     74
    -75
    +75 +76 +77 +78
    -
    # File 'lib/hyde/dsl/methods_probe.rb', line 67
    +      
    # File 'lib/hyde/dsl/methods_probe.rb', line 68
     
     def form?
    -  value, opts = Hyde::Util.parse_value(request.headers["content-type"])
    +  value, opts = Hyde::Util::ParserCommon.parse_value(
    +    request.headers["content-type"]
    +  )
       if value == "multipart/form-data" and
          opts["boundary"]
         true
    @@ -515,7 +616,6 @@
           
     
     
    -29
     30
     31
     32
    @@ -529,20 +629,21 @@
     40
     41
     42
    -43
    +43 +44
    -
    # File 'lib/hyde/dsl/methods_probe.rb', line 29
    +      
    # File 'lib/hyde/dsl/methods_probe.rb', line 30
     
     def header(key, value)
       return status(value) if key.downcase == "status"
     
    -  if key.match(/(?:[(),\/:;<=>?@\[\]{}"]|[^ -~])/)
    -    raise StandardError, "header key has invalid characters"
    +  if key.match(Hyde::Util::HeaderRegexp::TOKEN)
    +    raise ArgumentError, "header key has invalid characters"
       end
     
    -  if value.match(/[^ -~]/)
    -    raise StandardError, "value key has invalid characters"
    +  if value&.match(Hyde::Util::HeaderRegexp::PRINTABLE)
    +    raise ArgumentError, "value key has invalid characters"
       end
     
       @origin.response = (@origin.response or Hyde::Response.new)
    @@ -616,7 +717,6 @@ If no value is provided, deletes all key entries

     
     
    -49
     50
     51
     52
    @@ -630,22 +730,23 @@ If no value is provided, deletes all key entries

    60 61 62 -63
    +63 +64
    -
    # File 'lib/hyde/dsl/methods_probe.rb', line 49
    +      
    # File 'lib/hyde/dsl/methods_probe.rb', line 50
     
     def remove_header(key, value = nil)
       return unless @origin.response
     
       return if key.downcase == "status"
     
    -  if key.match(/(?:[(),\/:;<=>?@\[\]{}"]|[^ -~])/)
    -    raise StandardError, "header key has invalid characters"
    +  if key.match(Hyde::Util::HeaderRegexp::TOKEN)
    +    raise ArgumentError, "header key has invalid characters"
       end
     
    -  if value&.match(/[^ -~]/)
    -    raise StandardError, "value key has invalid characters"
    +  if value&.match(Hyde::Util::HeaderRegexp::PRINTABLE)
    +    raise ArgumentError, "value key has invalid characters"
       end
     
       @origin.response.delete_header(key, value)
    @@ -693,12 +794,12 @@ If no value is provided, deletes all key entries

     
     
    -13
     14
    -15
    +15 +16
    -
    # File 'lib/hyde/dsl/methods_probe.rb', line 13
    +      
    # File 'lib/hyde/dsl/methods_probe.rb', line 14
     
     def request
       @origin.request
    @@ -756,13 +857,13 @@ If no value is provided, deletes all key entries

     
     
    -19
     20
     21
    -22
    +22 +23
    -
    # File 'lib/hyde/dsl/methods_probe.rb', line 19
    +      
    # File 'lib/hyde/dsl/methods_probe.rb', line 20
     
     def status(status)
       @origin.response = (@origin.response or Hyde::Response.new)
    @@ -771,6 +872,53 @@ If no value is provided, deletes all key entries

    + + +
    +

    + + #unescape_html(text) ⇒ Object + + + + + +

    +
    +

    Unescape HTML entities

    + + +
    +
    +
    + + +

    See Also:

    + + +
    + + + + +
    +
    +
    +
    +105
    +106
    +107
    +
    +
    # File 'lib/hyde/dsl/methods_probe.rb', line 105
    +
    +def unescape_html(text)
    +  Hyde::Util.unescape_html(text)
    +end
    +
    @@ -778,7 +926,7 @@ If no value is provided, deletes all key entries

    diff --git a/doc/Hyde/DSL/TemplateMethods.html b/doc/Hyde/DSL/TemplateMethods.html index 3b42a87..ee6e23f 100644 --- a/doc/Hyde/DSL/TemplateMethods.html +++ b/doc/Hyde/DSL/TemplateMethods.html @@ -190,7 +190,7 @@ diff --git a/doc/Hyde/Error.html b/doc/Hyde/Error.html new file mode 100644 index 0000000..1c6964a --- /dev/null +++ b/doc/Hyde/Error.html @@ -0,0 +1,138 @@ + + + + + + + Exception: Hyde::Error + + — Documentation by YARD 0.9.34 + + + + + + + + + + + + + + + + + + + +
    + + +

    Exception: Hyde::Error + + + +

    +
    + +
    +
    Inherits:
    +
    + StandardError + +
      +
    • Object
    • + + + + + +
    + show all + +
    +
    + + + + + + + + + + + +
    +
    Defined in:
    +
    lib/hyde/util/errors.rb
    +
    + +
    + +

    Overview

    +
    +

    Generic error class, as recommended by Ruby documentation.

    + + +
    +
    +
    + + +
    +

    Direct Known Subclasses

    +

    ParsingError

    +
    + + + + + + + + + + +
    + + + +
    + + \ No newline at end of file diff --git a/doc/Hyde/Handlers.html b/doc/Hyde/Handlers.html index 454c598..83d1c61 100644 --- a/doc/Hyde/Handlers.html +++ b/doc/Hyde/Handlers.html @@ -117,7 +117,7 @@ diff --git a/doc/Hyde/Handlers/CONNECT.html b/doc/Hyde/Handlers/CONNECT.html index 39b54b3..cb7913c 100644 --- a/doc/Hyde/Handlers/CONNECT.html +++ b/doc/Hyde/Handlers/CONNECT.html @@ -210,7 +210,7 @@ diff --git a/doc/Hyde/Handlers/DELETE.html b/doc/Hyde/Handlers/DELETE.html index 4c76647..1cd40c1 100644 --- a/doc/Hyde/Handlers/DELETE.html +++ b/doc/Hyde/Handlers/DELETE.html @@ -210,7 +210,7 @@ diff --git a/doc/Hyde/Handlers/GET.html b/doc/Hyde/Handlers/GET.html index a727d53..e8ad466 100644 --- a/doc/Hyde/Handlers/GET.html +++ b/doc/Hyde/Handlers/GET.html @@ -350,7 +350,7 @@ Request's #splat and #param are passed to block.

    diff --git a/doc/Hyde/Handlers/HEAD.html b/doc/Hyde/Handlers/HEAD.html index 9f50cb1..91a32be 100644 --- a/doc/Hyde/Handlers/HEAD.html +++ b/doc/Hyde/Handlers/HEAD.html @@ -210,7 +210,7 @@ diff --git a/doc/Hyde/Handlers/Handler.html b/doc/Hyde/Handlers/Handler.html index 53722e7..b023bcf 100644 --- a/doc/Hyde/Handlers/Handler.html +++ b/doc/Hyde/Handlers/Handler.html @@ -587,7 +587,7 @@ Request's #splat and #param are passed to block.

    diff --git a/doc/Hyde/Handlers/OPTIONS.html b/doc/Hyde/Handlers/OPTIONS.html index a560b8a..18161bd 100644 --- a/doc/Hyde/Handlers/OPTIONS.html +++ b/doc/Hyde/Handlers/OPTIONS.html @@ -210,7 +210,7 @@ diff --git a/doc/Hyde/Handlers/PATCH.html b/doc/Hyde/Handlers/PATCH.html index 7f848f4..b92c5b3 100644 --- a/doc/Hyde/Handlers/PATCH.html +++ b/doc/Hyde/Handlers/PATCH.html @@ -210,7 +210,7 @@ diff --git a/doc/Hyde/Handlers/POST.html b/doc/Hyde/Handlers/POST.html index 8793c37..5aaf444 100644 --- a/doc/Hyde/Handlers/POST.html +++ b/doc/Hyde/Handlers/POST.html @@ -210,7 +210,7 @@ diff --git a/doc/Hyde/Handlers/PUT.html b/doc/Hyde/Handlers/PUT.html index 8d25ec2..2aeb100 100644 --- a/doc/Hyde/Handlers/PUT.html +++ b/doc/Hyde/Handlers/PUT.html @@ -210,7 +210,7 @@ diff --git a/doc/Hyde/Handlers/Serve.html b/doc/Hyde/Handlers/Serve.html index 3442688..6adbff5 100644 --- a/doc/Hyde/Handlers/Serve.html +++ b/doc/Hyde/Handlers/Serve.html @@ -459,7 +459,7 @@ Tries to serve files matched by handler

    diff --git a/doc/Hyde/Handlers/TRACE.html b/doc/Hyde/Handlers/TRACE.html index 5d1c505..c319859 100644 --- a/doc/Hyde/Handlers/TRACE.html +++ b/doc/Hyde/Handlers/TRACE.html @@ -210,7 +210,7 @@ diff --git a/doc/Hyde/Node.html b/doc/Hyde/Node.html index 1dc0019..8d5957b 100644 --- a/doc/Hyde/Node.html +++ b/doc/Hyde/Node.html @@ -692,7 +692,7 @@ Does nothing by default, behaviour should be overriden through

    diff --git a/doc/Hyde/ParsingError.html b/doc/Hyde/ParsingError.html new file mode 100644 index 0000000..b2e3c0a --- /dev/null +++ b/doc/Hyde/ParsingError.html @@ -0,0 +1,142 @@ + + + + + + + Exception: Hyde::ParsingError + + — Documentation by YARD 0.9.34 + + + + + + + + + + + + + + + + + + + +
    + + +

    Exception: Hyde::ParsingError + + + +

    +
    + +
    +
    Inherits:
    +
    + Error + +
      +
    • Object
    • + + + + + + + +
    + show all + +
    +
    + + + + + + + + + + + +
    +
    Defined in:
    +
    lib/hyde/util/errors.rb
    +
    + +
    + +

    Overview

    +
    +

    Error class raised by hyde/util/parseutils module.

    + + +
    +
    +
    + + +
    + + + + + + + + + + + + + + + +
    + + + +
    + + \ No newline at end of file diff --git a/doc/Hyde/Path.html b/doc/Hyde/Path.html index 7252e27..17e3c53 100644 --- a/doc/Hyde/Path.html +++ b/doc/Hyde/Path.html @@ -887,7 +887,7 @@ Finds the next appropriate path to go to.

    diff --git a/doc/Hyde/PathContext.html b/doc/Hyde/PathContext.html index c0cb6ba..f2ce1b0 100644 --- a/doc/Hyde/PathContext.html +++ b/doc/Hyde/PathContext.html @@ -232,7 +232,7 @@ diff --git a/doc/Hyde/Pattern.html b/doc/Hyde/Pattern.html index 0d7df14..b48ad74 100644 --- a/doc/Hyde/Pattern.html +++ b/doc/Hyde/Pattern.html @@ -542,7 +542,7 @@ Lighter version of match that doesn't assign any variables.

    diff --git a/doc/Hyde/PatternMatching.html b/doc/Hyde/PatternMatching.html index 72352ce..f7b9650 100644 --- a/doc/Hyde/PatternMatching.html +++ b/doc/Hyde/PatternMatching.html @@ -231,7 +231,7 @@ Used primarily to create patterns for path definitions.

    diff --git a/doc/Hyde/PatternMatching/Glob.html b/doc/Hyde/PatternMatching/Glob.html index 97b33fa..a86db66 100644 --- a/doc/Hyde/PatternMatching/Glob.html +++ b/doc/Hyde/PatternMatching/Glob.html @@ -638,7 +638,7 @@ Lighter version of match that doesn't assign any variables.

    diff --git a/doc/Hyde/PatternMatching/ReMatch.html b/doc/Hyde/PatternMatching/ReMatch.html index 2a12d0d..4d33f18 100644 --- a/doc/Hyde/PatternMatching/ReMatch.html +++ b/doc/Hyde/PatternMatching/ReMatch.html @@ -547,7 +547,7 @@ Lighter version of match that doesn't assign any variables.

    diff --git a/doc/Hyde/Probe.html b/doc/Hyde/Probe.html index 8a0c092..5c58e55 100644 --- a/doc/Hyde/Probe.html +++ b/doc/Hyde/Probe.html @@ -455,7 +455,7 @@ This behaviour should only be used internally.

    diff --git a/doc/Hyde/ProbeContext.html b/doc/Hyde/ProbeContext.html index 1a56156..314a636 100644 --- a/doc/Hyde/ProbeContext.html +++ b/doc/Hyde/ProbeContext.html @@ -178,7 +178,7 @@

    Methods included from DSL::ProbeMethods

    -

    #file, #form, #form?, #header, #remove_header, #request, #status

    +

    #escape_html, #file, #form, #form?, #header, #remove_header, #request, #status, #unescape_html

    @@ -239,7 +239,7 @@ diff --git a/doc/Hyde/ProcessorContext.html b/doc/Hyde/ProcessorContext.html index c4bc452..d600ae6 100644 --- a/doc/Hyde/ProcessorContext.html +++ b/doc/Hyde/ProcessorContext.html @@ -217,7 +217,7 @@ diff --git a/doc/Hyde/Request.html b/doc/Hyde/Request.html index 853a74a..cd4f3bb 100644 --- a/doc/Hyde/Request.html +++ b/doc/Hyde/Request.html @@ -1538,7 +1538,7 @@ diff --git a/doc/Hyde/Response.html b/doc/Hyde/Response.html index eb300b3..42a8eba 100644 --- a/doc/Hyde/Response.html +++ b/doc/Hyde/Response.html @@ -1140,7 +1140,7 @@ If no value is provided, deletes all key entries

    diff --git a/doc/Hyde/Server.html b/doc/Hyde/Server.html index 2a00424..a64c5a4 100644 --- a/doc/Hyde/Server.html +++ b/doc/Hyde/Server.html @@ -432,7 +432,7 @@ although server nesting for the purpose of creating virtual hosts is allowed.

    diff --git a/doc/Hyde/ServerContext.html b/doc/Hyde/ServerContext.html index 4b1aff6..da1c964 100644 --- a/doc/Hyde/ServerContext.html +++ b/doc/Hyde/ServerContext.html @@ -152,7 +152,7 @@ diff --git a/doc/Hyde/Template.html b/doc/Hyde/Template.html index 254ce83..5f044f8 100644 --- a/doc/Hyde/Template.html +++ b/doc/Hyde/Template.html @@ -756,7 +756,7 @@ diff --git a/doc/Hyde/TemplateContext.html b/doc/Hyde/TemplateContext.html index 4e16373..331ffa9 100644 --- a/doc/Hyde/TemplateContext.html +++ b/doc/Hyde/TemplateContext.html @@ -211,7 +211,7 @@

    Methods included from DSL::ProbeMethods

    -

    #file, #form, #form?, #header, #remove_header, #request, #status

    +

    #escape_html, #file, #form, #form?, #header, #remove_header, #request, #status, #unescape_html

    @@ -332,7 +332,7 @@ diff --git a/doc/Hyde/Templates.html b/doc/Hyde/Templates.html index a528448..94d6577 100644 --- a/doc/Hyde/Templates.html +++ b/doc/Hyde/Templates.html @@ -117,7 +117,7 @@ diff --git a/doc/Hyde/Templates/ERB.html b/doc/Hyde/Templates/ERB.html index 72bc568..8707eb5 100644 --- a/doc/Hyde/Templates/ERB.html +++ b/doc/Hyde/Templates/ERB.html @@ -302,7 +302,7 @@ diff --git a/doc/Hyde/Templates/Erubi.html b/doc/Hyde/Templates/Erubi.html index b507718..7abb91b 100644 --- a/doc/Hyde/Templates/Erubi.html +++ b/doc/Hyde/Templates/Erubi.html @@ -320,7 +320,7 @@ diff --git a/doc/Hyde/Util.html b/doc/Hyde/Util.html index 49287e0..db4cb24 100644 --- a/doc/Hyde/Util.html +++ b/doc/Hyde/Util.html @@ -79,7 +79,7 @@
    Defined in:
    lib/hyde/util/lookup.rb,
    - lib/hyde/util/html.rb,
    lib/hyde/util/query.rb,
    lib/hyde/util/header.rb,
    lib/hyde/util/sorting.rb,
    lib/hyde/util/multipart.rb
    + lib/hyde/util/html.rb,
    lib/hyde/util/query.rb,
    lib/hyde/util/multipart.rb,
    lib/hyde/util/parseutils.rb,
    lib/hyde/util/parsesorting.rb
    @@ -99,7 +99,7 @@

    - Modules: ParserCommon + Modules: HeaderRegexp, ParserCommon, ParserSorting @@ -245,52 +245,6 @@ Taken from WEBrick

    - - - -
  • - - - .make_value(input, opts, sep = ";") ⇒ String - - - - - - - - - - - - - -

    Construct a parametrized header value.

    -
    - -
  • - - -
  • - - - .parse_value(input, sep = ";") ⇒ Array(String, Hash) - - - - - - - - - - - - - -

    Parse parametrized header values.

    -
    -
  • @@ -398,8 +352,6 @@ Taken from WEBrick -87 -88 89 90 91 @@ -425,10 +377,12 @@ Taken from WEBrick # File 'lib/hyde/util/html.rb', line 87 +
    # File 'lib/hyde/util/html.rb', line 89
     
     def self.default_error_page(code, backtrace)
       backtrace ||= []
    @@ -477,12 +431,11 @@ Taken from WEBrick 
         Note:
    -    

    Do not use this to inject untrusted input into JavaScript code

    +

    Do not use this to inject untrusted input into JavaScript code or CSS style of the page.

    Return string with escaped HTML entities. -or CSS style of the page. This function is not adequate to prevent string interpolation.

    @@ -525,221 +478,21 @@ This function is not adequate to prevent string interpolation.

     
     
    +69
     70
     71
    -72
    +72 +73 +74
    -
    # File 'lib/hyde/util/html.rb', line 70
    +      
    # File 'lib/hyde/util/html.rb', line 69
     
     def self.escape_html(str)
    -  CGI.escapeHTML(str)
    -end
    - - - - - -
    -

    - - .make_value(input, opts, sep = ";") ⇒ String - - - - - -

    -
    -

    Construct a parametrized header value

    - - -
    -
    -
    -

    Parameters:

    -
      - -
    • - - input - - - (String) - - - -
    • - -
    • - - opts - - - (Hash) - - - -
    • - -
    - -

    Returns:

    -
      - -
    • - - - (String) - - - -
    • - -
    - -
    - - - - -
    -
    -
    -
    -30
    -31
    -32
    -33
    -34
    -35
    -36
    -37
    -38
    -39
    -40
    -41
    -42
    -43
    -44
    -
    -
    # File 'lib/hyde/util/header.rb', line 30
    -
    -def self.make_value(input, opts, sep = ";")
    -  unless input.match?(/^[\w!#$%&'*+-.^_`|~]*=?[^[:cntrl:]\\",;]+$/)
    -    raise StandardError, "input format is invalid"
    +  str = CGI.escapeHTML(str)
    +  str.gsub(/[^\x1-\x7E]/) do |match|
    +    "&##{match.ord};"
       end
    -
    -  output = input
    -  opts.each do |key, value|
    -    output += if value.is_a? String
    -                "#{sep} #{key}=#{value}"
    -              else
    -                "#{sep} #{key}"
    -              end
    -  end
    -  output
    -end
    -
    -
    - -
    -

    - - .parse_value(input, sep = ";") ⇒ Array(String, Hash) - - - - - -

    -
    -

    Parse parametrized header values

    - - -
    -
    -
    -

    Parameters:

    -
      - -
    • - - input - - - (String) - - - -
    • - -
    • - - sep - - - (String, Regexp) - - - (defaults to: ";") - - -
    • - -
    - -

    Returns:

    -
      - -
    • - - - (Array(String, Hash)) - - - -
    • - -
    - -
    - - - @@ -799,12 +552,12 @@ This function is not adequate to prevent string interpolation.

     
     
    -77
    -78
    -79
    +79 +80 +81 @@ -440,8 +479,6 @@
     
     
    -17
    -18
     19
     20
     21
    @@ -453,10 +490,20 @@
     27
     28
     29
    -30
    +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 @@ -504,8 +559,6 @@
     
     
    -17
    -18
     19
     20
     21
    @@ -517,10 +570,20 @@
     27
     28
     29
    -30
    +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 @@ -568,8 +639,6 @@
     
     
    -17
    -18
     19
     20
     21
    @@ -581,10 +650,20 @@
     27
     28
     29
    -30
    +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 @@ -613,7 +700,53 @@
    -

    +

    + + #decode(data) ⇒ Object + + + + + +

    +
    +

    Decode charset parameter

    + + +
    +
    +
    + + +
    -
    -
    -
    -11
    -12
    -13
    -14
    -15
    -16
    -17
    -18
    -19
    -20
    -21
    -22
    -23
    -24
    -
    -
    # File 'lib/hyde/util/header.rb', line 11
    -
    -def self.parse_value(input, sep = ";")
    -  parts = input.split(sep).map { |x| URI.decode_uri_component(x).strip }
    -  base = parts.shift
    -  opts = parts.map do |raw|
    -    key, value = raw.match(/
    -        \A                    # beginning of string
    -        ([!-~&&[^=;,]]+)      # key
    -        (?:=([\s!-~&&[^;,]]*)|) # optional value
    -        \Z                    # end of sting
    -    /x).to_a[1..]
    -    [key, ((value&.match?(/^".*"$/) ? value[1..-2] : value) or true)]
    -  end.to_h
    -  [base, opts]
     end
    -
    # File 'lib/hyde/util/html.rb', line 77
    +      
    # File 'lib/hyde/util/html.rb', line 79
     
     def self.unescape_html(str)
       CGI.unescapeHTML(str)
    @@ -819,7 +572,7 @@ This function is not adequate to prevent string interpolation.

    diff --git a/doc/Hyde/Util/FormPart.html b/doc/Hyde/Util/FormPart.html index fe45777..c533f40 100644 --- a/doc/Hyde/Util/FormPart.html +++ b/doc/Hyde/Util/FormPart.html @@ -246,6 +246,29 @@
  • + #decode(data) ⇒ Object + + + + + + + + + + + + + +

    Decode charset parameter.

    +
    + +
  • + + +
  • + + #file? ⇒ Boolean @@ -376,8 +399,6 @@
     
     
    -17
    -18
     19
     20
     21
    @@ -389,10 +410,20 @@
     27
     28
     29
    -30
    +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40
  • -
    # File 'lib/hyde/util/multipart.rb', line 17
    +      
    # File 'lib/hyde/util/multipart.rb', line 19
     
     FormPart = Struct.new(:data, :name, :filename,
                           :filetype, :tempfile, :headers) do
    @@ -402,10 +433,18 @@
         !tempfile.nil?
       end
     
    +  # Decode charset parameter
    +  def decode(data)
    +    data = Hyde::Util.unescape_html(data)
    +    return data unless self.headers['charset']
    +
    +    data.force_encoding(self.headers['charset']).encode("UTF-8")
    +  end
    +
       # If FormPart is not a file, simplify to string.
       # @return [FormPart, String]
       def simplify
    -    file? ? self : self.data
    +    file? ? self : decode(self.data)
       end
     end
    -
    # File 'lib/hyde/util/multipart.rb', line 17
    +      
    # File 'lib/hyde/util/multipart.rb', line 19
     
     FormPart = Struct.new(:data, :name, :filename,
                           :filetype, :tempfile, :headers) do
    @@ -466,10 +513,18 @@
         !tempfile.nil?
       end
     
    +  # Decode charset parameter
    +  def decode(data)
    +    data = Hyde::Util.unescape_html(data)
    +    return data unless self.headers['charset']
    +
    +    data.force_encoding(self.headers['charset']).encode("UTF-8")
    +  end
    +
       # If FormPart is not a file, simplify to string.
       # @return [FormPart, String]
       def simplify
    -    file? ? self : self.data
    +    file? ? self : decode(self.data)
       end
     end
    -
    # File 'lib/hyde/util/multipart.rb', line 17
    +      
    # File 'lib/hyde/util/multipart.rb', line 19
     
     FormPart = Struct.new(:data, :name, :filename,
                           :filetype, :tempfile, :headers) do
    @@ -530,10 +593,18 @@
         !tempfile.nil?
       end
     
    +  # Decode charset parameter
    +  def decode(data)
    +    data = Hyde::Util.unescape_html(data)
    +    return data unless self.headers['charset']
    +
    +    data.force_encoding(self.headers['charset']).encode("UTF-8")
    +  end
    +
       # If FormPart is not a file, simplify to string.
       # @return [FormPart, String]
       def simplify
    -    file? ? self : self.data
    +    file? ? self : decode(self.data)
       end
     end
    -
    # File 'lib/hyde/util/multipart.rb', line 17
    +      
    # File 'lib/hyde/util/multipart.rb', line 19
     
     FormPart = Struct.new(:data, :name, :filename,
                           :filetype, :tempfile, :headers) do
    @@ -594,10 +673,18 @@
         !tempfile.nil?
       end
     
    +  # Decode charset parameter
    +  def decode(data)
    +    data = Hyde::Util.unescape_html(data)
    +    return data unless self.headers['charset']
    +
    +    data.force_encoding(self.headers['charset']).encode("UTF-8")
    +  end
    +
       # If FormPart is not a file, simplify to string.
       # @return [FormPart, String]
       def simplify
    -    file? ? self : self.data
    +    file? ? self : decode(self.data)
       end
     end
    + + + + +
    +
    +
    +
    +28
    +29
    +30
    +31
    +32
    +33
    +
    +
    # File 'lib/hyde/util/multipart.rb', line 28
    +
    +def decode(data)
    +  data = Hyde::Util.unescape_html(data)
    +  return data unless self.headers['charset']
    +
    +  data.force_encoding(self.headers['charset']).encode("UTF-8")
    +end
    +
    +
    + +
    +

    #file?Boolean @@ -650,12 +783,12 @@
     
     
    -21
    -22
    -23
    +23 +24 +25

    -
    # File 'lib/hyde/util/multipart.rb', line 21
    +      
    # File 'lib/hyde/util/multipart.rb', line 23
     
     def file?
       !tempfile.nil?
    @@ -690,8 +823,6 @@
           
     
     
    -17
    -18
     19
     20
     21
    @@ -703,10 +834,20 @@
     27
     28
     29
    -30
    +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40
    -
    # File 'lib/hyde/util/multipart.rb', line 17
    +      
    # File 'lib/hyde/util/multipart.rb', line 19
     
     FormPart = Struct.new(:data, :name, :filename,
                           :filetype, :tempfile, :headers) do
    @@ -716,10 +857,18 @@
         !tempfile.nil?
       end
     
    +  # Decode charset parameter
    +  def decode(data)
    +    data = Hyde::Util.unescape_html(data)
    +    return data unless self.headers['charset']
    +
    +    data.force_encoding(self.headers['charset']).encode("UTF-8")
    +  end
    +
       # If FormPart is not a file, simplify to string.
       # @return [FormPart, String]
       def simplify
    -    file? ? self : self.data
    +    file? ? self : decode(self.data)
       end
     end
    @@ -765,15 +914,15 @@
     
     
    -27
    -28
    -29
    +37 +38 +39
    -
    # File 'lib/hyde/util/multipart.rb', line 27
    +      
    # File 'lib/hyde/util/multipart.rb', line 37
     
     def simplify
    -  file? ? self : self.data
    +  file? ? self : decode(self.data)
     end
    @@ -805,8 +954,6 @@
     
     
    -17
    -18
     19
     20
     21
    @@ -818,10 +965,20 @@
     27
     28
     29
    -30
    +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40
    -
    # File 'lib/hyde/util/multipart.rb', line 17
    +      
    # File 'lib/hyde/util/multipart.rb', line 19
     
     FormPart = Struct.new(:data, :name, :filename,
                           :filetype, :tempfile, :headers) do
    @@ -831,10 +988,18 @@
         !tempfile.nil?
       end
     
    +  # Decode charset parameter
    +  def decode(data)
    +    data = Hyde::Util.unescape_html(data)
    +    return data unless self.headers['charset']
    +
    +    data.force_encoding(self.headers['charset']).encode("UTF-8")
    +  end
    +
       # If FormPart is not a file, simplify to string.
       # @return [FormPart, String]
       def simplify
    -    file? ? self : self.data
    +    file? ? self : decode(self.data)
       end
     end
    @@ -847,7 +1012,7 @@ diff --git a/doc/Hyde/Util/HeaderRegexp.html b/doc/Hyde/Util/HeaderRegexp.html new file mode 100644 index 0000000..b98f8f4 --- /dev/null +++ b/doc/Hyde/Util/HeaderRegexp.html @@ -0,0 +1,274 @@ + + + + + + + Module: Hyde::Util::HeaderRegexp + + — Documentation by YARD 0.9.34 + + + + + + + + + + + + + + + + + + + +
    + + +

    Module: Hyde::Util::HeaderRegexp + + + +

    +
    + + + + + + + + + + + +
    +
    Defined in:
    +
    lib/hyde/util/parseutils.rb
    +
    + +
    + +

    Overview

    +
    +

    (not exactly precise) Regular expressions for some RFC definitions

    + + +
    +
    +
    + + +
    + +

    + Constant Summary + collapse +

    + +
    + +
    TOKEN = +
    +
    +

    Matches the RFC2616 definiton of token

    + + +
    +
    +
    + + +
    +
    +
    /[!-~&&[^()<>@,;:\\"\/\[\]?={}\t]]+/.freeze
    + +
    QUOTED = +
    +
    +

    Matches the RFC2616 definition of quoted-string

    + + +
    +
    +
    + + +
    +
    +
    /"[\x0-\x7E&&[^\x1-\x8\xb-\x1f]]*(?<!\\)"/.freeze
    + +
    PRINTCHAR = +
    +
    +

    Matches any CHAR except CTLs

    + + +
    +
    +
    + + +
    +
    +
    /[\x2-\x7E]/.freeze
    + +
    PRINTABLE = +
    +
    +

    Matches 1 or more CHARs excluding CTLs

    + + +
    +
    +
    + + +
    +
    +
    /#{PRINTCHAR}+/o.freeze
    + + +
    /[\x21-\x7E&&[^",;\\]]*/.freeze
    + + +
    /(?:#{QUOTED}|#{COOKIE_OCTET})/o.freeze
    + + +
    TOKEN
    + + +
    /\A(#{COOKIE_NAME})=(#{COOKIE_VALUE})\Z/o.freeze
    + +
    PARAM_QUOTED = +
    +
    +

    Matches a very abstract definition of a quoted header paramter. +Captures name (1) and value (2).

    + + +
    +
    +
    + + +
    +
    +
    /\A(#{TOKEN})=?(#{QUOTED}|#{PRINTCHAR}*)\Z/o.freeze
    + +
    PARAM = +
    +
    +

    Matches a very abstract definition of a header parameter. +Captures name (1) and value (2).

    + + +
    +
    +
    + + +
    +
    +
    /\A(#{TOKEN})=?(#{PRINTCHAR}*)\Z/o.freeze
    + + +
    /\A(#{TOKEN})=?(#{QUOTED}|#{COOKIE_OCTET})\Z/o.freeze
    + +
    + + + + + + + + + + +
    + + + +
    + + \ No newline at end of file diff --git a/doc/Hyde/Util/Lookup.html b/doc/Hyde/Util/Lookup.html index 777d470..ec5a18f 100644 --- a/doc/Hyde/Util/Lookup.html +++ b/doc/Hyde/Util/Lookup.html @@ -589,7 +589,7 @@ diff --git a/doc/Hyde/Util/MultipartParser.html b/doc/Hyde/Util/MultipartParser.html index 92a54bb..5953275 100644 --- a/doc/Hyde/Util/MultipartParser.html +++ b/doc/Hyde/Util/MultipartParser.html @@ -89,7 +89,7 @@
    Includes:
    -
    ParserCommon
    +
    ParserSorting
    @@ -237,15 +237,15 @@
     
     
    -35
    -36
    -37
    -38
    -39
    -40
    +45 +46 +47 +48 +49 +50
    -
    # File 'lib/hyde/util/multipart.rb', line 35
    +      
    # File 'lib/hyde/util/multipart.rb', line 45
     
     def initialize(io, boundary)
       @input = io.is_a?(String) ? StringIO.new(io) : io
    @@ -303,16 +303,6 @@
           
     
     
    -48
    -49
    -50
    -51
    -52
    -53
    -54
    -55
    -56
    -57
     58
     59
     60
    @@ -355,10 +345,20 @@
     97
     98
     99
    -100
    +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110
    -
    # File 'lib/hyde/util/multipart.rb', line 48
    +      
    # File 'lib/hyde/util/multipart.rb', line 58
     
     def parse
       return @data unless @data.empty?
    @@ -457,12 +457,12 @@
           
     
     
    -106
    -107
    -108
    +116 +117 +118
    -
    # File 'lib/hyde/util/multipart.rb', line 106
    +      
    # File 'lib/hyde/util/multipart.rb', line 116
     
     def to_h
       flatten(sort(gen_hash(parse)))
    @@ -477,7 +477,7 @@
     
     
           
    diff --git a/doc/Hyde/Util/ParserCommon.html b/doc/Hyde/Util/ParserCommon.html
    index 2e255e0..eef0692 100644
    --- a/doc/Hyde/Util/ParserCommon.html
    +++ b/doc/Hyde/Util/ParserCommon.html
    @@ -74,23 +74,18 @@
       
     
       
    -  
    -
    Included in:
    -
    MultipartParser, Query
    -
    -
    Defined in:
    -
    lib/hyde/util/sorting.rb
    +
    lib/hyde/util/parseutils.rb

    Overview

    -

    Internal library for generating form hashes

    +

    Module for all things related to parsing HTTP and related syntax.

    @@ -99,6 +94,31 @@
    + +

    + Constant Summary + collapse +

    + +
    + +
    RFC1123_DATE = +
    +
    +

    strftime parameter to return a correct RFC 1123 date.

    + + +
    +
    +
    + + +
    +
    +
    "%a, %d %b %Y %H:%M:%S GMT"
    + +
    + @@ -106,11 +126,347 @@ + +

    + Class Method Summary + collapse +

    + + + + + + +
    +

    Class Method Details

    + + +
    +

    + + .make_value(input, opts, sep = ";") ⇒ String + + + + + +

    +
    +

    Construct a parametrized header value. +Does some input sanitization during construction

    + + +
    +
    +
    +

    Parameters:

    +
      + +
    • + + input + + + (String) + + + +
    • + +
    • + + opts + + + (Hash) + + + +
    • + +
    + +

    Returns:

    +
      + +
    • + + + (String) + + + +
    • + +
    +

    Raises:

    + + +
    + + + + +
    +
    +
    +
    +80
    +81
    +82
    +83
    +84
    +85
    +86
    +87
    +88
    +89
    +90
    +91
    +92
    +93
    +94
    +95
    +96
    +
    +
    # File 'lib/hyde/util/parseutils.rb', line 80
    +
    +def self.make_value(input, opts, sep = ";")
    +  output = input
    +  unless input.match? HeaderRegexp::PRINTABLE
    +    raise Hyde::ParsingError, "input is not ascii printable"
    +  end
    +
    +  opts.each do |key, value|
    +    check_param(key, value)
    +    newparam = if value.is_a? String
    +                 "#{sep} #{key}=#{value}"
    +               else
    +                 "#{sep} #{key}"
    +               end
    +    output += newparam
    +  end
    +  output
    +end
    +
    +
    + +
    +

    + + .parse_value(input, sep: ";", unquote: false, regexp: nil) ⇒ Array(String, Hash) + + + + + +

    +
    +

    Parse parametrized header values. +This method will try the best attempt at decoding parameters. +However, it does no decoding on the first argument.

    + + +
    +
    +
    +

    Parameters:

    +
      + +
    • + + input + + + (String) + + + +
    • + +
    • + + sep + + + (String, Regexp) + + + (defaults to: ";") + + +
    • + +
    • + + unquote + + + (Boolean) + + + (defaults to: false) + + + — +

      interpret params as possibly quoted

      +
      + +
    • + +
    • + + regexp + + + (Regexp, nil) + + + (defaults to: nil) + + + — +

      override param matching regexp

      +
      + +
    • + +
    + +

    Returns:

    +
      + +
    • + + + (Array(String, Hash)) + + + +
    • + +
    + +
    + + + + +
    +
    +
    +
    +51
    +52
    +53
    +54
    +55
    +56
    +57
    +58
    +59
    +60
    +61
    +62
    +63
    +64
    +65
    +66
    +67
    +68
    +69
    +70
    +
    +
    # File 'lib/hyde/util/parseutils.rb', line 51
    +
    +def self.parse_value(input, sep: ";", unquote: false, regexp: nil)
    +  parts = input.split(sep).map { |x| URI.decode_uri_component(x).strip }
    +  base = parts.shift
    +  opts = parts.map do |raw|
    +    key, value = raw.match(if regexp
    +                             regexp
    +                           elsif unquote
    +                             HeaderRegexp::PARAM_QUOTED
    +                           else
    +                             HeaderRegexp::PARAM
    +                           end).to_a[1..]
    +    value = case value
    +            when "" then true
    +            when /\A".*"\z/ then value.undump
    +            else value
    +            end
    +    [key, value]
    +  end.to_h
    +  [base, opts]
    +end
    +
    +
    + +
    diff --git a/doc/Hyde/Util/ParserSorting.html b/doc/Hyde/Util/ParserSorting.html new file mode 100644 index 0000000..60216a8 --- /dev/null +++ b/doc/Hyde/Util/ParserSorting.html @@ -0,0 +1,120 @@ + + + + + + + Module: Hyde::Util::ParserSorting + + — Documentation by YARD 0.9.34 + + + + + + + + + + + + + + + + + + + +
    + + +

    Module: Hyde::Util::ParserSorting + + + +

    +
    + + + + + + + + + +
    +
    Included in:
    +
    MultipartParser, Query
    +
    + + + +
    +
    Defined in:
    +
    lib/hyde/util/parsesorting.rb
    +
    + +
    + +

    Overview

    +
    +

    Internal library for generating form hashes

    + + +
    +
    +
    + + +
    + + + + + + + + +
    + + + +
    + + \ No newline at end of file diff --git a/doc/Hyde/Util/Query.html b/doc/Hyde/Util/Query.html index f376b4a..c123411 100644 --- a/doc/Hyde/Util/Query.html +++ b/doc/Hyde/Util/Query.html @@ -89,7 +89,7 @@
    Includes:
    -
    ParserCommon
    +
    ParserSorting
    @@ -106,8 +106,7 @@

    Overview

    -

    TODO: encoding support???? -Query string parser

    +

    Query string parser

    @@ -276,12 +275,12 @@ Query string parser

     
     
    +12
     13
    -14
    -15
    +14
    -
    # File 'lib/hyde/util/query.rb', line 13
    +      
    # File 'lib/hyde/util/query.rb', line 12
     
     def initialize(query)
       @query = query
    @@ -351,12 +350,12 @@ Query string parser

     
     
    +39
     40
    -41
    -42
    +41
    -
    # File 'lib/hyde/util/query.rb', line 40
    +      
    # File 'lib/hyde/util/query.rb', line 39
     
     def [](key)
       (@cache ||= parse)[key]
    @@ -412,12 +411,12 @@ Key semantics:

     
     
    +32
     33
    -34
    -35
    +34
    -
    # File 'lib/hyde/util/query.rb', line 33
    +      
    # File 'lib/hyde/util/query.rb', line 32
     
     def parse
       construct_deep_hash(URI.decode_www_form(@query))
    @@ -465,14 +464,14 @@ Key semantics:

     
     
    +18
     19
     20
     21
    -22
    -23
    +22
    -
    # File 'lib/hyde/util/query.rb', line 19
    +      
    # File 'lib/hyde/util/query.rb', line 18
     
     def parse_shallow
       URI.decode_www_form(@query)
    @@ -489,7 +488,7 @@ Key semantics:

    diff --git a/doc/_index.html b/doc/_index.html index ba43b1b..384037e 100644 --- a/doc/_index.html +++ b/doc/_index.html @@ -138,6 +138,13 @@ +
  • + Error + + (Hyde) + +
  • +
  • Erubi @@ -211,6 +218,13 @@
  • +
  • + HeaderRegexp + + (Hyde::Util) + +
  • +
  • Hyde @@ -315,6 +329,20 @@
  • +
  • + ParserSorting + + (Hyde::Util) + +
  • + +
  • + ParsingError + + (Hyde) + +
  • +
  • Path @@ -538,7 +566,7 @@ diff --git a/doc/class_list.html b/doc/class_list.html index cd336bd..1c1c893 100644 --- a/doc/class_list.html +++ b/doc/class_list.html @@ -43,7 +43,7 @@ diff --git a/doc/file.README.html b/doc/file.README.html index fa6cac2..5c9acd8 100644 --- a/doc/file.README.html +++ b/doc/file.README.html @@ -190,7 +190,7 @@ diff --git a/doc/index.html b/doc/index.html index d908c0a..1a9a115 100644 --- a/doc/index.html +++ b/doc/index.html @@ -190,7 +190,7 @@ diff --git a/doc/method_list.html b/doc/method_list.html index 3f95571..a95cd27 100644 --- a/doc/method_list.html +++ b/doc/method_list.html @@ -197,6 +197,14 @@
  • +
    + #decode + Hyde::Util::FormPart +
    +
  • + + +
  • default_error_page Hyde::Util @@ -204,7 +212,7 @@
  • -
  • +
  • #delete Hyde::DSL::PathConstructors @@ -212,7 +220,7 @@
  • -
  • +
  • #delete_header Hyde::Response @@ -220,7 +228,7 @@
  • -
  • +
  • #die Hyde::DSL::CommonMethods @@ -228,7 +236,7 @@
  • -
  • +
  • #erb Hyde::DSL::ProbeConstructors @@ -236,7 +244,7 @@
  • -
  • +
  • #erubi Hyde::DSL::ProbeConstructors @@ -244,7 +252,7 @@
  • -
  • +
  • escape_html Hyde::Util @@ -252,6 +260,14 @@
  • +
  • +
    + #escape_html + Hyde::DSL::ProbeMethods +
    +
  • + +
  • #file @@ -646,8 +662,8 @@
  • - make_value - Hyde::Util + make_value + Hyde::Util::ParserCommon
  • @@ -774,8 +790,8 @@
  • - parse_value - Hyde::Util + parse_value + Hyde::Util::ParserCommon
  • @@ -1229,6 +1245,14 @@
  • +
    + #unescape_html + Hyde::DSL::ProbeMethods +
    +
  • + + +
  • #validate Hyde::Response @@ -1236,7 +1260,7 @@
  • -
  • +
  • #value Hyde::Cookie diff --git a/doc/top-level-namespace.html b/doc/top-level-namespace.html index 2c8f5b6..92fa839 100644 --- a/doc/top-level-namespace.html +++ b/doc/top-level-namespace.html @@ -100,7 +100,7 @@
    diff --git a/examples/cookies/cookie.ru b/examples/cookies/cookie.ru new file mode 100644 index 0000000..228870c --- /dev/null +++ b/examples/cookies/cookie.ru @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +$LOAD_PATH.unshift("#{File.dirname(__FILE__)}/lib") +require 'hyde' +require 'hyde/util/cookie' + +app = Hyde::Server.new do + get "/set-cookie" do + header "set-cookie", Hyde::Cookie.new("test", (rand * 500).floor.to_s).to_s + header "content-type", "text/plain" + "Cookie set! Visit /get-cookie to view it" + end + get "/get-cookie" do + header "content-type", "text/plain" + request.headers["cookie"]&.pretty_inspect + end +end + +run app diff --git a/examples/cookies/lib b/examples/cookies/lib new file mode 120000 index 0000000..58677dd --- /dev/null +++ b/examples/cookies/lib @@ -0,0 +1 @@ +../../lib \ No newline at end of file diff --git a/examples/cookies/readme.txt b/examples/cookies/readme.txt new file mode 100644 index 0000000..a8501aa --- /dev/null +++ b/examples/cookies/readme.txt @@ -0,0 +1,3 @@ +shows basic usage of unsigned cookies + +please note that this does not sign cookies. diff --git a/examples/form2/index.rhtml b/examples/form2/index.rhtml index fd77c5b..b2cb90a 100644 --- a/examples/form2/index.rhtml +++ b/examples/form2/index.rhtml @@ -25,7 +25,7 @@
      <% formdata.each do |key, part| %> -
    • <%= key %>: <%= part.is_a?(String) ? part : "File: #{part.map do |x| x.filename end.join(',')}" %>
    • +
    • <%= key %>: <%= escape_html(part.is_a?(String) ? part : "File: #{part.map do |x| x.filename end.join(',')}") %>
    • <% end %>
    <% end %> diff --git a/hyde-0.9.gem b/hyde-0.9.gem new file mode 100644 index 0000000000000000000000000000000000000000..6c486a9840ffd45466141295d988545f92fd9bd1 GIT binary patch literal 35840 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\[\]{}"]|[^ -~])/) - raise StandardError, "header key has invalid characters" + unless key.match(Hyde::Util::HeaderRegexp::TOKEN) + raise ArgumentError, "header key has invalid characters" end - if value.match(/[^ -~]/) - raise StandardError, "value key has invalid characters" + unless value&.match(Hyde::Util::HeaderRegexp::PRINTABLE) + raise ArgumentError, "value key has invalid characters" end @origin.response = (@origin.response or Hyde::Response.new) @@ -51,12 +52,12 @@ module Hyde return if key.downcase == "status" - if key.match(/(?:[(),\/:;<=>?@\[\]{}"]|[^ -~])/) - raise StandardError, "header key has invalid characters" + unless key.match(Hyde::Util::HeaderRegexp::TOKEN) + raise ArgumentError, "header key has invalid characters" end - if value&.match(/[^ -~]/) - raise StandardError, "value key has invalid characters" + unless value&.match(Hyde::Util::HeaderRegexp::PRINTABLE) + raise ArgumentError, "value key has invalid characters" end @origin.response.delete_header(key, value) @@ -65,7 +66,9 @@ module Hyde # Checks if current request has multipart/form-data associated with it # @return [Boolean] def form? - value, opts = Hyde::Util.parse_value(request.headers["content-type"]) + value, opts = Hyde::Util::ParserCommon.parse_value( + request.headers["content-type"] + ) if value == "multipart/form-data" and opts["boundary"] true @@ -77,17 +80,31 @@ module Hyde # Returns formdata # @return [Hash{String=>(String,Hyde::Util::FormPart)}] def form - _, opts = Hyde::Util.parse_value(request.headers["content-type"]) + _, opts = Hyde::Util::ParserCommon.parse_value( + request.headers["content-type"] + ) Hyde::Util::MultipartParser.new( request.input, opts["boundary"] ).to_h end # Open a file relative to current filepath - # @see File#open + # @see File.open def file(path, mode = "r", *all, &block) File.open("#{request.filepath}/#{path}", mode, *all, &block) end + + # Escape HTML entities + # @see Hyde::Util.escape_html + def escape_html(text) + Hyde::Util.escape_html(text) + end + + # Unescape HTML entities + # @see Hyde::Util.escape_html + def unescape_html(text) + Hyde::Util.unescape_html(text) + end end end end diff --git a/lib/hyde/node.rb b/lib/hyde/node.rb index 1434de8..1613862 100644 --- a/lib/hyde/node.rb +++ b/lib/hyde/node.rb @@ -19,7 +19,7 @@ module Hyde # Set Node file root (like root in Nginx) # @param path [String] def root=(path) - raise StandardError, "path should be a String" unless path.is_a? String + raise ArgumentError, "path should be a String" unless path.is_a? String @properties["path"] = File.expand_path(path) @root = File.expand_path(path) diff --git a/lib/hyde/util/cookie.rb b/lib/hyde/util/cookie.rb index b7ece50..7f4b997 100644 --- a/lib/hyde/util/cookie.rb +++ b/lib/hyde/util/cookie.rb @@ -1,12 +1,24 @@ # frozen_string_literal: true -require_relative 'header' +require_relative 'parseutils' +require_relative 'errors' module Hyde # Utility class for handling cookies class Cookie - # @param data [String] raw cookie data + # @param key [String] cookie name + # @param value [String] cookie value + # @param params [Hash] cookie parameters + # @raise Hyde::ParsingError invalid cookie parameters def initialize(key, value, params = {}) + unless key.match? Hyde::Util::HeaderRegexp::COOKIE_NAME + raise Hyde::ParsingError, "invalid cookie key: #{key}" + end + + unless value.match? Hyde::Util::HeaderRegexp::COOKIE_VALUE + raise Hyde::ParsingError, "invalid cookie value: #{value}" + end + @key = key @value = value @params = params @@ -15,7 +27,7 @@ module Hyde # Convert cookie to "Set-Cookie: " string representation. # @return [String] def to_s - Hyde::Util.make_value(to_short, @params) + Hyde::Util::ParserCommon.make_value(to_short, @params) end # Convert cookie to "Cookie: " string representation (no params) @@ -30,7 +42,10 @@ module Hyde # @param data [String] value part of "Set-Cookie: " header # @return [Cookie] def self.from_setcookie_string(data) - kvpair, params = Hyde::Util.parse_value(data) + kvpair, params = Hyde::Util::ParserCommon.parse_value( + data, + regexp: Hyde::Util::HeaderRegexp::COOKIE_PARAM + ) key, value = kvpair.split("=").map(&:strip) Cookie.new(key, value, params) end diff --git a/lib/hyde/util/errors.rb b/lib/hyde/util/errors.rb new file mode 100644 index 0000000..fa6dc97 --- /dev/null +++ b/lib/hyde/util/errors.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +module Hyde + # Generic error class, as recommended by Ruby documentation. + class Error < ::StandardError + end + + # Error class raised by hyde/util/parseutils module. + class ParsingError < Error + end +end diff --git a/lib/hyde/util/header.rb b/lib/hyde/util/header.rb deleted file mode 100644 index 1e1a56c..0000000 --- a/lib/hyde/util/header.rb +++ /dev/null @@ -1,46 +0,0 @@ -# frozen_string_literal: true - -require 'uri' - -module Hyde - module Util - # Parse parametrized header values - # @param input [String] - # @param sep [String, Regexp] - # @return [Array(String, Hash)] - def self.parse_value(input, sep = ";") - parts = input.split(sep).map { |x| URI.decode_uri_component(x).strip } - base = parts.shift - opts = parts.map do |raw| - key, value = raw.match(/ - \A # beginning of string - ([!-~&&[^=;,]]+) # key - (?:=([\s!-~&&[^;,]]*)|) # optional value - \Z # end of sting - /x).to_a[1..] - [key, ((value&.match?(/^".*"$/) ? value[1..-2] : value) or true)] - end.to_h - [base, opts] - end - - # Construct a parametrized header value - # @param input [String] - # @param opts [Hash] - # @return [String] - def self.make_value(input, opts, sep = ";") - unless input.match?(/^[\w!#$%&'*+-.^_`|~]*=?[^[:cntrl:]\\",;]+$/) - raise StandardError, "input format is invalid" - end - - output = input - opts.each do |key, value| - output += if value.is_a? String - "#{sep} #{key}=#{value}" - else - "#{sep} #{key}" - end - end - output - end - end -end diff --git a/lib/hyde/util/html.rb b/lib/hyde/util/html.rb index c7449a1..6a4d7d3 100644 --- a/lib/hyde/util/html.rb +++ b/lib/hyde/util/html.rb @@ -62,13 +62,15 @@ module Hyde }.freeze # Return string with escaped HTML entities. - # @note Do **not** use this to inject untrusted input into JavaScript code - # or CSS style of the page. + # @note Do **not** use this to inject untrusted input into JavaScript code or CSS style of the page. # This function is not adequate to prevent string interpolation. # @param str [String] # @return [String] def self.escape_html(str) - CGI.escapeHTML(str) + str = CGI.escapeHTML(str) + str.gsub(/[^\x1-\x7E]/) do |match| + "&##{match.ord};" + end end # Return string with unescaped HTML entities. diff --git a/lib/hyde/util/multipart.rb b/lib/hyde/util/multipart.rb index 8b589ad..9dab275 100644 --- a/lib/hyde/util/multipart.rb +++ b/lib/hyde/util/multipart.rb @@ -3,7 +3,9 @@ require 'uri' require 'stringio' require 'tempfile' -require_relative 'header' +require_relative 'parseutils' +require_relative 'parsesorting' +require_relative 'html' module Hyde module Util @@ -22,16 +24,24 @@ module Hyde !tempfile.nil? end + # Decode charset parameter + def decode(data) + data = Hyde::Util.unescape_html(data) + return data unless self.headers['charset'] + + data.force_encoding(self.headers['charset']).encode("UTF-8") + end + # If FormPart is not a file, simplify to string. # @return [FormPart, String] def simplify - file? ? self : self.data + file? ? self : decode(self.data) end end # A very naive implementation of a Multipart form parser. class MultipartParser - include Hyde::Util::ParserCommon + include Hyde::Util::ParserSorting def initialize(io, boundary) @input = io.is_a?(String) ? StringIO.new(io) : io @boundary = boundary @@ -158,7 +168,7 @@ module Hyde return unless line.match(/^[\w!#$%&'*+-.^_`|~]+:.*\r\n$/) k, v = line.match(/^([\w!#$%&'*+-.^_`|~]+):(.*)\r\n$/).to_a[1..] - headers_hash[k.downcase] = Hyde::Util.parse_value(v) + headers_hash[k.downcase] = Hyde::Util::ParserCommon.parse_value(v) end end end diff --git a/lib/hyde/util/sorting.rb b/lib/hyde/util/parsesorting.rb similarity index 97% rename from lib/hyde/util/sorting.rb rename to lib/hyde/util/parsesorting.rb index a9a1af4..4746639 100644 --- a/lib/hyde/util/sorting.rb +++ b/lib/hyde/util/parsesorting.rb @@ -3,7 +3,7 @@ module Hyde module Util # Internal library for generating form hashes - module ParserCommon + module ParserSorting private # Sort key-value pair arrays diff --git a/lib/hyde/util/parseutils.rb b/lib/hyde/util/parseutils.rb new file mode 100644 index 0000000..6f56b94 --- /dev/null +++ b/lib/hyde/util/parseutils.rb @@ -0,0 +1,113 @@ +# frozen_string_literal: true + +require 'uri' +require 'date' +require_relative 'errors' + +module Hyde + module Util + # (not exactly precise) Regular expressions for some RFC definitions + module HeaderRegexp + # Matches the RFC2616 definiton of token + TOKEN = /[!-~&&[^()<>@,;:\\"\/\[\]?={}\t]]+/.freeze + # Matches the RFC2616 definition of quoted-string + QUOTED = /"[\x0-\x7E&&[^\x1-\x8\xb-\x1f]]*(?