From 66e69e8a64b88cc671d780171c2b9d7f66f922cc Mon Sep 17 00:00:00 2001 From: Tony Garnock-Jones Date: Mon, 1 Jun 2009 22:53:19 +0100 Subject: [PATCH] Initial commit --- .gitignore | 1 + Makefile | 32 + NOTES | 0 deps/mochiweb-r88/LICENSE | 9 + deps/mochiweb-r88/Makefile | 8 + deps/mochiweb-r88/README | 1 + deps/mochiweb-r88/ebin/mochifmt.beam | Bin 0 -> 18812 bytes deps/mochiweb-r88/ebin/mochifmt_records.beam | Bin 0 -> 1792 bytes deps/mochiweb-r88/ebin/mochifmt_std.beam | Bin 0 -> 1556 bytes deps/mochiweb-r88/ebin/mochihex.beam | Bin 0 -> 3068 bytes deps/mochiweb-r88/ebin/mochijson.beam | Bin 0 -> 24036 bytes deps/mochiweb-r88/ebin/mochijson2.beam | Bin 0 -> 25664 bytes deps/mochiweb-r88/ebin/mochinum.beam | Bin 0 -> 9616 bytes deps/mochiweb-r88/ebin/mochiweb.app | 32 + deps/mochiweb-r88/ebin/mochiweb.beam | Bin 0 -> 4452 bytes deps/mochiweb-r88/ebin/mochiweb_app.beam | Bin 0 -> 1136 bytes deps/mochiweb-r88/ebin/mochiweb_charref.beam | Bin 0 -> 19400 bytes deps/mochiweb-r88/ebin/mochiweb_cookies.beam | Bin 0 -> 10840 bytes deps/mochiweb-r88/ebin/mochiweb_echo.beam | Bin 0 -> 1684 bytes deps/mochiweb-r88/ebin/mochiweb_headers.beam | Bin 0 -> 8708 bytes deps/mochiweb-r88/ebin/mochiweb_html.beam | Bin 0 -> 42880 bytes deps/mochiweb-r88/ebin/mochiweb_http.beam | Bin 0 -> 6616 bytes .../mochiweb-r88/ebin/mochiweb_multipart.beam | Bin 0 -> 19864 bytes deps/mochiweb-r88/ebin/mochiweb_request.beam | Bin 0 -> 25404 bytes deps/mochiweb-r88/ebin/mochiweb_response.beam | Bin 0 -> 2292 bytes deps/mochiweb-r88/ebin/mochiweb_skel.beam | Bin 0 -> 4592 bytes .../ebin/mochiweb_socket_server.beam | Bin 0 -> 9672 bytes deps/mochiweb-r88/ebin/mochiweb_sup.beam | Bin 0 -> 1596 bytes deps/mochiweb-r88/ebin/mochiweb_util.beam | Bin 0 -> 30008 bytes deps/mochiweb-r88/ebin/reloader.beam | Bin 0 -> 5212 bytes deps/mochiweb-r88/priv/skel/Makefile | 5 + .../priv/skel/priv/www/index.html | 8 + deps/mochiweb-r88/priv/skel/src/Makefile | 9 + deps/mochiweb-r88/priv/skel/src/skel.app | 14 + deps/mochiweb-r88/priv/skel/src/skel.erl | 30 + deps/mochiweb-r88/priv/skel/src/skel.hrl | 1 + deps/mochiweb-r88/priv/skel/src/skel_app.erl | 22 + deps/mochiweb-r88/priv/skel/src/skel_deps.erl | 84 ++ deps/mochiweb-r88/priv/skel/src/skel_sup.erl | 54 ++ deps/mochiweb-r88/priv/skel/src/skel_web.erl | 43 + deps/mochiweb-r88/priv/skel/start-dev.sh | 3 + deps/mochiweb-r88/priv/skel/start.sh | 3 + .../mochiweb-r88/priv/skel/support/include.mk | 46 + deps/mochiweb-r88/scripts/new_mochiweb.erl | 27 + deps/mochiweb-r88/src/Makefile | 12 + deps/mochiweb-r88/src/mochifmt.erl | 426 +++++++++ deps/mochiweb-r88/src/mochifmt_records.erl | 30 + deps/mochiweb-r88/src/mochifmt_std.erl | 23 + deps/mochiweb-r88/src/mochihex.erl | 75 ++ deps/mochiweb-r88/src/mochijson.erl | 529 +++++++++++ deps/mochiweb-r88/src/mochijson2.erl | 597 ++++++++++++ deps/mochiweb-r88/src/mochinum.erl | 289 ++++++ deps/mochiweb-r88/src/mochiweb.app | 32 + deps/mochiweb-r88/src/mochiweb.erl | 110 +++ deps/mochiweb-r88/src/mochiweb_app.erl | 20 + deps/mochiweb-r88/src/mochiweb_charref.erl | 295 ++++++ deps/mochiweb-r88/src/mochiweb_cookies.erl | 250 +++++ deps/mochiweb-r88/src/mochiweb_echo.erl | 31 + deps/mochiweb-r88/src/mochiweb_headers.erl | 178 ++++ deps/mochiweb-r88/src/mochiweb_html.erl | 880 ++++++++++++++++++ deps/mochiweb-r88/src/mochiweb_http.erl | 132 +++ deps/mochiweb-r88/src/mochiweb_multipart.erl | 429 +++++++++ deps/mochiweb-r88/src/mochiweb_request.erl | 740 +++++++++++++++ deps/mochiweb-r88/src/mochiweb_response.erl | 56 ++ deps/mochiweb-r88/src/mochiweb_skel.erl | 71 ++ .../src/mochiweb_socket_server.erl | 248 +++++ deps/mochiweb-r88/src/mochiweb_sup.erl | 34 + deps/mochiweb-r88/src/mochiweb_util.erl | 576 ++++++++++++ deps/mochiweb-r88/src/reloader.erl | 124 +++ deps/mochiweb-r88/support/include.mk | 46 + doc/buildtoc.py | 63 ++ doc/footer.html | 4 + doc/header.html | 12 + doc/index.md | 4 + priv/www/background-banner.jpg | Bin 0 -> 6668 bytes priv/www/index.html | 16 + priv/www/style.css | 106 +++ src/Makefile | 11 + src/rabpubsubhub.app | 14 + src/rabpubsubhub.erl | 20 + src/rabpubsubhub_app.erl | 12 + src/rabpubsubhub_deps.erl | 83 ++ src/rabpubsubhub_sup.erl | 50 + src/rabpubsubhub_web.erl | 39 + start-dev.sh | 3 + start.sh | 3 + support/include.mk | 46 + 87 files changed, 7151 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 NOTES create mode 100644 deps/mochiweb-r88/LICENSE create mode 100644 deps/mochiweb-r88/Makefile create mode 100644 deps/mochiweb-r88/README create mode 100644 deps/mochiweb-r88/ebin/mochifmt.beam create mode 100644 deps/mochiweb-r88/ebin/mochifmt_records.beam create mode 100644 deps/mochiweb-r88/ebin/mochifmt_std.beam create mode 100644 deps/mochiweb-r88/ebin/mochihex.beam create mode 100644 deps/mochiweb-r88/ebin/mochijson.beam create mode 100644 deps/mochiweb-r88/ebin/mochijson2.beam create mode 100644 deps/mochiweb-r88/ebin/mochinum.beam create mode 100644 deps/mochiweb-r88/ebin/mochiweb.app create mode 100644 deps/mochiweb-r88/ebin/mochiweb.beam create mode 100644 deps/mochiweb-r88/ebin/mochiweb_app.beam create mode 100644 deps/mochiweb-r88/ebin/mochiweb_charref.beam create mode 100644 deps/mochiweb-r88/ebin/mochiweb_cookies.beam create mode 100644 deps/mochiweb-r88/ebin/mochiweb_echo.beam create mode 100644 deps/mochiweb-r88/ebin/mochiweb_headers.beam create mode 100644 deps/mochiweb-r88/ebin/mochiweb_html.beam create mode 100644 deps/mochiweb-r88/ebin/mochiweb_http.beam create mode 100644 deps/mochiweb-r88/ebin/mochiweb_multipart.beam create mode 100644 deps/mochiweb-r88/ebin/mochiweb_request.beam create mode 100644 deps/mochiweb-r88/ebin/mochiweb_response.beam create mode 100644 deps/mochiweb-r88/ebin/mochiweb_skel.beam create mode 100644 deps/mochiweb-r88/ebin/mochiweb_socket_server.beam create mode 100644 deps/mochiweb-r88/ebin/mochiweb_sup.beam create mode 100644 deps/mochiweb-r88/ebin/mochiweb_util.beam create mode 100644 deps/mochiweb-r88/ebin/reloader.beam create mode 100644 deps/mochiweb-r88/priv/skel/Makefile create mode 100644 deps/mochiweb-r88/priv/skel/priv/www/index.html create mode 100644 deps/mochiweb-r88/priv/skel/src/Makefile create mode 100644 deps/mochiweb-r88/priv/skel/src/skel.app create mode 100644 deps/mochiweb-r88/priv/skel/src/skel.erl create mode 100644 deps/mochiweb-r88/priv/skel/src/skel.hrl create mode 100644 deps/mochiweb-r88/priv/skel/src/skel_app.erl create mode 100644 deps/mochiweb-r88/priv/skel/src/skel_deps.erl create mode 100644 deps/mochiweb-r88/priv/skel/src/skel_sup.erl create mode 100644 deps/mochiweb-r88/priv/skel/src/skel_web.erl create mode 100755 deps/mochiweb-r88/priv/skel/start-dev.sh create mode 100755 deps/mochiweb-r88/priv/skel/start.sh create mode 100644 deps/mochiweb-r88/priv/skel/support/include.mk create mode 100644 deps/mochiweb-r88/scripts/new_mochiweb.erl create mode 100644 deps/mochiweb-r88/src/Makefile create mode 100644 deps/mochiweb-r88/src/mochifmt.erl create mode 100644 deps/mochiweb-r88/src/mochifmt_records.erl create mode 100644 deps/mochiweb-r88/src/mochifmt_std.erl create mode 100644 deps/mochiweb-r88/src/mochihex.erl create mode 100644 deps/mochiweb-r88/src/mochijson.erl create mode 100644 deps/mochiweb-r88/src/mochijson2.erl create mode 100644 deps/mochiweb-r88/src/mochinum.erl create mode 100644 deps/mochiweb-r88/src/mochiweb.app create mode 100644 deps/mochiweb-r88/src/mochiweb.erl create mode 100644 deps/mochiweb-r88/src/mochiweb_app.erl create mode 100644 deps/mochiweb-r88/src/mochiweb_charref.erl create mode 100644 deps/mochiweb-r88/src/mochiweb_cookies.erl create mode 100644 deps/mochiweb-r88/src/mochiweb_echo.erl create mode 100644 deps/mochiweb-r88/src/mochiweb_headers.erl create mode 100644 deps/mochiweb-r88/src/mochiweb_html.erl create mode 100644 deps/mochiweb-r88/src/mochiweb_http.erl create mode 100644 deps/mochiweb-r88/src/mochiweb_multipart.erl create mode 100644 deps/mochiweb-r88/src/mochiweb_request.erl create mode 100644 deps/mochiweb-r88/src/mochiweb_response.erl create mode 100644 deps/mochiweb-r88/src/mochiweb_skel.erl create mode 100644 deps/mochiweb-r88/src/mochiweb_socket_server.erl create mode 100644 deps/mochiweb-r88/src/mochiweb_sup.erl create mode 100644 deps/mochiweb-r88/src/mochiweb_util.erl create mode 100644 deps/mochiweb-r88/src/reloader.erl create mode 100644 deps/mochiweb-r88/support/include.mk create mode 100644 doc/buildtoc.py create mode 100644 doc/footer.html create mode 100644 doc/header.html create mode 100644 doc/index.md create mode 100644 priv/www/background-banner.jpg create mode 100644 priv/www/index.html create mode 100644 priv/www/style.css create mode 100644 src/Makefile create mode 100644 src/rabpubsubhub.app create mode 100644 src/rabpubsubhub.erl create mode 100644 src/rabpubsubhub_app.erl create mode 100644 src/rabpubsubhub_deps.erl create mode 100644 src/rabpubsubhub_sup.erl create mode 100644 src/rabpubsubhub_web.erl create mode 100755 start-dev.sh create mode 100755 start.sh create mode 100644 support/include.mk diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..17278c0 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.beam diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..919deb1 --- /dev/null +++ b/Makefile @@ -0,0 +1,32 @@ +MARKDOWN_SOURCES=$(wildcard doc/*.md) +MARKDOWN_TARGETS=$(patsubst doc/%.md,doc/html/%.html,$(MARKDOWN_SOURCES)) + +all: ebin + (cd src;$(MAKE)) + +docs: html-docs + +html-docs: doc/html $(MARKDOWN_TARGETS) + +doc/html: + mkdir -p doc/html + +doc/html/%.html: doc/%.md + (title=`grep '^# ' $< | head -1 | sed -e 's:^# ::'` ;\ + t=/tmp/$*.md ;\ + sed -e "s:@TITLE@:$$title:g" < doc/header.html > $@ ;\ + python doc/buildtoc.py < $< > $$t ;\ + markdown $$t >> $@ ;\ + rm $$t ;\ + cat doc/footer.html >> $@) + +ebin: + mkdir -p ebin + +clean: clean-docs + (cd src;$(MAKE) clean) + +clean-docs: clean-html + +clean-html: + rm -rf doc/html diff --git a/NOTES b/NOTES new file mode 100644 index 0000000..e69de29 diff --git a/deps/mochiweb-r88/LICENSE b/deps/mochiweb-r88/LICENSE new file mode 100644 index 0000000..c85b65a --- /dev/null +++ b/deps/mochiweb-r88/LICENSE @@ -0,0 +1,9 @@ +This is the MIT license. + +Copyright (c) 2007 Mochi Media, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/deps/mochiweb-r88/Makefile b/deps/mochiweb-r88/Makefile new file mode 100644 index 0000000..72f4b80 --- /dev/null +++ b/deps/mochiweb-r88/Makefile @@ -0,0 +1,8 @@ +all: + (cd src;$(MAKE)) + +test: + (cd src;$(MAKE) test) + +clean: + (cd src;$(MAKE) clean) diff --git a/deps/mochiweb-r88/README b/deps/mochiweb-r88/README new file mode 100644 index 0000000..f5d2c0d --- /dev/null +++ b/deps/mochiweb-r88/README @@ -0,0 +1 @@ +MochiWeb is an Erlang library for building lightweight HTTP servers. diff --git a/deps/mochiweb-r88/ebin/mochifmt.beam b/deps/mochiweb-r88/ebin/mochifmt.beam new file mode 100644 index 0000000000000000000000000000000000000000..396530fd4f90cebf1f3407dac7f2eb4d762371c8 GIT binary patch literal 18812 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�(%|m;oQ~>mWi95F97c3J=>;1jGvnmdmX0 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 H$I<@>Znb_J literal 0 HcmV?d00001 diff --git a/deps/mochiweb-r88/ebin/mochifmt_records.beam b/deps/mochiweb-r88/ebin/mochifmt_records.beam new file mode 100644 index 0000000000000000000000000000000000000000..a0a56fc97c1f033baafd949cd93089ae97eab7bd GIT binary patch literal 1792 zcmbVMYfuwc6yD9|r9rYJpp>Gf2Ar`x5*{jItxG@@tAGZkj8tmRqMP-zSSkT^^ff;{$XTCk>o^$p)=iYnn zjf`0vfFS(0s%2rT!&uskAjlyAF>a={24k|BO=Ku7&FHL{g~|{bEmoGaXsIcBicL%> zO*V>8F(%TY7f>e3Oj%erlhMj1vUDPq2^6D6pGX4IDQOIy20d23iKbI+X*`NyXht}h zV;ZCbc@m8l9hHgth|ILkW}?6}nHEFeM{`hIIIW`)1Qi1n2Y@3BKmx!=agJmTj>`D> zGE~Wt1mY-xEAdE~bh%_XMo2L!8q1NWlxWhTczI~NT&9xaL821GMXFYosGXAtL!6BW zawX(mgfy7IA_O>2U|bOoaS6r*lz|cmHTtj~$cF*I+)%=p$sADUtfIBBPI!iJ?gRk+PGKy|0BP7^0$=6YM`>(&#}#wzlK?IRtT`3o1^dtU4}__>tkFyv_!`hh_ehi! zlYG*X{L--=D{W)6R4TIa(<+&~`IE1UKX^t`!oV4Vwn*6#3!3J?%!;h|7tQ5Lys)}H zsI|fEhCQJVV>_ZBSB>935dC3@SC$)rVw+TFhc0GGnQX7S?+W@~-m?zN0}g8j_qHA8 z_C%)HBVLb{%-K18)aj_3@JhiMH||{bu4tb3=wQy(XFugrn>U?^xEj?FV%fV>T^3Td z(0$actwBYeNe{@6tQ}w7e2lG%kxXr{Q=a#0e8=x*`{am1l20+!k2?*_`9S}375Qe{ z+-BD*zii`yn*r-8?}tixCrR@bH{FRjV|!E&@PyQY-9L^veV4oClD)uIT6|;g$~RF1 z`!_5W-$}?jND=c+xf%F5@twc=mPefVc4e@=Yee|VKIKrunxY(`x7+b{S=O36ITd-U z#3OO9>wX^Xk-ZgNnC;c2?rbhtkeP4Zm~ChNbc$L~Jbb=8sC(^#qpp2pL_wrij`#Dq zOY`5E>K8Kgm8JZ!?xu|K?8}wJ_FG;7G4G3VV*hgY6Wyu18P)zglzVj6XBW;f>G92U2z}IB~Mdv--H%xnndhFVC-1g{2S;oJ;S@>X0dfdj6n)-m>`ZfF*8ew#hpxFE+h-au)fX`-G_H<@SV!=(nzzu}<9=9o~DSrn}-BL+$$ESJ~7| z`dBM=#42iQD(msp>D$tsO?4jEZ+0E(w!3;17FYi;wQ!cG(6hsk#8w_@U91^+Sy|n> zWVqEfl<@HK`$u~BJ)`#9V|AK7pI0+y3wB3sZOjvAToc^Z4|tw?NbfW@=It)Mu3h@< t!|5&kcc+i|o>rXxD(36;2WNjiBY2kg{(mlAk@s^CcV4=N_U=cJ{{h`eS2zFw literal 0 HcmV?d00001 diff --git a/deps/mochiweb-r88/ebin/mochifmt_std.beam b/deps/mochiweb-r88/ebin/mochifmt_std.beam new file mode 100644 index 0000000000000000000000000000000000000000..e94d9bb97da8c10276789bcd87cd180e7e3728dc GIT binary patch literal 1556 zcmbVMeNYr-7=QQfI1V`CS|+0M5A>1}N{$I?SWc8kj$+hKUht8UCY%se z#z>`3mhsZD6wj42!fZ?C%Sms5(cbBCCn?6q(*ZA2%5qL#%?MtSD~m%4bLh1^&A=K$ z5IP9-*&yN}qA;BTUjVQqHORyW1-=YmR%YKfeQrIVQ$u2njw>vg!L%!Y62TDAJbcoA%?)l6!@zIcG-MJZ#jc{S+Pk4 z-U%>`&WbB-xq8^2Sylt_8)z zHHJAz$sDQ=eTqC*Li0+7c(9_4vfJ&n-L7+=wC9nRzZfkzIh;3=`l8X0iviPK^|Juv z@f6BOx*%PW1ZZ3}UieqK6f{tliyUfToDQ7+^Tw4HbF;T+iF1uAZ(*$)C1^;8CDq5fNWzPOV+og_=e?5A7WM*Phj_%RdZQ2iZ+)8#YT;m_&Y%^^} z)zti~UhVPT#(AciCcYu=z6@9EM~V5BV)4VVj}QaHqxJPaeOhqz!i96oM_=mxaS?7zPV#Hd*-!UpOx1gS;;4uxagvYe}{-1AEs6V`I-v{^r)bJ3Zn%)ji$6 zn7VeTA$bGa`B?Cq-1yKUX=vF0^39yHXUo_V-Pcb!>hxKm>p!2}VtTJWzW2nBwzl&x gbe4W{zGv4T4XMS3j=x(>wNC%W|1fjf^SJ>05A)XPi2wiq literal 0 HcmV?d00001 diff --git a/deps/mochiweb-r88/ebin/mochihex.beam b/deps/mochiweb-r88/ebin/mochihex.beam new file mode 100644 index 0000000000000000000000000000000000000000..2c45525e84f2d550d9cf005b3aa05a41eb24e4e5 GIT binary patch literal 3068 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 literal 0 HcmV?d00001 diff --git a/deps/mochiweb-r88/ebin/mochijson.beam b/deps/mochiweb-r88/ebin/mochijson.beam new file mode 100644 index 0000000000000000000000000000000000000000..8e65032c66039ede7bb0f5c4531572f2a9bb591c GIT binary patch literal 24036 zcmcG$2{@GN`#3&h1~bFUFm}qCY{O{C7LC2gmMz(rvF}Tgl(keUq7o`uN@R;l$d*W= z5K39Hq=-b?ME~a*L%jn0Ls-201H(dBOTGL(7(Jc+-F#tY zf_vW%cq8rFu|<9YX^geZ4|LF*IrX!~7WByga-@L+G7@gPkK7LO?gyP>hSS zn`0<7*bhxT=oQMg0j(pg89j_4gx)>C*UgtPn0kN;q~qAwDlj-85N^Z1u@K+}Vlq2} zp;+evd(aO^>lo%5f^`oD#bKfD%4qn(80PN#kwm042IMsDFaT}pnESr6g9wo z&7kB_&i)bfu3*SRnL_c^2eTP;tuf2iJetyp2=^NmW zqx!q86*I3jq74cbE~i1Pp#eTrf3HJS$5oZ%y#nCDfR(;#sX!3lKN|rfaG?gHL%clv zQNT*j?!nHkXaLC5!;m~Z6|O;3K?yzl0L8um&Y_rfB6QNytn27OHEd+i`a7*9Baq0^ zpBm2K6$0~(a`yFw;3C?H!!229!vWjes_gXCpfIlkbn5ENG+?ci5ojnRAUG6t$SV-H zS^;;)zz*gJaIOLY{#3dEA7<({Z(zWn;sDi^$uGby%$MruOl>4-0;pt+(<%h z8__+?pGYEt0dvC>SNWBbUn}Gwu9^WT@O2e)Ac34j+)UhPOY3(32O|EzRMST1k2*Hu znlJxBfsMEpBK|1R20nm5Y~coR3l9?fFA;%2ii4bl#M7~h;E_Vi2s}j}K|l*Z93BXE z1OZ8+W8a2HQs6=cAxH>mO2@8+M>^5k;o)|C9(cH2ARYT&JTjW1kB8g&B9rOZUGT_k zV>|*`M#mnDM>ZJa@yO?N?C0>v5o5d%a*mEY2am+yjqxNThK-K>HUf!ZX9jT+$c?yC|Ly}h^m4lq$P^b))K{=lOTe&nl?n>rgTG`qYxJo!^who zQh|5~svwcH2nduk#Dfx{n2S<`Q4}(okz`6i5h!RBJ&+F&=la*?^8eJFTLB`VC3!yN0CxC>PjvdDi_~cWDHm&i=3;5*S$R{t%CoeYzM}YXdApsI5S50u5wJ3NA(AfEUKA*?41vUK+XW;f18kxIn}PzQ z2(Tr?*cAW7MxtTM{sY_gHEi40u|=(8+YVz>0@%a=Hf0i|f&z}kh;LxTKc~k-rhuM` zBBZ)nxL%^F0x1ElD6MM+5455rDnf#k$ZBLr2`EDH5X& z=tu!wXaE{$JVe0@;dQYhC=~3zn4O?f8dOr0AtM^Q63`w35bwJ39r^ z?Sk~KLHdo*t{7-H5~EAef45T&Rwh6{M4%tjRpssdi}K7sr7Wo2s|-1ynDKbncxW24gk`p*Y0MmG z%mRrqU)7jB5@WHdF#uUgKvp!3f#<5mK%o^8vV(iu!5Xvfg7#g5Y#O2cF_0}1W4)>| z&}!eR#sFlqsxbiVU(*->ZPzpgKsyP@5!RR^O#^@E%zmTJXi}}~%s~M%`$I2||E3ow zAi5k7-B|*1L19VYiB&;G(c?$J3#?EV3CI^6ZyjDO%In52VLpa2ky15hLoiwPhWgLo_iCRBg2Do9RRWO%0Z z2NMv11K2HyofMpkD8(7|$Ap=P#{)Ev{{xLQ6hOeKDnJ3EU{fYEUS?ix8pBF+0d&r4*=E={GIh+fb0N42+W<@ zS_HhgG;}~4r;evM>5)+S=K7-gWEwZYB+v+W(E$(^HJ}jOPAC+K2~mN4f`I01bWMGO`M5C__h6$7k_IcbR@ z2+~k2L5qSS!__z%tDrM5a^PHgBm$tL0J;JPqkwTZ!XgCJ2nztr{vXsx0`!4p#RJuA zLq~DCP&5*AR0WCyiiulSOx&NAwb4vC)+{R$m_sxW2`o(krN2pl{!@ZDG{A{=(n7-$ z#H~sIs@Ele*R^XBp!GRcB@hA<#K8U+yJm(!WpRJHW;|?>3Bb1WfNdR9ff9g4By6;; z1lYC`VB1On0`7p010j=;nBy!Ypp-T9I0ww*++WQ@1xf^%&TX`5V!&(wFC0L<98Ovs zu!SVAS_sFV7Qz8rNZ{&%nO~nnk0h!OTRRH)2s;T_O43GK|A%4yV-#RpKMh9U3=(r% z1xf*fnzBBqDSz7f#t~Sz^<%)+&up}H&>fg{%DP#n{IgmA!8Cf6Y1q60)3AA;h0Qy8 z&8C5^r2J{#=V0?r1?IgAl&2{}sbEP<)>eVifpMnO%-kGI8m`h%x;Y5E>8>~lCFnd^ z9lD6boL7V{tU?zgpmZ|!C=zp#8IA;CqX1kK1JZL9=myaDjdgwB_*37Kt2<3t-?Z5q z9)1w8(tz;kL?{nlq36+-gO|W^Fc*orqzvV0YigUjf=Y8Wb5Mz4$A&=}*ia}F>knmt zR+q6Z&=qVjbQLQHWrL;nHLN?7gT!Qjp01j%A#os&(~L{a1$nM!ZpA~8KV)`#uK5sU zL8(AfQaLCgaD1c?bikUzdE=VG8D5ug0@c7dJDL_YNrDvm!i zob_Mexb`<3m;VbKSdqUWx$<8iiTN9jtN#U#uD{{P{x5J~<^G1`+JA$@?r%tP{tG0H zf5VZ>EMy9NAKu(+!;>X27Z5;htOgJ*FoObfS+zR0RjUJ2By4s6Ha+HTh4RVpMm3cT zFWHfpd=`?a3RD1Wu;33HG?#`7U>kIrV>91igCO#1(-sc@V9M9m;*b*(bK}pDRPbk@ zx(Nc4A()Q~m7!ALecI+KP>HB2NKW8n%N3k#g-Amst{^a#gusERNLw8$Mq-K-pgx3zhHUEfBEg&|vtjDI7e~C?^@Dd13hQkvDDg+K#A`O*lV~OBN z!0l0hO3Ab%r)I!cGmS5lmM8_xEzN7n00&{Bgf)kx&E7DKGa`TuCxHyFn*mll5>qM- zmC^8(k>OefP>RHqDMOVsxuv0UZ7c~q2{7C@|JBe9l$CZ^2+ z7Q}yqEkMqMMow&9a}_kqVC(z;9CM{KbccX5fdyRE+#O}8YE5%@wXw3w&|Py)b5~$4 zcL_LS3FxjXyaT_BRdHn@MgN-bekL-zqGW+4g%1$Gs{(!j2^ zY<>M=1P=Vb9*+Q4x@g)u;Jz|cyQY*{0?t$dss=^{p8w}@{+kA>rJx#`?6ox6YyJz_ zLA1b9VT=8n#I-WeL%_N+RJSHb9RX)11w8-`DGAkSW2KRp2md#l-TT+ktdoZ70SO9j z@0w-RD??2*+9aq6_MIl+JA0u9oH^8p#571iO*EZ0{hxJe{#QO4|9`{WBn{mp;LMSj zhyP+n7Js*+=Cue7!q%fd<95rRak~}7ZA%be9xFq`Kv$wL6>9wZZJu zp$#Tr5UobQv^26lEs?-nH?kJ3z`BTywl1OtMQt>Sw6;$M8&|;WQx+l0zxAffvHj7T z42RVJ=-nX&b&6tzB%x=bDM(Bw3m)Ja1)Pkoa{|x0qcA5hE*cxO`F3@g2@auPMLZ)y z1F(Pt+!zk9sC^C=w_RWn+l`Hada(A;3#=;Ci;aZ(u(nV?Sn$5Y%0RD>nCHsSz?u#Q zw6S5}Z7tRfxMF?5TZm>zT&grQ5Qp_;!Ds&)TiJ7(t8AriSO(~0KDgbC5CM)nk92+mN6! z`0!_pwuBnQSwKTb%pe6Ufk7mLkE$@->+`Yt-{a`e#%4VK+H6<>Y6UyTRzSCv7EMG~ z4~1Ldv(Q#DP6ba9f>nf5)u+=Z01b`&8I<3`TmR8N~-8BqVc z@gDRu91cGNGy>2ka2Gm<#C!s+fF?fwIRetqXKkDS1x%Q<+tD9L%-lb>;s2w}FC^x( z95jytiNHjU;X~2xs^^uU1r$gdiWW%F3@phE?F#q{xB^}T(tJf?zDPqe1RSS2^bLtw zq`_P?_$v+OMq<7J(P73*!5Hi2G4)|@IC-+yx36is*Uyz_8BNP7#0Ax ztsbVdDG&i(d8MJBC>$s4nENL@pi2O!E5P|n8Tw6|UzMRH0+>KG3GlT9Y~J+8`_gwpWmtWePa#qFw9%0{vGgIPTRWqTdS85(*^3D%TPSFfbBGK*kM$l%NA& z8&Gf(@J$Ye!Uk?kh-grX0<}~jqy$=-NKa!zg@_VW1xW~Ow!r>O4D8QnCP4;AAqlYD zV(>ZhtTd4U2m`->Q_S@WHu$7;ttyHhF~JaAJSixc=r0-B(J^BsK*FQ5l~prCXB%(n(B z0GNLb76Y&V4aUP~=>&a!47f4ZCD5(abAbBItM#i#ZTc8NxLzM4M9|g8fUUwRel%z& zvf2(~0dAu7F`_6?zX@E3@%~OgfjfBMEc~l?@h}lFa9KqwM1z+tx`fph9NB8Gzvc5qryz1VKb&(V}fnTI%X~fBCR7gIHc3V6ZBCA01FSm;!i?K z@Tw4b0688M@CEoLkqmAUwc&#~utg?_k%)X`5UarF3xI-#IuYoDk4WTSEeiy04}3wH zR?M$N6d{cyb7TZ=ApqB@JCU*{Q^Obj0d0)NQ@w%K|-#{0@8ZlEo(6R?bg-}k z8(NI>r(c@!>6ecyYl%I15_u$Mi~T$*dj-R8ctOVLxP`lT|AEb7dsQ_ipS~fF-KQo! za7&sw{s79H$Z~G2*d&A>D^zw@N9ZW^IBs&lQz97QMT6Up`}Ji;jn$J4rwNuW~Pb)JWEY4%{YbtsPp zaWtn0>OWMTYDwWdKW1hcHzNMyS!nHBiCz00G%cC#U3gv%Fgg|QxOCIA5pZfeQ`Cpb4=-T=*ZAGdy+^v8-tZ)Qu`FaEym96?35?cs#o zymXY~kp}skk`Mz$Z9OMdL% zVYA4Emwf2Onv~(GP$Y2j^HiEPUjXln4X0w20%P{gJtr7yRk^eMn{3}7vYHq_b5FTy ztjZ!>n5v&&a57KFpTBCa1q+M6=y!piyPtL&CJ)p$*0tk~rN7ozGwIJ8aLEj0WX>J5 zYOie22_MnOsxRu?>eCw`?L_U}+=U6|3%_!4Av514rp7ljv(|UNd;gb?YrN@MJqOM5 zFFD0zh8}(4BgJ(p9WwYXE1@DOc@KfvpLaYeCuBqz#bS3i;lR`Kl4@8NwA) z#+NT^IDbJ}DQhkNR#sF0eWdK$hu4u8Idva&?bxAu#N6xoX4(949s6*p>TG%B!eZ;y zqRyKBi?Xk8nO!MSQ@qxbgItzcx);3rgp$bJ+YMKK{rtYgy>Z9w*spvqi<-u!f4;tY z)tPk1-Cp3NtNmc;G@l0PWJV&?GGHh2=)~JIo}+?YUyODk5PJe#fz2WtfCa-ow9%h7 z`p`zd+2||aJA9fSaDsfJuWa-WkF_?ie^Wt%{Sx*iH;}+TBZqx&EYriG% z3=IvGqahBbx)6hvm5Cv)!Oo$so^m03Sy?$M_!J&Sh4k`4 zQ2uKyUjDAWVQy461kR1>66Qhs#+;@pM)*e&;8z|#2zmusMcHjIDRiipAJvn=1*}hA zIU}7}of(`3oTX{7W|w7b|g)gkWgSULvg^0h6E30J+Q}y zTK3+ryCmd{3w6H9bw@9hNUz1taEVRyI}chc)`jk^*yOP{k*SeAnemcO<1mkfFnzK_ zWwTo)b$zN0gkf8eefy({XN)!_Rk{Ncz00<|VGZ-Pfli z^lP#|jy$#OYt9L~9%S!#0q>@eKSiuFE_c%oNp&@pQDJ&|$IrM$X17Jl;98I9qXVTC34HLx##2KwrrD}J0b zp2(x5+n79URNfuzlabnMRBqAz{zeIQtM0Ay`|Ldwl=My?=}g3*Q+&Pabu)oqrmp4S zn`cQ`hjMRL_r3p=y2$NHy?a1D)86kzuSzNlUOsYnT#A3vA*P&d6N#Z{DXks$7kHFL zzm~OgpMBlj#Nir!8O!os&*c%q^AUo4PQI)0sxTW%GKO9=vgmsiLRdD_c6mIV@7{`L zKZ_NPxlw_~g}u^7G<5+}D9qlQEM%cAaHQ8<+rq@zSM)`!Uc>3=5j}LmvfkB?dVF8` zxBTK?E~`28qip!`@nyXe*4U-T4P|HJt&-dxYU~ve_@MIA3>|bD?JUTP|7vCsE|jjP zxhHmeZjuupQT{YblfuBL)dPk7i|j3m7d{A~hxa80$egK-4bCdMud&5@UKtya#p^Zs z?Grnxq|f`NTUH@D=E~zr6S_kpX-e{lFJhcu#IXM77`}+T#O9ydjm*CB{Hrm^Bxdxw zXWwrN&QBI6d%u)WjD|m|Cc0R&Yu^$6*V!P?b%NTTBlnh;C}k zdi!ZVrkdAacW}0a0r9+6;v;LXs@Is~F4u7fu8Up0iB#*cGzq*^?cnaVKl`>t_VxOd zc7v8F<{qV2X^+r9MW##&g1tFHh(?NBQO4#W+Lw1uqDr0b?YJ8#e_rj$rlg|WSE&j4 zQwa2=n%OJ$4qSh?M?_PITU$ncr)tLBIZ^&i1vVZdGQADTfzHHW=iHg}aZvek^t=aZ9hwPM2)SbBUTAs;JbT zZYILq*EzVaCo`FU!L@S9wN(5zVs384%-I`E*Dy%d3uk@e;~7>gs1S}~RIIq5c<@3> zsL8|SyQ$+d7SFDyeCxK#+@+)+_0}cZXiMkU?WaezwyU&z?ooarw9jalTWZ;zmI9x& zZJDud+qc>U3x#fHSAAV$x9HZ%a!zIIH9O@)B9l$g9h~-&Nk7{N9){XKNY)~3X)`XT z&(BhcGMKwhmD{dIoQu4|+ZJvkow@gXSb2-Mgv%9CEqV<>x~p;$dPP@bqn}+Yq^Fi3 z9Lz6nOE$I@Nf^xWSJ|JJ%_aPJC|E3Wy3v!Lyj{BQ1t#I1a+75o7bT7h1J;x`1iVbR znkp$$c21jQ6}HJ%PO$k1d0ZI@-JZhz%*X4_io1&Zv{qa7^Q2$9Y%ZTSzOCgbgRa{r z!IU;tud8v^mcV_j_x7%*JRd4k58f5?aoelf`<#bS`)9KaI`8wD3tA^$o!YYGLhja8 ze9gd6?C=pwCz>>2STdIQ6BdSmi`<+Pu?+;?PamEZ>hSmo1i#T3xC@KDM}g4>ucZ{UgVk zUhzdFd&<-K4??&iXAY~lraOGIw{EA6-1>Z?@B8Vesyw!YgQU9*-nOVScl-8jrSI(( z;^?~@RJ;jyjqG3dr2D<}A6`-EW@Y zQsAemswTDl;p3j^{D|q(AL>dLOCrQ3DHK!{Vff6JgZIo_#_k5R+ubdex+Z_&QlZ@XueL@$j!d1H!;auRnH zxZgBLSJcjRDsd*-*6r!N_g~dmlq8SZZke_scb}dOmnx35irKU2*3R#PpRKHKdiSD| z$C1UBth}-W8!V4o z`qv*+mpRq{&^zG8?_F!k2iODXMDs*^i(DBn3ew1guCzk8Wu{9bGRyVgZ&6W4C_Z}DzNMqPG%i=R@F zh+lX^coDL>Y^$A9q1pjT_{WBClHrwyy2`YthS=s_9!swfe4Tyf#~rbsVLrt@4}bVf z4yAgx8GE)F`?hsj5noBhd}J8^>p|Kl46#}AJC zKRkMKZ&1|_4M-$cUA<6sb-UnN8jvhsJ?eFNj!w68pW9~8YjP?7 zbTD;bY2v9xoVxsBo{4$ErXR&@W?si3D9?V6;N|b(SKkiPKf!jXUi?$5&(=(5KBDT;qd}mJTCevAM8N2I|{rbvhySJW^ zE#EI`rOT&v|B#Jr3E_QVgQF}XZ59|s&z8Sqy`X3m{n@7Z`>n1s&WHP4_RzIV;>SYt z^pAa3IVBJAT-8r{#=nWDWs=y!*YKUr<#6L;Dvx6sPZ45F_Wc9X1oAswcOLPJ=(-xkChHb6rz`pC(VXI(?hPsA@sFSV!lzp|Up;*G&XxmLc820( zB=ZMmy2cwer=ucw*?r?NwAjCek!PTA*QDSPq4Gn!4~^!ZdR0`LH*1-V_b;74cLby{ z@k-ktCdVJEar`iG4_dzXvY&h8%$MM7wmouDyg3nT~p_PgVZM+LR2pIMi)j=$7U3J%pSR!DTl2 zl@!15qj@v4h%ckajwTfZcPH}YEX(5q;fTr_lv|fTHmLFnKdROD>jY?+Gc)@v@$)m^ znkBMdc&Ttp|H}j4_&~X4(oyHvgN~jxk(=b}I`7u>d)2669)z0`%Hv2bjJw5Ho*W3r zUr;@EK`xAO!bQE{!$C%`RyDLFOZlcx$sAF;dOovU(>So}^=BS~fzK@Rn?6BDze3%g zhmz-qstODi8G62~Fq~Q#PPh0TP@-Hf>C16lZ*tp3h5)TQ0RvyVbRr1iPcj3&>b*E_ z#v5F~H)(G6ytqQteExdtQKobCR1PF#E!0?7j9l7|UfRy_^R9D2JZ_)XzPN@uQOu!2 zL9zL8p-*aBC#ts(u_@}^8MpUrtKl;oO1MzVbiURn$pXu1hU&E4 z3~P3ZV7AT@UTX!f^_6-A<9v9R(_znXQJ&*|j?#i+*B|10oYS<9-yA#peK9R)iOusN z+i6IjBB!CXM|m%2TcXbU!JMb8m?y2D2;U2kl z<*19?i6v|}Gk3?>oTA9Pw&=0B>?P%@xS%(6@fP##jNx{%Y9;j%L0Rz(Wykw%By15nmdAO}hZ!zTEcbB=Y zv$v@2Sg&^MZ{$0d#1H6((59BWY?YhYDj#x#+^%r!>9RO|6}L4?e(Ov-7CSEH^u*6# zOHSL%!=Vqq<+&Q2sU1V2aXtH^F#J=ErTHhX`Iet9J=>lhHF1z*a!&FCQ8YEHI4icw z;u2VIKOowZrN65&bCshj`O^(vdEwXR&aEg>&Xfi-4|52o`I_9o0;#98V96Pt_n*} zFsCz>@6DDKs3ZG-|CJxbaA;KKYqQPxi3+W&W7tVujNp*X`;!%X)>)}JotIM1lt>HM zJm;QFl2_I#-?n6N+1&j%W`uW?TKR;_yu{XVyk?bUG7t`b(I!%myko^%`TfMP z<{x<{pS<7<3*UP;Bp_R+GeUxi=yFtsseo|w-YIdT%H%=bIfn~s;@qL)Uk=WttCdR%TywNn0{a}fXCuA4byMnWK z?xVibD@5{i#Y?zpWCrN4WS&yFy(>G-^I@J2|BI{3^6!6cuQIHOd$N!+i)p*G-M^ER zc_fl=+F39uZR+ubfeddZ$kTA4w%CtiFLl$v*C*^E`wkcT-vyZ0E}B0VUtK7#FMe~+ zu;D&FPXzL?!j{r}OEH8ebM>x}z>% zqdwz5^LfXt|IEYh+3IC?)u-dWb~Bphm(+(Lgl7@)VVM0pHw7M@&YlU_bjhVJrDW;M zz)D|=P0=RQPus2M%LpF=E=La-7k|BHT>1FG=#uT$7sH9-*XzS@?*lGZt@NFk4)`I) zx)5aZWOB(?BM19xcs_>hhwt`xK1;S9;#*XE(%yf}uQ*vwPK7T2Xy^7RWJ-RS-+Q|^ zA>5o>htO@Lqc(P3_UVPnQ@4~uCkHw0)8_ddtH2SI4Fk9C5?|RD^4_^4 z%=JLvpl8sBclC#FRcG@T5(hD?#l%56Z$qV$yP;#L_53;5OTRcoj86Fv@O>t<#O=J+ z^#gmfZ_GI5`!BKV06W}ydjqcL`7VdV`dpy>`%iWWy}Imx3v}cQy*CwRMEUOLf#-o2kFH<9+giYM(mdNvubojK4MU z+Pq>B(*3S8Mdz0nMdZNofoekifl21~om*VO#o205BZIbnCkB7)`Dpwl{)tWw_*qAI zXrISfO;K4!=8N|(oVe(-?Q^gW$2P)=11*W|KlU{9c?rq&yNz=05qD_(k|ynGaz9p$ zvy8#8xKWvJOWUD@BS*uwJP)Dz1XZ1TmM_g_^w>`1$t99q+i|2%=#Cf$Me8VQwu+J* z+oGbm9Yg!=Ia`{FqKVe!Tn>*M7VYzg?Kq<`TnJ}+&CDXY+WS(^L1Bm3bZ@1vL-A`n z`;NS~y~xa>%}=%7YjSI`9ti10`IQ{PZ?3*S#a4EIsu8@*-5f@B#qWv2rHJdX&b*uA zYl-WB{@(7PeYE{Uop*duF;!fICC4_Abg_h5vZn;9Ua9U(8jmz= zjx6n~Lr+K79^Mp!ICQzN<>$(b{LRlI_b7f%3iDqJluUVL(q3dtHz`l6j$DZT^!>n% z%l)Jj@7I2J2NgI=7}-l$l{M>zB36{+F&ztAbrKi$omIQFBl-hjKWZpf{j1Qz&nt>U z<`W+!8y?Ty=ZVbm?j|_Rw$(L_c6bh1x<3j%lP@!A!}`c`$Mc?Na-r$3nI2E+5;AVJ zJ6y>RwEwE5P`tCep^)`SrNa}S+q?K{jHYIsE+Rh>SEjropG6&hvw2U|wt^w+m^1Fc7{>i6~^KjA4T(53Nad)o;IHp)H@mlTfAEITjgIo0I*&j2M%CR^&${vg= z9$7S-Sfs}Gb2FBfY8+gS-o31aE)^84J(4JHHB#t1#k zeij7QK*UaeSlz7kqWec|xMF0Z4K&`Od+_9GdJB#LzksA49 z}WxScgp45tzyz)cykj2Rfjjiw)}*r^L({z{6eZ{Iz|)u-19c+_ASK zP!D5(GRf;;;s`VurzEhZf2Ay@p+KX#SW{1n#u13Ik>7t@IL3EQPlEe-NfsNW6EiWR z;(zy2=OI=7eV;CU^P|?~o&Pav_3qSj%rr$+3I0vn`6U zU$6+8nM!T9-P_oa6q^|nn|TN4e|O=yMAfTkljo=DM)eX3P`Te#Nw}`~B9xALF)Fu8 zmGs2(VP6bvJY_6+WWHxK5uNKL%u_snx+PYR?Bl24;FEj)q1TUXm!>atjhnu-9_3E& zYI*k9dES$7NA;@F;wR6q(%QE!*+%mRU{gr<=CC15IoOLN9d)C0+p75cx*rZ)l!`j~cEc6^G8{YGs&7efSGuJ8m#Tm2nNpAz5IPoR=CPkSuu(%Cp z1Rp_JmL|R_;3bHAyGBd5q6M#>ee3@sNgfsOB8i^8g#lC*)tG7Ppx+cAwcOsF-r^H1 zbFZq!+y6!M@#@wfCGQtk$zN0sKOL)7@@}CYE)aIvUvEY>4$pPpk*s({{C;Z3$%4;U z@;#p#>+K`Ah-L&T#!+xj>ON;=Dvc@T|F$|B_LJ$8m1%r{;-R8oe@m(Q(ieQbNgW0v zTIDa(CQtZJ+&(~;Ocsu>*&2vwcDnG`bbJ<@D3?`(72BV)HE_$J*y}osuw!gFM3>l# zW@CL+MA)UXmHnd8xZU=Y6R&gx$n&-(eIsH*9(xnhjYbiBf?XLtWUuUQu72%VH^lHO zBokSJ^B=Pq*0UTw7A|-1gPg`zlyj=KveUNXO?lY!z(@v@s-g-MajgI^$;4 z#{d(wQRCI2ZcBRNW~3;U+eNP_`S5l0w$5^@LM>IfmTI8QFw8w+oTIl-Z~G0Zp8P1! z_DaPwns(ao{`Gz1jW-bx4hgg?Ljg5 z@Y(DWV5}*dr5pK#W4lnJT=ePokx89>r>*A94adB)sd^&{G5ihPFrYiySS!`0hY-_9QFeR?T|#n#=4<(?mpnkfbJHlG~+TRu|YGJU#N&8P1%U8#{NI5dA5nD79HkQ>JB#lh(DO4V1zIPBKQGIaq&FBdqE)#f+s}3ec z?^S3Jw}=(D(3&uG(!_S&!4BU^yu%E=CrfE=lhT|QMS|zHlVeWgLYC}OrWCWVtXE+f zNrl>3Z?~O{&f&FerCMAT&!tT$Ng{LxU5HGx#pdwEtWQ(-({8p(nhTaG8f>%86&vK+ z@~YU3-S70d_~ZVk&mlq4Jz7m(+b>+YVpn$U*r&s~{@mlQ7+RGl`aDpFt8z3gUo`A^ z+>IA!<&W-yK2E=Kd9i#qP^@=h#lD*+gj5je#k+j zdEH?#bLej5{n`r(GPcAGhh=Rac~0GOYKz(Zj@B^lo-ad3H*eF(Hg)#CzNM zt1N+xiSMrqO*U}8=J;NfTzoprgq++hFmlDcRXv%v#9U<0f`Vzq*H*?sn~7HJ8@AC* z_r945`KecLdOlVVQl6d}={@!;>DVjrl?l!Mk17r6Dw>YJ0{6)?oV-+uA`|Rw? z-XUccVq+E0vi9u&OMrbkrQ-V*6dFZ-DeqgTa_ZVZEaBEe*8gWHgz;W|MeqzIy3)ycx--hJUcg4Tn!~P(Y{HjU!o-Sb! zo>3L*nC4vU8i+0Q*gStEVS;|j1)X^|A#|afG~zSDHSge<=-CtzC2VE*pe}jgr`G9y z*lx{A<(eEEn`(|NLvo%avlZ<)}P zP$(>Dw|2T%I$~ER5>uDzNk2Bvx&LA*XweqMIAAa|2sSRt;ib(#3u>%`XB}<^g~JXp zHCt0&^Xc82sBpRm*Q1oiW^3-uI_M5QIJxxWxMgjPwQxq@tV6=0S$OH_n+FDCv#{b` z41RM8AAWd$=}P=U!jZz3NS?F9xr^Vu---Dss<-VgKH^xQI(7JJcA<}=U}i5^$og8j zMk#DBy8rUlfg!Pp2``|=u9vqO%WWP$ayM#Hz2VJpkGG#xZj*buW54)zA1~j&+vPSs zs#BW2ee>lur#zb;xfA>JT`Mk}>ez4O3wvJI%QvT7L*%$KBMB*4e7PlT=t&20%xL*Qa86^v%ZHq_vOX! zMc@0#DK>JVaYGBQ5plO0S<_mpH9e&(?p!nb8TQHr|10!{+^|x(cA|Bt(p=17v}Y6j zg@PL~gAMct;;!4nOKZB}hYyg;Umoj$|FtuZ$yM|l^n!o+adQ_5>d;I5#h#~lSTL3C z+s(t@F3nn#ml)OEsySFKMv)U~`bLvWk1y*uW*yxhB3PArQ1E{0;~-{{Nv&@wA;M=$ zmA?5s%cN`%pfh{LF?wWNoKU|YsrR_%Q0AU5C2z#P`dm{Y=ZBF~H8Z^!{I<9GSoA$l z7yGp`-?8WOOW&z_<)26NmXrB!&)_*AfO+IURXX{7vQ|- zWm9@tLPpu6di9$HE`0}>$#IU{KTNQR)WC-5aFq< z`qQWO&yVX48d^S*PVG|BYjzAcStRIgw9KhEZGHWBi0Ko$@0>S6YlKcK?q{^rW4IBb zmK&tjTV?NUaf>roCAItJF})R!Et}1}1mz#d_2TEUY+@c1$K|EiR-Yw3-z==(e&J2f z!R)K70)IQy%_$f)o_-L_pIc;=)~4#i0|XF+H>{BP0>D#k)Irr z+_48nFKrV4$(OpxplVmnM~?P}C&o*iIr;I=E?#bKxx7~@6=hPGXL54qR;z1AR>V5) zZhhq&*=>y;=m>~L9Yvv+Tb>=ETT)`-y6d$=7xgubJ4EV(q%}MHyaMNZWs+*g(;M<$ z2SWXCo!~qd@H;8${M${Gg~fL^)ADIom0I1^&e~P#e!sG~EbIDN=JcZqUzuBGYFyus zzm;{+!bW;5s|by%l$TZ-HP&ZL?vCtieR$?V^7|dn_Wj=RXr=b|0(5epe7eeJ{v&2j zYdho+v)s2LneTQuM9^{7I~DFMm^o`|7|zyb9~-D8&nszm)xkVyhOX*>$}@w<9kvYT zH~p^vZ20Xl^g;HvaBoJ8Kv|};)4jp`w!}VUO$@RImC@q=?$E5reU-6r!q^>gp|Rg_ z*C&pq&yOTB2X{QVs(srr@3y1D`8Zb9C%ccDiAJ%WuWLs+3eb9REq($pRv)%IJ z#4!5icCl~=cj=CHgZv;_PYU;&qVCx+tC9?nNVC_&Ot)UP-;I(V*v@vjKi!~xG~40q zC&g{aR`o6@-_in$(wB;8r$~C|Nc!x936H)SNWXN5dFhZc6!-G$O^c;lD*?^jrx8OM z=m0^)?XwA6^(x-A^6m2dmXcBZ?B-2;?-3#2`g>Ux7E5eWO-P74vk{A;k zeCC_>jlAr)Q+{8;QvvHF{p+Jc*PUgAldNV$9!*=em*io8El8F9vJAMP^`+JTF(CS$ z@tv&RfQ3khUNOr`m!X2c^&+2V`>`E4W_0rz)Gvl2j8oNlFLVrC~nZ_?Z5`K|n!F-fH?I<)PwtQaZ zef@l9o{~hOQfx-w@VHp2Xu$qwTDtt1QuOXCdw&~sR^E#~d2fgCO}8}v-59^)p1W(i zuV39%d_9^z;g^-h9gcdcTcCJtuIFUxuTAI1KNXzeqT43DHLx!7Ss^o4Af~=&$8WV; zT1U6OT~<5tu$PaAUte@Qxg{q!<(gpoPXuB)y69uPP6dNTSvh_1-5}$}w(WDM*SHs< z8t6BI!Ot-#7zU1``QDy2GMDqEIsyrP$pv-)a z{rKgV-SHZ+7ZOt%w=A+{l<21ga9D5`XXXhuim9JG`|)gWc=j1_op6(UiAz#HB(^UW zEFQhl%Nd@QVoS}t$@BZ^N`V<(*_NQ3S^d(6;Yf0PVS|lm;9bQc%X=~Wdww_Mzjw_* z3r0?)wndvbF&us}dvIr1x5bIx8{ooU{;>21vcw1S{o@s^*JsM7>JlC;pIl$^;swHLA!GbCCU-}k$%>d zeOa7o{os0fsj15&XrGwr@I~TZti%j!wCe z=dhN9+b`)`k}jJLYl+<)DZ75HB-ONkw_gA5)~Xd0hkDd0|FZb=fz`(1A~SYp%+w9D zt4qNjPne^9o|of^lXe2S?#qO!A2&+%oqyeUE%MmYIEF_yn$AQs<_7p*-X1AlGta|b z&M|t=A&b0`Byi{86_@z^n-Y@_+;*~7V6tB1UcmhB$N%on(ylu*hBP~bOU~{weePxa z-0Rb4R^U;5c2bZ~h{HvWs1%(AScQI8OH8Xl#J67>!@3_qSG>-{;V}Y^? z5ho(kweKOOzf`1F1n@^$hOg%=r*mVkWw~1St89&EoE;IktA!8qE$<&0p_5Kc(Hz#C z88(ZnLor14S8LYpN}N*b%l@<@ZiO?*vgL5?_j>aF=#aXHojp?2HBMA5%KeMOLWJGp7T#mMZS_h z{*wGQ#ZCM{`8n+NSC@E;W$91vik2X2%hI3OYpH*E+|x8G?UHk)1k2;6iLGz-^VH2R z91l+U+T%Rr@s6@j<;7MNgq!iS##>!23zZU+6-SQy%Z{8goBgEDw4ca5yUR3FC$DI_ zm8&L=`L2$P&g|2;kHU-7uER$zqP$%!tGcGoG2yMw^MbC#1{t&+Ys9A4I3 zxPvMjxZ`hgPGAjTUc5lb4;NnHQsheizW`kTqW`um3UZa4nJ+2^ zQLb2$uP#Pas2|#V45PKsYVAX6-K81QY9$=`aJl9)?o|aiw`m~*MAJDD>0aJjR7j_F i^l}$@`SgXoEZ*rrT^Cn-G*x4L{89XW0kI5sIRF5vPv`vr literal 0 HcmV?d00001 diff --git a/deps/mochiweb-r88/ebin/mochijson2.beam b/deps/mochiweb-r88/ebin/mochijson2.beam new file mode 100644 index 0000000000000000000000000000000000000000..c07c9ee345ab5dd31abe82e032395cca880bf5f6 GIT binary patch literal 25664 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 WSexsyC!TG!KVFyoUxJVXK>z?_#bl-c literal 0 HcmV?d00001 diff --git a/deps/mochiweb-r88/ebin/mochinum.beam b/deps/mochiweb-r88/ebin/mochinum.beam new file mode 100644 index 0000000000000000000000000000000000000000..c936b7eef5f5b7f88074287353028eb96c4ad875 GIT binary patch literal 9616 zcmbtZ2|QHq*PrXoFqX@VOR}V4FqSZkHPp2)MKqR_Y%_z&V3?7mlu9L)vbBhcNGd8S zk~Zm=7HOAONkzM#_LTRzlltrbzW>krd4KQybmlqFxz9Q0d(L^DbFZ7ntl1U>f_|=x zyR)yeI4OZZfB+FJ$O%cDIDTSkf;5*O%NL8Jc`1UZ#8@SMqL>%UONkICMF{vJF=Pry zMT;O~k~k$bkpnpr!N?=Ur0~*(GGJ+hFe#0Uj~pIf0GYtiq7-6uR3cZApD5y`h$FZ> z4nHAEAT8h}#){*lOiW0)Q4vWb6!68e+*B}dqQpGqKbJ?~)_=mXfSsl~0kZ;80v^zbNm7dC zi6h3%RZM>_H@7eqaP-2NU}TztWV_;I$ zOcQrl248?*m?r^7Axx1*!C)04Wu-k2p2~x1E-(RgfhqJX5|cugwT2gym^2gxGbVbB zCQ&4yK!%}o3`Q{(n8-v7?Rc;=OoWLPBpw$MVJeYGLng|T{8b>-BnYjf&?xS*rZB8b zLm_|sC(;Xsgi^{46ewGPKmm$ABnsk7fHq1gx6z=TCPx4=Du8#8Q39bDl`hRhsL*LC zS7W=3=q*MFbXrC!VrdnmA#A)5DbKoD6aQTA|r$F+n5lU%0QG+9Xtj)3~rK@ z6=_U-2E(i@5++2cu304iY?O*(u0)KbVv0;a9Z+O~8b(D>2vLE^z?(m=>x8I6WEG4K zLu6Hqh+;4uWPk*)(ID~!A7EKcN?8t{Of*5%VTgj!+-1#B)LoW|LnNy+kVz1%NTDqz z(Ke!z6vK3u38KNmW57HqD4DjC0+BTsh$agH&?4z9ScZ1c9+?bJ1w;dR{8b-dm`=yF z=wnKn3`C0+g`q?Yc*AhL)3`+$+RmRf`Dt26di%ASbTYdEF7#JE1k166>L^PK| z0D2S*RLuewL$r~;ElB|4*Y7T8M|UC#>9WnrE) zkS{t!)&*c7GT1Ie2BN0|VV+DN*N4b@m?s8q=x32A6csAiR5Y;BaD5b(p%8xVE~?6J z{JAvBcuYhTR;B<*243KY0kBL2g*H^g@WN)(sVsTYRTjiDM2t~9k-bzDHAal+$HtBb z5Haa^1;C&~$5^fyi;bZGJqHvQ-`hGUNr;$X5ZP$_QfG+l+}(lEwY zM2rr?(8FLWV`K`d{_Tn}V)EO!6EX!Nn`Ds)OciGs4_-$P-_1BgjOdCH*%+0DS#dBO z`WS>MlC?ULwbcK4E%n!0Q;-QwAw)J)6bcuUgftEZgSi+7#@H&Ds~*O(!su=o8Yn|~ zN3jLRnL?pN!VE&BjMKfvk9xXK9A{9%IV1*wL17RgNRbQzIMlZc5p!_Jz`?G-cL|YJ z4L~yk!Q6_419qchdTdOEgK=R1*aA2=2S=0)PA4ci&>2(UO3p0P4F)-6GOb{V?oUOq ziv#iO;krx^fHoK{6of*IL;N?~AQ&cM!*SpsHfmTUVktKnVL@ceERrx1ri%;_YZ2;% zSV3fPt|bx%VvUL*vK7c+q#l)nBL;9)T!BA4(OekgaC0Lm#7M*r?@>h1qVP>+jc1%& zfoGiQR)JTCX;s0(^}yBudO%z6f6}w@;Xq_t5CJU`BHJluM`n>C$)F!`035P1Xjd3V z1%vf4wiV{;hOq)MIu}F5OvK&Q!4+}GBeDk(O@+wz@+2|}v%o=dfXGupBLzG}b^r-w zA|6%_R)`0_*byvtg2;{lW~3e$1v)CI80buc$WH%~sR9{9p2kGHhz>-=OAP~B(<>2Y zh&-K%c)|`a;)xq`1!Az@F+6m=pP_Sqh8_q)cawx}#m2;->5FrP|1D+C<88SBTW%29 zMPf?_W+1Mxm_j4Ct4P7h*&x5Jcs*38FrIUGOy)LzfxC|@ME39jmGtxh;Jti^5ZN2# z)qnz#eG*97;8BOpBgtYE0xU*mpfm!Uip+p%R0@HPGt5I20t;s_St|;`H3M)23g+*@ zggxn=EKgSeVFuneW&)E8VA9tfnF%`VOqltry}H0O(3oe@Arr(8bZ1Y0ynp*C&@j-< z@NJEPM<&7sNv05~C=Q55pofxxf0Zx_A(Ui6Aw-3eaObEP@uxEh=t9IFW#WKY8Gs{z zt`Z6Q0t{1_m1Bm^- z=ny#oZ`ZRysD>a^HU^=fiw8mEKwJVEc#sJaJoXSJ2ZOCHgsEWT(;`7X`nfO!EHolO zeskf!xu~C<l_Z1T) z45nhgUr2t5Ah7Cfy-Ma|&-z~?uxk3dpD!QdM5jLgDN-LD#bi$Zlt~&F?m&I?2PS6> zOYnr@c;*8CXr(K=gP7Ie4{I7X6=enSY?bE#WBqqkXRr)sFl>Ea7%nIIk$$1)?jG#qI9;UD0T~ zq8w0BGf+{kWJizfH+;jbv`4^8G@##a(IyBF{k_4#gpP0Sf2I6CC`|ADX%trgTYp@Q z{)57)KNLJ8K~v zfd6F&=cU}!@mvw+0K(%p2ZZ0}kMUq2!~!zH8F1n25J31Ed<5rG;ztgQ@_=wTo;%z( z&P&bYi*fq_fHtO=aUvsoqa!0Dldi->+FWH3EPJwN8Zg(Yc0}jMSHEZry^BqV5J?wl z!YX=}+3qRJ^kf2asopuG(v75$=Hchd!GZk^bw;E2IxhRDr!IP9K=JEj^r+;L^+|10J z7tK$^O+oy40``xs@Dn+LR4$K8kmK^AQ)4CHcqAEplUmza}p@4Q)?o;aZE)LErpKB)V6=5U_>{U<{sN1Ygb>N702*2m3A`-}E4 zmr-M_VS6`g()QMdduXk2kyTH*A*-dY-Q-&ShVSzKm9j_TZRoA4>hoEl|=28zFUi&@j7f; z>dl@%K)ZU^eRJLDmGuU!&hlFAKF%%+fr?FKa=ZnDQ^jgWb>$i$-OWm?!%4XXzc*{acbs%*dX|-H;>vKE4_JW4tjeNn)or0R-L;YXgC)His zJv6#FaH-9tzi%#9$y$B+UUqcEdfB~$kIvN|mOh|y?%rFx{?`wU{ZN4$2;B}T0$>O4q1epT0|X;ZrU$!@8WWfKb5 zabpA9*!w>2nH&79qkQk*sdK*EQFS-|;Qvw}KikGM*d}t{=rb($Tx&u~?z#4}JhuA7 z%nPd)4esFI=w&@UF-bcjLp`>oo4edFwb{PgNQs$hu(tGI>YhmI`$+Ci+4`sF6 z^X3>@?hofqoG<&=dhWd4lN=5|yuKsWW5q6C_(cwqrz*&*=tRM-q2b{gtJ% z(burshONG`@T)CFf3XMsGO_-1?J9|GNYgci$y&E61Duw&E+HI$2hEtpyEZfcPts0O z->s-KYIg3&{1<(Xsyj~2P#rbXcQ(51JLj4K>vsD3MUAqVjo!`m-X9FgH8d-zYgY}T z7Wtt?yGlM7-gS#JfI_TlFMOcc{BUkwoBVW>>^mN{D@oYMKdO}GvXn)^AlB#`rG}8R2VYpyfU3t9ngwh;O%JqY~HiFeEZ{1e~Z#pu$;(VA@ECF`5*=%*kl( z1P>$2J?y?W+~>2EXT7{L+IFYrT>4wT6)n*gGexg)Sa-mNc9vrPCX;`LD5bYHZP zM<#AS{SWh-;-{)b2ee4ZJ6cv9sqorX<6IjZoa>SIXbYd7uNy2#Je)c3L~qu$lKlN8@4MzV z9;+%T-FLjCR3Xdj;-S_4t6Um`lY90Vb~+l|Ulm?`Y}GvX){WZTw-&4xS$`KD_s=|X zW#ra{unix~Bd-03^8U6uwcBG#?QL?Y^(*x?ex>Uzy03B9XXInB=RWJdonC32U$WV2Za6y`gVqI ze0D-Z=7Udpm73kajy#9=tX8?=1mOyf<%J-dGsq@QEoaTE2^XcG6JM{BT07zV@f$*e z=C}Z>H8oQ5Y^SAfFt??7M~^%)`kbPWXs>C1&Er{(*n^n7i`?fS>{_d>YExP-Go?3f z*%y&vxI?LIJ3FBw#Yn^~%G`U0Jo`phgmI$6>f+6LX_w2AR|jY`L>T9NY`kzS3w?OS zw&&y7);m=_S0-=X+nd~Ec8wADb<6H+B@fE&zGlU|Pe}dh@BQ9>*4L?_@A9dPl*#Hc z?#va@LKmMs)2u7L=4f6XLVcDyok10vRx|X^JFdA0UvB9PyAv}tq-%luotDttJ7t}> zJ8uRrioLUL`y5+`Ric7Tp?!Q8LGH#Z(NKAI3z5cB#h_;~6{TI-NjL}vaspP|2o3!}S^pBR3fcKC(o+z$?C`rW%{VXI%N6@ELjEy2Jc zmwU4!z)8z{;m3XIw+{6CpPC_)>1_Fl^ct3HOv}Ec!SwAnK*(>Pb9(nY;XTI-)XVIDitwh^-Q{UY8$?HnqS`??pYb3 zR{6N@fo54Ra${hyE$(pt?Yu|Dfs5ElAN+3SrB(iDL-+CpTA5q6ugyCFYi z2kq9hlK!k0yYFhBr!Km@5R%q)TT~o)2ll%sI1+9#!17+!bzgVLUhTbo!*db0ORcck zJnVa8_n|2ByjS7+4U3$)pZ+?0{;T;HTW;yB@{{f7Z<;@tb^qJU%1v>FuNuwTYl~kF z9PG)}l%;PyB)g?J>*l(zar2bHRh#_0j?8_gAlVIxz|F_RA8lygO z=3LZ@3m!T3=<7{lKBqUK!1P7@lJFx33{RfCp4R0x&^^=e=7($rA>m~vw%J(bbclsa z&e|z{1qCwp*Usd=pF6nI(6YtGX8VF|ex2UlJMt%sxp0N6qw}iwvX<>Vt3Ri8`hU?$ zSWaHE;4y8@UfWd-6RuEpCsYtl`Ml~`5Zl4Gavd;^_6qTGz5e0V#@TZgu2Xufc4>9< z4#9rc7&g3O_H&p1jVbz>UPlHfTOr=I$tQPJIZRmHp>xh;r$)T><6|6@_1`qRSUJ#iGzr_wq%r!7dPpU5xGLQPT?jH;JpWX(K5 za+J=zZ!s)l3@*x9y0ysvV$+;sNh?#X%<+1>N?UD}b_jB0SF+>s>5m-i4}EJ+Pfos{ zVd%UzyJy5%apAQz>htq1J{>37O4lCA*PV*pGO3N$YL$OMVAJGt=iE|?+jOsy^uf%c zM;V?3_YuQ^CB<&N)k9%!v3q5FK6`KcuIGJvM)y|b{pOVD=%;8j~|fA9G_ z&dmQAF|S)TebPb~Y`cH>mR!a%v~;s7D<;U-*5^`e?q*e+``*N~!lj$B$9{6nMWMZZ z;X^VZgf}Y79Y}Q!`n`UmMm*JfT^1K38U_oAiamww2l;D8=DbS-k<*IpQsqFs!RUJL=-kW6P z$ba8()N)C|f&li!Q+rk>D{f6z{5o8QIc|Qv{)+wJnE|bDTbE;Y8zywQ<_t?cUTl*y ztlXBdye|bZ4bLX45!+S@$ZGrd$uT|1YS;SQU#f0eDGZ>6U0x*&aB0pxA(k}_*QG8K zdeg!#t`d5?G`lUPJhq(MdU?C~Nj2JLQpZyPZL|KyOYKUQ!y)ryX6qf0Y09v+_0=_T zI!~_dnKDw^rPcUW^H^V5Ti|J}p&gI@u7462Z?>zWL460Yw{b~R9&ItXt;S^zPhgf` zDPm=wBEGG8rz87tL+|&V-tW8O>efy3T;4VKuD|%=XM6H=&$gzyr6;70m)cwVWJwiH zgIF3mRepq8wBuQRw)Dx$O5Lh^cP3`}FISqKb~QbEgx01Mb5?1=yPC}*4d+)4P8|u8 zo!)d7jr@|M+TkwOAH$|~&tK+HwSHy6hU9&{$pd_L#OWvdrL;HrYM3i}Jo9hiSUmR- zbw*oKW+`NF9UGz-yZz|=WO%SDw?FE`u+@v_D^1?(mg^PW>T29JJ!HC4?T*dv-&+H2 zv!?}5-?3xhbzx`744b8gpY>mQ)=wAxeaJrKw03!h?bL3WXV@Z!PRGEP%UV}IWPUUm zT0r0W!Y7yBda0AB=JZ#P)%(BJx6|EPBFw^`d_VPfW@_u~6yMWFSkCOPX#E@$-3N@0 z^U|WiECtQBr!1(Xi&(8`x5g1-G*o?e+j0t>KZB!NY-iD@?qGh`Ca-PFz}$%_kIl$; zjMR9jS-j;9k!QAmWB5#v~mAhFmkw1wM4Sek*2O^OU^#mn!Gj z9NzLhGM4-J`n&DTau2C#UGc5-yQV(E(Bim@m2N$z#wI5FVuD(mmgyDhgISz@y$4(OSG#w`#50{k|a8{d4<0&oAHm{_@^4-#lMZ+O#MH zahsDkK`~ijGZ_#By^bJ=oM|v=w7L>X?PRs7EH-D2rOa$I;VQFPuhXb(I+KxZwVBLx z8)da|hHZ20Hl3dMQ`1tae9B^dLugEU7E^s!(!O?kGA!VaOM_0;dwOgoM$6%>^C(2?mSw^Vz z7BH@|VHP)-^6h#mS7$6RjR2mxZ|QvTo0g?f;sU!-EE7AM!LW3tbeqLaITfh%R*Ekk z8jw^b)@wvLGGHnbqZ(AN1XDr~Ck}Yd1OQxSfN%g;l%R0~5mexUAY?Q~Cpcat7h?i5 zR7T@YLr~I{Kmfxa2oj?M3nBUjA-=Hh`S0|}JLkLU2PF#s|b0RdX&gI4LFRWLzNCfzX?K9~!WD?phe zfs+V9neK4N$%K$(2gw0=Buxo6i~-xgcqBU*{1bwii8zi2roec@2#yDjB?X#LA(ShD zCTA4@O{q|3Xn>Z%G67Aa&{!*gW_4&4ii$Oxfwsosw^^}y&mo}gVX1-TA;qEIexUWT zR6y%j^gqT4pgv|sY1U%(TpNSy6F9;t2ULiklNT(1?K>I@mBpd}_9Y6}0 ze3U5wq*#VB1L8RXkX+zME(h~*J4?Z;l;3R_I94G|AYJx$UD3CiOjuVQ3h7}n29uQ1-SPC7U zTJRYQAq2z(I_CINF+*cGVK8?XAd?7QGB%NifsbE?&5{W=B*kC`JL(BU4*)s<4FG*D zJH3_}V0Q+9iwQ6+v)+=~Z^`&uGB|pF+QaYgu&?V|@`$%&9+3F}u!fNU?yqH7AFLDB z2W_|jaCrfE1HiUmZGHgI*B@XM0PI2dT81_-P9y-d8J1y87)JsCCzKmf?Yx_w^DuqRj}xFUL0N{FimT)exR=9?r1C(h=b|b98 z7XZc*1He840N_H=FDI2x(%GQRSb&()jhei?JT-u!ry#G75&g*$m$H|8Uo3d}<>5fV zu4%E+p(iIz>`pl=i4-^PmPURXOGBJ|IYE<58W(em32pTx6>^7~*;;_j_ zxN4J{blfWpRK89n(U=THMF^ryF&2OrrH;6C=*BRa;g!q^{Tu|QDakKJVx?JDZ~>Fr zOvbW8X+AYqYN6%=g3{V-X6f*SMQn+S6I(SFl})3SS}ht!9VWXCT%3xLgufI*Dv?Mj zway4bp}Jy({L4{vMvdMMu677JpHkZk9rq!}q!_Rf&~q{36eEe1M8i~co6bOKoz>t9 z@}mk>jZitOd{vQ-eyk?DMMD)MiGQt<;aN$*EwV_Vw%Wjv4X#KB4|z`M#;O^!OL~)t zPaoVkLn~{)gNX=Q!n2xXHNj+BZ|b!5m8YH`UUF+mW?^Htu&cP~^m6*b^5ohrpIxcT z_P3}{^elXQV!v61LL_us$xBlVYe)P2Ii zYERwO_uDsQPZ>`HUEU$7F)njs1*(G%4yr`Khac^m`TmKS=R5CqxL!E=kMI98QQcfW zvtxzlhc#QPKbZXeS3&Y!+#Z93NyXr+IAI0yysph%KOb27>GN}&m$6GnZ<#u1Jl1#_|7jE2VpEozNSve-U zfU)NOu~5IFEZ<*Od!$OTv-67DQqufcZ4Z}J%_J?MCq^V6Ii^i}T2*y-#ayOmSAwF8 z`gxFBp1scS?4Os+}NIZyh}v~$4t?c?WfB27uI>GZaMXQxbPo|nv3s_ zx#w5+q*k$@bbGkI;mgDMyKc z=5O8WLwq!Ze0j(m&#slU`^(ooTv>adwXY^O(H-khZuNM1^t=QU<1KrhW}UiN`SsJkwe+_wJkjI1De$hx_9+_*bAwpS6ZPiG$n_5% zJNwelmhY$yj}y1AtV#%2y|VD!RpIKarDuSf6H24KI=3tT)fgQ==Zyxv2gvt z=vfsVq8U|zJxgkfi=Jnvh+|v3<2+6-dat|oY(RlMwBNs{_4N1J^6{0E3Yk zernyK?5!^b%eqJPdyh)}=BTyqZ<35x7cyT}ZGVxw{?VefVb_lAJP>Ux+4bnkw1F0R z-Ge*bPcyUTQ{8KyO_F~SQkZ60`tNSd-A2*1h~9>G(wZ{ntjqAaZQGTzjhgq_`gyIf zzs}n>_0_gXP4XXpnbeuEq2c_@?Ky=LpI^**arIfd>T=e#!5wYykHkJYw<0p#*zh22 z#PSZ^R_EJsm#phf#_ux*aH1=_&61`0)|`%8*EUXeJKgDKVChaaHf7Jsou5W zZf3VfHPwGJJ9O{wD{fAXGKH@@-F#)@KBIVNoiu3GZ@<>LHV#xRA$-cY?bp`sYdnEB zUAw*Waa6T*(JRkad$*kIY2x%HcZHUyNALYq_VYyN=325l#%oH(Xt&grQx7jYdXcep zk&0HF5Z`n9((O(Exz`)pwyZAZU_;Tp;I+HD4*#oqUTw>V>!Qy))QfQ@9{JRaw@X z-g%|>Jom}AA9c4>1)cKdw%X>oem}`NmXh?xL_ee;#dev4TujyMaj?zRLOrz)0N2v6^u!8)S?lrm~b>Jjzv9& zf|9%oTag-rawt25NN^ktKq(dx^}m6_PQgZi!bV}?>?X$?7w4h`muGTpwsSroZ_eQ= zO%4@s^*)C=V7GG$UD>&VK64HuTrM-HCTa~m56a^Tn-@};|G`3MHYQQ97~DA9w64+6 z7gCPtGRN&Rn++>P1_e9EG^R0Z!~%D3qhE8_Bm#3nli7lk?ZEV_rKV}< z_*-|~+oyG3omV4FV1+sfW7W`zz`{uwt0&~b<92%xFUiamCq;p8!d@gGA;TuGb5(b$HXVW2y2%8`NVOZt3Q(s#Yby0|uOv{r6yCDM-0XVzN_KBe;? z7e5+(*OSs0cPNdQZ#a89-hUqEx%$p!wy2o6ICM3yrP=#%al5m=c1^?F^omQj@48-|_x=YW@OPTRpP!W_zo3gMZ+}&ZD*03Y}Abxw^9Vzn&zP zj-2SX1b=v&2fHdRcc1BLd66H`mlsSumEN-mz8c=SHmiTMq$+t|axeW|^fpe=)iie&E1zS(nyeW3hyD;s}V8x7MGn*m@fOx;Hhj!&(6@GCx literal 0 HcmV?d00001 diff --git a/deps/mochiweb-r88/ebin/mochiweb_charref.beam b/deps/mochiweb-r88/ebin/mochiweb_charref.beam new file mode 100644 index 0000000000000000000000000000000000000000..9a6d837646e9a728b9720506d9742587b11f27e4 GIT binary patch literal 19400 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 literal 0 HcmV?d00001 diff --git a/deps/mochiweb-r88/ebin/mochiweb_cookies.beam b/deps/mochiweb-r88/ebin/mochiweb_cookies.beam new file mode 100644 index 0000000000000000000000000000000000000000..1b0f0ebc81945fd03b66b2746d060e73437c10d7 GIT binary patch literal 10840 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 literal 0 HcmV?d00001 diff --git a/deps/mochiweb-r88/ebin/mochiweb_echo.beam b/deps/mochiweb-r88/ebin/mochiweb_echo.beam new file mode 100644 index 0000000000000000000000000000000000000000..4fa087b06779cf9a296352f15777c80f118e55ff GIT binary patch literal 1684 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 O*q?sOf^XlaNd7+}A@;ri literal 0 HcmV?d00001 diff --git a/deps/mochiweb-r88/ebin/mochiweb_http.beam b/deps/mochiweb-r88/ebin/mochiweb_http.beam new file mode 100644 index 0000000000000000000000000000000000000000..9e56e2ebc86816c202b52b5e1e5e1ce975fa8606 GIT binary patch literal 6616 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 E0Fq+eTL1t6 literal 0 HcmV?d00001 diff --git a/deps/mochiweb-r88/ebin/mochiweb_multipart.beam b/deps/mochiweb-r88/ebin/mochiweb_multipart.beam new file mode 100644 index 0000000000000000000000000000000000000000..96a501130e24321a26060ad896c73d2eee3168f5 GIT binary patch literal 19864 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 Ch7hj+ literal 0 HcmV?d00001 diff --git a/deps/mochiweb-r88/ebin/mochiweb_request.beam b/deps/mochiweb-r88/ebin/mochiweb_request.beam new file mode 100644 index 0000000000000000000000000000000000000000..978a5b3d1132355a74077addb094aabd1228fc55 GIT binary patch literal 25404 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 AqW}N^ literal 0 HcmV?d00001 diff --git a/deps/mochiweb-r88/ebin/mochiweb_response.beam b/deps/mochiweb-r88/ebin/mochiweb_response.beam new file mode 100644 index 0000000000000000000000000000000000000000..8ec96a332a4e52389d3abc6241bb5253a385f669 GIT binary patch literal 2292 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 E2g8Er7XSbN literal 0 HcmV?d00001 diff --git a/deps/mochiweb-r88/ebin/mochiweb_sup.beam b/deps/mochiweb-r88/ebin/mochiweb_sup.beam new file mode 100644 index 0000000000000000000000000000000000000000..1b4537cbbadd432c314bc710d4262d305cdab7bf GIT binary patch literal 1596 zcmbVMeM}Tb6ra644?Q5Rd$wA!?cre?EBAq-0u8c&fFNI@rxldDy}jjbxx3wSZ})%- z4fN1RmGrC@F;#JQJIQa~n>TNM z@6DSxn^{m42Y@O*Eh9BART4Y^fNF^F2#>%xxeC^1@yTAbPohQ1;^ugl8ctc!&-nx~ zO!hiN+RiEjmy+W-DcmF2WjAZ#_%cDoif)>BOmwi40H&o#4W-agSpd&B0nDTz(*QVOkTV#K#ds1a>|CG2|E_ zv_vMM$-;9qH~`BeaXHK#WP7|w#zQC};0jQ@kmI480D(b_>0@i*(5A?!4DF?$J`N%b z0{Nmfq@#iMt00ga*-#8LkVIT)C~`Ro<*)=IqjD!>wOVafYp{IKnnPR9^%dj~WYgFC z&J_&)7+a9{iP4}3{rv%U$Aj&@0B1`6iT!BHH z=zdUa^$Qu9DoG;Lg@7A=ai_u#aKDd76O1Rop}K*ZrVHn*8gJ^dilcRF%8`SLmL;bk zhJ5URvM>U9g3?&v|)l*;soA@ zctZx9sj$1iJfqpiiaw(x@Kp|@o%I_<)(;J=Q<6MAxWfqQS+_Igu1GSyC$&S#&3)LtL9R!})1;)i2<{9Ro zP)dpOuuhc?_Q%gOMo*+w^mN)3I*$()lVM#T^VKpLs}=fF7~s1vPqq0Zcn4Plg|Ons zDtF37Kk>sRqOH__LpeV9gg7{5xa-uNw;yTLkMR1>kI&Cu)=DkSPw8$OT0D8{&Y1gy z3(Lw@<@Rme&`Gvmbsos8dOmAHbW~H3II-7R8mWqIjY(eH5PLd!`{t*aLz}y4^Jsd- zB7L-aWOmMq#@4{1oVRME+LDs#5gTUZ_4)R-TNbzj+s<~Z$g4fw`K|hm)hDL8&-9L_ z*WBv5gpFK0_CxosOTAkfR^9DU9i25(FjA|n$&w>-a^1$3r`*yv6TSTA9zJ7blz44tvv!1>)skDA7Y?1WBY|Iqe_0k>SMvLrro@>Y zC#%(q54EgW_}=rCqx;XEoMLN=vq+~-%&n652CwyWe7&dnE4AgBjbBBO zmwURpo?a^+y7Kpxf|={iH>Xqo5J?O0XVj4cm4*7IclRy%S8kXmMDG4(df1hEAx>X0 XvR!ju`^(1%@<-M@!`Y8$9SijGR@tW>1X zK}VEMN-3ojDW#Np*S_Xr#_xB(|NFV0=YF2||9n1gpSEl7wfA0ot+l^v?Y*zVxJ;Zh zj6#XGaBvp73*(|9C=}U|6bi);kBIUQ2~7-~>k}Ur8cq)hoF5Px92zGd7#r>v8B8^$ zauwqvd#H?Pzu36YxX`FbMto#IU{GjeV1QhBs3=aP5F3~f7%K`?>}@ezP2?98=o1?l z?iUxD5a<)_7Z)Ngii-`643?c26&fiM7mkTS0>i_7V&bFX0_pw{0Y1@Dv2k*uXurfr zMpSfQBvGb8g-n!45!H*kD+B?{xWGs>CgP+>Fz|^Ej~6NVfoeW+Q9ej0mC(qzz~I2x zp0e`X&`7`7q@E%pJ~lis(myI7P%bDcJRqD7qKg83Vnj+k8K1DgB<0>hf?s%iphA2k zF`8102vkc52t@s5>}=(N{K7?nvT?ETfiycCJDOi)fMTe~XKqwfc%WY-ZLTPsHa9Ak z4nvBf!$ad#dNMvD|B%3lKspjRDlsrtQV|arvg_KXYhA^IpnpZLh$?qrs*E8fcv zHCOH}#l#23CMgET2Z}^K5uq?lP$;PE7ZJz^oDT*R!9E>?j<|(HHE~&DEu84lV+plBCBkOn z4icNH_cW036!myyxSX7I_{b&p(l$o?V8bKB!vOq>{E|cnE97tnW7zZ+}Q56&NP~~eJ zPhc-bty7xv25ykB_>-S!x7MAWi2XA_tR3D z0*Z?T%~jS_##Kg*$)e_>KgXf11+|R#fF?)st}G$T(aRKPIdTMEEP*9W9tNb+S%f5Xbs4oQOe(z} zAJ>5PERHFz!D5(;>of#1ENI-HO4o!|z`Z{YA3#&0uz9!^OCUp2qrga7AO*-JaHX*5 zYzqpNJ^)#SC5s5M>`m6V9?b@lg#(Ska-cCe4m36hr9%p(>&P(UfSv8QT8tBEt z2U;=(Y-g4%%~^(qBqv!71WAD^#awS3jY?m2LneRj>d@teh+p zAI!91y0AH}ifXPjBp-)HXL~>=LujcE*Fj1YCTd1!`hro6fT{u1!3347Cvau6KnN44 zGL}(m1kNUWdok zDxJ&6&4GiZBM&#@Fxei?meAIbCJzm%bTg*F!HNGWhDQ?SQu+c;_Oj#uS zK~7s`3qb@#4FQEVU2Z9p!imG}X#xr-9=GQRgiMMf%6LG=D+w|bffr5SNI0yep)7Nd z08k`L1HB;6^kq6T2B~wLX&jETET=~jm=HT2ZUv2CD#0N3V*pvB!8*ENWq}ft2_Ykx zA^=a9LDQ*VaU4x8!-yrBSRu65gVqjKxTD104lpN4Bjh2?AsQHPB?14~yV@fhIG1{ubR zM|7jo#~}(-yYcaf1Yo)N1kNZ9K0(u$VTX@rjKbX+Mz}kbK3<6_Kw-=htc1@b+=U2S zC~P6ec1NJ5fl-l6|DYbsCUEBT%o&V4k%x~1JAi6^d&nS;C>D8uX!;e+qGdGPw zrB5e(iw%A=6G}6n9S7RY6bn3?N}q)&AnGc?eRHXF zA7k7%9r>d#BPgAq_!{GW=?o!;hnA(ovS)|;Q|W#J#X;iST!Eqypu!)j@6i;ESYp>U z#si!X+W>~OlN}yRr3V<}fli2RAR|aj2{guooWyLM2(}?qdXShcfWN?yC73LNdbM0CeO{kQ z2aAXC@L9;i0B7MaNkd`UB9z%E#z$M@k>F&Jgp;Y0PL?1|?D4#{JwywL8qzDGiwDhB z9r>Rt$JG)o>hwtDeNiwF7Y2$J<0F{PFl>4e3`>Lpldkr6Zxi*= z2ROy?@E8)rXkUR=GZt)Gkcxye#luMEFm?hTPlS=s)*unK22g_K)zT44matKfVWB04 zjVDp*iG6s03iA;MkTr>q&nILZiYIdz=EUF8ruYI5gF~e!_eKGU6tKHNQlKEQppV!v z%0ggj0b(r@n`8-X=;AHKk4DGy)#H*?Tjdt*x~j4}+y zO|!;RVX{+c9!i2fQ|LgmaCD#vyaZmd0!MPSnzK6W0%ch=Sl~gihwUOPd80Wn+)9YDJbaZpQMH;%Uxn6821gyLR`(1j3j-Pw16ok& zYhXZ27;vrF#+g+5Iz$0cGWmEmVPh_yh2m%y#L?0CdIksIK&7u&W(#1SCSG|qTjLu+ z*o}l!pq-yAEXPoVX1Qk%i?oM%MsVNIXS#YC$Vh;Y)n~#$q--9(PHMs=U3#Z%BXXfl zAUO|o*(}y&E0w+lQ9zfie0)2Jmu&#WJ_3lnK?;Z4`V0?Tw)5~UQp5Lj=pB3qjA8}j z<`4m&4m;K>Fc|WpD=f6N-C zqm23ZaYh_|nh}VfV~Fr#MhJeM5sP17_~RED0r(|GC|<@$#4l6nJNfu7N1>yaKaAnU z2!%2Hh5n*6D2W)+Jp4+Uz*Qjj?OjlE6u5ebOS_>Z(GR5Y8s!Dj2ta^b7%Y!U&+Wq| zSj@&(+%XSI{sPx%acR%r*rYI`@VyL6d>?~{?+2C#sPw&k*d>VBrHC2q>r)pmu3Imz z+ux_oPh6KPt~=1D&azM4L8$wyZjpFC814|2p5LbF5waL`!o$Cn`(%g7WZkI*rzFJZ@Q+q>G?iQkpU%sh72e% zCo;ocFhhTF#|!_5jy+>&iJM;R(^S+m26(7wl!)-q1iS=1w3JFOAr%=Q?paC}wZ+9t zeHcacFd~A2KiO1A+_bDuQ~y3qqsXSZ;-;7TH1+G#lyqwPKylM6pg%cR4QJ}vH%trz9Ykd4V8ELPx7zT&|2klU*sPya5_6BMO(Z7n1-yxzu7r%{GvfB{! zX5lv(2KX&1{U#s3Z3z({HrO<0M;5vX0Nro#fC>v1ah|)~XF-74J3Rab+B^}pcVQG; z80DTo(T)Qr@ER(;8j&oh^cqFHnyx{!L>En#<3haJ1MuOs2(R{KdZ~Ir*_W=xf*p4C zJuiPRxV&)mazqzPM@v(@+EUR-iA~-;0vioLyINVW5ZDdff}m@|sI@Q@I&Vyc)L zP%u>nxIjIoCdAY*7N(9!ECy`R>oIj9rjGT)G!UsD!4=aGVj7qx)*q2H330LhLaaZg zg$+O?Epe*>LTmu0jp-ngHp3V3(h*`hm@YODk#re@5ow?h8;I#)Y(&x{RKVCmjE(7I z28g6D7S%wA8DJdD5Ru^S3+iYn#0;@Pm=PikVk9Gykq|S&24lvEG?-z)gNt#m-RqHy zJp<#rg7Ke=jjxZ4KZuVvkj8IdAwxBg#&57hgEx@IZ}4J-Ly0tgg9vR;8c5?eWHTb6 zL>j;06eAc)r12XF<2Upezu^l~vO&uD4Pbn*PJ@*38^HJwz#F8D-vGvk$#0M{eghaE zrl>*6_zhtE07Q})zd>yL1}Wn=fbjux4N}H$!1Q1*)PS*j?bU$n^#bfQ2JH1x%3d&S zjikLA39vSj_G+|b1VSBYuSPEh1mH%}UX3C~2$V>BHD)7S8cBOKo2*=v-tS0mU9rm#`UUX5Tcn8!vbdo_Z+ zU@{w}?9~YNf*Ea;vR5O3l@LM#)LDKiHiWxh?xm7Gt3;bKqPYnfJV$h zh*@C6u;GX_Og!RnAvPSd#CV8gDISq0#CVt$HUg2X5GES25khPPHWK3_(n#b)jTpZd z6paWJuK*Nd0Ti#rpfEsARAG%b!C|SX_prp_<4r6f3mvnYEYYs4iNr;d7b6}@Brcjn zXiAz$Tr_1fU^CK0;-cvknwusP7frQ{XejmIqUj3?15Hx6XaZb-HJYSw(FC|a45e_< z1h_CqBuNl!0$f0pZIZ%86W{_6+9ZXGCcp*3UJ}Hb02fFzDO@xGF1Uy!!9^3`0;Z%1 zli;FB9K@QWaM1*~AVihmq6u&T=Y%HA7n$crzKynhC5llfkT+2xiSBXq&wlu=j2zLE9`sQ`Jm@ zwmBOCs+k0B^C<+=W)if`wFutLJ)mtSK-(+@+GYSPfV^1>w9Nq8NFI>XVSB*&-yo#n z--QHxNLdnDL}E!)l2{ViM`B523yCFR0gGDRfG?z$NkCSpX%!qf~46TwTeVQGd93t@Fh#BD|q_Z>uBH;B0J#d`%~ zG#`ts@fO%Cv`F>}JiG;NFSd|CX(0|hEo8uGAr3t)WWZ=44m~Yoz-S>tZwnbPT27%= zuZ0Y~EwyM(Y3Tt<%NGXRT4}-1vEAe!O98FP9m_wH02rl-^=AQ zd3Y-e670}g;Q<_aZK2g4MZi{(P?VMpr8Kl1YRwjA?>z;jy=ddmdP;bzycSC347icf zS_{b$sPN$K)fb??LKbNKB82lLrV3?@iHzThsS42n6JsHg8rmVWVk{v#U}F6cNu4no zk@^YI0Ta_iBn^TSrYS@ROiT-r`ipsK3DE%)(?+BL47kJAifMychyHVgVGNj9Qc-Xf zDMi6cq!dLZiJ}nOTWf&=uq2g4efubhEb|v7;ZE0Il|*d+qNEKX^;Hs<#a67Zk_3Bg zAv#54x=3#wu?=;F=oF3VA<{qw+)r%9^q@D)COSDIDw~*4Okao&(ijJk42W69I6`!g z#s(phAu+YsARz_^X>2ee84>e~4VFwW24@mfV=SH-6Cv7%Vy1{RL_9OZ&J)fgh%{6z zAy%252I$ z1Tn$?+9hIw^Ys-I+y%rWVmLC07Vn@u=u|~4`gP^5{Z=fFFF8e6JnJ_I3aA( z7bk>G`r?GJiC%A%ZAVe|Gep@55M{rJqwFwbtu#K~K^~Snh!-h3$Q@e;v198XcP<^o zj;({-xpWXawhnT~){%{{+d=M?I*1)x2f0`3Aa-mWJv+7z*s(!VsUuzo9G1aA9a4wo z4md1>k{wb5|;U?0}n9#B@n+Vs^mIDneAr&8iN#Sw&2}r<6-{ioLs&@a_@+`&B_^kvvc91n)*h zmGbUR@NQV{JEczio#5TD+;>XditYsO2LI@kx)t3C-c67sx1u}2yJ5NSl=ALQ@NQV{ zJ24}OmLvat_WT!+`46B$*g*1p@;?C${{JniAVd7;P>wqOQ)mz~ED8Jw8W0r!3Jt>f zC6D_$QBeF2LD2((;tv!Q8ADkx|JF44yu*Z#caedyi-i`0E;2B7S)!odMFz$$A~1H5 zfw4=(7z%Y{VC>39$m=2lW7jDpQWqH*yJ}Im@9GJRU0;ZfQlX&>LIdh8Wwb5`4TyzQ zXy}5_09tin8X&jL|9as^{Z}jxPaXfGmIsTW+f#(WZ2yPG{BN0UyMIx5P~0EAT=QQ9 zIRd5xixOOl7!_O!LIsy1o+d5@p@K^hE6JrGRB$N>6^{S2!#VvIhO3rVE)V42+ha&$ z3K-Iu3Wl6ap&HAG4GEulFmVB&OCt?QBMeC+4M`&mNh1wOBMeC+4M`I*?4TcMNE%^C z8fi!xVMrQjNE%voxik_jT-q0g5fmk8fjnAu;nKV7f;?Jv;nMp=74op^B2w=Q6_D2? zgph1ifTRUmR=D)Om;`zBL=`T*TNTLbibw1{qXG%-V&T$zMuj|jZUvX#T`c4|@a_>@ zdY@ZCeh>s2xb&V;AwL+f4wv3DD&$Szr2@G0zGwq^Q-%{F^`227&lPvqu zj0&V-@MZ&Cde5kkw-j5o_lyc8E3v(L&!|8eDYjSd3l%`JW(?!O#ikc0G=vjbz{wQA ziJTZGE(j+Me6Bo!6Kk$K%LWqLfkm-&U@;w`0&bDP>-1cCFXV~xBuL~%$oJ$)kjQ7l zGQpMK3lxA9ygAL4KZR^APXa~07R|YQ4^ZU4AP1Dkm_P$Sq6a)MFs2G4W2*30iYf(T z!Cxk(4h_UNbJ6RAkVkK1!9@ZTc>oGxEd>;L^ynS2AAslt%+Nc8kVj`8xJb|yx zBteV37%lQrXptw*JkUyl7J1^#10)GrnRCc20Z@YaT(c=IFKySXV1VEo{|1r*up>n5_pivW$cCToVkosKoLJ8;4;u- z3ob+a*n-P=g|NWrdAh=7z|$1~8Us@$MRNUb6G=+%G5@cFKqHn|8+f9GOp3Je5^Dp` z7Li$zHWITUZNyKNknTv8Q()M!|B7|V5x|1v2!ENlJ|mbzN&MXEA6OPReSvy(#~eMP zm9{Rqq8l?1!W02vQvqR^IKU9w!+w0O61fm55etzLxezH43y~7J5Glbz#N#UYgOV^+ zO#ENy1_O|xNlAq4s+0yy#{G*-$g$o2-VNqON@SQ9DUo4b2$9GCi{1XA$nFFa;79}c zCrB425(QJJNrftpJ|)jSC1Qu)Pu#L+YLw8_;4n4t1+KBOKn>o}r|VFSRS<HK4CKk zc&n2$+s~3=2cH7KC*$ynzMl9~2FZ5~dN8}!;L~gL!6kgf;LIW!sPnk}I6d6@!7mXQ zYk)AbK|)PT4qkO<*ulrA#{Gfj12io@7k-R^$rRFP#3;RAPM{Aeh_3=@#3z)-19)6* z)KMFVE=>3YdmI#WfQv7vscX&E0hM%UY|t0NABXr-k3)XMH%y8tK8%?n{x}ijpgE%t z273e_2+aIIBt1Tt-6Im{s4vx(Q6b@wlp*!BQ6cH5FN>u2gR448M}0NPJZdvQDz7b-*CF$${Xj^l zElP%vdd3!RV@kis@K`K8}!jWS%b72IYIknn~u7Y_p`=%t3VL`NX{Rhdi>~G;uu@{Vog2 z!#^sT11BO&p*)7GEhHp+4Vg#d+=Il2g!HI@WJTt4;A%RA2YDYJ# zuzl2Sp5Q$6PTleF*ZiqjO^*)wsO@}aH&yqs#hQlYe&O7~E;s8kOczssrxlNV2@IiPf_$-XVv_Ul-s-)g_MocGtTNc`;bX7LjB4MXpJ ztXjC|+kx|w%nlq^neY0(KD;z2*xhT@sgawLeb-(7P_XpPqW8nAYgLciEh^7*W8~%Hv#dOgWuOQ>_;bC1;N54+Yi|1O5N-l_AT&nAx-Z{?L$MDvUYnKOD ze2=%BPyLj89vg9S+>*=7hhQ_T?7!r4)Q`%AF6#=Td}ThF*}QyQ{INaH&6cZdGKg4Y z<~_ZXk#w_*HKo2dV)~SdlZpyI%BHTE9Xg|UW0YL#@NENf)MdOkx6YyOKX}BM@@=#g z=a%;PD1U@;6cfxL`H_BreApLKj}Q5fbmYU*z9Q{UQ=tz2Q8D=AO=RKb3S6gq-s@`#S? z$A*OH$Ob5n=tyrgmx%5R3C$nMkAT!45)Bd>AH`HeM>I5lD39VVim#{*s+Wg^c=qf8 zP+mqD7l-7YkQRZ4dnge<3gfh zi8cWgwYgDq1;mdNMg#_g`kDDhMa-KA3+C9!AmFix;46!|DMUr1UnCTveguK$pBNBE zu`-_`f?p&wkBf>-3N{Z2OfV;ZqA(;bF50~3R}ZWfT9=>I5$3+_*po@QVQs|7D4AQ ziX6|(%4|3qN;VFD;4egeF3iniKdPUKpS+)rp9P_hC@MbIKQN5q`k#}e$5zBIM4G`5 zL&7vrgmXo4u)IG@^Po`nZJEPKG4bLC?UVHOM#UUqc zg$>)L#-BYLb*MGr)3Mjyi@q+p6m|4`eB04T%++yGZC$ESfpxa~Ozz_YKfd1ZFpirr zb1ZMNU*M^C(?nD2idOB(%c`4J6NwFWpv~iaJhZv!k<5kSL#Enpbx+>yo02ZS(b+NZ z}t7#1x>m1Q}{$lO8;1351N?Hfb*1bb7 zFkbh1GR0asCh*{GAcI@2ABT)E~8er|Ek%6#f=^5nb?Z%^sr%bfDm zWEo*$-Iwzp#veE|yZ`N`nwt+!-Y(jcu>EAS^NDY_PCvd8kR7b_cv5}Mz^{T;f3`bC zSJ=Esa@mN-p6hq@$O5mp?%IUapCY_QJh;DY0^|1Z={FyFuF?AD*FWfp_XU%P!AHZx zOOH-J<&eK~ZJ_@lWyK@rUxUZiX{b527fs5W6gJg$wdLpeG1WolYp%W-yZfAjoN&5Z z&HJiHb?vR6=KAIS@SZVAbLYUsC-ut00TbeFJ{}YdNqWGk+i>PBW4BJ!l&h}-D&~*% zs;M!`m=-@~#k#r1PQ&h}%rzDa+nC#b-d9KCVfuWRI^Ohl&A0KUAgLYE8S~DQF+RQjpoOb`pM1=q|B7N>@7SnW%GX4 zJYR;Nsm#o#b1oP1Zwzuubl()cw(v%D{*CA~qw+lXzy9SA-f~BG`pcpFZbZ+{ekowN ztbhgs0{nfSE2J(Aymrp!hm35}W1WMW-?>+cK1|RHSg9Vla)W=#lI*eV$I=4=y*}1W zo4rfB+J5hejh)lBob~b;H8<5WXY$6Fr6XriU%fu^K=sT_f8B)p3WJi}PR-sq_Nk3( zm96UapsWlr|?@(rF-<8j#&Z0=d-4}Z?Cl!wp>ogq+HNsoiU(J&~q^yXrYnj zwte4hLHVuC8u-$%wKl%^Y=Irmsboctb=Q;=cYNlAeW2-$G=~3q_?>{ZcXbq6KJ|L& zr@0yVqgCGx+mLKEG;mAN%;~D{2N&}v>1wqfyZg&ZP@X>d}uWJCld##2jXaZr|%;Q|vY= zUS26Cq|H*PWkeP~IOULo5|*iVOuKuHQ@*iUifXsByX=i^SNFtdbUd6s_xrMntYFg# z!KaSrExNOLe5&G-5sWjUqzC0rUq!Ui(vxowKeB)5arq8rd`DzLfBGe@-!a;!g|}yw z=3u^*S@ffEd6BQn#+~}AwCm{j5u<#uCpi~*T-N8~WzN1uhv>KJR;3)f6RyRH;kDde zI%mhL`R*6y9$j!k2L6kN6&_plvC@8~-pxv7jn1Gcjv|KH`Yr2!d{)b?9mH-mkC~lH z`*Zec*(LRx7dBqMz*ALxSSN2_w#nedl$_#gOx?=0wFBZELT|9^%?7cT1j{CA$tP)1 zle8d1N!oU}(|DTs)JAi2_I~ruf|8{|{*H9d`Nevp1-pxikyi(^18RrmyLqXRE3eV++oI3_Wu= z?N{g^i&wr1%YD~4nxvi&2ugmwuq5xA+|Deo<${UE&5RBE4^6nRckIU!l;%hpL7bY-al@_58Dfl^&-qN zUZ~bxC3jGBqnT&f;T6xHUAE^=4^31UwO~S7qQZyDt>cPoHU^E^zXF@ke;ePs6RonQXiR3db5-i<<|j_PuOa;^O~MidX=Yh_{BB&-2zq;FZvKLw zyQ-fKxphvX<(!5obG1ofW&bT#Wtl6NZPFWE`KQP*A--tZJI#mMn^KnsYt#l5&+!u_ z_7~L8YAgEw>X^!R9~Ikzk(%XP`GY@3cgyX&n!GYdQOn&;=KY`--7?fAqJETVHr=aQegdDoZV9ADXFOeRR)To+qc~;ql~WhhHV=tuTP7>YHowxdtIi9%w z);U&?%hrIaJEJ$3TKX>uKGle0Kq8c*Js@Z@8HYaM63tv4fcmllm$RHc@a?DWyQz;xSf z%Z~buNg+CWjH_-RZjQ5>^j7tIqkYrrpRsUhT>W#rZR(Z*zRODPR+}!4Q@XQ;Dt~@M z>nWFa`#v20u=x8f<>Eo5dgIhz@7FxwwNq;VrXuU4LZ@mz{$hT5?z=TL&D2E`k_Cr4 zDLbvErU$uP+T?_&R$diZm0 zY8U&IVf!|F<3U&Bm4$`&Ayxyq!Aqa}Fm3Y_=O?vwcAGT+^m;LBP@DcU6BUz}DxL>z z11|1e_Uil7br~I7>9;z-o^L+%S>z>JAo>vAVX*FMVjC?5Q7hu*~Z>sOr_aK^<{C5*kpsLmHz>Q<_O$Gm+XQ8no)_;nO>dnZ@o|v;=mPd=)7Ol#2Gl)8 zIm_~%+;w?pSr_bCJYSjnVV76GZ`N){ckM23IsbNi$Zpq=-BUw$-(C6bhjB9Zb<$`i z^@ZlA#+S={%dmjyUsG$|$c3GskZ*T4`SQN1)nhhkZW?0#W^$ABk*d{4hG-ujqMb_B zUNJ;_;}GqN`v+C$9S*lR^jU3E)1duTZq`$h7zLlz3aV}zubHFV>?7Z)VE&4q{MbV7 z;vPzAmZiCm{^n%ow%UM8bMpru%SydIdE?Z)>2s{6ZhWZz`Qf8U1;;PBgjI#fK5tPU z`0P!RuJ@*gG}U7}3>tBD)hOdfQLopm7`gTEXZgcJqV`13_mXSdF=Jrq%Dpe$Sx?R~ zpPW^HayE9)BJ5h*%_|FxTCu^wH^Ui5`m*1wg-8P)P^G#-VPVM}K zscIisQ&jD%6K>jH{_%CC{w4bW zGUu}Q<;)LFR(pLH)BV$H<32d5@psrmL2usEoX!{#7Mppm6rWu{Q5y%&J?< z5<=Y0=`vp_TaPU|{rF?i^T`H}nzgy3Hdc+QZ*zEd%)#=`m=$v~G90omd1YT>RW+Er z-W?U3eMqBZY*o~~*KLzOC%*qYDrbYo?+Lo43cg$9eKqSdp7q;wEFkD<$MTxTvlF@o zlxRX69qyAo(Y16!`l$hC7caVY_=>3U%fA~LUz&l5wsdk2UvH4_Xh=Q$ojaoH z?DAC?CoX-u#ayj(!=8&1+iEM?EY+L3Ps8#%k6MtPAt*YpHf28_Pnw$PJ zW#ebcpY~6Dmr(Zb_T5a{y-x~xmz0wqq{~0n&*Eil^iq*4U+-5MtmL?R$HL4`g-a^@ zB@-9Yx_Au>RYuS+J*;b~ENeaKy2Ph(!S~XNPH)5Cwavl5oVrx+pUd!+*Q@htJ09EV zw>>$-{ZL~6wqrU$mTmN^iyUgVs_%0S|I51m!`l>=eau)`{e#xts*=3h=(mFXm+i|! z7Iw((x~Mb2S363*Bx#FoC3W#zMGJ-IlE`?KHeNK{P2XX7hBc3WDd*}rvm^H{sMkw} z_ZLpc)^KV)Q#ehFG3eN)wMNHO+A?fzM{mw{x9eDWC$#L$ozVN2`hUq6tVoJ**?IHO zou)BwV{>O)7l@LSci!|=ON!uS2Dr+NA9Qf6-n2Z|(+>seg)eHyB}|cdw5gbLO6{O` znQ(7P^pM-ub(%Zn%A2P7TynP?druJZtCnB^fT#$<*J=32Yhn%cQxlcFB>;wNJs7HP3a%6zTD35_!>spAmaq{a+&bo>>&)9&PLLxX zU*4am?LhpWTN6&d7rw&kiY)7D2h;4p#N2C1F{+DXgo!dU{}A`x3%dbw8rL)$TFS6dHk#}#m84$_;7Ia>j#TZ z7&{!fxTTP?-t)fo-t{6Es^{KmZ`V}4%<;T6DN_C5N8_zw_a^Q=vD-zocmVg8?B_(@ ztAo|nsqso@uJsSd947cO?1> ze(tq%7b}Bw&erQ@7_;}pylHBkuXcSQRd?bMn>p^n$%hz%LyYhu%26ZP_CpWuF?1Sd ztlC;~`$*HO5bmd>h;`@I+?!cQUv-o2G5pHaKttDAF5iK)eR_uI#JZc6HaV-~#~ao) z-7DVpepY5&p~8~~r)(a+h2kOiuLtgjqS zLdIcx$eofK&1D{DZAL7tYVYZw%B&Ig`mW#Hsqfu|r8{Kq?=+~U+v{##kajq6@*}MB zyv?hfhiDZO+beJGVlkf_s-F=v&tUS5r<)@tes;SNy3)hh`}%6W;fj}Wkrf?I`9~7+ zw!Jc0c`wHMV6^}0LGHRSW3HL+tJf~BUc1s`>_-Pyn-?puG-YIyO`7&d$JmG)0rLwl zjZd%A)gGf*5ot8i_rSRYN7rhf+qs#J%l8j!tXpVWo3$=JXjo^hwfsT1P=DY(4svu2F-zdzM?2DlOY;=rqgx?bX?>>J|I)9^G=T3|j1Z zy2#GU-#q-p*uaaa{?W?@l%fs z+R(4o{LQ?WS3@(x-a73HJ4Kf(Fdg9L{AAmeM_2IB4H@}b4kNbH@pS#CS@YV)KaE>; zy!d5N#gKXGcQ0D0zJE7&_B75)=C!2ytw~w_M+Q}j!rju7i>D;|tz{b=V!o2E%=!Ev z(s)&S?s_-13iHcr$MBVHZZ!TFB(llhsa?}*k@Kk1^27S-chfyjei)a%bz;n`Nm&IY z_udpI?AbW(Ou)zRwasUbCfxcxZ{6hwt#iv?be*0N&b(A^?zU4SuJGVhW6VwOY{f?- z*B7GI^^pc=MkzMzHCw)8)ao*sg&XW+H#DF7zI}rBnN3yMZ*$r*%Wr6`U3PZzob=6A z@3)#n*)HB$5p23+6k}oMxkJ8-2Ie$W<~^98Y(Ky-@bP4ap^tBHZ|*S`4Lwt<->Px& z+N*oVAHH`y*6&4-HGXA(WYoHeg}O2Zzh`tiagHq=)!>!Dm0vz>jnaw!A>%VkKhH4D z`FU1u*rK-;X_xqVzbY#>tlr6bd^t>Ijwr@|-v$pG-(PD#R(ku}t{eNNqPS#L%hnz3 zE$6ydw@}JkK0V&MSV5GvIegBQ71JGN6;&)Lamv16cxPqt2F+6&RZnePvB7oX&ZxYD zac)cHw#--?lHMsa>74x1$oP}d`7JT5RrxyvAL=U(*12DOcy-!NbCE9R=nBdYZ@bJI8MfK=<8Q{UdtEp4zi!bVb2H^bpzXfEimvS) zfpZRJT%ED*^-u++EvMrBixVrHp14nNzZ!c`e%^wto1Vu;oj9J|aJ0g+V~TZ#@|G<# zi=UOuWIgxS_W6F)^Y>Bf$BEh=Z6ij^6^wYUu`KGYbwq$;YeHk%)2~}rpZ+%L{?gTD zMv0Sl9M3FkeSNrLC;Pk7wb?fcFL)@`%iR**E%`iS?S;+t@1~wyB$p8@mvCRv^4=zT z^EgWAAS^CWIk&#u>7?#XkJr4VlNOw{+VZVyR@>Db+rJxfj@{nZWJmd9_havdoN)E% zYGbxx>l)7E#pVxw%~@1mvQ^c-?RuEyyCTKrq9K$w-?(|? z62oZ~%_mpqRG*9YzBR$+#)*oPor`lnbst$c$)zn=?wKC_itWuE+h-N#+$q2>O{;u& zal2spZHMW2Zpcx)*Gi&wM;GNQ>UrBM<=)Kpox9B_c%b2pAJJ^BJJT{L8ky$?Ps@t( zD6wy_d=l3la(|JE=4tf@6s=4dtv#0vSuF>cdJotd1uAQE%nFQ0UOTz(*)Www-7gb= zysW6G_0N~Jm;Y{mb+e#)M*YV^FW-5i4`7SOC#hKdEL_fCpZXGG4FG!IvKlwXi*!xnO`odF^-$+X&?! zUj-k(Cf%QN_fW||Pl0XA?b;8Po_|~wi^smv(llGB*)g5c`8a=J)wUZWZj|5i{jo%u zX|IWYji)~djavUJNnob(BIbI@6oq|@sP{_Q8iUP_E~(Jlw^-HVlCf#-zHfKerc`KC z+U180`g!ER1=HvXH{S}k>U$@C)4SJB=D!KFcoV3XWiF?7NoIvlJAO!3@Od=t^XQ+i z-@A*({2szy@?h{(l~4LV!cu;n32E+sduwYAQ~5jA|I?O`{N0v<)iGoK*yJvm{oL5C zYjeWs^Ydx1n7x~^l6K{6pze5!q;F<> zN9-20os8;^aO#|^&C2sHnCW%sa4~1c(T@7}#Z%WFnpbqbV5yomwP@{fy`Yp?KgS)& z3O;jF^?j$0QTOk|#Va4S96K^+2Q$@urN^ZWx5vgJx^u=t83DrlxPrp6F>GcH{QK~L_1~@6e@~jTGS%y`L+5_J@n_8G z`?7blN3efm?BA;|Hp)F5h?PujF1z<a}}(YIu^C2SNad0YMaog>6fUu zaqjlDyqO_~PP2TUkF{5+c(3Tvq`EU(rU;XHT1kDniQaE@^>|ZZ5ypPHN%OJUzCAKU z+A>dXQ=fixC_CExt~xR$G;z-O3yUf{{QmeWt}$=lo};qb{PElGKEgqlvJCgr?@Z+n z4N1!%p!%M@t}w$ReEF$awcL(=9$!sA)@$9dPjxppiA=xa_U^R8`_mb@pr@U1$>h^F+V!8<(u<=LSLvo4SBXBZ^69vW z@1xYh%)x;bsnbLvwP!(klx7w6)H%m>a`!&`F=UXk!yx6@aXPOz@vqk{I@?d{4?AVa zaprdgyH{BAJJtK&GuW+;I<1bjt&T&SLR2aj(|t!zrYBrJxI6RNFgueef5L`L$#}O| z)#sq{DX+(>>96UEez}iTDevg~*p#HCH}sJiI+r($*Pd@49VdKa5d0@)%#!PtTncxI z?2O-AC*IVv<}P_1T%{+vwQ=<4d4EdTIWqUDN+p zWn8J#yp6f4gF@`7-`yy`vr<28iT>_3|JR`U$?wjnDdaY?%mRa0DW&SU=3M$BlZ?`h zu0Hd&TLexVW-{yLaL1R5TUs)AKbqIiEVy8bT-MAD^snmt!SuG3ELwh7F4Ibw!5x0bsB5^HB;5qRXXpNInSRT~2oE(w~w;S2Ll+zO2f4?jwxp-GW zqwPa1zwT!K4D&c=L*|421NO{VTzI`eiQ#!^h$ZYm%8TJCTlSPNH3s_j)7QUdpnq+6l@3;+ zb8~f`?fQMMm=9tiJ+(7eKZy}MiAmb8dvFyei_i2_hQozyQ~vpFf)@gIwC|jy5MF(~ zR_??0*}AsQ)mry5 zMyX|ei@xSRLT0%3W!o%9rf<|F=dfi3Q>L_yyRHtmYX^>1UAJcWs`D}Jx+bfSjaafa zeV2CN=lcG?yfxd-4LkDcs?2MD{m&ZQgJSjR zQ2I@qG9OOtaKZKI^=#)|S*zdA=2pzQ=~oz7{Gcni)nMGO>vjE9eUIJFDDk#C(PU`w zOLfKQD=#yTf9ELurVk&*PZ^?fL&2``&hK>7v|jD-Z$y?{VZ%B z7;go__sN~R9A|9jC(6IDE;rr*lm*Kd+*Ds#cJim&r4LnByLZmp6k+-5kBY~a`@7&h Gm;VR%(AEC{IB07VfC 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 VniTK5sQhEYXx6VkO|2jh{tJP16*T|= literal 0 HcmV?d00001 diff --git a/deps/mochiweb-r88/priv/skel/Makefile b/deps/mochiweb-r88/priv/skel/Makefile new file mode 100644 index 0000000..7b44214 --- /dev/null +++ b/deps/mochiweb-r88/priv/skel/Makefile @@ -0,0 +1,5 @@ +all: + (cd src;$(MAKE)) + +clean: + (cd src;$(MAKE) clean) diff --git a/deps/mochiweb-r88/priv/skel/priv/www/index.html b/deps/mochiweb-r88/priv/skel/priv/www/index.html new file mode 100644 index 0000000..8e7a2c6 --- /dev/null +++ b/deps/mochiweb-r88/priv/skel/priv/www/index.html @@ -0,0 +1,8 @@ + + +It Worked + + +MochiWeb running. + + diff --git a/deps/mochiweb-r88/priv/skel/src/Makefile b/deps/mochiweb-r88/priv/skel/src/Makefile new file mode 100644 index 0000000..7a44a74 --- /dev/null +++ b/deps/mochiweb-r88/priv/skel/src/Makefile @@ -0,0 +1,9 @@ +include ../support/include.mk + +all: $(EBIN_FILES) + +debug: + $(MAKE) DEBUG=-DDEBUG + +clean: + rm -rf $(EBIN_FILES) diff --git a/deps/mochiweb-r88/priv/skel/src/skel.app b/deps/mochiweb-r88/priv/skel/src/skel.app new file mode 100644 index 0000000..fc16aae --- /dev/null +++ b/deps/mochiweb-r88/priv/skel/src/skel.app @@ -0,0 +1,14 @@ +{application, skel, + [{description, "skel"}, + {vsn, "0.01"}, + {modules, [ + skel, + skel_app, + skel_sup, + skel_web, + skel_deps + ]}, + {registered, []}, + {mod, {skel_app, []}}, + {env, []}, + {applications, [kernel, stdlib, crypto]}]}. diff --git a/deps/mochiweb-r88/priv/skel/src/skel.erl b/deps/mochiweb-r88/priv/skel/src/skel.erl new file mode 100644 index 0000000..49263c8 --- /dev/null +++ b/deps/mochiweb-r88/priv/skel/src/skel.erl @@ -0,0 +1,30 @@ +%% @author author +%% @copyright YYYY author. + +%% @doc TEMPLATE. + +-module(skel). +-author('author '). +-export([start/0, stop/0]). + +ensure_started(App) -> + case application:start(App) of + ok -> + ok; + {error, {already_started, App}} -> + ok + end. + +%% @spec start() -> ok +%% @doc Start the skel server. +start() -> + skel_deps:ensure(), + ensure_started(crypto), + application:start(skel). + +%% @spec stop() -> ok +%% @doc Stop the skel server. +stop() -> + Res = application:stop(skel), + application:stop(crypto), + Res. diff --git a/deps/mochiweb-r88/priv/skel/src/skel.hrl b/deps/mochiweb-r88/priv/skel/src/skel.hrl new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/deps/mochiweb-r88/priv/skel/src/skel.hrl @@ -0,0 +1 @@ + diff --git a/deps/mochiweb-r88/priv/skel/src/skel_app.erl b/deps/mochiweb-r88/priv/skel/src/skel_app.erl new file mode 100644 index 0000000..6b8228e --- /dev/null +++ b/deps/mochiweb-r88/priv/skel/src/skel_app.erl @@ -0,0 +1,22 @@ +%% @author author +%% @copyright YYYY author. + +%% @doc Callbacks for the skel application. + +-module(skel_app). +-author('author '). + +-behaviour(application). +-export([start/2,stop/1]). + + +%% @spec start(_Type, _StartArgs) -> ServerRet +%% @doc application start callback for skel. +start(_Type, _StartArgs) -> + skel_deps:ensure(), + skel_sup:start_link(). + +%% @spec stop(_State) -> ServerRet +%% @doc application stop callback for skel. +stop(_State) -> + ok. diff --git a/deps/mochiweb-r88/priv/skel/src/skel_deps.erl b/deps/mochiweb-r88/priv/skel/src/skel_deps.erl new file mode 100644 index 0000000..b53552d --- /dev/null +++ b/deps/mochiweb-r88/priv/skel/src/skel_deps.erl @@ -0,0 +1,84 @@ +%% @author author +%% @copyright YYYY author. + +%% @doc Ensure that the relatively-installed dependencies are on the code +%% loading path, and locate resources relative +%% to this application's path. + +-module(skel_deps). +-author('author '). + +-export([ensure/0, ensure/1]). +-export([get_base_dir/0, get_base_dir/1]). +-export([local_path/1, local_path/2]). +-export([deps_on_path/0, new_siblings/1]). + +%% @spec deps_on_path() -> [ProjNameAndVers] +%% @doc List of project dependencies on the path. +deps_on_path() -> + F = fun (X, Acc) -> + ProjDir = filename:dirname(X), + case {filename:basename(X), + filename:basename(filename:dirname(ProjDir))} of + {"ebin", "deps"} -> + [filename:basename(ProjDir) | Acc]; + _ -> + Acc + end + end, + ordsets:from_list(lists:foldl(F, [], code:get_path())). + +%% @spec new_siblings(Module) -> [Dir] +%% @doc Find new siblings paths relative to Module that aren't already on the +%% code path. +new_siblings(Module) -> + Existing = deps_on_path(), + SiblingEbin = filelib:wildcard(local_path(["deps", "*", "ebin"], Module)), + Siblings = [filename:dirname(X) || X <- SiblingEbin, + ordsets:is_element( + filename:basename(filename:dirname(X)), + Existing) =:= false], + lists:filter(fun filelib:is_dir/1, + lists:append([[filename:join([X, "ebin"]), + filename:join([X, "include"])] || + X <- Siblings])). + + +%% @spec ensure(Module) -> ok +%% @doc Ensure that all ebin and include paths for dependencies +%% of the application for Module are on the code path. +ensure(Module) -> + code:add_paths(new_siblings(Module)), + code:clash(), + ok. + +%% @spec ensure() -> ok +%% @doc Ensure that the ebin and include paths for dependencies of +%% this application are on the code path. Equivalent to +%% ensure(?Module). +ensure() -> + ensure(?MODULE). + +%% @spec get_base_dir(Module) -> string() +%% @doc Return the application directory for Module. It assumes Module is in +%% a standard OTP layout application in the ebin or src directory. +get_base_dir(Module) -> + {file, Here} = code:is_loaded(Module), + filename:dirname(filename:dirname(Here)). + +%% @spec get_base_dir() -> string() +%% @doc Return the application directory for this application. Equivalent to +%% get_base_dir(?MODULE). +get_base_dir() -> + get_base_dir(?MODULE). + +%% @spec local_path([string()], Module) -> string() +%% @doc Return an application-relative directory from Module's application. +local_path(Components, Module) -> + filename:join([get_base_dir(Module) | Components]). + +%% @spec local_path(Components) -> string() +%% @doc Return an application-relative directory for this application. +%% Equivalent to local_path(Components, ?MODULE). +local_path(Components) -> + local_path(Components, ?MODULE). diff --git a/deps/mochiweb-r88/priv/skel/src/skel_sup.erl b/deps/mochiweb-r88/priv/skel/src/skel_sup.erl new file mode 100644 index 0000000..92e0cae --- /dev/null +++ b/deps/mochiweb-r88/priv/skel/src/skel_sup.erl @@ -0,0 +1,54 @@ +%% @author author +%% @copyright YYYY author. + +%% @doc Supervisor for the skel application. + +-module(skel_sup). +-author('author '). + +-behaviour(supervisor). + +%% External exports +-export([start_link/0, upgrade/0]). + +%% supervisor callbacks +-export([init/1]). + +%% @spec start_link() -> ServerRet +%% @doc API for starting the supervisor. +start_link() -> + supervisor:start_link({local, ?MODULE}, ?MODULE, []). + +%% @spec upgrade() -> ok +%% @doc Add processes if necessary. +upgrade() -> + {ok, {_, Specs}} = init([]), + + Old = sets:from_list( + [Name || {Name, _, _, _} <- supervisor:which_children(?MODULE)]), + New = sets:from_list([Name || {Name, _, _, _, _, _} <- Specs]), + Kill = sets:subtract(Old, New), + + sets:fold(fun (Id, ok) -> + supervisor:terminate_child(?MODULE, Id), + supervisor:delete_child(?MODULE, Id), + ok + end, ok, Kill), + + [supervisor:start_child(?MODULE, Spec) || Spec <- Specs], + ok. + +%% @spec init([]) -> SupervisorTree +%% @doc supervisor callback. +init([]) -> + Ip = case os:getenv("MOCHIWEB_IP") of false -> "0.0.0.0"; Any -> Any end, + WebConfig = [ + {ip, Ip}, + {port, 8000}, + {docroot, skel_deps:local_path(["priv", "www"])}], + Web = {skel_web, + {skel_web, start, [WebConfig]}, + permanent, 5000, worker, dynamic}, + + Processes = [Web], + {ok, {{one_for_one, 10, 10}, Processes}}. diff --git a/deps/mochiweb-r88/priv/skel/src/skel_web.erl b/deps/mochiweb-r88/priv/skel/src/skel_web.erl new file mode 100644 index 0000000..3679670 --- /dev/null +++ b/deps/mochiweb-r88/priv/skel/src/skel_web.erl @@ -0,0 +1,43 @@ +%% @author author +%% @copyright YYYY author. + +%% @doc Web server for skel. + +-module(skel_web). +-author('author '). + +-export([start/1, stop/0, loop/2]). + +%% External API + +start(Options) -> + {DocRoot, Options1} = get_option(docroot, Options), + Loop = fun (Req) -> + ?MODULE:loop(Req, DocRoot) + end, + mochiweb_http:start([{name, ?MODULE}, {loop, Loop} | Options1]). + +stop() -> + mochiweb_http:stop(?MODULE). + +loop(Req, DocRoot) -> + "/" ++ Path = Req:get(path), + case Req:get(method) of + Method when Method =:= 'GET'; Method =:= 'HEAD' -> + case Path of + _ -> + Req:serve_file(Path, DocRoot) + end; + 'POST' -> + case Path of + _ -> + Req:not_found() + end; + _ -> + Req:respond({501, [], []}) + end. + +%% Internal API + +get_option(Option, Options) -> + {proplists:get_value(Option, Options), proplists:delete(Option, Options)}. diff --git a/deps/mochiweb-r88/priv/skel/start-dev.sh b/deps/mochiweb-r88/priv/skel/start-dev.sh new file mode 100755 index 0000000..6f02962 --- /dev/null +++ b/deps/mochiweb-r88/priv/skel/start-dev.sh @@ -0,0 +1,3 @@ +#!/bin/sh +cd `dirname $0` +exec erl -pa $PWD/ebin $PWD/deps/*/ebin -boot start_sasl -s reloader -s skel diff --git a/deps/mochiweb-r88/priv/skel/start.sh b/deps/mochiweb-r88/priv/skel/start.sh new file mode 100755 index 0000000..599f8e6 --- /dev/null +++ b/deps/mochiweb-r88/priv/skel/start.sh @@ -0,0 +1,3 @@ +#!/bin/sh +cd `dirname $0` +exec erl -pa $PWD/ebin $PWD/deps/*/ebin -boot start_sasl -s skel diff --git a/deps/mochiweb-r88/priv/skel/support/include.mk b/deps/mochiweb-r88/priv/skel/support/include.mk new file mode 100644 index 0000000..64b50fe --- /dev/null +++ b/deps/mochiweb-r88/priv/skel/support/include.mk @@ -0,0 +1,46 @@ +## -*- makefile -*- + +###################################################################### +## Erlang + +ERL := erl +ERLC := $(ERL)c + +INCLUDE_DIRS := ../include $(wildcard ../deps/*/include) +EBIN_DIRS := $(wildcard ../deps/*/ebin) +ERLC_FLAGS := -W $(INCLUDE_DIRS:../%=-I ../%) $(EBIN_DIRS:%=-pa %) + +ifndef no_debug_info + ERLC_FLAGS += +debug_info +endif + +ifdef debug + ERLC_FLAGS += -Ddebug +endif + +EBIN_DIR := ../ebin +DOC_DIR := ../doc +EMULATOR := beam + +ERL_SOURCES := $(wildcard *.erl) +ERL_HEADERS := $(wildcard *.hrl) $(wildcard ../include/*.hrl) +ERL_OBJECTS := $(ERL_SOURCES:%.erl=$(EBIN_DIR)/%.$(EMULATOR)) +ERL_DOCUMENTS := $(ERL_SOURCES:%.erl=$(DOC_DIR)/%.html) +ERL_OBJECTS_LOCAL := $(ERL_SOURCES:%.erl=./%.$(EMULATOR)) +APP_FILES := $(wildcard *.app) +EBIN_FILES = $(ERL_OBJECTS) $(ERL_DOCUMENTS) $(APP_FILES:%.app=../ebin/%.app) +EBIN_FILES_NO_DOCS = $(ERL_OBJECTS) $(APP_FILES:%.app=../ebin/%.app) +MODULES = $(ERL_SOURCES:%.erl=%) + +../ebin/%.app: %.app + cp $< $@ + +$(EBIN_DIR)/%.$(EMULATOR): %.erl + $(ERLC) $(ERLC_FLAGS) -o $(EBIN_DIR) $< + +./%.$(EMULATOR): %.erl + $(ERLC) $(ERLC_FLAGS) -o . $< + +$(DOC_DIR)/%.html: %.erl + $(ERL) -noshell -run edoc file $< -run init stop + mv *.html $(DOC_DIR) diff --git a/deps/mochiweb-r88/scripts/new_mochiweb.erl b/deps/mochiweb-r88/scripts/new_mochiweb.erl new file mode 100644 index 0000000..918f01e --- /dev/null +++ b/deps/mochiweb-r88/scripts/new_mochiweb.erl @@ -0,0 +1,27 @@ +#!/usr/bin/env escript +%% -*- mode: erlang -*- +-export([main/1]). + +%% External API + +main([Name]) -> + main([Name, "."]); +main([Name, Dest]) -> + ensure(), + DestDir = filename:absname(Dest), + ok = mochiweb_skel:skelcopy(DestDir, Name); +main(_) -> + usage(). + +%% Internal API + +ensure() -> + code:add_patha(filename:join(filename:dirname(escript:script_name()), + "../ebin")). + +usage() -> + io:format("usage: ~s name [destdir]~n", + [filename:basename(escript:script_name())]), + halt(1). + + diff --git a/deps/mochiweb-r88/src/Makefile b/deps/mochiweb-r88/src/Makefile new file mode 100644 index 0000000..af1e600 --- /dev/null +++ b/deps/mochiweb-r88/src/Makefile @@ -0,0 +1,12 @@ +include ../support/include.mk + +all: $(EBIN_FILES) + +debug: + $(MAKE) DEBUG=-DDEBUG + +clean: + rm -rf $(EBIN_FILES) + +test: all + $(ERL) -noshell -pa ../ebin -s mochiweb test -s init stop diff --git a/deps/mochiweb-r88/src/mochifmt.erl b/deps/mochiweb-r88/src/mochifmt.erl new file mode 100644 index 0000000..da0a133 --- /dev/null +++ b/deps/mochiweb-r88/src/mochifmt.erl @@ -0,0 +1,426 @@ +%% @author Bob Ippolito +%% @copyright 2008 Mochi Media, Inc. + +%% @doc String Formatting for Erlang, inspired by Python 2.6 +%% (PEP 3101). +%% +-module(mochifmt). +-author('bob@mochimedia.com'). +-export([format/2, format_field/2, convert_field/2, get_value/2, get_field/2]). +-export([tokenize/1, format/3, get_field/3, format_field/3]). +-export([bformat/2, bformat/3]). +-export([f/2, f/3]). +-export([test/0]). + +-record(conversion, {length, precision, ctype, align, fill_char, sign}). + +%% @spec tokenize(S::string()) -> tokens() +%% @doc Tokenize a format string into mochifmt's internal format. +tokenize(S) -> + {?MODULE, tokenize(S, "", [])}. + +%% @spec convert_field(Arg, Conversion::conversion()) -> term() +%% @doc Process Arg according to the given explicit conversion specifier. +convert_field(Arg, "") -> + Arg; +convert_field(Arg, "r") -> + repr(Arg); +convert_field(Arg, "s") -> + str(Arg). + +%% @spec get_value(Key::string(), Args::args()) -> term() +%% @doc Get the Key from Args. If Args is a tuple then convert Key to +%% an integer and get element(1 + Key, Args). If Args is a list and Key +%% can be parsed as an integer then use lists:nth(1 + Key, Args), +%% otherwise try and look for Key in Args as a proplist, converting +%% Key to an atom or binary if necessary. +get_value(Key, Args) when is_tuple(Args) -> + element(1 + list_to_integer(Key), Args); +get_value(Key, Args) when is_list(Args) -> + try lists:nth(1 + list_to_integer(Key), Args) + catch error:_ -> + {_K, V} = proplist_lookup(Key, Args), + V + end. + +%% @spec get_field(Key::string(), Args) -> term() +%% @doc Consecutively call get_value/2 on parts of Key delimited by ".", +%% replacing Args with the result of the previous get_value. This +%% is used to implement formats such as {0.0}. +get_field(Key, Args) -> + get_field(Key, Args, ?MODULE). + +%% @spec get_field(Key::string(), Args, Module) -> term() +%% @doc Consecutively call Module:get_value/2 on parts of Key delimited by ".", +%% replacing Args with the result of the previous get_value. This +%% is used to implement formats such as {0.0}. +get_field(Key, Args, Module) -> + {Name, Next} = lists:splitwith(fun (C) -> C =/= $. end, Key), + Res = try Module:get_value(Name, Args) + catch error:undef -> get_value(Name, Args) end, + case Next of + "" -> + Res; + "." ++ S1 -> + get_field(S1, Res, Module) + end. + +%% @spec format(Format::string(), Args) -> iolist() +%% @doc Format Args with Format. +format(Format, Args) -> + format(Format, Args, ?MODULE). + +%% @spec format(Format::string(), Args, Module) -> iolist() +%% @doc Format Args with Format using Module. +format({?MODULE, Parts}, Args, Module) -> + format2(Parts, Args, Module, []); +format(S, Args, Module) -> + format(tokenize(S), Args, Module). + +%% @spec format_field(Arg, Format) -> iolist() +%% @doc Format Arg with Format. +format_field(Arg, Format) -> + format_field(Arg, Format, ?MODULE). + +%% @spec format_field(Arg, Format, _Module) -> iolist() +%% @doc Format Arg with Format. +format_field(Arg, Format, _Module) -> + F = default_ctype(Arg, parse_std_conversion(Format)), + fix_padding(fix_sign(convert2(Arg, F), F), F). + +%% @spec f(Format::string(), Args) -> string() +%% @doc Format Args with Format and return a string(). +f(Format, Args) -> + f(Format, Args, ?MODULE). + +%% @spec f(Format::string(), Args, Module) -> string() +%% @doc Format Args with Format using Module and return a string(). +f(Format, Args, Module) -> + case lists:member(${, Format) of + true -> + binary_to_list(bformat(Format, Args, Module)); + false -> + Format + end. + +%% @spec bformat(Format::string(), Args) -> binary() +%% @doc Format Args with Format and return a binary(). +bformat(Format, Args) -> + iolist_to_binary(format(Format, Args)). + +%% @spec bformat(Format::string(), Args, Module) -> binary() +%% @doc Format Args with Format using Module and return a binary(). +bformat(Format, Args, Module) -> + iolist_to_binary(format(Format, Args, Module)). + +%% @spec test() -> ok +%% @doc Run tests. +test() -> + ok = test_tokenize(), + ok = test_format(), + ok = test_std(), + ok = test_records(), + ok. + +%% Internal API + +add_raw("", Acc) -> + Acc; +add_raw(S, Acc) -> + [{raw, lists:reverse(S)} | Acc]. + +tokenize([], S, Acc) -> + lists:reverse(add_raw(S, Acc)); +tokenize("{{" ++ Rest, S, Acc) -> + tokenize(Rest, "{" ++ S, Acc); +tokenize("{" ++ Rest, S, Acc) -> + {Format, Rest1} = tokenize_format(Rest), + tokenize(Rest1, "", [{format, make_format(Format)} | add_raw(S, Acc)]); +tokenize("}}" ++ Rest, S, Acc) -> + tokenize(Rest, "}" ++ S, Acc); +tokenize([C | Rest], S, Acc) -> + tokenize(Rest, [C | S], Acc). + +tokenize_format(S) -> + tokenize_format(S, 1, []). + +tokenize_format("}" ++ Rest, 1, Acc) -> + {lists:reverse(Acc), Rest}; +tokenize_format("}" ++ Rest, N, Acc) -> + tokenize_format(Rest, N - 1, "}" ++ Acc); +tokenize_format("{" ++ Rest, N, Acc) -> + tokenize_format(Rest, 1 + N, "{" ++ Acc); +tokenize_format([C | Rest], N, Acc) -> + tokenize_format(Rest, N, [C | Acc]). + +make_format(S) -> + {Name0, Spec} = case lists:splitwith(fun (C) -> C =/= $: end, S) of + {_, ""} -> + {S, ""}; + {SN, ":" ++ SS} -> + {SN, SS} + end, + {Name, Transform} = case lists:splitwith(fun (C) -> C =/= $! end, Name0) of + {_, ""} -> + {Name0, ""}; + {TN, "!" ++ TT} -> + {TN, TT} + end, + {Name, Transform, Spec}. + +proplist_lookup(S, P) -> + A = try list_to_existing_atom(S) + catch error:_ -> make_ref() end, + B = try list_to_binary(S) + catch error:_ -> make_ref() end, + proplist_lookup2({S, A, B}, P). + +proplist_lookup2({KS, KA, KB}, [{K, V} | _]) + when KS =:= K orelse KA =:= K orelse KB =:= K -> + {K, V}; +proplist_lookup2(Keys, [_ | Rest]) -> + proplist_lookup2(Keys, Rest). + +format2([], _Args, _Module, Acc) -> + lists:reverse(Acc); +format2([{raw, S} | Rest], Args, Module, Acc) -> + format2(Rest, Args, Module, [S | Acc]); +format2([{format, {Key, Convert, Format0}} | Rest], Args, Module, Acc) -> + Format = f(Format0, Args, Module), + V = case Module of + ?MODULE -> + V0 = get_field(Key, Args), + V1 = convert_field(V0, Convert), + format_field(V1, Format); + _ -> + V0 = try Module:get_field(Key, Args) + catch error:undef -> get_field(Key, Args, Module) end, + V1 = try Module:convert_field(V0, Convert) + catch error:undef -> convert_field(V0, Convert) end, + try Module:format_field(V1, Format) + catch error:undef -> format_field(V1, Format, Module) end + end, + format2(Rest, Args, Module, [V | Acc]). + +default_ctype(_Arg, C=#conversion{ctype=N}) when N =/= undefined -> + C; +default_ctype(Arg, C) when is_integer(Arg) -> + C#conversion{ctype=decimal}; +default_ctype(Arg, C) when is_float(Arg) -> + C#conversion{ctype=general}; +default_ctype(_Arg, C) -> + C#conversion{ctype=string}. + +fix_padding(Arg, #conversion{length=undefined}) -> + Arg; +fix_padding(Arg, F=#conversion{length=Length, fill_char=Fill0, align=Align0, + ctype=Type}) -> + Padding = Length - iolist_size(Arg), + Fill = case Fill0 of + undefined -> + $\s; + _ -> + Fill0 + end, + Align = case Align0 of + undefined -> + case Type of + string -> + left; + _ -> + right + end; + _ -> + Align0 + end, + case Padding > 0 of + true -> + do_padding(Arg, Padding, Fill, Align, F); + false -> + Arg + end. + +do_padding(Arg, Padding, Fill, right, _F) -> + [lists:duplicate(Padding, Fill), Arg]; +do_padding(Arg, Padding, Fill, center, _F) -> + LPadding = lists:duplicate(Padding div 2, Fill), + RPadding = case Padding band 1 of + 1 -> + [Fill | LPadding]; + _ -> + LPadding + end, + [LPadding, Arg, RPadding]; +do_padding([$- | Arg], Padding, Fill, sign_right, _F) -> + [[$- | lists:duplicate(Padding, Fill)], Arg]; +do_padding(Arg, Padding, Fill, sign_right, #conversion{sign=$-}) -> + [lists:duplicate(Padding, Fill), Arg]; +do_padding([S | Arg], Padding, Fill, sign_right, #conversion{sign=S}) -> + [[S | lists:duplicate(Padding, Fill)], Arg]; +do_padding(Arg, Padding, Fill, sign_right, #conversion{sign=undefined}) -> + [lists:duplicate(Padding, Fill), Arg]; +do_padding(Arg, Padding, Fill, left, _F) -> + [Arg | lists:duplicate(Padding, Fill)]. + +fix_sign(Arg, #conversion{sign=$+}) when Arg >= 0 -> + [$+, Arg]; +fix_sign(Arg, #conversion{sign=$\s}) when Arg >= 0 -> + [$\s, Arg]; +fix_sign(Arg, _F) -> + Arg. + +ctype($\%) -> percent; +ctype($s) -> string; +ctype($b) -> bin; +ctype($o) -> oct; +ctype($X) -> upper_hex; +ctype($x) -> hex; +ctype($c) -> char; +ctype($d) -> decimal; +ctype($g) -> general; +ctype($f) -> fixed; +ctype($e) -> exp. + +align($<) -> left; +align($>) -> right; +align($^) -> center; +align($=) -> sign_right. + +convert2(Arg, F=#conversion{ctype=percent}) -> + [convert2(100.0 * Arg, F#conversion{ctype=fixed}), $\%]; +convert2(Arg, #conversion{ctype=string}) -> + str(Arg); +convert2(Arg, #conversion{ctype=bin}) -> + erlang:integer_to_list(Arg, 2); +convert2(Arg, #conversion{ctype=oct}) -> + erlang:integer_to_list(Arg, 8); +convert2(Arg, #conversion{ctype=upper_hex}) -> + erlang:integer_to_list(Arg, 16); +convert2(Arg, #conversion{ctype=hex}) -> + string:to_lower(erlang:integer_to_list(Arg, 16)); +convert2(Arg, #conversion{ctype=char}) when Arg < 16#80 -> + [Arg]; +convert2(Arg, #conversion{ctype=char}) -> + xmerl_ucs:to_utf8(Arg); +convert2(Arg, #conversion{ctype=decimal}) -> + integer_to_list(Arg); +convert2(Arg, #conversion{ctype=general, precision=undefined}) -> + try mochinum:digits(Arg) + catch error:undef -> io_lib:format("~g", [Arg]) end; +convert2(Arg, #conversion{ctype=fixed, precision=undefined}) -> + io_lib:format("~f", [Arg]); +convert2(Arg, #conversion{ctype=exp, precision=undefined}) -> + io_lib:format("~e", [Arg]); +convert2(Arg, #conversion{ctype=general, precision=P}) -> + io_lib:format("~." ++ integer_to_list(P) ++ "g", [Arg]); +convert2(Arg, #conversion{ctype=fixed, precision=P}) -> + io_lib:format("~." ++ integer_to_list(P) ++ "f", [Arg]); +convert2(Arg, #conversion{ctype=exp, precision=P}) -> + io_lib:format("~." ++ integer_to_list(P) ++ "e", [Arg]). + +str(A) when is_atom(A) -> + atom_to_list(A); +str(I) when is_integer(I) -> + integer_to_list(I); +str(F) when is_float(F) -> + try mochinum:digits(F) + catch error:undef -> io_lib:format("~g", [F]) end; +str(L) when is_list(L) -> + L; +str(B) when is_binary(B) -> + B; +str(P) -> + repr(P). + +repr(P) when is_float(P) -> + try mochinum:digits(P) + catch error:undef -> float_to_list(P) end; +repr(P) -> + io_lib:format("~p", [P]). + +parse_std_conversion(S) -> + parse_std_conversion(S, #conversion{}). + +parse_std_conversion("", Acc) -> + Acc; +parse_std_conversion([Fill, Align | Spec], Acc) + when Align =:= $< orelse Align =:= $> orelse Align =:= $= orelse Align =:= $^ -> + parse_std_conversion(Spec, Acc#conversion{fill_char=Fill, + align=align(Align)}); +parse_std_conversion([Align | Spec], Acc) + when Align =:= $< orelse Align =:= $> orelse Align =:= $= orelse Align =:= $^ -> + parse_std_conversion(Spec, Acc#conversion{align=align(Align)}); +parse_std_conversion([Sign | Spec], Acc) + when Sign =:= $+ orelse Sign =:= $- orelse Sign =:= $\s -> + parse_std_conversion(Spec, Acc#conversion{sign=Sign}); +parse_std_conversion("0" ++ Spec, Acc) -> + Align = case Acc#conversion.align of + undefined -> + sign_right; + A -> + A + end, + parse_std_conversion(Spec, Acc#conversion{fill_char=$0, align=Align}); +parse_std_conversion(Spec=[D|_], Acc) when D >= $0 andalso D =< $9 -> + {W, Spec1} = lists:splitwith(fun (C) -> C >= $0 andalso C =< $9 end, Spec), + parse_std_conversion(Spec1, Acc#conversion{length=list_to_integer(W)}); +parse_std_conversion([$. | Spec], Acc) -> + case lists:splitwith(fun (C) -> C >= $0 andalso C =< $9 end, Spec) of + {"", Spec1} -> + parse_std_conversion(Spec1, Acc); + {P, Spec1} -> + parse_std_conversion(Spec1, + Acc#conversion{precision=list_to_integer(P)}) + end; +parse_std_conversion([Type], Acc) -> + parse_std_conversion("", Acc#conversion{ctype=ctype(Type)}). + +test_tokenize() -> + {?MODULE, [{raw, "ABC"}]} = tokenize("ABC"), + {?MODULE, [{format, {"0", "", ""}}]} = tokenize("{0}"), + {?MODULE, [{raw, "ABC"}, {format, {"1", "", ""}}, {raw, "DEF"}]} = + tokenize("ABC{1}DEF"), + ok. + +test_format() -> + <<" -4">> = bformat("{0:4}", [-4]), + <<" 4">> = bformat("{0:4}", [4]), + <<" 4">> = bformat("{0:{0}}", [4]), + <<"4 ">> = bformat("{0:4}", ["4"]), + <<"4 ">> = bformat("{0:{0}}", ["4"]), + <<"1.2yoDEF">> = bformat("{2}{0}{1}{3}", {yo, "DE", 1.2, <<"F">>}), + <<"cafebabe">> = bformat("{0:x}", {16#cafebabe}), + <<"CAFEBABE">> = bformat("{0:X}", {16#cafebabe}), + <<"CAFEBABE">> = bformat("{0:X}", {16#cafebabe}), + <<"755">> = bformat("{0:o}", {8#755}), + <<"a">> = bformat("{0:c}", {97}), + %% Horizontal ellipsis + <<226, 128, 166>> = bformat("{0:c}", {16#2026}), + <<"11">> = bformat("{0:b}", {3}), + <<"11">> = bformat("{0:b}", [3]), + <<"11">> = bformat("{three:b}", [{three, 3}]), + <<"11">> = bformat("{three:b}", [{"three", 3}]), + <<"11">> = bformat("{three:b}", [{<<"three">>, 3}]), + <<"\"foo\"">> = bformat("{0!r}", {"foo"}), + <<"2008-5-4">> = bformat("{0.0}-{0.1}-{0.2}", {{2008,5,4}}), + <<"2008-05-04">> = bformat("{0.0:04}-{0.1:02}-{0.2:02}", {{2008,5,4}}), + <<"foo6bar-6">> = bformat("foo{1}{0}-{1}", {bar, 6}), + <<"-'atom test'-">> = bformat("-{arg!r}-", [{arg, 'atom test'}]), + <<"2008-05-04">> = bformat("{0.0:0{1.0}}-{0.1:0{1.1}}-{0.2:0{1.2}}", + {{2008,5,4}, {4, 2, 2}}), + ok. + +test_std() -> + M = mochifmt_std:new(), + <<"01">> = bformat("{0}{1}", [0, 1], M), + ok. + +test_records() -> + M = mochifmt_records:new([{conversion, record_info(fields, conversion)}]), + R = #conversion{length=long, precision=hard, sign=peace}, + long = M:get_value("length", R), + hard = M:get_value("precision", R), + peace = M:get_value("sign", R), + <<"long hard">> = bformat("{length} {precision}", R, M), + <<"long hard">> = bformat("{0.length} {0.precision}", [R], M), + ok. diff --git a/deps/mochiweb-r88/src/mochifmt_records.erl b/deps/mochiweb-r88/src/mochifmt_records.erl new file mode 100644 index 0000000..94c7797 --- /dev/null +++ b/deps/mochiweb-r88/src/mochifmt_records.erl @@ -0,0 +1,30 @@ +%% @author Bob Ippolito +%% @copyright 2008 Mochi Media, Inc. + +%% @doc Formatter that understands records. +%% +%% Usage: +%% +%% 1> M = mochifmt_records:new([{rec, record_info(fields, rec)}]), +%% M:format("{0.bar}", [#rec{bar=foo}]). +%% foo + +-module(mochifmt_records, [Recs]). +-author('bob@mochimedia.com'). +-export([get_value/2]). + +get_value(Key, Rec) when is_tuple(Rec) and is_atom(element(1, Rec)) -> + try begin + Atom = list_to_existing_atom(Key), + {_, Fields} = proplists:lookup(element(1, Rec), Recs), + element(get_rec_index(Atom, Fields, 2), Rec) + end + catch error:_ -> mochifmt:get_value(Key, Rec) + end; +get_value(Key, Args) -> + mochifmt:get_value(Key, Args). + +get_rec_index(Atom, [Atom | _], Index) -> + Index; +get_rec_index(Atom, [_ | Rest], Index) -> + get_rec_index(Atom, Rest, 1 + Index). diff --git a/deps/mochiweb-r88/src/mochifmt_std.erl b/deps/mochiweb-r88/src/mochifmt_std.erl new file mode 100644 index 0000000..9442016 --- /dev/null +++ b/deps/mochiweb-r88/src/mochifmt_std.erl @@ -0,0 +1,23 @@ +%% @author Bob Ippolito +%% @copyright 2008 Mochi Media, Inc. + +%% @doc Template module for a mochifmt formatter. + +-module(mochifmt_std, []). +-author('bob@mochimedia.com'). +-export([format/2, get_value/2, format_field/2, get_field/2, convert_field/2]). + +format(Format, Args) -> + mochifmt:format(Format, Args, THIS). + +get_field(Key, Args) -> + mochifmt:get_field(Key, Args, THIS). + +convert_field(Key, Args) -> + mochifmt:convert_field(Key, Args). + +get_value(Key, Args) -> + mochifmt:get_value(Key, Args). + +format_field(Arg, Format) -> + mochifmt:format_field(Arg, Format, THIS). diff --git a/deps/mochiweb-r88/src/mochihex.erl b/deps/mochiweb-r88/src/mochihex.erl new file mode 100644 index 0000000..7fe6899 --- /dev/null +++ b/deps/mochiweb-r88/src/mochihex.erl @@ -0,0 +1,75 @@ +%% @author Bob Ippolito +%% @copyright 2006 Mochi Media, Inc. + +%% @doc Utilities for working with hexadecimal strings. + +-module(mochihex). +-author('bob@mochimedia.com'). + +-export([test/0, to_hex/1, to_bin/1, to_int/1, dehex/1, hexdigit/1]). + +%% @type iolist() = [char() | binary() | iolist()] +%% @type iodata() = iolist() | binary() + +%% @spec to_hex(integer | iolist()) -> string() +%% @doc Convert an iolist to a hexadecimal string. +to_hex(0) -> + "0"; +to_hex(I) when is_integer(I), I > 0 -> + to_hex_int(I, []); +to_hex(B) -> + to_hex(iolist_to_binary(B), []). + +%% @spec to_bin(string()) -> binary() +%% @doc Convert a hexadecimal string to a binary. +to_bin(L) -> + to_bin(L, []). + +%% @spec to_int(string()) -> integer() +%% @doc Convert a hexadecimal string to an integer. +to_int(L) -> + erlang:list_to_integer(L, 16). + +%% @spec dehex(char()) -> integer() +%% @doc Convert a hex digit to its integer value. +dehex(C) when C >= $0, C =< $9 -> + C - $0; +dehex(C) when C >= $a, C =< $f -> + C - $a + 10; +dehex(C) when C >= $A, C =< $F -> + C - $A + 10. + +%% @spec hexdigit(integer()) -> char() +%% @doc Convert an integer less than 16 to a hex digit. +hexdigit(C) when C >= 0, C =< 9 -> + C + $0; +hexdigit(C) when C =< 15 -> + C + $a - 10. + +%% @spec test() -> ok +%% @doc Test this module. +test() -> + "ff000ff1" = to_hex([255, 0, 15, 241]), + <<255, 0, 15, 241>> = to_bin("ff000ff1"), + 16#ff000ff1 = to_int("ff000ff1"), + "ff000ff1" = to_hex(16#ff000ff1), + ok. + + +%% Internal API + +to_hex(<<>>, Acc) -> + lists:reverse(Acc); +to_hex(<>, Acc) -> + to_hex(Rest, [hexdigit(C2), hexdigit(C1) | Acc]). + +to_hex_int(0, Acc) -> + Acc; +to_hex_int(I, Acc) -> + to_hex_int(I bsr 4, [hexdigit(I band 15) | Acc]). + +to_bin([], Acc) -> + iolist_to_binary(lists:reverse(Acc)); +to_bin([C1, C2 | Rest], Acc) -> + to_bin(Rest, [(dehex(C1) bsl 4) bor dehex(C2) | Acc]). + diff --git a/deps/mochiweb-r88/src/mochijson.erl b/deps/mochiweb-r88/src/mochijson.erl new file mode 100644 index 0000000..0ce5b5e --- /dev/null +++ b/deps/mochiweb-r88/src/mochijson.erl @@ -0,0 +1,529 @@ +%% @author Bob Ippolito +%% @copyright 2006 Mochi Media, Inc. + +%% @doc Yet another JSON (RFC 4627) library for Erlang. +-module(mochijson). +-author('bob@mochimedia.com'). +-export([encoder/1, encode/1]). +-export([decoder/1, decode/1]). +-export([binary_encoder/1, binary_encode/1]). +-export([binary_decoder/1, binary_decode/1]). +-export([test/0]). + +% This is a macro to placate syntax highlighters.. +-define(Q, $\"). +-define(ADV_COL(S, N), S#decoder{column=N+S#decoder.column}). +-define(INC_COL(S), S#decoder{column=1+S#decoder.column}). +-define(INC_LINE(S), S#decoder{column=1, line=1+S#decoder.line}). + +%% @type iolist() = [char() | binary() | iolist()] +%% @type iodata() = iolist() | binary() +%% @type json_string() = atom | string() | binary() +%% @type json_number() = integer() | float() +%% @type json_array() = {array, [json_term()]} +%% @type json_object() = {struct, [{json_string(), json_term()}]} +%% @type json_term() = json_string() | json_number() | json_array() | +%% json_object() +%% @type encoding() = utf8 | unicode +%% @type encoder_option() = {input_encoding, encoding()} | +%% {handler, function()} +%% @type decoder_option() = {input_encoding, encoding()} | +%% {object_hook, function()} +%% @type bjson_string() = binary() +%% @type bjson_number() = integer() | float() +%% @type bjson_array() = [bjson_term()] +%% @type bjson_object() = {struct, [{bjson_string(), bjson_term()}]} +%% @type bjson_term() = bjson_string() | bjson_number() | bjson_array() | +%% bjson_object() +%% @type binary_encoder_option() = {handler, function()} +%% @type binary_decoder_option() = {object_hook, function()} + +-record(encoder, {input_encoding=unicode, + handler=null}). + +-record(decoder, {input_encoding=utf8, + object_hook=null, + line=1, + column=1, + state=null}). + +%% @spec encoder([encoder_option()]) -> function() +%% @doc Create an encoder/1 with the given options. +encoder(Options) -> + State = parse_encoder_options(Options, #encoder{}), + fun (O) -> json_encode(O, State) end. + +%% @spec encode(json_term()) -> iolist() +%% @doc Encode the given as JSON to an iolist. +encode(Any) -> + json_encode(Any, #encoder{}). + +%% @spec decoder([decoder_option()]) -> function() +%% @doc Create a decoder/1 with the given options. +decoder(Options) -> + State = parse_decoder_options(Options, #decoder{}), + fun (O) -> json_decode(O, State) end. + +%% @spec decode(iolist()) -> json_term() +%% @doc Decode the given iolist to Erlang terms. +decode(S) -> + json_decode(S, #decoder{}). + +%% @spec binary_decoder([binary_decoder_option()]) -> function() +%% @doc Create a binary_decoder/1 with the given options. +binary_decoder(Options) -> + mochijson2:decoder(Options). + +%% @spec binary_encoder([binary_encoder_option()]) -> function() +%% @doc Create a binary_encoder/1 with the given options. +binary_encoder(Options) -> + mochijson2:encoder(Options). + +%% @spec binary_encode(bjson_term()) -> iolist() +%% @doc Encode the given as JSON to an iolist, using lists for arrays and +%% binaries for strings. +binary_encode(Any) -> + mochijson2:encode(Any). + +%% @spec binary_decode(iolist()) -> bjson_term() +%% @doc Decode the given iolist to Erlang terms, using lists for arrays and +%% binaries for strings. +binary_decode(S) -> + mochijson2:decode(S). + +test() -> + test_all(), + mochijson2:test(). + +%% Internal API + +parse_encoder_options([], State) -> + State; +parse_encoder_options([{input_encoding, Encoding} | Rest], State) -> + parse_encoder_options(Rest, State#encoder{input_encoding=Encoding}); +parse_encoder_options([{handler, Handler} | Rest], State) -> + parse_encoder_options(Rest, State#encoder{handler=Handler}). + +parse_decoder_options([], State) -> + State; +parse_decoder_options([{input_encoding, Encoding} | Rest], State) -> + parse_decoder_options(Rest, State#decoder{input_encoding=Encoding}); +parse_decoder_options([{object_hook, Hook} | Rest], State) -> + parse_decoder_options(Rest, State#decoder{object_hook=Hook}). + +json_encode(true, _State) -> + "true"; +json_encode(false, _State) -> + "false"; +json_encode(null, _State) -> + "null"; +json_encode(I, _State) when is_integer(I) -> + integer_to_list(I); +json_encode(F, _State) when is_float(F) -> + mochinum:digits(F); +json_encode(L, State) when is_list(L); is_binary(L); is_atom(L) -> + json_encode_string(L, State); +json_encode({array, Props}, State) when is_list(Props) -> + json_encode_array(Props, State); +json_encode({struct, Props}, State) when is_list(Props) -> + json_encode_proplist(Props, State); +json_encode(Bad, #encoder{handler=null}) -> + exit({json_encode, {bad_term, Bad}}); +json_encode(Bad, State=#encoder{handler=Handler}) -> + json_encode(Handler(Bad), State). + +json_encode_array([], _State) -> + "[]"; +json_encode_array(L, State) -> + F = fun (O, Acc) -> + [$,, json_encode(O, State) | Acc] + end, + [$, | Acc1] = lists:foldl(F, "[", L), + lists:reverse([$\] | Acc1]). + +json_encode_proplist([], _State) -> + "{}"; +json_encode_proplist(Props, State) -> + F = fun ({K, V}, Acc) -> + KS = case K of + K when is_atom(K) -> + json_encode_string_utf8(atom_to_list(K)); + K when is_integer(K) -> + json_encode_string(integer_to_list(K), State); + K when is_list(K); is_binary(K) -> + json_encode_string(K, State) + end, + VS = json_encode(V, State), + [$,, VS, $:, KS | Acc] + end, + [$, | Acc1] = lists:foldl(F, "{", Props), + lists:reverse([$\} | Acc1]). + +json_encode_string(A, _State) when is_atom(A) -> + json_encode_string_unicode(xmerl_ucs:from_utf8(atom_to_list(A))); +json_encode_string(B, _State) when is_binary(B) -> + json_encode_string_unicode(xmerl_ucs:from_utf8(B)); +json_encode_string(S, #encoder{input_encoding=utf8}) -> + json_encode_string_utf8(S); +json_encode_string(S, #encoder{input_encoding=unicode}) -> + json_encode_string_unicode(S). + +json_encode_string_utf8(S) -> + [?Q | json_encode_string_utf8_1(S)]. + +json_encode_string_utf8_1([C | Cs]) when C >= 0, C =< 16#7f -> + NewC = case C of + $\\ -> "\\\\"; + ?Q -> "\\\""; + _ when C >= $\s, C < 16#7f -> C; + $\t -> "\\t"; + $\n -> "\\n"; + $\r -> "\\r"; + $\f -> "\\f"; + $\b -> "\\b"; + _ when C >= 0, C =< 16#7f -> unihex(C); + _ -> exit({json_encode, {bad_char, C}}) + end, + [NewC | json_encode_string_utf8_1(Cs)]; +json_encode_string_utf8_1(All=[C | _]) when C >= 16#80, C =< 16#10FFFF -> + json_encode_string_unicode(xmerl_ucs:from_utf8(All)); +json_encode_string_utf8_1([]) -> + "\"". + +json_encode_string_unicode(S) -> + [?Q | json_encode_string_unicode_1(S)]. + +json_encode_string_unicode_1([C | Cs]) -> + NewC = case C of + $\\ -> "\\\\"; + ?Q -> "\\\""; + _ when C >= $\s, C < 16#7f -> C; + $\t -> "\\t"; + $\n -> "\\n"; + $\r -> "\\r"; + $\f -> "\\f"; + $\b -> "\\b"; + _ when C >= 0, C =< 16#10FFFF -> unihex(C); + _ -> exit({json_encode, {bad_char, C}}) + end, + [NewC | json_encode_string_unicode_1(Cs)]; +json_encode_string_unicode_1([]) -> + "\"". + +dehex(C) when C >= $0, C =< $9 -> + C - $0; +dehex(C) when C >= $a, C =< $f -> + C - $a + 10; +dehex(C) when C >= $A, C =< $F -> + C - $A + 10. + +hexdigit(C) when C >= 0, C =< 9 -> + C + $0; +hexdigit(C) when C =< 15 -> + C + $a - 10. + +unihex(C) when C < 16#10000 -> + <> = <>, + Digits = [hexdigit(D) || D <- [D3, D2, D1, D0]], + [$\\, $u | Digits]; +unihex(C) when C =< 16#10FFFF -> + N = C - 16#10000, + S1 = 16#d800 bor ((N bsr 10) band 16#3ff), + S2 = 16#dc00 bor (N band 16#3ff), + [unihex(S1), unihex(S2)]. + +json_decode(B, S) when is_binary(B) -> + json_decode(binary_to_list(B), S); +json_decode(L, S) -> + {Res, L1, S1} = decode1(L, S), + {eof, [], _} = tokenize(L1, S1#decoder{state=trim}), + Res. + +decode1(L, S=#decoder{state=null}) -> + case tokenize(L, S#decoder{state=any}) of + {{const, C}, L1, S1} -> + {C, L1, S1}; + {start_array, L1, S1} -> + decode_array(L1, S1#decoder{state=any}, []); + {start_object, L1, S1} -> + decode_object(L1, S1#decoder{state=key}, []) + end. + +make_object(V, #decoder{object_hook=null}) -> + V; +make_object(V, #decoder{object_hook=Hook}) -> + Hook(V). + +decode_object(L, S=#decoder{state=key}, Acc) -> + case tokenize(L, S) of + {end_object, Rest, S1} -> + V = make_object({struct, lists:reverse(Acc)}, S1), + {V, Rest, S1#decoder{state=null}}; + {{const, K}, Rest, S1} when is_list(K) -> + {colon, L2, S2} = tokenize(Rest, S1), + {V, L3, S3} = decode1(L2, S2#decoder{state=null}), + decode_object(L3, S3#decoder{state=comma}, [{K, V} | Acc]) + end; +decode_object(L, S=#decoder{state=comma}, Acc) -> + case tokenize(L, S) of + {end_object, Rest, S1} -> + V = make_object({struct, lists:reverse(Acc)}, S1), + {V, Rest, S1#decoder{state=null}}; + {comma, Rest, S1} -> + decode_object(Rest, S1#decoder{state=key}, Acc) + end. + +decode_array(L, S=#decoder{state=any}, Acc) -> + case tokenize(L, S) of + {end_array, Rest, S1} -> + {{array, lists:reverse(Acc)}, Rest, S1#decoder{state=null}}; + {start_array, Rest, S1} -> + {Array, Rest1, S2} = decode_array(Rest, S1#decoder{state=any}, []), + decode_array(Rest1, S2#decoder{state=comma}, [Array | Acc]); + {start_object, Rest, S1} -> + {Array, Rest1, S2} = decode_object(Rest, S1#decoder{state=key}, []), + decode_array(Rest1, S2#decoder{state=comma}, [Array | Acc]); + {{const, Const}, Rest, S1} -> + decode_array(Rest, S1#decoder{state=comma}, [Const | Acc]) + end; +decode_array(L, S=#decoder{state=comma}, Acc) -> + case tokenize(L, S) of + {end_array, Rest, S1} -> + {{array, lists:reverse(Acc)}, Rest, S1#decoder{state=null}}; + {comma, Rest, S1} -> + decode_array(Rest, S1#decoder{state=any}, Acc) + end. + +tokenize_string(IoList=[C | _], S=#decoder{input_encoding=utf8}, Acc) + when is_list(C); is_binary(C); C >= 16#7f -> + List = xmerl_ucs:from_utf8(iolist_to_binary(IoList)), + tokenize_string(List, S#decoder{input_encoding=unicode}, Acc); +tokenize_string("\"" ++ Rest, S, Acc) -> + {lists:reverse(Acc), Rest, ?INC_COL(S)}; +tokenize_string("\\\"" ++ Rest, S, Acc) -> + tokenize_string(Rest, ?ADV_COL(S, 2), [$\" | Acc]); +tokenize_string("\\\\" ++ Rest, S, Acc) -> + tokenize_string(Rest, ?ADV_COL(S, 2), [$\\ | Acc]); +tokenize_string("\\/" ++ Rest, S, Acc) -> + tokenize_string(Rest, ?ADV_COL(S, 2), [$/ | Acc]); +tokenize_string("\\b" ++ Rest, S, Acc) -> + tokenize_string(Rest, ?ADV_COL(S, 2), [$\b | Acc]); +tokenize_string("\\f" ++ Rest, S, Acc) -> + tokenize_string(Rest, ?ADV_COL(S, 2), [$\f | Acc]); +tokenize_string("\\n" ++ Rest, S, Acc) -> + tokenize_string(Rest, ?ADV_COL(S, 2), [$\n | Acc]); +tokenize_string("\\r" ++ Rest, S, Acc) -> + tokenize_string(Rest, ?ADV_COL(S, 2), [$\r | Acc]); +tokenize_string("\\t" ++ Rest, S, Acc) -> + tokenize_string(Rest, ?ADV_COL(S, 2), [$\t | Acc]); +tokenize_string([$\\, $u, C3, C2, C1, C0 | Rest], S, Acc) -> + % coalesce UTF-16 surrogate pair? + C = dehex(C0) bor + (dehex(C1) bsl 4) bor + (dehex(C2) bsl 8) bor + (dehex(C3) bsl 12), + tokenize_string(Rest, ?ADV_COL(S, 6), [C | Acc]); +tokenize_string([C | Rest], S, Acc) when C >= $\s; C < 16#10FFFF -> + tokenize_string(Rest, ?ADV_COL(S, 1), [C | Acc]). + +tokenize_number(IoList=[C | _], Mode, S=#decoder{input_encoding=utf8}, Acc) + when is_list(C); is_binary(C); C >= 16#7f -> + List = xmerl_ucs:from_utf8(iolist_to_binary(IoList)), + tokenize_number(List, Mode, S#decoder{input_encoding=unicode}, Acc); +tokenize_number([$- | Rest], sign, S, []) -> + tokenize_number(Rest, int, ?INC_COL(S), [$-]); +tokenize_number(Rest, sign, S, []) -> + tokenize_number(Rest, int, S, []); +tokenize_number([$0 | Rest], int, S, Acc) -> + tokenize_number(Rest, frac, ?INC_COL(S), [$0 | Acc]); +tokenize_number([C | Rest], int, S, Acc) when C >= $1, C =< $9 -> + tokenize_number(Rest, int1, ?INC_COL(S), [C | Acc]); +tokenize_number([C | Rest], int1, S, Acc) when C >= $0, C =< $9 -> + tokenize_number(Rest, int1, ?INC_COL(S), [C | Acc]); +tokenize_number(Rest, int1, S, Acc) -> + tokenize_number(Rest, frac, S, Acc); +tokenize_number([$., C | Rest], frac, S, Acc) when C >= $0, C =< $9 -> + tokenize_number(Rest, frac1, ?ADV_COL(S, 2), [C, $. | Acc]); +tokenize_number([E | Rest], frac, S, Acc) when E == $e; E == $E -> + tokenize_number(Rest, esign, ?INC_COL(S), [$e, $0, $. | Acc]); +tokenize_number(Rest, frac, S, Acc) -> + {{int, lists:reverse(Acc)}, Rest, S}; +tokenize_number([C | Rest], frac1, S, Acc) when C >= $0, C =< $9 -> + tokenize_number(Rest, frac1, ?INC_COL(S), [C | Acc]); +tokenize_number([E | Rest], frac1, S, Acc) when E == $e; E == $E -> + tokenize_number(Rest, esign, ?INC_COL(S), [$e | Acc]); +tokenize_number(Rest, frac1, S, Acc) -> + {{float, lists:reverse(Acc)}, Rest, S}; +tokenize_number([C | Rest], esign, S, Acc) when C == $-; C == $+ -> + tokenize_number(Rest, eint, ?INC_COL(S), [C | Acc]); +tokenize_number(Rest, esign, S, Acc) -> + tokenize_number(Rest, eint, S, Acc); +tokenize_number([C | Rest], eint, S, Acc) when C >= $0, C =< $9 -> + tokenize_number(Rest, eint1, ?INC_COL(S), [C | Acc]); +tokenize_number([C | Rest], eint1, S, Acc) when C >= $0, C =< $9 -> + tokenize_number(Rest, eint1, ?INC_COL(S), [C | Acc]); +tokenize_number(Rest, eint1, S, Acc) -> + {{float, lists:reverse(Acc)}, Rest, S}. + +tokenize([], S=#decoder{state=trim}) -> + {eof, [], S}; +tokenize([L | Rest], S) when is_list(L) -> + tokenize(L ++ Rest, S); +tokenize([B | Rest], S) when is_binary(B) -> + tokenize(xmerl_ucs:from_utf8(B) ++ Rest, S); +tokenize("\r\n" ++ Rest, S) -> + tokenize(Rest, ?INC_LINE(S)); +tokenize("\n" ++ Rest, S) -> + tokenize(Rest, ?INC_LINE(S)); +tokenize([C | Rest], S) when C == $\s; C == $\t -> + tokenize(Rest, ?INC_COL(S)); +tokenize("{" ++ Rest, S) -> + {start_object, Rest, ?INC_COL(S)}; +tokenize("}" ++ Rest, S) -> + {end_object, Rest, ?INC_COL(S)}; +tokenize("[" ++ Rest, S) -> + {start_array, Rest, ?INC_COL(S)}; +tokenize("]" ++ Rest, S) -> + {end_array, Rest, ?INC_COL(S)}; +tokenize("," ++ Rest, S) -> + {comma, Rest, ?INC_COL(S)}; +tokenize(":" ++ Rest, S) -> + {colon, Rest, ?INC_COL(S)}; +tokenize("null" ++ Rest, S) -> + {{const, null}, Rest, ?ADV_COL(S, 4)}; +tokenize("true" ++ Rest, S) -> + {{const, true}, Rest, ?ADV_COL(S, 4)}; +tokenize("false" ++ Rest, S) -> + {{const, false}, Rest, ?ADV_COL(S, 5)}; +tokenize("\"" ++ Rest, S) -> + {String, Rest1, S1} = tokenize_string(Rest, ?INC_COL(S), []), + {{const, String}, Rest1, S1}; +tokenize(L=[C | _], S) when C >= $0, C =< $9; C == $- -> + case tokenize_number(L, sign, S, []) of + {{int, Int}, Rest, S1} -> + {{const, list_to_integer(Int)}, Rest, S1}; + {{float, Float}, Rest, S1} -> + {{const, list_to_float(Float)}, Rest, S1} + end. + +%% testing constructs borrowed from the Yaws JSON implementation. + +%% Create an object from a list of Key/Value pairs. + +obj_new() -> + {struct, []}. + +is_obj({struct, Props}) -> + F = fun ({K, _}) when is_list(K) -> + true; + (_) -> + false + end, + lists:all(F, Props). + +obj_from_list(Props) -> + Obj = {struct, Props}, + case is_obj(Obj) of + true -> Obj; + false -> exit(json_bad_object) + end. + +%% Test for equivalence of Erlang terms. +%% Due to arbitrary order of construction, equivalent objects might +%% compare unequal as erlang terms, so we need to carefully recurse +%% through aggregates (tuples and objects). + +equiv({struct, Props1}, {struct, Props2}) -> + equiv_object(Props1, Props2); +equiv({array, L1}, {array, L2}) -> + equiv_list(L1, L2); +equiv(N1, N2) when is_number(N1), is_number(N2) -> N1 == N2; +equiv(S1, S2) when is_list(S1), is_list(S2) -> S1 == S2; +equiv(true, true) -> true; +equiv(false, false) -> true; +equiv(null, null) -> true. + +%% Object representation and traversal order is unknown. +%% Use the sledgehammer and sort property lists. + +equiv_object(Props1, Props2) -> + L1 = lists:keysort(1, Props1), + L2 = lists:keysort(1, Props2), + Pairs = lists:zip(L1, L2), + true = lists:all(fun({{K1, V1}, {K2, V2}}) -> + equiv(K1, K2) and equiv(V1, V2) + end, Pairs). + +%% Recursively compare tuple elements for equivalence. + +equiv_list([], []) -> + true; +equiv_list([V1 | L1], [V2 | L2]) -> + case equiv(V1, V2) of + true -> + equiv_list(L1, L2); + false -> + false + end. + +test_all() -> + test_one(e2j_test_vec(utf8), 1). + +test_one([], _N) -> + %% io:format("~p tests passed~n", [N-1]), + ok; +test_one([{E, J} | Rest], N) -> + %% io:format("[~p] ~p ~p~n", [N, E, J]), + true = equiv(E, decode(J)), + true = equiv(E, decode(encode(E))), + test_one(Rest, 1+N). + +e2j_test_vec(unicode) -> + [ + {"foo" ++ [500] ++ "bar", [$", $f, $o, $o, 500, $b, $a, $r, $"]} + ]; +e2j_test_vec(utf8) -> + [ + {1, "1"}, + {3.1416, "3.14160"}, % text representation may truncate, trail zeroes + {-1, "-1"}, + {-3.1416, "-3.14160"}, + {12.0e10, "1.20000e+11"}, + {1.234E+10, "1.23400e+10"}, + {-1.234E-10, "-1.23400e-10"}, + {10.0, "1.0e+01"}, + {123.456, "1.23456E+2"}, + {10.0, "1e1"}, + {"foo", "\"foo\""}, + {"foo" ++ [5] ++ "bar", "\"foo\\u0005bar\""}, + {"", "\"\""}, + {"\"", "\"\\\"\""}, + {"\n\n\n", "\"\\n\\n\\n\""}, + {"\\", "\"\\\\\""}, + {"\" \b\f\r\n\t\"", "\"\\\" \\b\\f\\r\\n\\t\\\"\""}, + {obj_new(), "{}"}, + {obj_from_list([{"foo", "bar"}]), "{\"foo\":\"bar\"}"}, + {obj_from_list([{"foo", "bar"}, {"baz", 123}]), + "{\"foo\":\"bar\",\"baz\":123}"}, + {{array, []}, "[]"}, + {{array, [{array, []}]}, "[[]]"}, + {{array, [1, "foo"]}, "[1,\"foo\"]"}, + + % json array in a json object + {obj_from_list([{"foo", {array, [123]}}]), + "{\"foo\":[123]}"}, + + % json object in a json object + {obj_from_list([{"foo", obj_from_list([{"bar", true}])}]), + "{\"foo\":{\"bar\":true}}"}, + + % fold evaluation order + {obj_from_list([{"foo", {array, []}}, + {"bar", obj_from_list([{"baz", true}])}, + {"alice", "bob"}]), + "{\"foo\":[],\"bar\":{\"baz\":true},\"alice\":\"bob\"}"}, + + % json object in a json array + {{array, [-123, "foo", obj_from_list([{"bar", {array, []}}]), null]}, + "[-123,\"foo\",{\"bar\":[]},null]"} + ]. diff --git a/deps/mochiweb-r88/src/mochijson2.erl b/deps/mochiweb-r88/src/mochijson2.erl new file mode 100644 index 0000000..97961c8 --- /dev/null +++ b/deps/mochiweb-r88/src/mochijson2.erl @@ -0,0 +1,597 @@ +%% @author Bob Ippolito +%% @copyright 2007 Mochi Media, Inc. + +%% @doc Yet another JSON (RFC 4627) library for Erlang. mochijson2 works +%% with binaries as strings, arrays as lists (without an {array, _}) +%% wrapper and it only knows how to decode UTF-8 (and ASCII). + +-module(mochijson2). +-author('bob@mochimedia.com'). +-export([encoder/1, encode/1]). +-export([decoder/1, decode/1]). +-export([test/0]). + +% This is a macro to placate syntax highlighters.. +-define(Q, $\"). +-define(ADV_COL(S, N), S#decoder{offset=N+S#decoder.offset, + column=N+S#decoder.column}). +-define(INC_COL(S), S#decoder{offset=1+S#decoder.offset, + column=1+S#decoder.column}). +-define(INC_LINE(S), S#decoder{offset=1+S#decoder.offset, + column=1, + line=1+S#decoder.line}). +-define(INC_CHAR(S, C), + case C of + $\n -> + S#decoder{column=1, + line=1+S#decoder.line, + offset=1+S#decoder.offset}; + _ -> + S#decoder{column=1+S#decoder.column, + offset=1+S#decoder.offset} + end). +-define(IS_WHITESPACE(C), + (C =:= $\s orelse C =:= $\t orelse C =:= $\r orelse C =:= $\n)). + +%% @type iolist() = [char() | binary() | iolist()] +%% @type iodata() = iolist() | binary() +%% @type json_string() = atom | binary() +%% @type json_number() = integer() | float() +%% @type json_array() = [json_term()] +%% @type json_object() = {struct, [{json_string(), json_term()}]} +%% @type json_term() = json_string() | json_number() | json_array() | +%% json_object() + +-record(encoder, {handler=null}). + +-record(decoder, {object_hook=null, + offset=0, + line=1, + column=1, + state=null}). + +%% @spec encoder([encoder_option()]) -> function() +%% @doc Create an encoder/1 with the given options. +encoder(Options) -> + State = parse_encoder_options(Options, #encoder{}), + fun (O) -> json_encode(O, State) end. + +%% @spec encode(json_term()) -> iolist() +%% @doc Encode the given as JSON to an iolist. +encode(Any) -> + json_encode(Any, #encoder{}). + +%% @spec decoder([decoder_option()]) -> function() +%% @doc Create a decoder/1 with the given options. +decoder(Options) -> + State = parse_decoder_options(Options, #decoder{}), + fun (O) -> json_decode(O, State) end. + +%% @spec decode(iolist()) -> json_term() +%% @doc Decode the given iolist to Erlang terms. +decode(S) -> + json_decode(S, #decoder{}). + +test() -> + test_all(). + +%% Internal API + +parse_encoder_options([], State) -> + State; +parse_encoder_options([{handler, Handler} | Rest], State) -> + parse_encoder_options(Rest, State#encoder{handler=Handler}). + +parse_decoder_options([], State) -> + State; +parse_decoder_options([{object_hook, Hook} | Rest], State) -> + parse_decoder_options(Rest, State#decoder{object_hook=Hook}). + +json_encode(true, _State) -> + <<"true">>; +json_encode(false, _State) -> + <<"false">>; +json_encode(null, _State) -> + <<"null">>; +json_encode(I, _State) when is_integer(I) andalso I >= -2147483648 andalso I =< 2147483647 -> + %% Anything outside of 32-bit integers should be encoded as a float + integer_to_list(I); +json_encode(I, _State) when is_integer(I) -> + mochinum:digits(float(I)); +json_encode(F, _State) when is_float(F) -> + mochinum:digits(F); +json_encode(S, State) when is_binary(S); is_atom(S) -> + json_encode_string(S, State); +json_encode(Array, State) when is_list(Array) -> + json_encode_array(Array, State); +json_encode({struct, Props}, State) when is_list(Props) -> + json_encode_proplist(Props, State); +json_encode(Bad, #encoder{handler=null}) -> + exit({json_encode, {bad_term, Bad}}); +json_encode(Bad, State=#encoder{handler=Handler}) -> + json_encode(Handler(Bad), State). + +json_encode_array([], _State) -> + <<"[]">>; +json_encode_array(L, State) -> + F = fun (O, Acc) -> + [$,, json_encode(O, State) | Acc] + end, + [$, | Acc1] = lists:foldl(F, "[", L), + lists:reverse([$\] | Acc1]). + +json_encode_proplist([], _State) -> + <<"{}">>; +json_encode_proplist(Props, State) -> + F = fun ({K, V}, Acc) -> + KS = json_encode_string(K, State), + VS = json_encode(V, State), + [$,, VS, $:, KS | Acc] + end, + [$, | Acc1] = lists:foldl(F, "{", Props), + lists:reverse([$\} | Acc1]). + +json_encode_string(A, _State) when is_atom(A) -> + L = atom_to_list(A), + case json_string_is_safe(L) of + true -> + [?Q, L, ?Q]; + false -> + json_encode_string_unicode(xmerl_ucs:from_utf8(L), [?Q]) + end; +json_encode_string(B, _State) when is_binary(B) -> + case json_bin_is_safe(B) of + true -> + [?Q, B, ?Q]; + false -> + json_encode_string_unicode(xmerl_ucs:from_utf8(B), [?Q]) + end; +json_encode_string(I, _State) when is_integer(I) -> + [?Q, integer_to_list(I), ?Q]; +json_encode_string(L, _State) when is_list(L) -> + case json_string_is_safe(L) of + true -> + [?Q, L, ?Q]; + false -> + json_encode_string_unicode(L, [?Q]) + end. + +json_string_is_safe([]) -> + true; +json_string_is_safe([C | Rest]) -> + case C of + ?Q -> + false; + $\\ -> + false; + $\b -> + false; + $\f -> + false; + $\n -> + false; + $\r -> + false; + $\t -> + false; + C when C >= 0, C < $\s; C >= 16#7f, C =< 16#10FFFF -> + false; + C when C < 16#7f -> + json_string_is_safe(Rest); + _ -> + false + end. + +json_bin_is_safe(<<>>) -> + true; +json_bin_is_safe(<>) -> + case C of + ?Q -> + false; + $\\ -> + false; + $\b -> + false; + $\f -> + false; + $\n -> + false; + $\r -> + false; + $\t -> + false; + C when C >= 0, C < $\s; C >= 16#7f, C =< 16#10FFFF -> + false; + C when C < 16#7f -> + json_bin_is_safe(Rest); + _ -> + false + end. + +json_encode_string_unicode([], Acc) -> + lists:reverse([$\" | Acc]); +json_encode_string_unicode([C | Cs], Acc) -> + Acc1 = case C of + ?Q -> + [?Q, $\\ | Acc]; + %% Escaping solidus is only useful when trying to protect + %% against "" injection attacks which are only + %% possible when JSON is inserted into a HTML document + %% in-line. mochijson2 does not protect you from this, so + %% if you do insert directly into HTML then you need to + %% uncomment the following case or escape the output of encode. + %% + %% $/ -> + %% [$/, $\\ | Acc]; + %% + $\\ -> + [$\\, $\\ | Acc]; + $\b -> + [$b, $\\ | Acc]; + $\f -> + [$f, $\\ | Acc]; + $\n -> + [$n, $\\ | Acc]; + $\r -> + [$r, $\\ | Acc]; + $\t -> + [$t, $\\ | Acc]; + C when C >= 0, C < $\s; C >= 16#7f, C =< 16#10FFFF -> + [unihex(C) | Acc]; + C when C < 16#7f -> + [C | Acc]; + _ -> + exit({json_encode, {bad_char, C}}) + end, + json_encode_string_unicode(Cs, Acc1). + +hexdigit(C) when C >= 0, C =< 9 -> + C + $0; +hexdigit(C) when C =< 15 -> + C + $a - 10. + +unihex(C) when C < 16#10000 -> + <> = <>, + Digits = [hexdigit(D) || D <- [D3, D2, D1, D0]], + [$\\, $u | Digits]; +unihex(C) when C =< 16#10FFFF -> + N = C - 16#10000, + S1 = 16#d800 bor ((N bsr 10) band 16#3ff), + S2 = 16#dc00 bor (N band 16#3ff), + [unihex(S1), unihex(S2)]. + +json_decode(L, S) when is_list(L) -> + json_decode(iolist_to_binary(L), S); +json_decode(B, S) -> + {Res, S1} = decode1(B, S), + {eof, _} = tokenize(B, S1#decoder{state=trim}), + Res. + +decode1(B, S=#decoder{state=null}) -> + case tokenize(B, S#decoder{state=any}) of + {{const, C}, S1} -> + {C, S1}; + {start_array, S1} -> + decode_array(B, S1); + {start_object, S1} -> + decode_object(B, S1) + end. + +make_object(V, #decoder{object_hook=null}) -> + V; +make_object(V, #decoder{object_hook=Hook}) -> + Hook(V). + +decode_object(B, S) -> + decode_object(B, S#decoder{state=key}, []). + +decode_object(B, S=#decoder{state=key}, Acc) -> + case tokenize(B, S) of + {end_object, S1} -> + V = make_object({struct, lists:reverse(Acc)}, S1), + {V, S1#decoder{state=null}}; + {{const, K}, S1} -> + {colon, S2} = tokenize(B, S1), + {V, S3} = decode1(B, S2#decoder{state=null}), + decode_object(B, S3#decoder{state=comma}, [{K, V} | Acc]) + end; +decode_object(B, S=#decoder{state=comma}, Acc) -> + case tokenize(B, S) of + {end_object, S1} -> + V = make_object({struct, lists:reverse(Acc)}, S1), + {V, S1#decoder{state=null}}; + {comma, S1} -> + decode_object(B, S1#decoder{state=key}, Acc) + end. + +decode_array(B, S) -> + decode_array(B, S#decoder{state=any}, []). + +decode_array(B, S=#decoder{state=any}, Acc) -> + case tokenize(B, S) of + {end_array, S1} -> + {lists:reverse(Acc), S1#decoder{state=null}}; + {start_array, S1} -> + {Array, S2} = decode_array(B, S1), + decode_array(B, S2#decoder{state=comma}, [Array | Acc]); + {start_object, S1} -> + {Array, S2} = decode_object(B, S1), + decode_array(B, S2#decoder{state=comma}, [Array | Acc]); + {{const, Const}, S1} -> + decode_array(B, S1#decoder{state=comma}, [Const | Acc]) + end; +decode_array(B, S=#decoder{state=comma}, Acc) -> + case tokenize(B, S) of + {end_array, S1} -> + {lists:reverse(Acc), S1#decoder{state=null}}; + {comma, S1} -> + decode_array(B, S1#decoder{state=any}, Acc) + end. + +tokenize_string(B, S=#decoder{offset=O}) -> + case tokenize_string_fast(B, O) of + {escape, O1} -> + Length = O1 - O, + S1 = ?ADV_COL(S, Length), + <<_:O/binary, Head:Length/binary, _/binary>> = B, + tokenize_string(B, S1, lists:reverse(binary_to_list(Head))); + O1 -> + Length = O1 - O, + <<_:O/binary, String:Length/binary, ?Q, _/binary>> = B, + {{const, String}, ?ADV_COL(S, Length + 1)} + end. + +tokenize_string_fast(B, O) -> + case B of + <<_:O/binary, ?Q, _/binary>> -> + O; + <<_:O/binary, C, _/binary>> when C =/= $\\ -> + tokenize_string_fast(B, 1 + O); + _ -> + {escape, O} + end. + +tokenize_string(B, S=#decoder{offset=O}, Acc) -> + case B of + <<_:O/binary, ?Q, _/binary>> -> + {{const, iolist_to_binary(lists:reverse(Acc))}, ?INC_COL(S)}; + <<_:O/binary, "\\\"", _/binary>> -> + tokenize_string(B, ?ADV_COL(S, 2), [$\" | Acc]); + <<_:O/binary, "\\\\", _/binary>> -> + tokenize_string(B, ?ADV_COL(S, 2), [$\\ | Acc]); + <<_:O/binary, "\\/", _/binary>> -> + tokenize_string(B, ?ADV_COL(S, 2), [$/ | Acc]); + <<_:O/binary, "\\b", _/binary>> -> + tokenize_string(B, ?ADV_COL(S, 2), [$\b | Acc]); + <<_:O/binary, "\\f", _/binary>> -> + tokenize_string(B, ?ADV_COL(S, 2), [$\f | Acc]); + <<_:O/binary, "\\n", _/binary>> -> + tokenize_string(B, ?ADV_COL(S, 2), [$\n | Acc]); + <<_:O/binary, "\\r", _/binary>> -> + tokenize_string(B, ?ADV_COL(S, 2), [$\r | Acc]); + <<_:O/binary, "\\t", _/binary>> -> + tokenize_string(B, ?ADV_COL(S, 2), [$\t | Acc]); + <<_:O/binary, "\\u", C3, C2, C1, C0, _/binary>> -> + %% coalesce UTF-16 surrogate pair? + C = erlang:list_to_integer([C3, C2, C1, C0], 16), + Acc1 = lists:reverse(xmerl_ucs:to_utf8(C), Acc), + tokenize_string(B, ?ADV_COL(S, 6), Acc1); + <<_:O/binary, C, _/binary>> -> + tokenize_string(B, ?INC_CHAR(S, C), [C | Acc]) + end. + +tokenize_number(B, S) -> + case tokenize_number(B, sign, S, []) of + {{int, Int}, S1} -> + {{const, list_to_integer(Int)}, S1}; + {{float, Float}, S1} -> + {{const, list_to_float(Float)}, S1} + end. + +tokenize_number(B, sign, S=#decoder{offset=O}, []) -> + case B of + <<_:O/binary, $-, _/binary>> -> + tokenize_number(B, int, ?INC_COL(S), [$-]); + _ -> + tokenize_number(B, int, S, []) + end; +tokenize_number(B, int, S=#decoder{offset=O}, Acc) -> + case B of + <<_:O/binary, $0, _/binary>> -> + tokenize_number(B, frac, ?INC_COL(S), [$0 | Acc]); + <<_:O/binary, C, _/binary>> when C >= $1 andalso C =< $9 -> + tokenize_number(B, int1, ?INC_COL(S), [C | Acc]) + end; +tokenize_number(B, int1, S=#decoder{offset=O}, Acc) -> + case B of + <<_:O/binary, C, _/binary>> when C >= $0 andalso C =< $9 -> + tokenize_number(B, int1, ?INC_COL(S), [C | Acc]); + _ -> + tokenize_number(B, frac, S, Acc) + end; +tokenize_number(B, frac, S=#decoder{offset=O}, Acc) -> + case B of + <<_:O/binary, $., C, _/binary>> when C >= $0, C =< $9 -> + tokenize_number(B, frac1, ?ADV_COL(S, 2), [C, $. | Acc]); + <<_:O/binary, E, _/binary>> when E =:= $e orelse E =:= $E -> + tokenize_number(B, esign, ?INC_COL(S), [$e, $0, $. | Acc]); + _ -> + {{int, lists:reverse(Acc)}, S} + end; +tokenize_number(B, frac1, S=#decoder{offset=O}, Acc) -> + case B of + <<_:O/binary, C, _/binary>> when C >= $0 andalso C =< $9 -> + tokenize_number(B, frac1, ?INC_COL(S), [C | Acc]); + <<_:O/binary, E, _/binary>> when E =:= $e orelse E =:= $E -> + tokenize_number(B, esign, ?INC_COL(S), [$e | Acc]); + _ -> + {{float, lists:reverse(Acc)}, S} + end; +tokenize_number(B, esign, S=#decoder{offset=O}, Acc) -> + case B of + <<_:O/binary, C, _/binary>> when C =:= $- orelse C=:= $+ -> + tokenize_number(B, eint, ?INC_COL(S), [C | Acc]); + _ -> + tokenize_number(B, eint, S, Acc) + end; +tokenize_number(B, eint, S=#decoder{offset=O}, Acc) -> + case B of + <<_:O/binary, C, _/binary>> when C >= $0 andalso C =< $9 -> + tokenize_number(B, eint1, ?INC_COL(S), [C | Acc]) + end; +tokenize_number(B, eint1, S=#decoder{offset=O}, Acc) -> + case B of + <<_:O/binary, C, _/binary>> when C >= $0 andalso C =< $9 -> + tokenize_number(B, eint1, ?INC_COL(S), [C | Acc]); + _ -> + {{float, lists:reverse(Acc)}, S} + end. + +tokenize(B, S=#decoder{offset=O}) -> + case B of + <<_:O/binary, C, _/binary>> when ?IS_WHITESPACE(C) -> + tokenize(B, ?INC_CHAR(S, C)); + <<_:O/binary, "{", _/binary>> -> + {start_object, ?INC_COL(S)}; + <<_:O/binary, "}", _/binary>> -> + {end_object, ?INC_COL(S)}; + <<_:O/binary, "[", _/binary>> -> + {start_array, ?INC_COL(S)}; + <<_:O/binary, "]", _/binary>> -> + {end_array, ?INC_COL(S)}; + <<_:O/binary, ",", _/binary>> -> + {comma, ?INC_COL(S)}; + <<_:O/binary, ":", _/binary>> -> + {colon, ?INC_COL(S)}; + <<_:O/binary, "null", _/binary>> -> + {{const, null}, ?ADV_COL(S, 4)}; + <<_:O/binary, "true", _/binary>> -> + {{const, true}, ?ADV_COL(S, 4)}; + <<_:O/binary, "false", _/binary>> -> + {{const, false}, ?ADV_COL(S, 5)}; + <<_:O/binary, "\"", _/binary>> -> + tokenize_string(B, ?INC_COL(S)); + <<_:O/binary, C, _/binary>> when (C >= $0 andalso C =< $9) + orelse C =:= $- -> + tokenize_number(B, S); + <<_:O/binary>> -> + trim = S#decoder.state, + {eof, S} + end. + +%% testing constructs borrowed from the Yaws JSON implementation. + +%% Create an object from a list of Key/Value pairs. + +obj_new() -> + {struct, []}. + +is_obj({struct, Props}) -> + F = fun ({K, _}) when is_binary(K) -> + true; + (_) -> + false + end, + lists:all(F, Props). + +obj_from_list(Props) -> + Obj = {struct, Props}, + case is_obj(Obj) of + true -> Obj; + false -> exit({json_bad_object, Obj}) + end. + +%% Test for equivalence of Erlang terms. +%% Due to arbitrary order of construction, equivalent objects might +%% compare unequal as erlang terms, so we need to carefully recurse +%% through aggregates (tuples and objects). + +equiv({struct, Props1}, {struct, Props2}) -> + equiv_object(Props1, Props2); +equiv(L1, L2) when is_list(L1), is_list(L2) -> + equiv_list(L1, L2); +equiv(N1, N2) when is_number(N1), is_number(N2) -> N1 == N2; +equiv(B1, B2) when is_binary(B1), is_binary(B2) -> B1 == B2; +equiv(true, true) -> true; +equiv(false, false) -> true; +equiv(null, null) -> true. + +%% Object representation and traversal order is unknown. +%% Use the sledgehammer and sort property lists. + +equiv_object(Props1, Props2) -> + L1 = lists:keysort(1, Props1), + L2 = lists:keysort(1, Props2), + Pairs = lists:zip(L1, L2), + true = lists:all(fun({{K1, V1}, {K2, V2}}) -> + equiv(K1, K2) and equiv(V1, V2) + end, Pairs). + +%% Recursively compare tuple elements for equivalence. + +equiv_list([], []) -> + true; +equiv_list([V1 | L1], [V2 | L2]) -> + case equiv(V1, V2) of + true -> + equiv_list(L1, L2); + false -> + false + end. + +test_all() -> + [1199344435545.0, 1] = decode(<<"[1199344435545.0,1]">>), + test_one(e2j_test_vec(utf8), 1). + +test_one([], _N) -> + %% io:format("~p tests passed~n", [N-1]), + ok; +test_one([{E, J} | Rest], N) -> + %% io:format("[~p] ~p ~p~n", [N, E, J]), + true = equiv(E, decode(J)), + true = equiv(E, decode(encode(E))), + test_one(Rest, 1+N). + +e2j_test_vec(utf8) -> + [ + {1, "1"}, + {3.1416, "3.14160"}, %% text representation may truncate, trail zeroes + {-1, "-1"}, + {-3.1416, "-3.14160"}, + {12.0e10, "1.20000e+11"}, + {1.234E+10, "1.23400e+10"}, + {-1.234E-10, "-1.23400e-10"}, + {10.0, "1.0e+01"}, + {123.456, "1.23456E+2"}, + {10.0, "1e1"}, + {<<"foo">>, "\"foo\""}, + {<<"foo", 5, "bar">>, "\"foo\\u0005bar\""}, + {<<"">>, "\"\""}, + {<<"\n\n\n">>, "\"\\n\\n\\n\""}, + {<<"\" \b\f\r\n\t\"">>, "\"\\\" \\b\\f\\r\\n\\t\\\"\""}, + {obj_new(), "{}"}, + {obj_from_list([{<<"foo">>, <<"bar">>}]), "{\"foo\":\"bar\"}"}, + {obj_from_list([{<<"foo">>, <<"bar">>}, {<<"baz">>, 123}]), + "{\"foo\":\"bar\",\"baz\":123}"}, + {[], "[]"}, + {[[]], "[[]]"}, + {[1, <<"foo">>], "[1,\"foo\"]"}, + + %% json array in a json object + {obj_from_list([{<<"foo">>, [123]}]), + "{\"foo\":[123]}"}, + + %% json object in a json object + {obj_from_list([{<<"foo">>, obj_from_list([{<<"bar">>, true}])}]), + "{\"foo\":{\"bar\":true}}"}, + + %% fold evaluation order + {obj_from_list([{<<"foo">>, []}, + {<<"bar">>, obj_from_list([{<<"baz">>, true}])}, + {<<"alice">>, <<"bob">>}]), + "{\"foo\":[],\"bar\":{\"baz\":true},\"alice\":\"bob\"}"}, + + %% json object in a json array + {[-123, <<"foo">>, obj_from_list([{<<"bar">>, []}]), null], + "[-123,\"foo\",{\"bar\":[]},null]"} + ]. diff --git a/deps/mochiweb-r88/src/mochinum.erl b/deps/mochiweb-r88/src/mochinum.erl new file mode 100644 index 0000000..4f88f9a --- /dev/null +++ b/deps/mochiweb-r88/src/mochinum.erl @@ -0,0 +1,289 @@ +%% @copyright 2007 Mochi Media, Inc. +%% @author Bob Ippolito + +%% @doc Useful numeric algorithms for floats that cover some deficiencies +%% in the math module. More interesting is digits/1, which implements +%% the algorithm from: +%% http://www.cs.indiana.edu/~burger/fp/index.html +%% See also "Printing Floating-Point Numbers Quickly and Accurately" +%% in Proceedings of the SIGPLAN '96 Conference on Programming Language +%% Design and Implementation. + +-module(mochinum). +-author("Bob Ippolito "). +-export([digits/1, frexp/1, int_pow/2, int_ceil/1, test/0]). + +%% IEEE 754 Float exponent bias +-define(FLOAT_BIAS, 1022). +-define(MIN_EXP, -1074). +-define(BIG_POW, 4503599627370496). + +%% External API + +%% @spec digits(number()) -> string() +%% @doc Returns a string that accurately represents the given integer or float +%% using a conservative amount of digits. Great for generating +%% human-readable output, or compact ASCII serializations for floats. +digits(N) when is_integer(N) -> + integer_to_list(N); +digits(0.0) -> + "0.0"; +digits(Float) -> + {Frac, Exp} = frexp(Float), + Exp1 = Exp - 53, + Frac1 = trunc(abs(Frac) * (1 bsl 53)), + [Place | Digits] = digits1(Float, Exp1, Frac1), + R = insert_decimal(Place, [$0 + D || D <- Digits]), + case Float < 0 of + true -> + [$- | R]; + _ -> + R + end. + +%% @spec frexp(F::float()) -> {Frac::float(), Exp::float()} +%% @doc Return the fractional and exponent part of an IEEE 754 double, +%% equivalent to the libc function of the same name. +%% F = Frac * pow(2, Exp). +frexp(F) -> + frexp1(unpack(F)). + +%% @spec int_pow(X::integer(), N::integer()) -> Y::integer() +%% @doc Moderately efficient way to exponentiate integers. +%% int_pow(10, 2) = 100. +int_pow(_X, 0) -> + 1; +int_pow(X, N) when N > 0 -> + int_pow(X, N, 1). + +%% @spec int_ceil(F::float()) -> integer() +%% @doc Return the ceiling of F as an integer. The ceiling is defined as +%% F when F == trunc(F); +%% trunc(F) when F < 0; +%% trunc(F) + 1 when F > 0. +int_ceil(X) -> + T = trunc(X), + case (X - T) of + Neg when Neg < 0 -> T; + Pos when Pos > 0 -> T + 1; + _ -> T + end. + + +%% Internal API + +int_pow(X, N, R) when N < 2 -> + R * X; +int_pow(X, N, R) -> + int_pow(X * X, N bsr 1, case N band 1 of 1 -> R * X; 0 -> R end). + +insert_decimal(0, S) -> + "0." ++ S; +insert_decimal(Place, S) when Place > 0 -> + L = length(S), + case Place - L of + 0 -> + S ++ ".0"; + N when N < 0 -> + {S0, S1} = lists:split(L + N, S), + S0 ++ "." ++ S1; + N when N < 6 -> + %% More places than digits + S ++ lists:duplicate(N, $0) ++ ".0"; + _ -> + insert_decimal_exp(Place, S) + end; +insert_decimal(Place, S) when Place > -6 -> + "0." ++ lists:duplicate(abs(Place), $0) ++ S; +insert_decimal(Place, S) -> + insert_decimal_exp(Place, S). + +insert_decimal_exp(Place, S) -> + [C | S0] = S, + S1 = case S0 of + [] -> + "0"; + _ -> + S0 + end, + Exp = case Place < 0 of + true -> + "e-"; + false -> + "e+" + end, + [C] ++ "." ++ S1 ++ Exp ++ integer_to_list(abs(Place - 1)). + + +digits1(Float, Exp, Frac) -> + Round = ((Frac band 1) =:= 0), + case Exp >= 0 of + true -> + BExp = 1 bsl Exp, + case (Frac /= ?BIG_POW) of + true -> + scale((Frac * BExp * 2), 2, BExp, BExp, + Round, Round, Float); + false -> + scale((Frac * BExp * 4), 4, (BExp * 2), BExp, + Round, Round, Float) + end; + false -> + case (Exp == ?MIN_EXP) orelse (Frac /= ?BIG_POW) of + true -> + scale((Frac * 2), 1 bsl (1 - Exp), 1, 1, + Round, Round, Float); + false -> + scale((Frac * 4), 1 bsl (2 - Exp), 2, 1, + Round, Round, Float) + end + end. + +scale(R, S, MPlus, MMinus, LowOk, HighOk, Float) -> + Est = int_ceil(math:log10(abs(Float)) - 1.0e-10), + %% Note that the scheme implementation uses a 326 element look-up table + %% for int_pow(10, N) where we do not. + case Est >= 0 of + true -> + fixup(R, S * int_pow(10, Est), MPlus, MMinus, Est, + LowOk, HighOk); + false -> + Scale = int_pow(10, -Est), + fixup(R * Scale, S, MPlus * Scale, MMinus * Scale, Est, + LowOk, HighOk) + end. + +fixup(R, S, MPlus, MMinus, K, LowOk, HighOk) -> + TooLow = case HighOk of + true -> + (R + MPlus) >= S; + false -> + (R + MPlus) > S + end, + case TooLow of + true -> + [(K + 1) | generate(R, S, MPlus, MMinus, LowOk, HighOk)]; + false -> + [K | generate(R * 10, S, MPlus * 10, MMinus * 10, LowOk, HighOk)] + end. + +generate(R0, S, MPlus, MMinus, LowOk, HighOk) -> + D = R0 div S, + R = R0 rem S, + TC1 = case LowOk of + true -> + R =< MMinus; + false -> + R < MMinus + end, + TC2 = case HighOk of + true -> + (R + MPlus) >= S; + false -> + (R + MPlus) > S + end, + case TC1 of + false -> + case TC2 of + false -> + [D | generate(R * 10, S, MPlus * 10, MMinus * 10, + LowOk, HighOk)]; + true -> + [D + 1] + end; + true -> + case TC2 of + false -> + [D]; + true -> + case R * 2 < S of + true -> + [D]; + false -> + [D + 1] + end + end + end. + +unpack(Float) -> + <> = <>, + {Sign, Exp, Frac}. + +frexp1({_Sign, 0, 0}) -> + {0.0, 0}; +frexp1({Sign, 0, Frac}) -> + Exp = log2floor(Frac), + <> = <>, + {Frac1, -(?FLOAT_BIAS) - 52 + Exp}; +frexp1({Sign, Exp, Frac}) -> + <> = <>, + {Frac1, Exp - ?FLOAT_BIAS}. + +log2floor(Int) -> + log2floor(Int, 0). + +log2floor(0, N) -> + N; +log2floor(Int, N) -> + log2floor(Int bsr 1, 1 + N). + + +test() -> + ok = test_frexp(), + ok = test_int_ceil(), + ok = test_int_pow(), + ok = test_digits(), + ok. + +test_int_ceil() -> + 1 = int_ceil(0.0001), + 0 = int_ceil(0.0), + 1 = int_ceil(0.99), + 1 = int_ceil(1.0), + -1 = int_ceil(-1.5), + -2 = int_ceil(-2.0), + ok. + +test_int_pow() -> + 1 = int_pow(1, 1), + 1 = int_pow(1, 0), + 1 = int_pow(10, 0), + 10 = int_pow(10, 1), + 100 = int_pow(10, 2), + 1000 = int_pow(10, 3), + ok. + +test_digits() -> + "0" = digits(0), + "0.0" = digits(0.0), + "1.0" = digits(1.0), + "-1.0" = digits(-1.0), + "0.1" = digits(0.1), + "0.01" = digits(0.01), + "0.001" = digits(0.001), + ok. + +test_frexp() -> + %% zero + {0.0, 0} = frexp(0.0), + %% one + {0.5, 1} = frexp(1.0), + %% negative one + {-0.5, 1} = frexp(-1.0), + %% small denormalized number + %% 4.94065645841246544177e-324 + <> = <<0,0,0,0,0,0,0,1>>, + {0.5, -1073} = frexp(SmallDenorm), + %% large denormalized number + %% 2.22507385850720088902e-308 + <> = <<0,15,255,255,255,255,255,255>>, + {0.99999999999999978, -1022} = frexp(BigDenorm), + %% small normalized number + %% 2.22507385850720138309e-308 + <> = <<0,16,0,0,0,0,0,0>>, + {0.5, -1021} = frexp(SmallNorm), + %% large normalized number + %% 1.79769313486231570815e+308 + <> = <<127,239,255,255,255,255,255,255>>, + {0.99999999999999989, 1024} = frexp(LargeNorm), + ok. diff --git a/deps/mochiweb-r88/src/mochiweb.app b/deps/mochiweb-r88/src/mochiweb.app new file mode 100644 index 0000000..cd8dbb2 --- /dev/null +++ b/deps/mochiweb-r88/src/mochiweb.app @@ -0,0 +1,32 @@ +{application, mochiweb, + [{description, "MochiMedia Web Server"}, + {vsn, "0.01"}, + {modules, [ + mochihex, + mochijson, + mochijson2, + mochinum, + mochiweb, + mochiweb_app, + mochiweb_charref, + mochiweb_cookies, + mochiweb_echo, + mochiweb_headers, + mochiweb_html, + mochiweb_http, + mochiweb_multipart, + mochiweb_request, + mochiweb_response, + mochiweb_skel, + mochiweb_socket_server, + mochiweb_sup, + mochiweb_util, + reloader, + mochifmt, + mochifmt_std, + mochifmt_records + ]}, + {registered, []}, + {mod, {mochiweb_app, []}}, + {env, []}, + {applications, [kernel, stdlib]}]}. diff --git a/deps/mochiweb-r88/src/mochiweb.erl b/deps/mochiweb-r88/src/mochiweb.erl new file mode 100644 index 0000000..0f4d52a --- /dev/null +++ b/deps/mochiweb-r88/src/mochiweb.erl @@ -0,0 +1,110 @@ +%% @author Bob Ippolito +%% @copyright 2007 Mochi Media, Inc. + +%% @doc Start and stop the MochiWeb server. + +-module(mochiweb). +-author('bob@mochimedia.com'). + +-export([start/0, stop/0]). +-export([new_request/1, new_response/1]). +-export([all_loaded/0, all_loaded/1, reload/0]). +-export([test/0]). + +%% @spec start() -> ok +%% @doc Start the MochiWeb server. +start() -> + ensure_started(crypto), + application:start(mochiweb). + +%% @spec stop() -> ok +%% @doc Stop the MochiWeb server. +stop() -> + Res = application:stop(mochiweb), + application:stop(crypto), + Res. + +%% @spec test() -> ok +%% @doc Run all of the tests for MochiWeb. +test() -> + mochiweb_util:test(), + mochiweb_headers:test(), + mochiweb_cookies:test(), + mochihex:test(), + mochinum:test(), + mochijson:test(), + mochiweb_charref:test(), + mochiweb_html:test(), + mochifmt:test(), + test_request(), + ok. + +reload() -> + [c:l(Module) || Module <- all_loaded()]. + +all_loaded() -> + all_loaded(filename:dirname(code:which(?MODULE))). + +all_loaded(Base) when is_atom(Base) -> + []; +all_loaded(Base) -> + FullBase = Base ++ "/", + F = fun ({_Module, Loaded}, Acc) when is_atom(Loaded) -> + Acc; + ({Module, Loaded}, Acc) -> + case lists:prefix(FullBase, Loaded) of + true -> + [Module | Acc]; + false -> + Acc + end + end, + lists:foldl(F, [], code:all_loaded()). + + +%% @spec new_request({Socket, Request, Headers}) -> MochiWebRequest +%% @doc Return a mochiweb_request data structure. +new_request({Socket, {Method, {abs_path, Uri}, Version}, Headers}) -> + mochiweb_request:new(Socket, + Method, + Uri, + Version, + mochiweb_headers:make(Headers)); +% this case probably doesn't "exist". +new_request({Socket, {Method, {absoluteURI, _Protocol, _Host, _Port, Uri}, + Version}, Headers}) -> + mochiweb_request:new(Socket, + Method, + Uri, + Version, + mochiweb_headers:make(Headers)); +%% Request-URI is "*" +%% From http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.2 +new_request({Socket, {Method, '*'=Uri, Version}, Headers}) -> + mochiweb_request:new(Socket, + Method, + Uri, + Version, + mochiweb_headers:make(Headers)). + +%% @spec new_response({Request, integer(), Headers}) -> MochiWebResponse +%% @doc Return a mochiweb_response data structure. +new_response({Request, Code, Headers}) -> + mochiweb_response:new(Request, + Code, + mochiweb_headers:make(Headers)). + +%% Internal API + +test_request() -> + R = mochiweb_request:new(z, z, "/foo/bar/baz%20wibble+quux?qs=2", z, []), + "/foo/bar/baz wibble quux" = R:get(path), + ok. + +ensure_started(App) -> + case application:start(App) of + ok -> + ok; + {error, {already_started, App}} -> + ok + end. diff --git a/deps/mochiweb-r88/src/mochiweb_app.erl b/deps/mochiweb-r88/src/mochiweb_app.erl new file mode 100644 index 0000000..2b437f6 --- /dev/null +++ b/deps/mochiweb-r88/src/mochiweb_app.erl @@ -0,0 +1,20 @@ +%% @author Bob Ippolito +%% @copyright 2007 Mochi Media, Inc. + +%% @doc Callbacks for the mochiweb application. + +-module(mochiweb_app). +-author('bob@mochimedia.com'). + +-behaviour(application). +-export([start/2,stop/1]). + +%% @spec start(_Type, _StartArgs) -> ServerRet +%% @doc application start callback for mochiweb. +start(_Type, _StartArgs) -> + mochiweb_sup:start_link(). + +%% @spec stop(_State) -> ServerRet +%% @doc application stop callback for mochiweb. +stop(_State) -> + ok. diff --git a/deps/mochiweb-r88/src/mochiweb_charref.erl b/deps/mochiweb-r88/src/mochiweb_charref.erl new file mode 100644 index 0000000..59fd4a4 --- /dev/null +++ b/deps/mochiweb-r88/src/mochiweb_charref.erl @@ -0,0 +1,295 @@ +%% @author Bob Ippolito +%% @copyright 2007 Mochi Media, Inc. + +%% @doc Converts HTML 4 charrefs and entities to codepoints. +-module(mochiweb_charref). +-export([charref/1, test/0]). + +%% External API. + +%% @spec charref(S) -> integer() | undefined +%% @doc Convert a decimal charref, hex charref, or html entity to a unicode +%% codepoint, or return undefined on failure. +%% The input should not include an ampersand or semicolon. +%% charref("#38") = 38, charref("#x26") = 38, charref("amp") = 38. +charref(B) when is_binary(B) -> + charref(binary_to_list(B)); +charref([$#, C | L]) when C =:= $x orelse C =:= $X -> + try erlang:list_to_integer(L, 16) + catch + error:badarg -> undefined + end; +charref([$# | L]) -> + try list_to_integer(L) + catch + error:badarg -> undefined + end; +charref(L) -> + entity(L). + +%% @spec test() -> ok +%% @doc Run tests for mochiweb_charref. +test() -> + 1234 = charref("#1234"), + 255 = charref("#xfF"), + 255 = charref("#XFf"), + 38 = charref("amp"), + undefined = charref("not_an_entity"), + ok. + +%% Internal API. + +entity("nbsp") -> 160; +entity("iexcl") -> 161; +entity("cent") -> 162; +entity("pound") -> 163; +entity("curren") -> 164; +entity("yen") -> 165; +entity("brvbar") -> 166; +entity("sect") -> 167; +entity("uml") -> 168; +entity("copy") -> 169; +entity("ordf") -> 170; +entity("laquo") -> 171; +entity("not") -> 172; +entity("shy") -> 173; +entity("reg") -> 174; +entity("macr") -> 175; +entity("deg") -> 176; +entity("plusmn") -> 177; +entity("sup2") -> 178; +entity("sup3") -> 179; +entity("acute") -> 180; +entity("micro") -> 181; +entity("para") -> 182; +entity("middot") -> 183; +entity("cedil") -> 184; +entity("sup1") -> 185; +entity("ordm") -> 186; +entity("raquo") -> 187; +entity("frac14") -> 188; +entity("frac12") -> 189; +entity("frac34") -> 190; +entity("iquest") -> 191; +entity("Agrave") -> 192; +entity("Aacute") -> 193; +entity("Acirc") -> 194; +entity("Atilde") -> 195; +entity("Auml") -> 196; +entity("Aring") -> 197; +entity("AElig") -> 198; +entity("Ccedil") -> 199; +entity("Egrave") -> 200; +entity("Eacute") -> 201; +entity("Ecirc") -> 202; +entity("Euml") -> 203; +entity("Igrave") -> 204; +entity("Iacute") -> 205; +entity("Icirc") -> 206; +entity("Iuml") -> 207; +entity("ETH") -> 208; +entity("Ntilde") -> 209; +entity("Ograve") -> 210; +entity("Oacute") -> 211; +entity("Ocirc") -> 212; +entity("Otilde") -> 213; +entity("Ouml") -> 214; +entity("times") -> 215; +entity("Oslash") -> 216; +entity("Ugrave") -> 217; +entity("Uacute") -> 218; +entity("Ucirc") -> 219; +entity("Uuml") -> 220; +entity("Yacute") -> 221; +entity("THORN") -> 222; +entity("szlig") -> 223; +entity("agrave") -> 224; +entity("aacute") -> 225; +entity("acirc") -> 226; +entity("atilde") -> 227; +entity("auml") -> 228; +entity("aring") -> 229; +entity("aelig") -> 230; +entity("ccedil") -> 231; +entity("egrave") -> 232; +entity("eacute") -> 233; +entity("ecirc") -> 234; +entity("euml") -> 235; +entity("igrave") -> 236; +entity("iacute") -> 237; +entity("icirc") -> 238; +entity("iuml") -> 239; +entity("eth") -> 240; +entity("ntilde") -> 241; +entity("ograve") -> 242; +entity("oacute") -> 243; +entity("ocirc") -> 244; +entity("otilde") -> 245; +entity("ouml") -> 246; +entity("divide") -> 247; +entity("oslash") -> 248; +entity("ugrave") -> 249; +entity("uacute") -> 250; +entity("ucirc") -> 251; +entity("uuml") -> 252; +entity("yacute") -> 253; +entity("thorn") -> 254; +entity("yuml") -> 255; +entity("fnof") -> 402; +entity("Alpha") -> 913; +entity("Beta") -> 914; +entity("Gamma") -> 915; +entity("Delta") -> 916; +entity("Epsilon") -> 917; +entity("Zeta") -> 918; +entity("Eta") -> 919; +entity("Theta") -> 920; +entity("Iota") -> 921; +entity("Kappa") -> 922; +entity("Lambda") -> 923; +entity("Mu") -> 924; +entity("Nu") -> 925; +entity("Xi") -> 926; +entity("Omicron") -> 927; +entity("Pi") -> 928; +entity("Rho") -> 929; +entity("Sigma") -> 931; +entity("Tau") -> 932; +entity("Upsilon") -> 933; +entity("Phi") -> 934; +entity("Chi") -> 935; +entity("Psi") -> 936; +entity("Omega") -> 937; +entity("alpha") -> 945; +entity("beta") -> 946; +entity("gamma") -> 947; +entity("delta") -> 948; +entity("epsilon") -> 949; +entity("zeta") -> 950; +entity("eta") -> 951; +entity("theta") -> 952; +entity("iota") -> 953; +entity("kappa") -> 954; +entity("lambda") -> 955; +entity("mu") -> 956; +entity("nu") -> 957; +entity("xi") -> 958; +entity("omicron") -> 959; +entity("pi") -> 960; +entity("rho") -> 961; +entity("sigmaf") -> 962; +entity("sigma") -> 963; +entity("tau") -> 964; +entity("upsilon") -> 965; +entity("phi") -> 966; +entity("chi") -> 967; +entity("psi") -> 968; +entity("omega") -> 969; +entity("thetasym") -> 977; +entity("upsih") -> 978; +entity("piv") -> 982; +entity("bull") -> 8226; +entity("hellip") -> 8230; +entity("prime") -> 8242; +entity("Prime") -> 8243; +entity("oline") -> 8254; +entity("frasl") -> 8260; +entity("weierp") -> 8472; +entity("image") -> 8465; +entity("real") -> 8476; +entity("trade") -> 8482; +entity("alefsym") -> 8501; +entity("larr") -> 8592; +entity("uarr") -> 8593; +entity("rarr") -> 8594; +entity("darr") -> 8595; +entity("harr") -> 8596; +entity("crarr") -> 8629; +entity("lArr") -> 8656; +entity("uArr") -> 8657; +entity("rArr") -> 8658; +entity("dArr") -> 8659; +entity("hArr") -> 8660; +entity("forall") -> 8704; +entity("part") -> 8706; +entity("exist") -> 8707; +entity("empty") -> 8709; +entity("nabla") -> 8711; +entity("isin") -> 8712; +entity("notin") -> 8713; +entity("ni") -> 8715; +entity("prod") -> 8719; +entity("sum") -> 8721; +entity("minus") -> 8722; +entity("lowast") -> 8727; +entity("radic") -> 8730; +entity("prop") -> 8733; +entity("infin") -> 8734; +entity("ang") -> 8736; +entity("and") -> 8743; +entity("or") -> 8744; +entity("cap") -> 8745; +entity("cup") -> 8746; +entity("int") -> 8747; +entity("there4") -> 8756; +entity("sim") -> 8764; +entity("cong") -> 8773; +entity("asymp") -> 8776; +entity("ne") -> 8800; +entity("equiv") -> 8801; +entity("le") -> 8804; +entity("ge") -> 8805; +entity("sub") -> 8834; +entity("sup") -> 8835; +entity("nsub") -> 8836; +entity("sube") -> 8838; +entity("supe") -> 8839; +entity("oplus") -> 8853; +entity("otimes") -> 8855; +entity("perp") -> 8869; +entity("sdot") -> 8901; +entity("lceil") -> 8968; +entity("rceil") -> 8969; +entity("lfloor") -> 8970; +entity("rfloor") -> 8971; +entity("lang") -> 9001; +entity("rang") -> 9002; +entity("loz") -> 9674; +entity("spades") -> 9824; +entity("clubs") -> 9827; +entity("hearts") -> 9829; +entity("diams") -> 9830; +entity("quot") -> 34; +entity("amp") -> 38; +entity("lt") -> 60; +entity("gt") -> 62; +entity("OElig") -> 338; +entity("oelig") -> 339; +entity("Scaron") -> 352; +entity("scaron") -> 353; +entity("Yuml") -> 376; +entity("circ") -> 710; +entity("tilde") -> 732; +entity("ensp") -> 8194; +entity("emsp") -> 8195; +entity("thinsp") -> 8201; +entity("zwnj") -> 8204; +entity("zwj") -> 8205; +entity("lrm") -> 8206; +entity("rlm") -> 8207; +entity("ndash") -> 8211; +entity("mdash") -> 8212; +entity("lsquo") -> 8216; +entity("rsquo") -> 8217; +entity("sbquo") -> 8218; +entity("ldquo") -> 8220; +entity("rdquo") -> 8221; +entity("bdquo") -> 8222; +entity("dagger") -> 8224; +entity("Dagger") -> 8225; +entity("permil") -> 8240; +entity("lsaquo") -> 8249; +entity("rsaquo") -> 8250; +entity("euro") -> 8364; +entity(_) -> undefined. + diff --git a/deps/mochiweb-r88/src/mochiweb_cookies.erl b/deps/mochiweb-r88/src/mochiweb_cookies.erl new file mode 100644 index 0000000..acea12a --- /dev/null +++ b/deps/mochiweb-r88/src/mochiweb_cookies.erl @@ -0,0 +1,250 @@ +%% @author Emad El-Haraty +%% @copyright 2007 Mochi Media, Inc. + +%% @doc HTTP Cookie parsing and generating (RFC 2109, RFC 2965). + +-module(mochiweb_cookies). +-export([parse_cookie/1, cookie/3, cookie/2, test/0]). + +-define(QUOTE, $\"). + +-define(IS_WHITESPACE(C), + (C =:= $\s orelse C =:= $\t orelse C =:= $\r orelse C =:= $\n)). + +%% RFC 2616 separators (called tspecials in RFC 2068) +-define(IS_SEPARATOR(C), + (C < 32 orelse + C =:= $\s orelse C =:= $\t orelse + C =:= $( orelse C =:= $) orelse C =:= $< orelse C =:= $> orelse + C =:= $@ orelse C =:= $, orelse C =:= $; orelse C =:= $: orelse + C =:= $\\ orelse C =:= $\" orelse C =:= $/ orelse + C =:= $[ orelse C =:= $] orelse C =:= $? orelse C =:= $= orelse + C =:= ${ orelse C =:= $})). + +%% @type proplist() = [{Key::string(), Value::string()}]. +%% @type header() = {Name::string(), Value::string()}. + +%% @spec cookie(Key::string(), Value::string()) -> header() +%% @doc Short-hand for cookie(Key, Value, []). +cookie(Key, Value) -> + cookie(Key, Value, []). + +%% @spec cookie(Key::string(), Value::string(), Options::[Option]) -> header() +%% where Option = {max_age, integer()} | {local_time, {date(), time()}} +%% | {domain, string()} | {path, string()} +%% | {secure, true | false} +%% +%% @doc Generate a Set-Cookie header field tuple. +cookie(Key, Value, Options) -> + Cookie = [any_to_list(Key), "=", quote(Value), "; Version=1"], + %% Set-Cookie: + %% Comment, Domain, Max-Age, Path, Secure, Version + %% Set-Cookie2: + %% Comment, CommentURL, Discard, Domain, Max-Age, Path, Port, Secure, + %% Version + ExpiresPart = + case proplists:get_value(max_age, Options) of + undefined -> + ""; + RawAge -> + When = case proplists:get_value(local_time, Options) of + undefined -> + calendar:local_time(); + LocalTime -> + LocalTime + end, + Age = case RawAge < 0 of + true -> + 0; + false -> + RawAge + end, + ["; Expires=", age_to_cookie_date(Age, When), + "; Max-Age=", quote(Age)] + end, + SecurePart = + case proplists:get_value(secure, Options) of + true -> + "; Secure"; + _ -> + "" + end, + DomainPart = + case proplists:get_value(domain, Options) of + undefined -> + ""; + Domain -> + ["; Domain=", quote(Domain)] + end, + PathPart = + case proplists:get_value(path, Options) of + undefined -> + ""; + Path -> + ["; Path=", quote(Path)] + end, + CookieParts = [Cookie, ExpiresPart, SecurePart, DomainPart, PathPart], + {"Set-Cookie", lists:flatten(CookieParts)}. + + +%% Every major browser incorrectly handles quoted strings in a +%% different and (worse) incompatible manner. Instead of wasting time +%% writing redundant code for each browser, we restrict cookies to +%% only contain characters that browsers handle compatibly. +%% +%% By replacing the definition of quote with this, we generate +%% RFC-compliant cookies: +%% +%% quote(V) -> +%% Fun = fun(?QUOTE, Acc) -> [$\\, ?QUOTE | Acc]; +%% (Ch, Acc) -> [Ch | Acc] +%% end, +%% [?QUOTE | lists:foldr(Fun, [?QUOTE], V)]. + +%% Convert to a string and raise an error if quoting is required. +quote(V0) -> + V = any_to_list(V0), + lists:all(fun(Ch) -> Ch =:= $/ orelse not ?IS_SEPARATOR(Ch) end, V) + orelse erlang:error({cookie_quoting_required, V}), + V. + +add_seconds(Secs, LocalTime) -> + Greg = calendar:datetime_to_gregorian_seconds(LocalTime), + calendar:gregorian_seconds_to_datetime(Greg + Secs). + +age_to_cookie_date(Age, LocalTime) -> + httpd_util:rfc1123_date(add_seconds(Age, LocalTime)). + +%% @spec parse_cookie(string()) -> [{K::string(), V::string()}] +%% @doc Parse the contents of a Cookie header field, ignoring cookie +%% attributes, and return a simple property list. +parse_cookie("") -> + []; +parse_cookie(Cookie) -> + parse_cookie(Cookie, []). + +%% @spec test() -> ok +%% @doc Run tests for mochiweb_cookies. +test() -> + parse_cookie_test(), + cookie_test(), + ok. + +%% Internal API + +parse_cookie([], Acc) -> + lists:reverse(Acc); +parse_cookie(String, Acc) -> + {{Token, Value}, Rest} = read_pair(String), + Acc1 = case Token of + "" -> + Acc; + "$" ++ _ -> + Acc; + _ -> + [{Token, Value} | Acc] + end, + parse_cookie(Rest, Acc1). + +read_pair(String) -> + {Token, Rest} = read_token(skip_whitespace(String)), + {Value, Rest1} = read_value(skip_whitespace(Rest)), + {{Token, Value}, skip_past_separator(Rest1)}. + +read_value([$= | Value]) -> + Value1 = skip_whitespace(Value), + case Value1 of + [?QUOTE | _] -> + read_quoted(Value1); + _ -> + read_token(Value1) + end; +read_value(String) -> + {"", String}. + +read_quoted([?QUOTE | String]) -> + read_quoted(String, []). + +read_quoted([], Acc) -> + {lists:reverse(Acc), []}; +read_quoted([?QUOTE | Rest], Acc) -> + {lists:reverse(Acc), Rest}; +read_quoted([$\\, Any | Rest], Acc) -> + read_quoted(Rest, [Any | Acc]); +read_quoted([C | Rest], Acc) -> + read_quoted(Rest, [C | Acc]). + +skip_whitespace(String) -> + F = fun (C) -> ?IS_WHITESPACE(C) end, + lists:dropwhile(F, String). + +read_token(String) -> + F = fun (C) -> not ?IS_SEPARATOR(C) end, + lists:splitwith(F, String). + +skip_past_separator([]) -> + []; +skip_past_separator([$; | Rest]) -> + Rest; +skip_past_separator([$, | Rest]) -> + Rest; +skip_past_separator([_ | Rest]) -> + skip_past_separator(Rest). + +parse_cookie_test() -> + %% RFC example + C1 = "$Version=\"1\"; Customer=\"WILE_E_COYOTE\"; $Path=\"/acme\"; + Part_Number=\"Rocket_Launcher_0001\"; $Path=\"/acme\"; + Shipping=\"FedEx\"; $Path=\"/acme\"", + [ + {"Customer","WILE_E_COYOTE"}, + {"Part_Number","Rocket_Launcher_0001"}, + {"Shipping","FedEx"} + ] = parse_cookie(C1), + %% Potential edge cases + [{"foo", "x"}] = parse_cookie("foo=\"\\x\""), + [] = parse_cookie("="), + [{"foo", ""}, {"bar", ""}] = parse_cookie(" foo ; bar "), + [{"foo", ""}, {"bar", ""}] = parse_cookie("foo=;bar="), + [{"foo", "\";"}, {"bar", ""}] = parse_cookie("foo = \"\\\";\";bar "), + [{"foo", "\";bar"}] = parse_cookie("foo=\"\\\";bar"). + +any_to_list(V) when is_list(V) -> + V; +any_to_list(V) when is_atom(V) -> + atom_to_list(V); +any_to_list(V) when is_binary(V) -> + binary_to_list(V); +any_to_list(V) when is_integer(V) -> + integer_to_list(V). + + +cookie_test() -> + C1 = {"Set-Cookie", + "Customer=WILE_E_COYOTE; " + "Version=1; " + "Path=/acme"}, + C1 = cookie("Customer", "WILE_E_COYOTE", [{path, "/acme"}]), + C1 = cookie("Customer", "WILE_E_COYOTE", + [{path, "/acme"}, {badoption, "negatory"}]), + C1 = cookie('Customer', 'WILE_E_COYOTE', [{path, '/acme'}]), + C1 = cookie(<<"Customer">>, <<"WILE_E_COYOTE">>, [{path, <<"/acme">>}]), + + {"Set-Cookie","=NoKey; Version=1"} = cookie("", "NoKey", []), + + LocalTime = calendar:universal_time_to_local_time({{2007, 5, 15}, {13, 45, 33}}), + C2 = {"Set-Cookie", + "Customer=WILE_E_COYOTE; " + "Version=1; " + "Expires=Tue, 15 May 2007 13:45:33 GMT; " + "Max-Age=0"}, + C2 = cookie("Customer", "WILE_E_COYOTE", + [{max_age, -111}, {local_time, LocalTime}]), + C3 = {"Set-Cookie", + "Customer=WILE_E_COYOTE; " + "Version=1; " + "Expires=Wed, 16 May 2007 13:45:50 GMT; " + "Max-Age=86417"}, + C3 = cookie("Customer", "WILE_E_COYOTE", + [{max_age, 86417}, {local_time, LocalTime}]), + ok. diff --git a/deps/mochiweb-r88/src/mochiweb_echo.erl b/deps/mochiweb-r88/src/mochiweb_echo.erl new file mode 100644 index 0000000..f0c455a --- /dev/null +++ b/deps/mochiweb-r88/src/mochiweb_echo.erl @@ -0,0 +1,31 @@ +%% @author Bob Ippolito +%% @copyright 2007 Mochi Media, Inc. + +%% @doc Simple and stupid echo server to demo mochiweb_socket_server. + +-module(mochiweb_echo). +-author('bob@mochimedia.com'). +-export([start/0, stop/0, loop/1]). + +stop() -> + mochiweb_socket_server:stop(?MODULE). + +start() -> + mochiweb_socket_server:start([{name, ?MODULE}, + {port, 6789}, + {ip, "127.0.0.1"}, + {max, 1}, + {loop, {?MODULE, loop}}]). + +loop(Socket) -> + case gen_tcp:recv(Socket, 0, 30000) of + {ok, Data} -> + case gen_tcp:send(Socket, Data) of + ok -> + loop(Socket); + _ -> + exit(normal) + end; + _Other -> + exit(normal) + end. diff --git a/deps/mochiweb-r88/src/mochiweb_headers.erl b/deps/mochiweb-r88/src/mochiweb_headers.erl new file mode 100644 index 0000000..df67c7d --- /dev/null +++ b/deps/mochiweb-r88/src/mochiweb_headers.erl @@ -0,0 +1,178 @@ +%% @author Bob Ippolito +%% @copyright 2007 Mochi Media, Inc. + +%% @doc Case preserving (but case insensitive) HTTP Header dictionary. + +-module(mochiweb_headers). +-author('bob@mochimedia.com'). +-export([empty/0, from_list/1, insert/3, enter/3, get_value/2, lookup/2]). +-export([get_primary_value/2]). +-export([default/3, enter_from_list/2, default_from_list/2]). +-export([to_list/1, make/1]). +-export([test/0]). + +%% @type headers(). +%% @type key() = atom() | binary() | string(). +%% @type value() = atom() | binary() | string() | integer(). + +%% @spec test() -> ok +%% @doc Run tests for this module. +test() -> + H = ?MODULE:make([{hdr, foo}, {"Hdr", "bar"}, {'Hdr', 2}]), + [{hdr, "foo, bar, 2"}] = ?MODULE:to_list(H), + H1 = ?MODULE:insert(taco, grande, H), + [{hdr, "foo, bar, 2"}, {taco, "grande"}] = ?MODULE:to_list(H1), + H2 = ?MODULE:make([{"Set-Cookie", "foo"}]), + [{"Set-Cookie", "foo"}] = ?MODULE:to_list(H2), + H3 = ?MODULE:insert("Set-Cookie", "bar", H2), + [{"Set-Cookie", "foo"}, {"Set-Cookie", "bar"}] = ?MODULE:to_list(H3), + "foo, bar" = ?MODULE:get_value("set-cookie", H3), + {value, {"Set-Cookie", "foo, bar"}} = ?MODULE:lookup("set-cookie", H3), + undefined = ?MODULE:get_value("shibby", H3), + none = ?MODULE:lookup("shibby", H3), + H4 = ?MODULE:insert("content-type", + "application/x-www-form-urlencoded; charset=utf8", + H3), + "application/x-www-form-urlencoded" = ?MODULE:get_primary_value( + "content-type", H4), + ok. + +%% @spec empty() -> headers() +%% @doc Create an empty headers structure. +empty() -> + gb_trees:empty(). + +%% @spec make(headers() | [{key(), value()}]) -> headers() +%% @doc Construct a headers() from the given list. +make(L) when is_list(L) -> + from_list(L); +%% assume a tuple is already mochiweb_headers. +make(T) when is_tuple(T) -> + T. + +%% @spec from_list([{key(), value()}]) -> headers() +%% @doc Construct a headers() from the given list. +from_list(List) -> + lists:foldl(fun ({K, V}, T) -> insert(K, V, T) end, empty(), List). + +%% @spec enter_from_list([{key(), value()}], headers()) -> headers() +%% @doc Insert pairs into the headers, replace any values for existing keys. +enter_from_list(List, T) -> + lists:foldl(fun ({K, V}, T1) -> enter(K, V, T1) end, T, List). + +%% @spec default_from_list([{key(), value()}], headers()) -> headers() +%% @doc Insert pairs into the headers for keys that do not already exist. +default_from_list(List, T) -> + lists:foldl(fun ({K, V}, T1) -> default(K, V, T1) end, T, List). + +%% @spec to_list(headers()) -> [{key(), string()}] +%% @doc Return the contents of the headers. The keys will be the exact key +%% that was first inserted (e.g. may be an atom or binary, case is +%% preserved). +to_list(T) -> + F = fun ({K, {array, L}}, Acc) -> + L1 = lists:reverse(L), + lists:foldl(fun (V, Acc1) -> [{K, V} | Acc1] end, Acc, L1); + (Pair, Acc) -> + [Pair | Acc] + end, + lists:reverse(lists:foldl(F, [], gb_trees:values(T))). + +%% @spec get_value(key(), headers()) -> string() | undefined +%% @doc Return the value of the given header using a case insensitive search. +%% undefined will be returned for keys that are not present. +get_value(K, T) -> + case lookup(K, T) of + {value, {_, V}} -> + expand(V); + none -> + undefined + end. + +%% @spec get_primary_value(key(), headers()) -> string() | undefined +%% @doc Return the value of the given header up to the first semicolon using +%% a case insensitive search. undefined will be returned for keys +%% that are not present. +get_primary_value(K, T) -> + case get_value(K, T) of + undefined -> + undefined; + V -> + lists:takewhile(fun (C) -> C =/= $; end, V) + end. + +%% @spec lookup(key(), headers()) -> {value, {key(), string()}} | none +%% @doc Return the case preserved key and value for the given header using +%% a case insensitive search. none will be returned for keys that are +%% not present. +lookup(K, T) -> + case gb_trees:lookup(normalize(K), T) of + {value, {K0, V}} -> + {value, {K0, expand(V)}}; + none -> + none + end. + +%% @spec default(key(), value(), headers()) -> headers() +%% @doc Insert the pair into the headers if it does not already exist. +default(K, V, T) -> + K1 = normalize(K), + V1 = any_to_list(V), + try gb_trees:insert(K1, {K, V1}, T) + catch + error:{key_exists, _} -> + T + end. + +%% @spec enter(key(), value(), headers()) -> headers() +%% @doc Insert the pair into the headers, replacing any pre-existing key. +enter(K, V, T) -> + K1 = normalize(K), + V1 = any_to_list(V), + gb_trees:enter(K1, {K, V1}, T). + +%% @spec insert(key(), value(), headers()) -> headers() +%% @doc Insert the pair into the headers, merging with any pre-existing key. +%% A merge is done with Value = V0 ++ ", " ++ V1. +insert(K, V, T) -> + K1 = normalize(K), + V1 = any_to_list(V), + try gb_trees:insert(K1, {K, V1}, T) + catch + error:{key_exists, _} -> + {K0, V0} = gb_trees:get(K1, T), + V2 = merge(K1, V1, V0), + gb_trees:update(K1, {K0, V2}, T) + end. + +%% Internal API + +expand({array, L}) -> + mochiweb_util:join(lists:reverse(L), ", "); +expand(V) -> + V. + +merge("set-cookie", V1, {array, L}) -> + {array, [V1 | L]}; +merge("set-cookie", V1, V0) -> + {array, [V1, V0]}; +merge(_, V1, V0) -> + V0 ++ ", " ++ V1. + +normalize(K) when is_list(K) -> + string:to_lower(K); +normalize(K) when is_atom(K) -> + normalize(atom_to_list(K)); +normalize(K) when is_binary(K) -> + normalize(binary_to_list(K)). + +any_to_list(V) when is_list(V) -> + V; +any_to_list(V) when is_atom(V) -> + atom_to_list(V); +any_to_list(V) when is_binary(V) -> + binary_to_list(V); +any_to_list(V) when is_integer(V) -> + integer_to_list(V). + + diff --git a/deps/mochiweb-r88/src/mochiweb_html.erl b/deps/mochiweb-r88/src/mochiweb_html.erl new file mode 100644 index 0000000..ec1ae9f --- /dev/null +++ b/deps/mochiweb-r88/src/mochiweb_html.erl @@ -0,0 +1,880 @@ +%% @author Bob Ippolito +%% @copyright 2007 Mochi Media, Inc. + +%% @doc Loosely tokenizes and generates parse trees for HTML 4. +-module(mochiweb_html). +-export([tokens/1, parse/1, parse_tokens/1, to_tokens/1, escape/1, + escape_attr/1, to_html/1, test/0]). + +% This is a macro to placate syntax highlighters.. +-define(QUOTE, $\"). +-define(SQUOTE, $\'). +-define(ADV_COL(S, N), + S#decoder{column=N+S#decoder.column, + offset=N+S#decoder.offset}). +-define(INC_COL(S), + S#decoder{column=1+S#decoder.column, + offset=1+S#decoder.offset}). +-define(INC_LINE(S), + S#decoder{column=1, + line=1+S#decoder.line, + offset=1+S#decoder.offset}). +-define(INC_CHAR(S, C), + case C of + $\n -> + S#decoder{column=1, + line=1+S#decoder.line, + offset=1+S#decoder.offset}; + _ -> + S#decoder{column=1+S#decoder.column, + offset=1+S#decoder.offset} + end). + +-define(IS_WHITESPACE(C), + (C =:= $\s orelse C =:= $\t orelse C =:= $\r orelse C =:= $\n)). +-define(IS_LITERAL_SAFE(C), + ((C >= $A andalso C =< $Z) orelse (C >= $a andalso C =< $z) + orelse (C >= $0 andalso C =< $9))). + +-record(decoder, {line=1, + column=1, + offset=0}). + +%% @type html_node() = {string(), [html_attr()], [html_node() | string()]} +%% @type html_attr() = {string(), string()} +%% @type html_token() = html_data() | start_tag() | end_tag() | inline_html() | html_comment() | html_doctype() +%% @type html_data() = {data, string(), Whitespace::boolean()} +%% @type start_tag() = {start_tag, Name, [html_attr()], Singleton::boolean()} +%% @type end_tag() = {end_tag, Name} +%% @type html_comment() = {comment, Comment} +%% @type html_doctype() = {doctype, [Doctype]} +%% @type inline_html() = {'=', iolist()} + +%% External API. + +%% @spec parse(string() | binary()) -> html_node() +%% @doc tokenize and then transform the token stream into a HTML tree. +parse(Input) -> + parse_tokens(tokens(Input)). + +%% @spec parse_tokens([html_token()]) -> html_node() +%% @doc Transform the output of tokens(Doc) into a HTML tree. +parse_tokens(Tokens) when is_list(Tokens) -> + %% Skip over doctype, processing instructions + F = fun (X) -> + case X of + {start_tag, _, _, false} -> + false; + _ -> + true + end + end, + [{start_tag, Tag, Attrs, false} | Rest] = lists:dropwhile(F, Tokens), + {Tree, _} = tree(Rest, [norm({Tag, Attrs})]), + Tree. + +%% @spec tokens(StringOrBinary) -> [html_token()] +%% @doc Transform the input UTF-8 HTML into a token stream. +tokens(Input) -> + tokens(iolist_to_binary(Input), #decoder{}, []). + +%% @spec to_tokens(html_node()) -> [html_token()] +%% @doc Convert a html_node() tree to a list of tokens. +to_tokens({Tag0}) -> + to_tokens({Tag0, [], []}); +to_tokens(T={'=', _}) -> + [T]; +to_tokens(T={doctype, _}) -> + [T]; +to_tokens(T={comment, _}) -> + [T]; +to_tokens({Tag0, Acc}) -> + to_tokens({Tag0, [], Acc}); +to_tokens({Tag0, Attrs, Acc}) -> + Tag = to_tag(Tag0), + to_tokens([{Tag, Acc}], [{start_tag, Tag, Attrs, is_singleton(Tag)}]). + +%% @spec to_html([html_token()] | html_node()) -> iolist() +%% @doc Convert a list of html_token() to a HTML document. +to_html(Node) when is_tuple(Node) -> + to_html(to_tokens(Node)); +to_html(Tokens) when is_list(Tokens) -> + to_html(Tokens, []). + +%% @spec escape(string() | atom() | binary()) -> binary() +%% @doc Escape a string such that it's safe for HTML (amp; lt; gt;). +escape(B) when is_binary(B) -> + escape(binary_to_list(B), []); +escape(A) when is_atom(A) -> + escape(atom_to_list(A), []); +escape(S) when is_list(S) -> + escape(S, []). + +%% @spec escape_attr(string() | binary() | atom() | integer() | float()) -> binary() +%% @doc Escape a string such that it's safe for HTML attrs +%% (amp; lt; gt; quot;). +escape_attr(B) when is_binary(B) -> + escape_attr(binary_to_list(B), []); +escape_attr(A) when is_atom(A) -> + escape_attr(atom_to_list(A), []); +escape_attr(S) when is_list(S) -> + escape_attr(S, []); +escape_attr(I) when is_integer(I) -> + escape_attr(integer_to_list(I), []); +escape_attr(F) when is_float(F) -> + escape_attr(mochinum:digits(F), []). + +%% @spec test() -> ok +%% @doc Run tests for mochiweb_html. +test() -> + test_destack(), + test_tokens(), + test_tokens2(), + test_parse(), + test_parse2(), + test_parse_tokens(), + test_escape(), + test_escape_attr(), + test_to_html(), + ok. + + +%% Internal API + +test_to_html() -> + Expect = <<"hey!

what's up

sucka
">>, + Expect = iolist_to_binary( + to_html({html, [], + [{<<"head">>, [], + [{title, <<"hey!">>}]}, + {body, [], + [{p, [{class, foo}], [<<"what's">>, <<" up">>, {br}]}, + {'div', <<"sucka">>}, + {comment, <<" comment! ">>}]}]})), + Expect1 = <<"">>, + Expect1 = iolist_to_binary( + to_html({doctype, + [<<"html">>, <<"PUBLIC">>, + <<"-//W3C//DTD XHTML 1.0 Transitional//EN">>, + <<"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">>]})), + ok. +to_html([], Acc) -> + lists:reverse(Acc); +to_html([{'=', Content} | Rest], Acc) -> + to_html(Rest, [Content | Acc]); +to_html([{pi, Tag, Attrs} | Rest], Acc) -> + Open = [<<">, + Tag, + attrs_to_html(Attrs, []), + <<"?>">>], + to_html(Rest, [Open | Acc]); +to_html([{comment, Comment} | Rest], Acc) -> + to_html(Rest, [[<<"">>] | Acc]); +to_html([{doctype, Parts} | Rest], Acc) -> + Inside = doctype_to_html(Parts, Acc), + to_html(Rest, [[<<">, Inside, <<">">>] | Acc]); +to_html([{data, Data, _Whitespace} | Rest], Acc) -> + to_html(Rest, [escape(Data) | Acc]); +to_html([{start_tag, Tag, Attrs, Singleton} | Rest], Acc) -> + Open = [<<"<">>, + Tag, + attrs_to_html(Attrs, []), + case Singleton of + true -> <<" />">>; + false -> <<">">> + end], + to_html(Rest, [Open | Acc]); +to_html([{end_tag, Tag} | Rest], Acc) -> + to_html(Rest, [[<<">, Tag, <<">">>] | Acc]). + +doctype_to_html([], Acc) -> + lists:reverse(Acc); +doctype_to_html([Word | Rest], Acc) -> + case lists:all(fun (C) -> ?IS_LITERAL_SAFE(C) end, + binary_to_list(iolist_to_binary(Word))) of + true -> + doctype_to_html(Rest, [[<<" ">>, Word] | Acc]); + false -> + doctype_to_html(Rest, [[<<" \"">>, escape_attr(Word), ?QUOTE] | Acc]) + end. + +attrs_to_html([], Acc) -> + lists:reverse(Acc); +attrs_to_html([{K, V} | Rest], Acc) -> + attrs_to_html(Rest, + [[<<" ">>, escape(K), <<"=\"">>, + escape_attr(V), <<"\"">>] | Acc]). + +test_escape() -> + <<"&quot;\"word <<up!&quot;">> = + escape(<<""\"word <>), + ok. + +test_escape_attr() -> + <<"&quot;"word <<up!&quot;">> = + escape_attr(<<""\"word <>), + ok. + +escape([], Acc) -> + list_to_binary(lists:reverse(Acc)); +escape("<" ++ Rest, Acc) -> + escape(Rest, lists:reverse("<", Acc)); +escape(">" ++ Rest, Acc) -> + escape(Rest, lists:reverse(">", Acc)); +escape("&" ++ Rest, Acc) -> + escape(Rest, lists:reverse("&", Acc)); +escape([C | Rest], Acc) -> + escape(Rest, [C | Acc]). + +escape_attr([], Acc) -> + list_to_binary(lists:reverse(Acc)); +escape_attr("<" ++ Rest, Acc) -> + escape_attr(Rest, lists:reverse("<", Acc)); +escape_attr(">" ++ Rest, Acc) -> + escape_attr(Rest, lists:reverse(">", Acc)); +escape_attr("&" ++ Rest, Acc) -> + escape_attr(Rest, lists:reverse("&", Acc)); +escape_attr([?QUOTE | Rest], Acc) -> + escape_attr(Rest, lists:reverse(""", Acc)); +escape_attr([C | Rest], Acc) -> + escape_attr(Rest, [C | Acc]). + +to_tag(A) when is_atom(A) -> + norm(atom_to_list(A)); +to_tag(L) -> + norm(L). + +to_tokens([], Acc) -> + lists:reverse(Acc); +to_tokens([{Tag, []} | Rest], Acc) -> + to_tokens(Rest, [{end_tag, to_tag(Tag)} | Acc]); +to_tokens([{Tag0, [{T0} | R1]} | Rest], Acc) -> + %% Allow {br} + to_tokens([{Tag0, [{T0, [], []} | R1]} | Rest], Acc); +to_tokens([{Tag0, [T0={'=', _C0} | R1]} | Rest], Acc) -> + %% Allow {'=', iolist()} + to_tokens([{Tag0, R1} | Rest], [T0 | Acc]); +to_tokens([{Tag0, [T0={comment, _C0} | R1]} | Rest], Acc) -> + %% Allow {comment, iolist()} + to_tokens([{Tag0, R1} | Rest], [T0 | Acc]); +to_tokens([{Tag0, [{T0, A0=[{_, _} | _]} | R1]} | Rest], Acc) -> + %% Allow {p, [{"class", "foo"}]} + to_tokens([{Tag0, [{T0, A0, []} | R1]} | Rest], Acc); +to_tokens([{Tag0, [{T0, C0} | R1]} | Rest], Acc) -> + %% Allow {p, "content"} and {p, <<"content">>} + to_tokens([{Tag0, [{T0, [], C0} | R1]} | Rest], Acc); +to_tokens([{Tag0, [{T0, A1, C0} | R1]} | Rest], Acc) when is_binary(C0) -> + %% Allow {"p", [{"class", "foo"}], <<"content">>} + to_tokens([{Tag0, [{T0, A1, binary_to_list(C0)} | R1]} | Rest], Acc); +to_tokens([{Tag0, [{T0, A1, C0=[C | _]} | R1]} | Rest], Acc) + when is_integer(C) -> + %% Allow {"p", [{"class", "foo"}], "content"} + to_tokens([{Tag0, [{T0, A1, [C0]} | R1]} | Rest], Acc); +to_tokens([{Tag0, [{T0, A1, C1} | R1]} | Rest], Acc) -> + %% Native {"p", [{"class", "foo"}], ["content"]} + Tag = to_tag(Tag0), + T1 = to_tag(T0), + case is_singleton(norm(T1)) of + true -> + to_tokens([{Tag, R1} | Rest], [{start_tag, T1, A1, true} | Acc]); + false -> + to_tokens([{T1, C1}, {Tag, R1} | Rest], + [{start_tag, T1, A1, false} | Acc]) + end; +to_tokens([{Tag0, [L | R1]} | Rest], Acc) when is_list(L) -> + %% List text + Tag = to_tag(Tag0), + to_tokens([{Tag, R1} | Rest], [{data, iolist_to_binary(L), false} | Acc]); +to_tokens([{Tag0, [B | R1]} | Rest], Acc) when is_binary(B) -> + %% Binary text + Tag = to_tag(Tag0), + to_tokens([{Tag, R1} | Rest], [{data, B, false} | Acc]). + +test_tokens() -> + [{start_tag, <<"foo">>, [{<<"bar">>, <<"baz">>}, + {<<"wibble">>, <<"wibble">>}, + {<<"alice">>, <<"bob">>}], true}] = + tokens(<<"">>), + [{start_tag, <<"foo">>, [{<<"bar">>, <<"baz">>}, + {<<"wibble">>, <<"wibble">>}, + {<<"alice">>, <<"bob">>}], true}] = + tokens(<<"">>), + [{comment, <<"[if lt IE 7]>\n\n>}] = + tokens(<<"">>), + [{start_tag, <<"script">>, [{<<"type">>, <<"text/javascript">>}], false}, + {data, <<" A= B <= C ">>, false}, + {end_tag, <<"script">>}] = + tokens(<<"">>), + [{start_tag, <<"textarea">>, [], false}, + {data, <<"">>, false}, + {end_tag, <<"textarea">>}] = + tokens(<<"">>), + ok. + +tokens(B, S=#decoder{offset=O}, Acc) -> + case B of + <<_:O/binary>> -> + lists:reverse(Acc); + _ -> + {Tag, S1} = tokenize(B, S), + case parse_flag(Tag) of + script -> + {Tag2, S2} = tokenize_script(B, S1), + tokens(B, S2, [Tag2, Tag | Acc]); + textarea -> + {Tag2, S2} = tokenize_textarea(B, S1), + tokens(B, S2, [Tag2, Tag | Acc]); + none -> + tokens(B, S1, [Tag | Acc]) + end + end. + +parse_flag({start_tag, B, _, false}) -> + case string:to_lower(binary_to_list(B)) of + "script" -> + script; + "textarea" -> + textarea; + _ -> + none + end; +parse_flag(_) -> + none. + +tokenize(B, S=#decoder{offset=O}) -> + case B of + <<_:O/binary, " + + + + CDATA>>]]> +">>, + Expect = {<<"html">>, [], + [{<<"head">>, [], + [{<<"meta">>, + [{<<"http-equiv">>,<<"Content-Type">>}, + {<<"content">>,<<"text/html; charset=UTF-8">>}], + []}, + {<<"title">>,[],[<<"Foo">>]}, + {<<"link">>, + [{<<"rel">>,<<"stylesheet">>}, + {<<"type">>,<<"text/css">>}, + {<<"href">>,<<"/static/rel/dojo/resources/dojo.css">>}, + {<<"media">>,<<"screen">>}], + []}, + {<<"link">>, + [{<<"rel">>,<<"stylesheet">>}, + {<<"type">>,<<"text/css">>}, + {<<"href">>,<<"/static/foo.css">>}, + {<<"media">>,<<"screen">>}], + []}, + {comment,<<"[if lt IE 7]>\n \n >}, + {<<"link">>, + [{<<"rel">>,<<"icon">>}, + {<<"href">>,<<"/static/images/favicon.ico">>}, + {<<"type">>,<<"image/x-icon">>}], + []}, + {<<"link">>, + [{<<"rel">>,<<"shortcut icon">>}, + {<<"href">>,<<"/static/images/favicon.ico">>}, + {<<"type">>,<<"image/x-icon">>}], + []}]}, + {<<"body">>, + [{<<"id">>,<<"home">>}, + {<<"class">>,<<"tundra">>}], + [<<"<CDATA>>">>]}]}, + Expect = parse(D0), + ok. + +test_tokens2() -> + D0 = <<"from __future__ import *http://bob.pythonmac.orgBob's Rants">>, + Expect = [{start_tag,<<"channel">>,[],false}, + {start_tag,<<"title">>,[],false}, + {data,<<"from __future__ import *">>,false}, + {end_tag,<<"title">>}, + {start_tag,<<"link">>,[],true}, + {data,<<"http://bob.pythonmac.org">>,false}, + {end_tag,<<"link">>}, + {start_tag,<<"description">>,[],false}, + {data,<<"Bob's Rants">>,false}, + {end_tag,<<"description">>}, + {end_tag,<<"channel">>}], + Expect = tokens(D0), + ok. + +test_parse2() -> + D0 = <<"from __future__ import *http://bob.pythonmac.org
fooBob's Rants
">>, + Expect = {<<"channel">>,[], + [{<<"title">>,[],[<<"from __future__ import *">>]}, + {<<"link">>,[],[ + <<"http://bob.pythonmac.org">>, + {<<"br">>,[],[]}, + <<"foo">>]}, + {<<"description">>,[],[<<"Bob's Rants">>]}]}, + Expect = parse(D0), + ok. + +test_parse_tokens() -> + D0 = [{doctype,[<<"HTML">>,<<"PUBLIC">>,<<"-//W3C//DTD HTML 4.01 Transitional//EN">>]}, + {data,<<"\n">>,true}, + {start_tag,<<"html">>,[],false}], + {<<"html">>, [], []} = parse_tokens(D0), + D1 = D0 ++ [{end_tag, <<"html">>}], + {<<"html">>, [], []} = parse_tokens(D1), + D2 = D0 ++ [{start_tag, <<"body">>, [], false}], + {<<"html">>, [], [{<<"body">>, [], []}]} = parse_tokens(D2), + D3 = D0 ++ [{start_tag, <<"head">>, [], false}, + {end_tag, <<"head">>}, + {start_tag, <<"body">>, [], false}], + {<<"html">>, [], [{<<"head">>, [], []}, {<<"body">>, [], []}]} = parse_tokens(D3), + D4 = D3 ++ [{data,<<"\n">>,true}, + {start_tag,<<"div">>,[{<<"class">>,<<"a">>}],false}, + {start_tag,<<"a">>,[{<<"name">>,<<"#anchor">>}],false}, + {end_tag,<<"a">>}, + {end_tag,<<"div">>}, + {start_tag,<<"div">>,[{<<"class">>,<<"b">>}],false}, + {start_tag,<<"div">>,[{<<"class">>,<<"c">>}],false}, + {end_tag,<<"div">>}, + {end_tag,<<"div">>}], + {<<"html">>, [], + [{<<"head">>, [], []}, + {<<"body">>, [], + [{<<"div">>, [{<<"class">>, <<"a">>}], [{<<"a">>, [{<<"name">>, <<"#anchor">>}], []}]}, + {<<"div">>, [{<<"class">>, <<"b">>}], [{<<"div">>, [{<<"class">>, <<"c">>}], []}]} + ]}]} = parse_tokens(D4), + D5 = [{start_tag,<<"html">>,[],false}, + {data,<<"\n">>,true}, + {data,<<"boo">>,false}, + {data,<<"hoo">>,false}, + {data,<<"\n">>,true}, + {end_tag,<<"html">>}], + {<<"html">>, [], [<<"\nboohoo\n">>]} = parse_tokens(D5), + D6 = [{start_tag,<<"html">>,[],false}, + {data,<<"\n">>,true}, + {data,<<"\n">>,true}, + {end_tag,<<"html">>}], + {<<"html">>, [], []} = parse_tokens(D6), + D7 = [{start_tag,<<"html">>,[],false}, + {start_tag,<<"ul">>,[],false}, + {start_tag,<<"li">>,[],false}, + {data,<<"word">>,false}, + {start_tag,<<"li">>,[],false}, + {data,<<"up">>,false}, + {end_tag,<<"li">>}, + {start_tag,<<"li">>,[],false}, + {data,<<"fdsa">>,false}, + {start_tag,<<"br">>,[],true}, + {data,<<"asdf">>,false}, + {end_tag,<<"ul">>}, + {end_tag,<<"html">>}], + {<<"html">>, [], + [{<<"ul">>, [], + [{<<"li">>, [], [<<"word">>]}, + {<<"li">>, [], [<<"up">>]}, + {<<"li">>, [], [<<"fdsa">>,{<<"br">>, [], []}, <<"asdf">>]}]}]} = parse_tokens(D7), + ok. + +tree_data([{data, Data, Whitespace} | Rest], AllWhitespace, Acc) -> + tree_data(Rest, (Whitespace andalso AllWhitespace), [Data | Acc]); +tree_data(Rest, AllWhitespace, Acc) -> + {iolist_to_binary(lists:reverse(Acc)), AllWhitespace, Rest}. + +tree([], Stack) -> + {destack(Stack), []}; +tree([{end_tag, Tag} | Rest], Stack) -> + case destack(norm(Tag), Stack) of + S when is_list(S) -> + tree(Rest, S); + Result -> + {Result, []} + end; +tree([{start_tag, Tag, Attrs, true} | Rest], S) -> + tree(Rest, append_stack_child(norm({Tag, Attrs}), S)); +tree([{start_tag, Tag, Attrs, false} | Rest], S) -> + tree(Rest, stack(norm({Tag, Attrs}), S)); +tree([T={pi, _Tag, _Attrs} | Rest], S) -> + tree(Rest, append_stack_child(T, S)); +tree([T={comment, _Comment} | Rest], S) -> + tree(Rest, append_stack_child(T, S)); +tree(L=[{data, _Data, _Whitespace} | _], S) -> + case tree_data(L, true, []) of + {_, true, Rest} -> + tree(Rest, S); + {Data, false, Rest} -> + tree(Rest, append_stack_child(Data, S)) + end. + +norm({Tag, Attrs}) -> + {norm(Tag), [{norm(K), iolist_to_binary(V)} || {K, V} <- Attrs], []}; +norm(Tag) when is_binary(Tag) -> + Tag; +norm(Tag) -> + list_to_binary(string:to_lower(Tag)). + +test_destack() -> + {<<"a">>, [], []} = + destack([{<<"a">>, [], []}]), + {<<"a">>, [], [{<<"b">>, [], []}]} = + destack([{<<"b">>, [], []}, {<<"a">>, [], []}]), + {<<"a">>, [], [{<<"b">>, [], [{<<"c">>, [], []}]}]} = + destack([{<<"c">>, [], []}, {<<"b">>, [], []}, {<<"a">>, [], []}]), + [{<<"a">>, [], [{<<"b">>, [], [{<<"c">>, [], []}]}]}] = + destack(<<"b">>, + [{<<"c">>, [], []}, {<<"b">>, [], []}, {<<"a">>, [], []}]), + [{<<"b">>, [], [{<<"c">>, [], []}]}, {<<"a">>, [], []}] = + destack(<<"c">>, + [{<<"c">>, [], []}, {<<"b">>, [], []},{<<"a">>, [], []}]), + ok. + +stack(T1={TN, _, _}, Stack=[{TN, _, _} | _Rest]) + when TN =:= <<"li">> orelse TN =:= <<"option">> -> + [T1 | destack(TN, Stack)]; +stack(T1={TN0, _, _}, Stack=[{TN1, _, _} | _Rest]) + when (TN0 =:= <<"dd">> orelse TN0 =:= <<"dt">>) andalso + (TN1 =:= <<"dd">> orelse TN1 =:= <<"dt">>) -> + [T1 | destack(TN1, Stack)]; +stack(T1, Stack) -> + [T1 | Stack]. + +append_stack_child(StartTag, [{Name, Attrs, Acc} | Stack]) -> + [{Name, Attrs, [StartTag | Acc]} | Stack]. + +destack(TagName, Stack) when is_list(Stack) -> + F = fun (X) -> + case X of + {TagName, _, _} -> + false; + _ -> + true + end + end, + case lists:splitwith(F, Stack) of + {_, []} -> + %% If we're parsing something like XML we might find + %% a tag that is normally a singleton + %% in HTML but isn't here + case {is_singleton(TagName), Stack} of + {true, [{T0, A0, Acc0} | Post0]} -> + case lists:splitwith(F, Acc0) of + {_, []} -> + %% Actually was a singleton + Stack; + {Pre, [{T1, A1, []} | Post1]} -> + [{T0, A0, [{T1, A1, lists:reverse(Pre)} | Post1]} + | Post0] + end; + _ -> + %% No match, no state change + Stack + end; + {_Pre, [_T]} -> + %% Unfurl the whole stack, we're done + destack(Stack); + {Pre, [T, {T0, A0, Acc0} | Post]} -> + %% Unfurl up to the tag, then accumulate it + [{T0, A0, [destack(Pre ++ [T]) | Acc0]} | Post] + end. + +destack([{Tag, Attrs, Acc}]) -> + {Tag, Attrs, lists:reverse(Acc)}; +destack([{T1, A1, Acc1}, {T0, A0, Acc0} | Rest]) -> + destack([{T0, A0, [{T1, A1, lists:reverse(Acc1)} | Acc0]} | Rest]). + +is_singleton(<<"br">>) -> true; +is_singleton(<<"hr">>) -> true; +is_singleton(<<"img">>) -> true; +is_singleton(<<"input">>) -> true; +is_singleton(<<"base">>) -> true; +is_singleton(<<"meta">>) -> true; +is_singleton(<<"link">>) -> true; +is_singleton(<<"area">>) -> true; +is_singleton(<<"param">>) -> true; +is_singleton(<<"col">>) -> true; +is_singleton(_) -> false. + +tokenize_data(B, S=#decoder{offset=O}) -> + tokenize_data(B, S, O, true). + +tokenize_data(B, S=#decoder{offset=O}, Start, Whitespace) -> + case B of + <<_:O/binary, C, _/binary>> when (C =/= $< andalso C =/= $&) -> + tokenize_data(B, ?INC_CHAR(S, C), Start, + (Whitespace andalso ?IS_WHITESPACE(C))); + _ -> + Len = O - Start, + <<_:Start/binary, Data:Len/binary, _/binary>> = B, + {{data, Data, Whitespace}, S} + end. + +tokenize_attributes(B, S) -> + tokenize_attributes(B, S, []). + +tokenize_attributes(B, S=#decoder{offset=O}, Acc) -> + case B of + <<_:O/binary>> -> + {lists:reverse(Acc), S}; + <<_:O/binary, C, _/binary>> when (C =:= $> orelse C =:= $/) -> + {lists:reverse(Acc), S}; + <<_:O/binary, "?>", _/binary>> -> + {lists:reverse(Acc), S}; + <<_:O/binary, C, _/binary>> when ?IS_WHITESPACE(C) -> + tokenize_attributes(B, ?INC_CHAR(S, C), Acc); + _ -> + {Attr, S1} = tokenize_literal(B, S), + {Value, S2} = tokenize_attr_value(Attr, B, S1), + tokenize_attributes(B, S2, [{Attr, Value} | Acc]) + end. + +tokenize_attr_value(Attr, B, S) -> + S1 = skip_whitespace(B, S), + O = S1#decoder.offset, + case B of + <<_:O/binary, "=", _/binary>> -> + tokenize_word_or_literal(B, ?INC_COL(S1)); + _ -> + {Attr, S1} + end. + +skip_whitespace(B, S=#decoder{offset=O}) -> + case B of + <<_:O/binary, C, _/binary>> when ?IS_WHITESPACE(C) -> + skip_whitespace(B, ?INC_CHAR(S, C)); + _ -> + S + end. + +tokenize_literal(Bin, S) -> + tokenize_literal(Bin, S, []). + +tokenize_literal(Bin, S=#decoder{offset=O}, Acc) -> + case Bin of + <<_:O/binary, $&, _/binary>> -> + {{data, Data, false}, S1} = tokenize_charref(Bin, ?INC_COL(S)), + tokenize_literal(Bin, S1, [Data | Acc]); + <<_:O/binary, C, _/binary>> when not (?IS_WHITESPACE(C) + orelse C =:= $> + orelse C =:= $/ + orelse C =:= $=) -> + tokenize_literal(Bin, ?INC_COL(S), [C | Acc]); + _ -> + {iolist_to_binary(lists:reverse(Acc)), S} + end. + +find_qgt(Bin, S=#decoder{offset=O}) -> + case Bin of + <<_:O/binary, "?>", _/binary>> -> + ?ADV_COL(S, 2); + <<_:O/binary, C, _/binary>> -> + find_qgt(Bin, ?INC_CHAR(S, C)); + _ -> + S + end. + +find_gt(Bin, S) -> + find_gt(Bin, S, false). + +find_gt(Bin, S=#decoder{offset=O}, HasSlash) -> + case Bin of + <<_:O/binary, $/, _/binary>> -> + find_gt(Bin, ?INC_COL(S), true); + <<_:O/binary, $>, _/binary>> -> + {?INC_COL(S), HasSlash}; + <<_:O/binary, C, _/binary>> -> + find_gt(Bin, ?INC_CHAR(S, C), HasSlash); + _ -> + {S, HasSlash} + end. + +tokenize_charref(Bin, S=#decoder{offset=O}) -> + tokenize_charref(Bin, S, O). + +tokenize_charref(Bin, S=#decoder{offset=O}, Start) -> + case Bin of + <<_:O/binary>> -> + <<_:Start/binary, Raw/binary>> = Bin, + {{data, Raw, false}, S}; + <<_:O/binary, C, _/binary>> when ?IS_WHITESPACE(C) + orelse C =:= ?SQUOTE + orelse C =:= ?QUOTE + orelse C =:= $/ + orelse C =:= $> -> + Len = O - Start, + <<_:Start/binary, Raw:Len/binary, _/binary>> = Bin, + {{data, Raw, false}, S}; + <<_:O/binary, $;, _/binary>> -> + Len = O - Start, + <<_:Start/binary, Raw:Len/binary, _/binary>> = Bin, + Data = case mochiweb_charref:charref(Raw) of + undefined -> + Start1 = Start - 1, + Len1 = Len + 2, + <<_:Start1/binary, R:Len1/binary, _/binary>> = Bin, + R; + Unichar -> + list_to_binary(xmerl_ucs:to_utf8(Unichar)) + end, + {{data, Data, false}, ?INC_COL(S)}; + _ -> + tokenize_charref(Bin, ?INC_COL(S), Start) + end. + +tokenize_doctype(Bin, S) -> + tokenize_doctype(Bin, S, []). + +tokenize_doctype(Bin, S=#decoder{offset=O}, Acc) -> + case Bin of + <<_:O/binary>> -> + {{doctype, lists:reverse(Acc)}, S}; + <<_:O/binary, $>, _/binary>> -> + {{doctype, lists:reverse(Acc)}, ?INC_COL(S)}; + <<_:O/binary, C, _/binary>> when ?IS_WHITESPACE(C) -> + tokenize_doctype(Bin, ?INC_CHAR(S, C), Acc); + _ -> + {Word, S1} = tokenize_word_or_literal(Bin, S), + tokenize_doctype(Bin, S1, [Word | Acc]) + end. + +tokenize_word_or_literal(Bin, S=#decoder{offset=O}) -> + case Bin of + <<_:O/binary, C, _/binary>> when ?IS_WHITESPACE(C) -> + {error, {whitespace, [C], S}}; + <<_:O/binary, C, _/binary>> when C =:= ?QUOTE orelse C =:= ?SQUOTE -> + tokenize_word(Bin, ?INC_COL(S), C); + _ -> + tokenize_literal(Bin, S, []) + end. + +tokenize_word(Bin, S, Quote) -> + tokenize_word(Bin, S, Quote, []). + +tokenize_word(Bin, S=#decoder{offset=O}, Quote, Acc) -> + case Bin of + <<_:O/binary>> -> + {iolist_to_binary(lists:reverse(Acc)), S}; + <<_:O/binary, Quote, _/binary>> -> + {iolist_to_binary(lists:reverse(Acc)), ?INC_COL(S)}; + <<_:O/binary, $&, _/binary>> -> + {{data, Data, false}, S1} = tokenize_charref(Bin, ?INC_COL(S)), + tokenize_word(Bin, S1, Quote, [Data | Acc]); + <<_:O/binary, C, _/binary>> -> + tokenize_word(Bin, ?INC_CHAR(S, C), Quote, [C | Acc]) + end. + +tokenize_cdata(Bin, S=#decoder{offset=O}) -> + tokenize_cdata(Bin, S, O). + +tokenize_cdata(Bin, S=#decoder{offset=O}, Start) -> + case Bin of + <<_:O/binary, "]]>", _/binary>> -> + Len = O - Start, + <<_:Start/binary, Raw:Len/binary, _/binary>> = Bin, + {{data, Raw, false}, ?ADV_COL(S, 3)}; + <<_:O/binary, C, _/binary>> -> + tokenize_cdata(Bin, ?INC_CHAR(S, C), Start); + _ -> + <<_:O/binary, Raw/binary>> = Bin, + {{data, Raw, false}, S} + end. + +tokenize_comment(Bin, S=#decoder{offset=O}) -> + tokenize_comment(Bin, S, O). + +tokenize_comment(Bin, S=#decoder{offset=O}, Start) -> + case Bin of + <<_:O/binary, "-->", _/binary>> -> + Len = O - Start, + <<_:Start/binary, Raw:Len/binary, _/binary>> = Bin, + {{comment, Raw}, ?ADV_COL(S, 3)}; + <<_:O/binary, C, _/binary>> -> + tokenize_comment(Bin, ?INC_CHAR(S, C), Start); + <<_:Start/binary, Raw/binary>> -> + {{comment, Raw}, S} + end. + +tokenize_script(Bin, S=#decoder{offset=O}) -> + tokenize_script(Bin, S, O). + +tokenize_script(Bin, S=#decoder{offset=O}, Start) -> + case Bin of + %% Just a look-ahead, we want the end_tag separately + <<_:O/binary, $<, $/, SS, CC, RR, II, PP, TT, _/binary>> + when (SS =:= $s orelse SS =:= $S) andalso + (CC =:= $c orelse CC =:= $C) andalso + (RR =:= $r orelse RR =:= $R) andalso + (II =:= $i orelse II =:= $I) andalso + (PP =:= $p orelse PP =:= $P) andalso + (TT=:= $t orelse TT =:= $T) -> + Len = O - Start, + <<_:Start/binary, Raw:Len/binary, _/binary>> = Bin, + {{data, Raw, false}, S}; + <<_:O/binary, C, _/binary>> -> + tokenize_script(Bin, ?INC_CHAR(S, C), Start); + <<_:Start/binary, Raw/binary>> -> + {{data, Raw, false}, S} + end. + +tokenize_textarea(Bin, S=#decoder{offset=O}) -> + tokenize_textarea(Bin, S, O). + +tokenize_textarea(Bin, S=#decoder{offset=O}, Start) -> + case Bin of + %% Just a look-ahead, we want the end_tag separately + <<_:O/binary, $<, $/, TT, EE, XX, TT2, AA, RR, EE2, AA2, _/binary>> + when (TT =:= $t orelse TT =:= $T) andalso + (EE =:= $e orelse EE =:= $E) andalso + (XX =:= $x orelse XX =:= $X) andalso + (TT2 =:= $t orelse TT2 =:= $T) andalso + (AA =:= $a orelse AA =:= $A) andalso + (RR =:= $r orelse RR =:= $R) andalso + (EE2 =:= $e orelse EE2 =:= $E) andalso + (AA2 =:= $a orelse AA2 =:= $A) -> + Len = O - Start, + <<_:Start/binary, Raw:Len/binary, _/binary>> = Bin, + {{data, Raw, false}, S}; + <<_:O/binary, C, _/binary>> -> + tokenize_textarea(Bin, ?INC_CHAR(S, C), Start); + <<_:Start/binary, Raw/binary>> -> + {{data, Raw, false}, S} + end. diff --git a/deps/mochiweb-r88/src/mochiweb_http.erl b/deps/mochiweb-r88/src/mochiweb_http.erl new file mode 100644 index 0000000..7bbe3c8 --- /dev/null +++ b/deps/mochiweb-r88/src/mochiweb_http.erl @@ -0,0 +1,132 @@ +%% @author Bob Ippolito +%% @copyright 2007 Mochi Media, Inc. + +%% @doc HTTP server. + +-module(mochiweb_http). +-author('bob@mochimedia.com'). +-export([start/0, start/1, stop/0, stop/1]). +-export([loop/2, default_body/1]). + +-define(IDLE_TIMEOUT, 30000). + +-define(DEFAULTS, [{name, ?MODULE}, + {port, 8888}]). + +set_default({Prop, Value}, PropList) -> + case proplists:is_defined(Prop, PropList) of + true -> + PropList; + false -> + [{Prop, Value} | PropList] + end. + +set_defaults(Defaults, PropList) -> + lists:foldl(fun set_default/2, PropList, Defaults). + +parse_options(Options) -> + {loop, HttpLoop} = proplists:lookup(loop, Options), + Loop = fun (S) -> + ?MODULE:loop(S, HttpLoop) + end, + Options1 = [{loop, Loop} | proplists:delete(loop, Options)], + set_defaults(?DEFAULTS, Options1). + +stop() -> + mochiweb_socket_server:stop(?MODULE). + +stop(Name) -> + mochiweb_socket_server:stop(Name). + +start() -> + start([{ip, "127.0.0.1"}, + {loop, {?MODULE, default_body}}]). + +start(Options) -> + mochiweb_socket_server:start(parse_options(Options)). + +frm(Body) -> + ["" + "
" + "" + "" + "
" + "
" + "
" + "" + "" + "" + "
" + "
", Body, "
" + ""]. + +default_body(Req, M, "/chunked") when M =:= 'GET'; M =:= 'HEAD' -> + Res = Req:ok({"text/plain", [], chunked}), + Res:write_chunk("First chunk\r\n"), + timer:sleep(5000), + Res:write_chunk("Last chunk\r\n"), + Res:write_chunk(""); +default_body(Req, M, _Path) when M =:= 'GET'; M =:= 'HEAD' -> + Body = io_lib:format("~p~n", [[{parse_qs, Req:parse_qs()}, + {parse_cookie, Req:parse_cookie()}, + Req:dump()]]), + Req:ok({"text/html", + [mochiweb_cookies:cookie("mochiweb_http", "test_cookie")], + frm(Body)}); +default_body(Req, 'POST', "/multipart") -> + Body = io_lib:format("~p~n", [[{parse_qs, Req:parse_qs()}, + {parse_cookie, Req:parse_cookie()}, + {body, Req:recv_body()}, + Req:dump()]]), + Req:ok({"text/html", [], frm(Body)}); +default_body(Req, 'POST', _Path) -> + Body = io_lib:format("~p~n", [[{parse_qs, Req:parse_qs()}, + {parse_cookie, Req:parse_cookie()}, + {parse_post, Req:parse_post()}, + Req:dump()]]), + Req:ok({"text/html", [], frm(Body)}); +default_body(Req, _Method, _Path) -> + Req:respond({501, [], []}). + +default_body(Req) -> + default_body(Req, Req:get(method), Req:get(path)). + +loop(Socket, Body) -> + inet:setopts(Socket, [{packet, http}]), + request(Socket, Body). + +request(Socket, Body) -> + case gen_tcp:recv(Socket, 0, ?IDLE_TIMEOUT) of + {ok, {http_request, Method, Path, Version}} -> + headers(Socket, {Method, Path, Version}, [], Body); + {error, {http_error, "\r\n"}} -> + request(Socket, Body); + {error, {http_error, "\n"}} -> + request(Socket, Body); + _Other -> + gen_tcp:close(Socket), + exit(normal) + end. + +headers(Socket, Request, Headers, Body) -> + case gen_tcp:recv(Socket, 0, ?IDLE_TIMEOUT) of + {ok, http_eoh} -> + inet:setopts(Socket, [{packet, raw}]), + Req = mochiweb:new_request({Socket, Request, + lists:reverse(Headers)}), + Body(Req), + case Req:should_close() of + true -> + gen_tcp:close(Socket), + exit(normal); + false -> + Req:cleanup(), + ?MODULE:loop(Socket, Body) + end; + {ok, {http_header, _, Name, _, Value}} -> + headers(Socket, Request, [{Name, Value} | Headers], Body); + _Other -> + gen_tcp:close(Socket), + exit(normal) + end. diff --git a/deps/mochiweb-r88/src/mochiweb_multipart.erl b/deps/mochiweb-r88/src/mochiweb_multipart.erl new file mode 100644 index 0000000..9eb4bad --- /dev/null +++ b/deps/mochiweb-r88/src/mochiweb_multipart.erl @@ -0,0 +1,429 @@ +%% @author Bob Ippolito +%% @copyright 2007 Mochi Media, Inc. + +%% @doc Utilities for parsing multipart/form-data. + +-module(mochiweb_multipart). +-author('bob@mochimedia.com'). + +-export([parse_form/1, parse_form/2]). +-export([parse_multipart_request/2]). +-export([test/0]). + +-define(CHUNKSIZE, 4096). + +-record(mp, {state, boundary, length, buffer, callback, req}). + +%% TODO: DOCUMENT THIS MODULE. + +parse_form(Req) -> + parse_form(Req, fun default_file_handler/2). + +parse_form(Req, FileHandler) -> + Callback = fun (Next) -> parse_form_outer(Next, FileHandler, []) end, + {_, _, Res} = parse_multipart_request(Req, Callback), + Res. + +parse_form_outer(eof, _, Acc) -> + lists:reverse(Acc); +parse_form_outer({headers, H}, FileHandler, State) -> + {"form-data", H1} = proplists:get_value("content-disposition", H), + Name = proplists:get_value("name", H1), + Filename = proplists:get_value("filename", H1), + case Filename of + undefined -> + fun (Next) -> + parse_form_value(Next, {Name, []}, FileHandler, State) + end; + _ -> + ContentType = proplists:get_value("content-type", H), + Handler = FileHandler(Filename, ContentType), + fun (Next) -> + parse_form_file(Next, {Name, Handler}, FileHandler, State) + end + end. + +parse_form_value(body_end, {Name, Acc}, FileHandler, State) -> + Value = binary_to_list(iolist_to_binary(lists:reverse(Acc))), + State1 = [{Name, Value} | State], + fun (Next) -> parse_form_outer(Next, FileHandler, State1) end; +parse_form_value({body, Data}, {Name, Acc}, FileHandler, State) -> + Acc1 = [Data | Acc], + fun (Next) -> parse_form_value(Next, {Name, Acc1}, FileHandler, State) end. + +parse_form_file(body_end, {Name, Handler}, FileHandler, State) -> + Value = Handler(eof), + State1 = [{Name, Value} | State], + fun (Next) -> parse_form_outer(Next, FileHandler, State1) end; +parse_form_file({body, Data}, {Name, Handler}, FileHandler, State) -> + H1 = Handler(Data), + fun (Next) -> parse_form_file(Next, {Name, H1}, FileHandler, State) end. + +default_file_handler(Filename, ContentType) -> + default_file_handler_1(Filename, ContentType, []). + +default_file_handler_1(Filename, ContentType, Acc) -> + fun(eof) -> + Value = iolist_to_binary(lists:reverse(Acc)), + {Filename, ContentType, Value}; + (Next) -> + default_file_handler_1(Filename, ContentType, [Next | Acc]) + end. + +parse_multipart_request(Req, Callback) -> + %% TODO: Support chunked? + Length = list_to_integer(Req:get_header_value("content-length")), + Boundary = iolist_to_binary( + get_boundary(Req:get_header_value("content-type"))), + Prefix = <<"\r\n--", Boundary/binary>>, + BS = size(Boundary), + Chunk = read_chunk(Req, Length), + Length1 = Length - size(Chunk), + <<"--", Boundary:BS/binary, "\r\n", Rest/binary>> = Chunk, + feed_mp(headers, #mp{boundary=Prefix, + length=Length1, + buffer=Rest, + callback=Callback, + req=Req}). + +parse_headers(<<>>) -> + []; +parse_headers(Binary) -> + parse_headers(Binary, []). + +parse_headers(Binary, Acc) -> + case find_in_binary(<<"\r\n">>, Binary) of + {exact, N} -> + <> = Binary, + parse_headers(Rest, [split_header(Line) | Acc]); + not_found -> + lists:reverse([split_header(Binary) | Acc]) + end. + +split_header(Line) -> + {Name, [$: | Value]} = lists:splitwith(fun (C) -> C =/= $: end, + binary_to_list(Line)), + {string:to_lower(string:strip(Name)), + mochiweb_util:parse_header(Value)}. + +read_chunk(Req, Length) when Length > 0 -> + case Length of + Length when Length < ?CHUNKSIZE -> + Req:recv(Length); + _ -> + Req:recv(?CHUNKSIZE) + end. + +read_more(State=#mp{length=Length, buffer=Buffer, req=Req}) -> + Data = read_chunk(Req, Length), + Buffer1 = <>, + State#mp{length=Length - size(Data), + buffer=Buffer1}. + +feed_mp(headers, State=#mp{buffer=Buffer, callback=Callback}) -> + {State1, P} = case find_in_binary(<<"\r\n\r\n">>, Buffer) of + {exact, N} -> + {State, N}; + _ -> + S1 = read_more(State), + %% Assume headers must be less than ?CHUNKSIZE + {exact, N} = find_in_binary(<<"\r\n\r\n">>, + S1#mp.buffer), + {S1, N} + end, + <> = State1#mp.buffer, + NextCallback = Callback({headers, parse_headers(Headers)}), + feed_mp(body, State1#mp{buffer=Rest, + callback=NextCallback}); +feed_mp(body, State=#mp{boundary=Prefix, buffer=Buffer, callback=Callback}) -> + case find_boundary(Prefix, Buffer) of + {end_boundary, Start, Skip} -> + <> = Buffer, + C1 = Callback({body, Data}), + C2 = C1(body_end), + {State#mp.length, Rest, C2(eof)}; + {next_boundary, Start, Skip} -> + <> = Buffer, + C1 = Callback({body, Data}), + feed_mp(headers, State#mp{callback=C1(body_end), + buffer=Rest}); + {maybe, Start} -> + <> = Buffer, + feed_mp(body, read_more(State#mp{callback=Callback({body, Data}), + buffer=Rest})); + not_found -> + {Data, Rest} = {Buffer, <<>>}, + feed_mp(body, read_more(State#mp{callback=Callback({body, Data}), + buffer=Rest})) + end. + +get_boundary(ContentType) -> + {"multipart/form-data", Opts} = mochiweb_util:parse_header(ContentType), + case proplists:get_value("boundary", Opts) of + S when is_list(S) -> + S + end. + +find_in_binary(B, Data) when size(B) > 0 -> + case size(Data) - size(B) of + Last when Last < 0 -> + partial_find(B, Data, 0, size(Data)); + Last -> + find_in_binary(B, size(B), Data, 0, Last) + end. + +find_in_binary(B, BS, D, N, Last) when N =< Last-> + case D of + <<_:N/binary, B:BS/binary, _/binary>> -> + {exact, N}; + _ -> + find_in_binary(B, BS, D, 1 + N, Last) + end; +find_in_binary(B, BS, D, N, Last) when N =:= 1 + Last -> + partial_find(B, D, N, BS - 1). + +partial_find(_B, _D, _N, 0) -> + not_found; +partial_find(B, D, N, K) -> + <> = B, + case D of + <<_Skip:N/binary, B1:K/binary>> -> + {partial, N, K}; + _ -> + partial_find(B, D, 1 + N, K - 1) + end. + +find_boundary(Prefix, Data) -> + case find_in_binary(Prefix, Data) of + {exact, Skip} -> + PrefixSkip = Skip + size(Prefix), + case Data of + <<_:PrefixSkip/binary, "\r\n", _/binary>> -> + {next_boundary, Skip, size(Prefix) + 2}; + <<_:PrefixSkip/binary, "--\r\n", _/binary>> -> + {end_boundary, Skip, size(Prefix) + 4}; + _ when size(Data) < PrefixSkip + 4 -> + %% Underflow + {maybe, Skip}; + _ -> + %% False positive + not_found + end; + {partial, Skip, Length} when (Skip + Length) =:= size(Data) -> + %% Underflow + {maybe, Skip}; + _ -> + not_found + end. + +with_socket_server(ServerFun, ClientFun) -> + {ok, Server} = mochiweb_socket_server:start([{ip, "127.0.0.1"}, + {port, 0}, + {loop, ServerFun}]), + Port = mochiweb_socket_server:get(Server, port), + {ok, Client} = gen_tcp:connect("127.0.0.1", Port, + [binary, {active, false}]), + Res = (catch ClientFun(Client)), + mochiweb_socket_server:stop(Server), + Res. + +fake_request(Socket, ContentType, Length) -> + mochiweb_request:new(Socket, + 'POST', + "/multipart", + {1,1}, + mochiweb_headers:make( + [{"content-type", ContentType}, + {"content-length", Length}])). + +test_callback(Expect, [Expect | Rest]) -> + case Rest of + [] -> + ok; + _ -> + fun (Next) -> test_callback(Next, Rest) end + end. + +test_parse3() -> + ContentType = "multipart/form-data; boundary=---------------------------7386909285754635891697677882", + BinContent = <<"-----------------------------7386909285754635891697677882\r\nContent-Disposition: form-data; name=\"hidden\"\r\n\r\nmultipart message\r\n-----------------------------7386909285754635891697677882\r\nContent-Disposition: form-data; name=\"file\"; filename=\"test_file.txt\"\r\nContent-Type: text/plain\r\n\r\nWoo multiline text file\n\nLa la la\r\n-----------------------------7386909285754635891697677882--\r\n">>, + Expect = [{headers, + [{"content-disposition", + {"form-data", [{"name", "hidden"}]}}]}, + {body, <<"multipart message">>}, + body_end, + {headers, + [{"content-disposition", + {"form-data", [{"name", "file"}, {"filename", "test_file.txt"}]}}, + {"content-type", {"text/plain", []}}]}, + {body, <<"Woo multiline text file\n\nLa la la">>}, + body_end, + eof], + TestCallback = fun (Next) -> test_callback(Next, Expect) end, + ServerFun = fun (Socket) -> + case gen_tcp:send(Socket, BinContent) of + ok -> + exit(normal) + end + end, + ClientFun = fun (Socket) -> + Req = fake_request(Socket, ContentType, + size(BinContent)), + Res = parse_multipart_request(Req, TestCallback), + {0, <<>>, ok} = Res, + ok + end, + ok = with_socket_server(ServerFun, ClientFun), + ok. + + +test_parse2() -> + ContentType = "multipart/form-data; boundary=---------------------------6072231407570234361599764024", + BinContent = <<"-----------------------------6072231407570234361599764024\r\nContent-Disposition: form-data; name=\"hidden\"\r\n\r\nmultipart message\r\n-----------------------------6072231407570234361599764024\r\nContent-Disposition: form-data; name=\"file\"; filename=\"\"\r\nContent-Type: application/octet-stream\r\n\r\n\r\n-----------------------------6072231407570234361599764024--\r\n">>, + Expect = [{headers, + [{"content-disposition", + {"form-data", [{"name", "hidden"}]}}]}, + {body, <<"multipart message">>}, + body_end, + {headers, + [{"content-disposition", + {"form-data", [{"name", "file"}, {"filename", ""}]}}, + {"content-type", {"application/octet-stream", []}}]}, + {body, <<>>}, + body_end, + eof], + TestCallback = fun (Next) -> test_callback(Next, Expect) end, + ServerFun = fun (Socket) -> + case gen_tcp:send(Socket, BinContent) of + ok -> + exit(normal) + end + end, + ClientFun = fun (Socket) -> + Req = fake_request(Socket, ContentType, + size(BinContent)), + Res = parse_multipart_request(Req, TestCallback), + {0, <<>>, ok} = Res, + ok + end, + ok = with_socket_server(ServerFun, ClientFun), + ok. + +test_parse_form() -> + ContentType = "multipart/form-data; boundary=AaB03x", + "AaB03x" = get_boundary(ContentType), + Content = mochiweb_util:join( + ["--AaB03x", + "Content-Disposition: form-data; name=\"submit-name\"", + "", + "Larry", + "--AaB03x", + "Content-Disposition: form-data; name=\"files\";" + ++ "filename=\"file1.txt\"", + "Content-Type: text/plain", + "", + "... contents of file1.txt ...", + "--AaB03x--", + ""], "\r\n"), + BinContent = iolist_to_binary(Content), + ServerFun = fun (Socket) -> + case gen_tcp:send(Socket, BinContent) of + ok -> + exit(normal) + end + end, + ClientFun = fun (Socket) -> + Req = fake_request(Socket, ContentType, + size(BinContent)), + Res = parse_form(Req), + [{"submit-name", "Larry"}, + {"files", {"file1.txt", {"text/plain",[]}, + <<"... contents of file1.txt ...">>} + }] = Res, + ok + end, + ok = with_socket_server(ServerFun, ClientFun), + ok. + +test_parse() -> + ContentType = "multipart/form-data; boundary=AaB03x", + "AaB03x" = get_boundary(ContentType), + Content = mochiweb_util:join( + ["--AaB03x", + "Content-Disposition: form-data; name=\"submit-name\"", + "", + "Larry", + "--AaB03x", + "Content-Disposition: form-data; name=\"files\";" + ++ "filename=\"file1.txt\"", + "Content-Type: text/plain", + "", + "... contents of file1.txt ...", + "--AaB03x--", + ""], "\r\n"), + BinContent = iolist_to_binary(Content), + Expect = [{headers, + [{"content-disposition", + {"form-data", [{"name", "submit-name"}]}}]}, + {body, <<"Larry">>}, + body_end, + {headers, + [{"content-disposition", + {"form-data", [{"name", "files"}, {"filename", "file1.txt"}]}}, + {"content-type", {"text/plain", []}}]}, + {body, <<"... contents of file1.txt ...">>}, + body_end, + eof], + TestCallback = fun (Next) -> test_callback(Next, Expect) end, + ServerFun = fun (Socket) -> + case gen_tcp:send(Socket, BinContent) of + ok -> + exit(normal) + end + end, + ClientFun = fun (Socket) -> + Req = fake_request(Socket, ContentType, + size(BinContent)), + Res = parse_multipart_request(Req, TestCallback), + {0, <<>>, ok} = Res, + ok + end, + ok = with_socket_server(ServerFun, ClientFun), + ok. + +test_find_boundary() -> + B = <<"\r\n--X">>, + {next_boundary, 0, 7} = find_boundary(B, <<"\r\n--X\r\nRest">>), + {next_boundary, 1, 7} = find_boundary(B, <<"!\r\n--X\r\nRest">>), + {end_boundary, 0, 9} = find_boundary(B, <<"\r\n--X--\r\nRest">>), + {end_boundary, 1, 9} = find_boundary(B, <<"!\r\n--X--\r\nRest">>), + not_found = find_boundary(B, <<"--X\r\nRest">>), + {maybe, 0} = find_boundary(B, <<"\r\n--X\r">>), + {maybe, 1} = find_boundary(B, <<"!\r\n--X\r">>), + P = <<"\r\n-----------------------------16037454351082272548568224146">>, + B0 = <<55,212,131,77,206,23,216,198,35,87,252,118,252,8,25,211,132,229, + 182,42,29,188,62,175,247,243,4,4,0,59, 13,10,45,45,45,45,45,45,45, + 45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45, + 49,54,48,51,55,52,53,52,51,53,49>>, + {maybe, 30} = find_boundary(P, B0), + ok. + +test_find_in_binary() -> + {exact, 0} = find_in_binary(<<"foo">>, <<"foobarbaz">>), + {exact, 1} = find_in_binary(<<"oo">>, <<"foobarbaz">>), + {exact, 8} = find_in_binary(<<"z">>, <<"foobarbaz">>), + not_found = find_in_binary(<<"q">>, <<"foobarbaz">>), + {partial, 7, 2} = find_in_binary(<<"azul">>, <<"foobarbaz">>), + {exact, 0} = find_in_binary(<<"foobarbaz">>, <<"foobarbaz">>), + {partial, 0, 3} = find_in_binary(<<"foobar">>, <<"foo">>), + {partial, 1, 3} = find_in_binary(<<"foobar">>, <<"afoo">>), + ok. + +test() -> + test_find_in_binary(), + test_find_boundary(), + test_parse(), + test_parse2(), + test_parse3(), + test_parse_form(), + ok. diff --git a/deps/mochiweb-r88/src/mochiweb_request.erl b/deps/mochiweb-r88/src/mochiweb_request.erl new file mode 100644 index 0000000..311ed50 --- /dev/null +++ b/deps/mochiweb-r88/src/mochiweb_request.erl @@ -0,0 +1,740 @@ +%% @author Bob Ippolito +%% @copyright 2007 Mochi Media, Inc. + +%% @doc MochiWeb HTTP Request abstraction. + +-module(mochiweb_request, [Socket, Method, RawPath, Version, Headers]). +-author('bob@mochimedia.com'). + +-include_lib("kernel/include/file.hrl"). + +-define(QUIP, "Any of you quaids got a smint?"). +-define(READ_SIZE, 8192). + +-export([get_header_value/1, get_primary_header_value/1, get/1, dump/0]). +-export([send/1, recv/1, recv/2, recv_body/0, recv_body/1]). +-export([start_response/1, start_response_length/1, start_raw_response/1]). +-export([respond/1, ok/1]). +-export([not_found/0, not_found/1]). +-export([parse_post/0, parse_qs/0]). +-export([should_close/0, cleanup/0]). +-export([parse_cookie/0, get_cookie_value/1]). +-export([serve_file/2, serve_file/3]). +-export([test/0]). + +-define(SAVE_QS, mochiweb_request_qs). +-define(SAVE_PATH, mochiweb_request_path). +-define(SAVE_RECV, mochiweb_request_recv). +-define(SAVE_BODY, mochiweb_request_body). +-define(SAVE_BODY_LENGTH, mochiweb_request_body_length). +-define(SAVE_POST, mochiweb_request_post). +-define(SAVE_COOKIE, mochiweb_request_cookie). +-define(SAVE_FORCE_CLOSE, mochiweb_request_force_close). + +%% @type iolist() = [iolist() | binary() | char()]. +%% @type iodata() = binary() | iolist(). +%% @type key() = atom() | string() | binary() +%% @type value() = atom() | string() | binary() | integer() +%% @type headers(). A mochiweb_headers structure. +%% @type response(). A mochiweb_response parameterized module instance. +%% @type ioheaders() = headers() | [{key(), value()}]. + +% 10 second default idle timeout +-define(IDLE_TIMEOUT, 10000). + +% Maximum recv_body() length of 1MB +-define(MAX_RECV_BODY, (1024*1024)). + +%% @spec get_header_value(K) -> undefined | Value +%% @doc Get the value of a given request header. +get_header_value(K) -> + mochiweb_headers:get_value(K, Headers). + +get_primary_header_value(K) -> + mochiweb_headers:get_primary_value(K, Headers). + +%% @type field() = socket | method | raw_path | version | headers | peer | path | body_length | range + +%% @spec get(field()) -> term() +%% @doc Return the internal representation of the given field. +get(socket) -> + Socket; +get(method) -> + Method; +get(raw_path) -> + RawPath; +get(version) -> + Version; +get(headers) -> + Headers; +get(peer) -> + case inet:peername(Socket) of + {ok, {Addr={10, _, _, _}, _Port}} -> + case get_header_value("x-forwarded-for") of + undefined -> + inet_parse:ntoa(Addr); + Hosts -> + string:strip(lists:last(string:tokens(Hosts, ","))) + end; + {ok, {{127, 0, 0, 1}, _Port}} -> + case get_header_value("x-forwarded-for") of + undefined -> + "127.0.0.1"; + Hosts -> + string:strip(lists:last(string:tokens(Hosts, ","))) + end; + {ok, {Addr, _Port}} -> + inet_parse:ntoa(Addr) + end; +get(path) -> + case erlang:get(?SAVE_PATH) of + undefined -> + {Path0, _, _} = mochiweb_util:urlsplit_path(RawPath), + Path = mochiweb_util:unquote(Path0), + put(?SAVE_PATH, Path), + Path; + Cached -> + Cached + end; +get(body_length) -> + erlang:get(?SAVE_BODY_LENGTH); +get(range) -> + case get_header_value(range) of + undefined -> + undefined; + RawRange -> + parse_range_request(RawRange) + end. + +%% @spec dump() -> {mochiweb_request, [{atom(), term()}]} +%% @doc Dump the internal representation to a "human readable" set of terms +%% for debugging/inspection purposes. +dump() -> + {?MODULE, [{method, Method}, + {version, Version}, + {raw_path, RawPath}, + {headers, mochiweb_headers:to_list(Headers)}]}. + +%% @spec send(iodata()) -> ok +%% @doc Send data over the socket. +send(Data) -> + case gen_tcp:send(Socket, Data) of + ok -> + ok; + _ -> + exit(normal) + end. + +%% @spec recv(integer()) -> binary() +%% @doc Receive Length bytes from the client as a binary, with the default +%% idle timeout. +recv(Length) -> + recv(Length, ?IDLE_TIMEOUT). + +%% @spec recv(integer(), integer()) -> binary() +%% @doc Receive Length bytes from the client as a binary, with the given +%% Timeout in msec. +recv(Length, Timeout) -> + case gen_tcp:recv(Socket, Length, Timeout) of + {ok, Data} -> + put(?SAVE_RECV, true), + Data; + _ -> + exit(normal) + end. + +%% @spec body_length() -> undefined | chunked | unknown_transfer_encoding | integer() +%% @doc Infer body length from transfer-encoding and content-length headers. +body_length() -> + case get_header_value("transfer-encoding") of + undefined -> + case get_header_value("content-length") of + undefined -> + undefined; + Length -> + list_to_integer(Length) + end; + "chunked" -> + chunked; + Unknown -> + {unknown_transfer_encoding, Unknown} + end. + + +%% @spec recv_body() -> binary() +%% @doc Receive the body of the HTTP request (defined by Content-Length). +%% Will only receive up to the default max-body length of 1MB. +recv_body() -> + recv_body(?MAX_RECV_BODY). + +%% @spec recv_body(integer()) -> binary() +%% @doc Receive the body of the HTTP request (defined by Content-Length). +%% Will receive up to MaxBody bytes. +recv_body(MaxBody) -> + case get_header_value("expect") of + "100-continue" -> + start_raw_response({100, gb_trees:empty()}); + _Else -> + ok + end, + Body = case body_length() of + undefined -> + undefined; + {unknown_transfer_encoding, Unknown} -> + exit({unknown_transfer_encoding, Unknown}); + chunked -> + read_chunked_body(MaxBody, []); + 0 -> + <<>>; + Length when is_integer(Length), Length =< MaxBody -> + recv(Length); + Length -> + exit({body_too_large, Length}) + end, + put(?SAVE_BODY, Body), + Body. + + +%% @spec start_response({integer(), ioheaders()}) -> response() +%% @doc Start the HTTP response by sending the Code HTTP response and +%% ResponseHeaders. The server will set header defaults such as Server +%% and Date if not present in ResponseHeaders. +start_response({Code, ResponseHeaders}) -> + HResponse = mochiweb_headers:make(ResponseHeaders), + HResponse1 = mochiweb_headers:default_from_list(server_headers(), + HResponse), + start_raw_response({Code, HResponse1}). + +%% @spec start_raw_response({integer(), headers()}) -> response() +%% @doc Start the HTTP response by sending the Code HTTP response and +%% ResponseHeaders. +start_raw_response({Code, ResponseHeaders}) -> + F = fun ({K, V}, Acc) -> + [make_io(K), <<": ">>, V, <<"\r\n">> | Acc] + end, + End = lists:foldl(F, [<<"\r\n">>], + mochiweb_headers:to_list(ResponseHeaders)), + send([make_version(Version), make_code(Code), <<"\r\n">> | End]), + mochiweb:new_response({THIS, Code, ResponseHeaders}). + + +%% @spec start_response_length({integer(), ioheaders(), integer()}) -> response() +%% @doc Start the HTTP response by sending the Code HTTP response and +%% ResponseHeaders including a Content-Length of Length. The server +%% will set header defaults such as Server +%% and Date if not present in ResponseHeaders. +start_response_length({Code, ResponseHeaders, Length}) -> + HResponse = mochiweb_headers:make(ResponseHeaders), + HResponse1 = mochiweb_headers:enter("Content-Length", Length, HResponse), + start_response({Code, HResponse1}). + +%% @spec respond({integer(), ioheaders(), iodata() | chunked | {file, IoDevice}}) -> response() +%% @doc Start the HTTP response with start_response, and send Body to the +%% client (if the get(method) /= 'HEAD'). The Content-Length header +%% will be set by the Body length, and the server will insert header +%% defaults. +respond({Code, ResponseHeaders, {file, IoDevice}}) -> + Length = iodevice_size(IoDevice), + Response = start_response_length({Code, ResponseHeaders, Length}), + case Method of + 'HEAD' -> + ok; + _ -> + iodevice_stream(IoDevice) + end, + Response; +respond({Code, ResponseHeaders, chunked}) -> + HResponse = mochiweb_headers:make(ResponseHeaders), + HResponse1 = case Method of + 'HEAD' -> + %% This is what Google does, http://www.google.com/ + %% is chunked but HEAD gets Content-Length: 0. + %% The RFC is ambiguous so emulating Google is smart. + mochiweb_headers:enter("Content-Length", "0", + HResponse); + _ when Version >= {1, 1} -> + %% Only use chunked encoding for HTTP/1.1 + mochiweb_headers:enter("Transfer-Encoding", "chunked", + HResponse); + _ -> + %% For pre-1.1 clients we send the data as-is + %% without a Content-Length header and without + %% chunk delimiters. Since the end of the document + %% is now ambiguous we must force a close. + put(?SAVE_FORCE_CLOSE, true), + HResponse + end, + start_response({Code, HResponse1}); +respond({Code, ResponseHeaders, Body}) -> + Response = start_response_length({Code, ResponseHeaders, iolist_size(Body)}), + case Method of + 'HEAD' -> + ok; + _ -> + send(Body) + end, + Response. + +%% @spec not_found() -> response() +%% @doc Alias for not_found([]). +not_found() -> + not_found([]). + +%% @spec not_found(ExtraHeaders) -> response() +%% @doc Alias for respond({404, [{"Content-Type", "text/plain"} +%% | ExtraHeaders], <<"Not found.">>}). +not_found(ExtraHeaders) -> + respond({404, [{"Content-Type", "text/plain"} | ExtraHeaders], + <<"Not found.">>}). + +%% @spec ok({value(), iodata()} | {value(), ioheaders(), iodata() | {file, IoDevice}}) -> +%% response() +%% @doc respond({200, [{"Content-Type", ContentType} | Headers], Body}). +ok({ContentType, Body}) -> + ok({ContentType, [], Body}); +ok({ContentType, ResponseHeaders, Body}) -> + HResponse = mochiweb_headers:make(ResponseHeaders), + case THIS:get(range) of + X when X =:= undefined; X =:= fail -> + HResponse1 = mochiweb_headers:enter("Content-Type", ContentType, HResponse), + respond({200, HResponse1, Body}); + Ranges -> + {PartList, Size} = range_parts(Body, Ranges), + case PartList of + [] -> %% no valid ranges + HResponse1 = mochiweb_headers:enter("Content-Type", + ContentType, + HResponse), + %% could be 416, for now we'll just return 200 + respond({200, HResponse1, Body}); + PartList -> + {RangeHeaders, RangeBody} = + parts_to_body(PartList, ContentType, Size), + HResponse1 = mochiweb_headers:enter_from_list( + [{"Accept-Ranges", "bytes"} | + RangeHeaders], + HResponse), + respond({206, HResponse1, RangeBody}) + end + end. + +%% @spec should_close() -> bool() +%% @doc Return true if the connection must be closed. If false, using +%% Keep-Alive should be safe. +should_close() -> + ForceClose = erlang:get(mochiweb_request_force_close) =/= undefined, + DidNotRecv = erlang:get(mochiweb_request_recv) =:= undefined, + ForceClose orelse Version < {1, 0} + %% Connection: close + orelse get_header_value("connection") =:= "close" + %% HTTP 1.0 requires Connection: Keep-Alive + orelse (Version =:= {1, 0} + andalso get_header_value("connection") =/= "Keep-Alive") + %% unread data left on the socket, can't safely continue + orelse (DidNotRecv + andalso get_header_value("content-length") =/= undefined + andalso list_to_integer(get_header_value("content-length")) > 0) + orelse (DidNotRecv + andalso get_header_value("transfer-encoding") =:= "chunked"). + +%% @spec cleanup() -> ok +%% @doc Clean up any junk in the process dictionary, required before continuing +%% a Keep-Alive request. +cleanup() -> + [erase(K) || K <- [?SAVE_QS, + ?SAVE_PATH, + ?SAVE_RECV, + ?SAVE_BODY, + ?SAVE_POST, + ?SAVE_COOKIE, + ?SAVE_FORCE_CLOSE]], + ok. + +%% @spec parse_qs() -> [{Key::string(), Value::string()}] +%% @doc Parse the query string of the URL. +parse_qs() -> + case erlang:get(?SAVE_QS) of + undefined -> + {_, QueryString, _} = mochiweb_util:urlsplit_path(RawPath), + Parsed = mochiweb_util:parse_qs(QueryString), + put(?SAVE_QS, Parsed), + Parsed; + Cached -> + Cached + end. + +%% @spec get_cookie_value(Key::string) -> string() | undefined +%% @doc Get the value of the given cookie. +get_cookie_value(Key) -> + proplists:get_value(Key, parse_cookie()). + +%% @spec parse_cookie() -> [{Key::string(), Value::string()}] +%% @doc Parse the cookie header. +parse_cookie() -> + case erlang:get(?SAVE_COOKIE) of + undefined -> + Cookies = case get_header_value("cookie") of + undefined -> + []; + Value -> + mochiweb_cookies:parse_cookie(Value) + end, + put(?SAVE_COOKIE, Cookies), + Cookies; + Cached -> + Cached + end. + +%% @spec parse_post() -> [{Key::string(), Value::string()}] +%% @doc Parse an application/x-www-form-urlencoded form POST. This +%% has the side-effect of calling recv_body(). +parse_post() -> + case erlang:get(?SAVE_POST) of + undefined -> + Parsed = case recv_body() of + undefined -> + []; + Binary -> + case get_primary_header_value("content-type") of + "application/x-www-form-urlencoded" ++ _ -> + mochiweb_util:parse_qs(Binary); + _ -> + [] + end + end, + put(?SAVE_POST, Parsed), + Parsed; + Cached -> + Cached + end. + +read_chunked_body(Max, Acc) -> + case read_chunk_length() of + 0 -> + read_chunk(0), + iolist_to_binary(lists:reverse(Acc)); + Length when Length > Max -> + exit({body_too_large, chunked}); + Length -> + read_chunked_body(Max - Length, [read_chunk(Length) | Acc]) + end. + +%% @spec read_chunk_length() -> integer() +%% @doc Read the length of the next HTTP chunk. +read_chunk_length() -> + inet:setopts(Socket, [{packet, line}]), + case gen_tcp:recv(Socket, 0, ?IDLE_TIMEOUT) of + {ok, Header} -> + inet:setopts(Socket, [{packet, raw}]), + Splitter = fun (C) -> + C =/= $\r andalso C =/= $\n andalso C =/= $ + end, + {Hex, _Rest} = lists:splitwith(Splitter, binary_to_list(Header)), + mochihex:to_int(Hex); + _ -> + exit(normal) + end. + +%% @spec read_chunk(integer()) -> Chunk::binary() | [Footer::binary()] +%% @doc Read in a HTTP chunk of the given length. If Length is 0, then read the +%% HTTP footers (as a list of binaries, since they're nominal). +read_chunk(0) -> + inet:setopts(Socket, [{packet, line}]), + F = fun (F1, Acc) -> + case gen_tcp:recv(Socket, 0, ?IDLE_TIMEOUT) of + {ok, <<"\r\n">>} -> + Acc; + {ok, Footer} -> + F1(F1, [Footer | Acc]); + _ -> + exit(normal) + end + end, + Footers = F(F, []), + inet:setopts(Socket, [{packet, raw}]), + Footers; +read_chunk(Length) -> + case gen_tcp:recv(Socket, 2 + Length, ?IDLE_TIMEOUT) of + {ok, <>} -> + Chunk; + _ -> + exit(normal) + end. + +%% @spec serve_file(Path, DocRoot) -> Response +%% @doc Serve a file relative to DocRoot. +serve_file(Path, DocRoot) -> + serve_file(Path, DocRoot, []). + +%% @spec serve_file(Path, DocRoot, ExtraHeaders) -> Response +%% @doc Serve a file relative to DocRoot. +serve_file(Path, DocRoot, ExtraHeaders) -> + case mochiweb_util:safe_relative_path(Path) of + undefined -> + not_found(ExtraHeaders); + RelPath -> + FullPath = filename:join([DocRoot, RelPath]), + File = case filelib:is_dir(FullPath) of + true -> + filename:join([FullPath, "index.html"]); + false -> + FullPath + end, + case file:read_file_info(File) of + {ok, FileInfo} -> + LastModified = httpd_util:rfc1123_date(FileInfo#file_info.mtime), + case get_header_value("if-modified-since") of + LastModified -> + respond({304, ExtraHeaders, ""}); + _ -> + case file:open(File, [raw, binary]) of + {ok, IoDevice} -> + ContentType = mochiweb_util:guess_mime(File), + Res = ok({ContentType, + [{"last-modified", LastModified} + | ExtraHeaders], + {file, IoDevice}}), + file:close(IoDevice), + Res; + _ -> + not_found(ExtraHeaders) + end + end; + {error, _} -> + not_found(ExtraHeaders) + end + end. + + +%% Internal API + +server_headers() -> + [{"Server", "MochiWeb/1.0 (" ++ ?QUIP ++ ")"}, + {"Date", httpd_util:rfc1123_date()}]. + +make_io(Atom) when is_atom(Atom) -> + atom_to_list(Atom); +make_io(Integer) when is_integer(Integer) -> + integer_to_list(Integer); +make_io(Io) when is_list(Io); is_binary(Io) -> + Io. + +make_code(X) when is_integer(X) -> + [integer_to_list(X), [" " | httpd_util:reason_phrase(X)]]; +make_code(Io) when is_list(Io); is_binary(Io) -> + Io. + +make_version({1, 0}) -> + <<"HTTP/1.0 ">>; +make_version(_) -> + <<"HTTP/1.1 ">>. + +iodevice_stream(IoDevice) -> + case file:read(IoDevice, ?READ_SIZE) of + eof -> + ok; + {ok, Data} -> + ok = send(Data), + iodevice_stream(IoDevice) + end. + + +parts_to_body([{Start, End, Body}], ContentType, Size) -> + %% return body for a range reponse with a single body + HeaderList = [{"Content-Type", ContentType}, + {"Content-Range", + ["bytes ", + make_io(Start), "-", make_io(End), + "/", make_io(Size)]}], + {HeaderList, Body}; +parts_to_body(BodyList, ContentType, Size) when is_list(BodyList) -> + %% return + %% header Content-Type: multipart/byteranges; boundary=441934886133bdee4 + %% and multipart body + Boundary = mochihex:to_hex(crypto:rand_bytes(8)), + HeaderList = [{"Content-Type", + ["multipart/byteranges; ", + "boundary=", Boundary]}], + MultiPartBody = multipart_body(BodyList, ContentType, Boundary, Size), + + {HeaderList, MultiPartBody}. + +multipart_body([], _ContentType, Boundary, _Size) -> + ["--", Boundary, "--\r\n"]; +multipart_body([{Start, End, Body} | BodyList], ContentType, Boundary, Size) -> + ["--", Boundary, "\r\n", + "Content-Type: ", ContentType, "\r\n", + "Content-Range: ", + "bytes ", make_io(Start), "-", make_io(End), + "/", make_io(Size), "\r\n\r\n", + Body, "\r\n" + | multipart_body(BodyList, ContentType, Boundary, Size)]. + +iodevice_size(IoDevice) -> + {ok, Size} = file:position(IoDevice, eof), + {ok, 0} = file:position(IoDevice, bof), + Size. + +range_parts({file, IoDevice}, Ranges) -> + Size = iodevice_size(IoDevice), + F = fun (Spec, Acc) -> + case range_skip_length(Spec, Size) of + invalid_range -> + Acc; + V -> + [V | Acc] + end + end, + LocNums = lists:foldr(F, [], Ranges), + {ok, Data} = file:pread(IoDevice, LocNums), + Bodies = lists:zipwith(fun ({Skip, Length}, PartialBody) -> + {Skip, Skip + Length - 1, PartialBody} + end, + LocNums, Data), + {Bodies, Size}; + +range_parts(Body0, Ranges) -> + Body = iolist_to_binary(Body0), + Size = size(Body), + F = fun(Spec, Acc) -> + case range_skip_length(Spec, Size) of + invalid_range -> + Acc; + {Skip, Length} -> + <<_:Skip/binary, PartialBody:Length/binary, _/binary>> = Body, + [{Skip, Skip + Length - 1, PartialBody} | Acc] + end + end, + {lists:foldr(F, [], Ranges), Size}. + +range_skip_length(Spec, Size) -> + case Spec of + {none, R} when R =< Size, R >= 0 -> + {Size - R, R}; + {none, _OutOfRange} -> + {0, Size}; + {R, none} when R >= 0, R < Size -> + {R, Size - R}; + {_OutOfRange, none} -> + invalid_range; + {Start, End} when 0 =< Start, Start =< End, End < Size -> + {Start, End - Start + 1}; + {_OutOfRange, _End} -> + invalid_range + end. + +parse_range_request(RawRange) when is_list(RawRange) -> + try + "bytes=" ++ RangeString = RawRange, + Ranges = string:tokens(RangeString, ","), + lists:map(fun ("-" ++ V) -> + {none, list_to_integer(V)}; + (R) -> + case string:tokens(R, "-") of + [S1, S2] -> + {list_to_integer(S1), list_to_integer(S2)}; + [S] -> + {list_to_integer(S), none} + end + end, + Ranges) + catch + _:_ -> + fail + end. + + +test() -> + ok = test_range(), + ok. + +test_range() -> + %% valid, single ranges + io:format("Testing parse_range_request with valid single ranges~n"), + io:format("1"), + [{20, 30}] = parse_range_request("bytes=20-30"), + io:format("2"), + [{20, none}] = parse_range_request("bytes=20-"), + io:format("3"), + [{none, 20}] = parse_range_request("bytes=-20"), + io:format(".. ok ~n"), + + + %% invalid, single ranges + io:format("Testing parse_range_request with invalid ranges~n"), + io:format("1"), + fail = parse_range_request(""), + io:format("2"), + fail = parse_range_request("garbage"), + io:format("3"), + fail = parse_range_request("bytes=-20-30"), + io:format(".. ok ~n"), + + %% valid, multiple range + io:format("Testing parse_range_request with valid multiple ranges~n"), + io:format("1"), + [{20, 30}, {50, 100}, {110, 200}] = + parse_range_request("bytes=20-30,50-100,110-200"), + io:format("2"), + [{20, none}, {50, 100}, {none, 200}] = + parse_range_request("bytes=20-,50-100,-200"), + io:format(".. ok~n"), + + %% no ranges + io:format("Testing out parse_range_request with no ranges~n"), + io:format("1"), + [] = parse_range_request("bytes="), + io:format(".. ok~n"), + + Body = <<"012345678901234567890123456789012345678901234567890123456789">>, + BodySize = size(Body), %% 60 + BodySize = 60, + + %% these values assume BodySize =:= 60 + io:format("Testing out range_skip_length on valid ranges~n"), + io:format("1"), + {1,9} = range_skip_length({1,9}, BodySize), %% 1-9 + io:format("2"), + {10,10} = range_skip_length({10,19}, BodySize), %% 10-19 + io:format("3"), + {40, 20} = range_skip_length({none, 20}, BodySize), %% -20 + io:format("4"), + {30, 30} = range_skip_length({30, none}, BodySize), %% 30- + io:format(".. ok ~n"), + + %% valid edge cases for range_skip_length + io:format("Testing out range_skip_length on valid edge case ranges~n"), + io:format("1"), + {BodySize, 0} = range_skip_length({none, 0}, BodySize), + io:format("2"), + {0, BodySize} = range_skip_length({none, BodySize}, BodySize), + io:format("3"), + {0, BodySize} = range_skip_length({0, none}, BodySize), + BodySizeLess1 = BodySize - 1, + io:format("4"), + {BodySizeLess1, 1} = range_skip_length({BodySize - 1, none}, BodySize), + + %% out of range, return whole thing + io:format("5"), + {0, BodySize} = range_skip_length({none, BodySize + 1}, BodySize), + io:format("6"), + {0, BodySize} = range_skip_length({none, -1}, BodySize), + io:format(".. ok ~n"), + + %% invalid ranges + io:format("Testing out range_skip_length on invalid ranges~n"), + io:format("1"), + invalid_range = range_skip_length({-1, 30}, BodySize), + io:format("2"), + invalid_range = range_skip_length({0, BodySize + 1}, BodySize), + io:format("3"), + invalid_range = range_skip_length({-1, BodySize + 1}, BodySize), + io:format("4"), + invalid_range = range_skip_length({BodySize, 40}, BodySize), + io:format("5"), + invalid_range = range_skip_length({-1, none}, BodySize), + io:format("6"), + invalid_range = range_skip_length({BodySize, none}, BodySize), + io:format(".. ok ~n"), + ok. + diff --git a/deps/mochiweb-r88/src/mochiweb_response.erl b/deps/mochiweb-r88/src/mochiweb_response.erl new file mode 100644 index 0000000..6285c4c --- /dev/null +++ b/deps/mochiweb-r88/src/mochiweb_response.erl @@ -0,0 +1,56 @@ +%% @author Bob Ippolito +%% @copyright 2007 Mochi Media, Inc. + +%% @doc Response abstraction. + +-module(mochiweb_response, [Request, Code, Headers]). +-author('bob@mochimedia.com'). + +-define(QUIP, "Any of you quaids got a smint?"). + +-export([get_header_value/1, get/1, dump/0]). +-export([send/1, write_chunk/1]). + +%% @spec get_header_value(string() | atom() | binary()) -> string() | undefined +%% @doc Get the value of the given response header. +get_header_value(K) -> + mochiweb_headers:get_value(K, Headers). + +%% @spec get(request | code | headers) -> term() +%% @doc Return the internal representation of the given field. +get(request) -> + Request; +get(code) -> + Code; +get(headers) -> + Headers. + +%% @spec dump() -> {mochiweb_request, [{atom(), term()}]} +%% @doc Dump the internal representation to a "human readable" set of terms +%% for debugging/inspection purposes. +dump() -> + [{request, Request:dump()}, + {code, Code}, + {headers, mochiweb_headers:to_list(Headers)}]. + +%% @spec send(iodata()) -> ok +%% @doc Send data over the socket if the method is not HEAD. +send(Data) -> + case Request:get(method) of + 'HEAD' -> + ok; + _ -> + Request:send(Data) + end. + +%% @spec write_chunk(iodata()) -> ok +%% @doc Write a chunk of a HTTP chunked response. If Data is zero length, +%% then the chunked response will be finished. +write_chunk(Data) -> + case Request:get(version) of + Version when Version >= {1, 1} -> + Length = iolist_size(Data), + send([io_lib:format("~.16b\r\n", [Length]), Data, <<"\r\n">>]); + _ -> + send(Data) + end. diff --git a/deps/mochiweb-r88/src/mochiweb_skel.erl b/deps/mochiweb-r88/src/mochiweb_skel.erl new file mode 100644 index 0000000..a1c37f9 --- /dev/null +++ b/deps/mochiweb-r88/src/mochiweb_skel.erl @@ -0,0 +1,71 @@ +-module(mochiweb_skel). +-export([skelcopy/2]). + +-include_lib("kernel/include/file.hrl"). + +%% External API + +skelcopy(DestDir, Name) -> + ok = ensuredir(DestDir), + LDst = case length(filename:dirname(DestDir)) of + 1 -> %% handle case when dirname returns "/" + 0; + N -> + N + 1 + end, + skelcopy(src(), DestDir, Name, LDst), + ok = file:make_symlink( + filename:join(filename:dirname(code:which(?MODULE)), ".."), + filename:join([DestDir, Name, "deps", "mochiweb-src"])). + + +%% Internal API + +src() -> + Dir = filename:dirname(code:which(?MODULE)), + filename:join(Dir, "../priv/skel"). + +skel() -> + "skel". + +skelcopy(Src, DestDir, Name, LDst) -> + {ok, Dest, _} = regexp:gsub(filename:basename(Src), skel(), Name), + case file:read_file_info(Src) of + {ok, #file_info{type=directory, mode=Mode}} -> + Dir = DestDir ++ "/" ++ Dest, + EDst = lists:nthtail(LDst, Dir), + ok = ensuredir(Dir), + ok = file:write_file_info(Dir, #file_info{mode=Mode}), + {ok, Files} = file:list_dir(Src), + io:format("~s/~n", [EDst]), + lists:foreach(fun ("." ++ _) -> ok; + (F) -> + skelcopy(filename:join(Src, F), + Dir, + Name, + LDst) + end, + Files), + ok; + {ok, #file_info{type=regular, mode=Mode}} -> + OutFile = filename:join(DestDir, Dest), + {ok, B} = file:read_file(Src), + {ok, S, _} = regexp:gsub(binary_to_list(B), skel(), Name), + ok = file:write_file(OutFile, list_to_binary(S)), + ok = file:write_file_info(OutFile, #file_info{mode=Mode}), + io:format(" ~s~n", [filename:basename(Src)]), + ok; + {ok, _} -> + io:format("ignored source file: ~p~n", [Src]), + ok + end. + +ensuredir(Dir) -> + case file:make_dir(Dir) of + ok -> + ok; + {error, eexist} -> + ok; + E -> + E + end. diff --git a/deps/mochiweb-r88/src/mochiweb_socket_server.erl b/deps/mochiweb-r88/src/mochiweb_socket_server.erl new file mode 100644 index 0000000..d4853da --- /dev/null +++ b/deps/mochiweb-r88/src/mochiweb_socket_server.erl @@ -0,0 +1,248 @@ +%% @author Bob Ippolito +%% @copyright 2007 Mochi Media, Inc. + +%% @doc MochiWeb socket server. + +-module(mochiweb_socket_server). +-author('bob@mochimedia.com'). +-behaviour(gen_server). + +-export([start/1, stop/1]). +-export([init/1, handle_call/3, handle_cast/2, terminate/2, code_change/3, + handle_info/2]). +-export([get/2]). + +-export([acceptor_loop/1]). + +-record(mochiweb_socket_server, + {port, + loop, + name=undefined, + max=2048, + ip=any, + listen=null, + acceptor=null, + backlog=30}). + +start(State=#mochiweb_socket_server{}) -> + start_server(State); +start(Options) -> + start(parse_options(Options)). + +get(Name, Property) -> + gen_server:call(Name, {get, Property}). + +stop(Name) when is_atom(Name) -> + gen_server:cast(Name, stop); +stop(Pid) when is_pid(Pid) -> + gen_server:cast(Pid, stop); +stop({local, Name}) -> + stop(Name); +stop({global, Name}) -> + stop(Name); +stop(Options) -> + State = parse_options(Options), + stop(State#mochiweb_socket_server.name). + +%% Internal API + +parse_options(Options) -> + parse_options(Options, #mochiweb_socket_server{}). + +parse_options([], State) -> + State; +parse_options([{name, L} | Rest], State) when is_list(L) -> + Name = {local, list_to_atom(L)}, + parse_options(Rest, State#mochiweb_socket_server{name=Name}); +parse_options([{name, A} | Rest], State) when is_atom(A) -> + Name = {local, A}, + parse_options(Rest, State#mochiweb_socket_server{name=Name}); +parse_options([{name, Name} | Rest], State) -> + parse_options(Rest, State#mochiweb_socket_server{name=Name}); +parse_options([{port, L} | Rest], State) when is_list(L) -> + Port = list_to_integer(L), + parse_options(Rest, State#mochiweb_socket_server{port=Port}); +parse_options([{port, Port} | Rest], State) -> + parse_options(Rest, State#mochiweb_socket_server{port=Port}); +parse_options([{ip, Ip} | Rest], State) -> + ParsedIp = case Ip of + any -> + any; + Ip when is_tuple(Ip) -> + Ip; + Ip when is_list(Ip) -> + {ok, IpTuple} = inet_parse:address(Ip), + IpTuple + end, + parse_options(Rest, State#mochiweb_socket_server{ip=ParsedIp}); +parse_options([{loop, Loop} | Rest], State) -> + parse_options(Rest, State#mochiweb_socket_server{loop=Loop}); +parse_options([{backlog, Backlog} | Rest], State) -> + parse_options(Rest, State#mochiweb_socket_server{backlog=Backlog}); +parse_options([{max, Max} | Rest], State) -> + MaxInt = case Max of + Max when is_list(Max) -> + list_to_integer(Max); + Max when is_integer(Max) -> + Max + end, + parse_options(Rest, State#mochiweb_socket_server{max=MaxInt}). + +start_server(State=#mochiweb_socket_server{name=Name}) -> + case Name of + undefined -> + gen_server:start_link(?MODULE, State, []); + _ -> + gen_server:start_link(Name, ?MODULE, State, []) + end. + +ipv6_supported() -> + case (catch inet:getaddr("localhost", inet6)) of + {ok, _Addr} -> + true; + {error, _} -> + false + end. + +init(State=#mochiweb_socket_server{ip=Ip, port=Port, backlog=Backlog}) -> + process_flag(trap_exit, true), + BaseOpts = [binary, + {reuseaddr, true}, + {packet, 0}, + {backlog, Backlog}, + {recbuf, 8192}, + {active, false}, + {nodelay, true}], + Opts = case Ip of + any -> + case ipv6_supported() of % IPv4, and IPv6 if supported + true -> [inet, inet6 | BaseOpts]; + _ -> BaseOpts + end; + {_, _, _, _} -> % IPv4 + [inet, {ip, Ip} | BaseOpts]; + {_, _, _, _, _, _, _, _} -> % IPv6 + [inet6, {ip, Ip} | BaseOpts] + end, + case gen_tcp_listen(Port, Opts, State) of + {stop, eacces} -> + case Port < 1024 of + true -> + case fdsrv:start() of + {ok, _} -> + case fdsrv:bind_socket(tcp, Port) of + {ok, Fd} -> + gen_tcp_listen(Port, [{fd, Fd} | Opts], State); + _ -> + {stop, fdsrv_bind_failed} + end; + _ -> + {stop, fdsrv_start_failed} + end; + false -> + {stop, eacces} + end; + Other -> + Other + end. + +gen_tcp_listen(Port, Opts, State) -> + case gen_tcp:listen(Port, Opts) of + {ok, Listen} -> + {ok, ListenPort} = inet:port(Listen), + {ok, new_acceptor(State#mochiweb_socket_server{listen=Listen, + port=ListenPort})}; + {error, Reason} -> + {stop, Reason} + end. + +new_acceptor(State=#mochiweb_socket_server{max=0}) -> + io:format("Not accepting new connections~n"), + State#mochiweb_socket_server{acceptor=null}; +new_acceptor(State=#mochiweb_socket_server{listen=Listen,loop=Loop}) -> + Pid = proc_lib:spawn_link(?MODULE, acceptor_loop, + [{self(), Listen, Loop}]), + State#mochiweb_socket_server{acceptor=Pid}. + +call_loop({M, F}, Socket) -> + M:F(Socket); +call_loop(Loop, Socket) -> + Loop(Socket). + +acceptor_loop({Server, Listen, Loop}) -> + case catch gen_tcp:accept(Listen) of + {ok, Socket} -> + gen_server:cast(Server, {accepted, self()}), + call_loop(Loop, Socket); + {error, closed} -> + exit({error, closed}); + Other -> + error_logger:error_report( + [{application, mochiweb}, + "Accept failed error", + lists:flatten(io_lib:format("~p", [Other]))]), + exit({error, accept_failed}) + end. + + +do_get(port, #mochiweb_socket_server{port=Port}) -> + Port. + +handle_call({get, Property}, _From, State) -> + Res = do_get(Property, State), + {reply, Res, State}; +handle_call(_Message, _From, State) -> + Res = error, + {reply, Res, State}. + +handle_cast({accepted, Pid}, + State=#mochiweb_socket_server{acceptor=Pid, max=Max}) -> + % io:format("accepted ~p~n", [Pid]), + State1 = State#mochiweb_socket_server{max=Max - 1}, + {noreply, new_acceptor(State1)}; +handle_cast(stop, State) -> + {stop, normal, State}. + +terminate(_Reason, #mochiweb_socket_server{listen=Listen, port=Port}) -> + gen_tcp:close(Listen), + case Port < 1024 of + true -> + catch fdsrv:stop(), + ok; + false -> + ok + end. + +code_change(_OldVsn, State, _Extra) -> + State. + +handle_info({'EXIT', Pid, normal}, + State=#mochiweb_socket_server{acceptor=Pid}) -> + % io:format("normal acceptor down~n"), + {noreply, new_acceptor(State)}; +handle_info({'EXIT', Pid, Reason}, + State=#mochiweb_socket_server{acceptor=Pid}) -> + error_logger:error_report({?MODULE, ?LINE, + {acceptor_error, Reason}}), + timer:sleep(100), + {noreply, new_acceptor(State)}; +handle_info({'EXIT', _LoopPid, Reason}, + State=#mochiweb_socket_server{acceptor=Pid, max=Max}) -> + case Reason of + normal -> + ok; + _ -> + error_logger:error_report({?MODULE, ?LINE, + {child_error, Reason}}) + end, + State1 = State#mochiweb_socket_server{max=Max + 1}, + State2 = case Pid of + null -> + new_acceptor(State1); + _ -> + State1 + end, + {noreply, State2}; +handle_info(Info, State) -> + error_logger:info_report([{'INFO', Info}, {'State', State}]), + {noreply, State}. diff --git a/deps/mochiweb-r88/src/mochiweb_sup.erl b/deps/mochiweb-r88/src/mochiweb_sup.erl new file mode 100644 index 0000000..5cb525b --- /dev/null +++ b/deps/mochiweb-r88/src/mochiweb_sup.erl @@ -0,0 +1,34 @@ +%% @author Bob Ippolito +%% @copyright 2007 Mochi Media, Inc. + +%% @doc Supervisor for the mochiweb application. + +-module(mochiweb_sup). +-author('bob@mochimedia.com'). + +-behaviour(supervisor). + +%% External exports +-export([start_link/0, upgrade/0]). + +%% supervisor callbacks +-export([init/1]). + +%% @spec start_link() -> ServerRet +%% @doc API for starting the supervisor. +start_link() -> + supervisor:start_link({local, ?MODULE}, ?MODULE, []). + +%% @spec upgrade() -> ok +%% @doc Add processes if necessary. +upgrade() -> + {ok, {_, Specs}} = init([]), + [supervisor:start_child(?MODULE, Spec) || Spec <- Specs], + ok. + +%% @spec init([]) -> SupervisorTree +%% @doc supervisor callback, ensures yaws is in embedded mode and then +%% returns the supervisor tree. +init([]) -> + Processes = [], + {ok, {{one_for_one, 10, 10}, Processes}}. diff --git a/deps/mochiweb-r88/src/mochiweb_util.erl b/deps/mochiweb-r88/src/mochiweb_util.erl new file mode 100644 index 0000000..123122e --- /dev/null +++ b/deps/mochiweb-r88/src/mochiweb_util.erl @@ -0,0 +1,576 @@ +%% @author Bob Ippolito +%% @copyright 2007 Mochi Media, Inc. + +%% @doc Utilities for parsing and quoting. + +-module(mochiweb_util). +-author('bob@mochimedia.com'). +-export([join/2, quote_plus/1, urlencode/1, parse_qs/1, unquote/1]). +-export([path_split/1]). +-export([urlsplit/1, urlsplit_path/1, urlunsplit/1, urlunsplit_path/1]). +-export([guess_mime/1, parse_header/1]). +-export([shell_quote/1, cmd/1, cmd_string/1, cmd_port/2]). +-export([record_to_proplist/2, record_to_proplist/3]). +-export([safe_relative_path/1, partition/2]). +-export([test/0]). + +-define(PERCENT, 37). % $\% +-define(FULLSTOP, 46). % $\. +-define(IS_HEX(C), ((C >= $0 andalso C =< $9) orelse + (C >= $a andalso C =< $f) orelse + (C >= $A andalso C =< $F))). +-define(QS_SAFE(C), ((C >= $a andalso C =< $z) orelse + (C >= $A andalso C =< $Z) orelse + (C >= $0 andalso C =< $9) orelse + (C =:= ?FULLSTOP orelse C =:= $- orelse C =:= $~ orelse + C =:= $_))). + +hexdigit(C) when C < 10 -> $0 + C; +hexdigit(C) when C < 16 -> $A + (C - 10). + +unhexdigit(C) when C >= $0, C =< $9 -> C - $0; +unhexdigit(C) when C >= $a, C =< $f -> C - $a + 10; +unhexdigit(C) when C >= $A, C =< $F -> C - $A + 10. + +%% @spec partition(String, Sep) -> {String, [], []} | {Prefix, Sep, Postfix} +%% @doc Inspired by Python 2.5's str.partition: +%% partition("foo/bar", "/") = {"foo", "/", "bar"}, +%% partition("foo", "/") = {"foo", "", ""}. +partition(String, Sep) -> + case partition(String, Sep, []) of + undefined -> + {String, "", ""}; + Result -> + Result + end. + +partition("", _Sep, _Acc) -> + undefined; +partition(S, Sep, Acc) -> + case partition2(S, Sep) of + undefined -> + [C | Rest] = S, + partition(Rest, Sep, [C | Acc]); + Rest -> + {lists:reverse(Acc), Sep, Rest} + end. + +partition2(Rest, "") -> + Rest; +partition2([C | R1], [C | R2]) -> + partition2(R1, R2); +partition2(_S, _Sep) -> + undefined. + + + +%% @spec safe_relative_path(string()) -> string() | undefined +%% @doc Return the reduced version of a relative path or undefined if it +%% is not safe. safe relative paths can be joined with an absolute path +%% and will result in a subdirectory of the absolute path. +safe_relative_path("/" ++ _) -> + undefined; +safe_relative_path(P) -> + safe_relative_path(P, []). + +safe_relative_path("", Acc) -> + case Acc of + [] -> + ""; + _ -> + string:join(lists:reverse(Acc), "/") + end; +safe_relative_path(P, Acc) -> + case partition(P, "/") of + {"", "/", _} -> + %% /foo or foo//bar + undefined; + {"..", _, _} when Acc =:= [] -> + undefined; + {"..", _, Rest} -> + safe_relative_path(Rest, tl(Acc)); + {Part, "/", ""} -> + safe_relative_path("", ["", Part | Acc]); + {Part, _, Rest} -> + safe_relative_path(Rest, [Part | Acc]) + end. + +%% @spec shell_quote(string()) -> string() +%% @doc Quote a string according to UNIX shell quoting rules, returns a string +%% surrounded by double quotes. +shell_quote(L) -> + shell_quote(L, [$\"]). + +%% @spec cmd_port([string()], Options) -> port() +%% @doc open_port({spawn, mochiweb_util:cmd_string(Argv)}, Options). +cmd_port(Argv, Options) -> + open_port({spawn, cmd_string(Argv)}, Options). + +%% @spec cmd([string()]) -> string() +%% @doc os:cmd(cmd_string(Argv)). +cmd(Argv) -> + os:cmd(cmd_string(Argv)). + +%% @spec cmd_string([string()]) -> string() +%% @doc Create a shell quoted command string from a list of arguments. +cmd_string(Argv) -> + join([shell_quote(X) || X <- Argv], " "). + +%% @spec join([string()], Separator) -> string() +%% @doc Join a list of strings together with the given separator +%% string or char. +join([], _Separator) -> + []; +join([S], _Separator) -> + lists:flatten(S); +join(Strings, Separator) -> + lists:flatten(revjoin(lists:reverse(Strings), Separator, [])). + +revjoin([], _Separator, Acc) -> + Acc; +revjoin([S | Rest], Separator, []) -> + revjoin(Rest, Separator, [S]); +revjoin([S | Rest], Separator, Acc) -> + revjoin(Rest, Separator, [S, Separator | Acc]). + +%% @spec quote_plus(atom() | integer() | string() | binary()) -> string() +%% @doc URL safe encoding of the given term. +quote_plus(Atom) when is_atom(Atom) -> + quote_plus(atom_to_list(Atom)); +quote_plus(Int) when is_integer(Int) -> + quote_plus(integer_to_list(Int)); +quote_plus(Binary) when is_binary(Binary) -> + quote_plus(binary_to_list(Binary)); +quote_plus(String) -> + quote_plus(String, []). + +quote_plus([], Acc) -> + lists:reverse(Acc); +quote_plus([C | Rest], Acc) when ?QS_SAFE(C) -> + quote_plus(Rest, [C | Acc]); +quote_plus([$\s | Rest], Acc) -> + quote_plus(Rest, [$+ | Acc]); +quote_plus([C | Rest], Acc) -> + <> = <>, + quote_plus(Rest, [hexdigit(Lo), hexdigit(Hi), ?PERCENT | Acc]). + +%% @spec urlencode([{Key, Value}]) -> string() +%% @doc URL encode the property list. +urlencode(Props) -> + RevPairs = lists:foldl(fun ({K, V}, Acc) -> + [[quote_plus(K), $=, quote_plus(V)] | Acc] + end, [], Props), + lists:flatten(revjoin(RevPairs, $&, [])). + +%% @spec parse_qs(string() | binary()) -> [{Key, Value}] +%% @doc Parse a query string or application/x-www-form-urlencoded. +parse_qs(Binary) when is_binary(Binary) -> + parse_qs(binary_to_list(Binary)); +parse_qs(String) -> + parse_qs(String, []). + +parse_qs([], Acc) -> + lists:reverse(Acc); +parse_qs(String, Acc) -> + {Key, Rest} = parse_qs_key(String), + {Value, Rest1} = parse_qs_value(Rest), + parse_qs(Rest1, [{Key, Value} | Acc]). + +parse_qs_key(String) -> + parse_qs_key(String, []). + +parse_qs_key([], Acc) -> + {qs_revdecode(Acc), ""}; +parse_qs_key([$= | Rest], Acc) -> + {qs_revdecode(Acc), Rest}; +parse_qs_key(Rest=[$; | _], Acc) -> + {qs_revdecode(Acc), Rest}; +parse_qs_key(Rest=[$& | _], Acc) -> + {qs_revdecode(Acc), Rest}; +parse_qs_key([C | Rest], Acc) -> + parse_qs_key(Rest, [C | Acc]). + +parse_qs_value(String) -> + parse_qs_value(String, []). + +parse_qs_value([], Acc) -> + {qs_revdecode(Acc), ""}; +parse_qs_value([$; | Rest], Acc) -> + {qs_revdecode(Acc), Rest}; +parse_qs_value([$& | Rest], Acc) -> + {qs_revdecode(Acc), Rest}; +parse_qs_value([C | Rest], Acc) -> + parse_qs_value(Rest, [C | Acc]). + +%% @spec unquote(string() | binary()) -> string() +%% @doc Unquote a URL encoded string. +unquote(Binary) when is_binary(Binary) -> + unquote(binary_to_list(Binary)); +unquote(String) -> + qs_revdecode(lists:reverse(String)). + +qs_revdecode(S) -> + qs_revdecode(S, []). + +qs_revdecode([], Acc) -> + Acc; +qs_revdecode([$+ | Rest], Acc) -> + qs_revdecode(Rest, [$\s | Acc]); +qs_revdecode([Lo, Hi, ?PERCENT | Rest], Acc) when ?IS_HEX(Lo), ?IS_HEX(Hi) -> + qs_revdecode(Rest, [(unhexdigit(Lo) bor (unhexdigit(Hi) bsl 4)) | Acc]); +qs_revdecode([C | Rest], Acc) -> + qs_revdecode(Rest, [C | Acc]). + +%% @spec urlsplit(Url) -> {Scheme, Netloc, Path, Query, Fragment} +%% @doc Return a 5-tuple, does not expand % escapes. Only supports HTTP style +%% URLs. +urlsplit(Url) -> + {Scheme, Url1} = urlsplit_scheme(Url), + {Netloc, Url2} = urlsplit_netloc(Url1), + {Path, Query, Fragment} = urlsplit_path(Url2), + {Scheme, Netloc, Path, Query, Fragment}. + +urlsplit_scheme(Url) -> + urlsplit_scheme(Url, []). + +urlsplit_scheme([], Acc) -> + {"", lists:reverse(Acc)}; +urlsplit_scheme(":" ++ Rest, Acc) -> + {string:to_lower(lists:reverse(Acc)), Rest}; +urlsplit_scheme([C | Rest], Acc) -> + urlsplit_scheme(Rest, [C | Acc]). + +urlsplit_netloc("//" ++ Rest) -> + urlsplit_netloc(Rest, []); +urlsplit_netloc(Path) -> + {"", Path}. + +urlsplit_netloc(Rest=[C | _], Acc) when C =:= $/; C =:= $?; C =:= $# -> + {lists:reverse(Acc), Rest}; +urlsplit_netloc([C | Rest], Acc) -> + urlsplit_netloc(Rest, [C | Acc]). + + +%% @spec path_split(string()) -> {Part, Rest} +%% @doc Split a path starting from the left, as in URL traversal. +%% path_split("foo/bar") = {"foo", "bar"}, +%% path_split("/foo/bar") = {"", "foo/bar"}. +path_split(S) -> + path_split(S, []). + +path_split("", Acc) -> + {lists:reverse(Acc), ""}; +path_split("/" ++ Rest, Acc) -> + {lists:reverse(Acc), Rest}; +path_split([C | Rest], Acc) -> + path_split(Rest, [C | Acc]). + + +%% @spec urlunsplit({Scheme, Netloc, Path, Query, Fragment}) -> string() +%% @doc Assemble a URL from the 5-tuple. Path must be absolute. +urlunsplit({Scheme, Netloc, Path, Query, Fragment}) -> + lists:flatten([case Scheme of "" -> ""; _ -> [Scheme, "://"] end, + Netloc, + urlunsplit_path({Path, Query, Fragment})]). + +%% @spec urlunsplit_path({Path, Query, Fragment}) -> string() +%% @doc Assemble a URL path from the 3-tuple. +urlunsplit_path({Path, Query, Fragment}) -> + lists:flatten([Path, + case Query of "" -> ""; _ -> [$? | Query] end, + case Fragment of "" -> ""; _ -> [$# | Fragment] end]). + +%% @spec urlsplit_path(Url) -> {Path, Query, Fragment} +%% @doc Return a 3-tuple, does not expand % escapes. Only supports HTTP style +%% paths. +urlsplit_path(Path) -> + urlsplit_path(Path, []). + +urlsplit_path("", Acc) -> + {lists:reverse(Acc), "", ""}; +urlsplit_path("?" ++ Rest, Acc) -> + {Query, Fragment} = urlsplit_query(Rest), + {lists:reverse(Acc), Query, Fragment}; +urlsplit_path("#" ++ Rest, Acc) -> + {lists:reverse(Acc), "", Rest}; +urlsplit_path([C | Rest], Acc) -> + urlsplit_path(Rest, [C | Acc]). + +urlsplit_query(Query) -> + urlsplit_query(Query, []). + +urlsplit_query("", Acc) -> + {lists:reverse(Acc), ""}; +urlsplit_query("#" ++ Rest, Acc) -> + {lists:reverse(Acc), Rest}; +urlsplit_query([C | Rest], Acc) -> + urlsplit_query(Rest, [C | Acc]). + +%% @spec guess_mime(string()) -> string() +%% @doc Guess the mime type of a file by the extension of its filename. +guess_mime(File) -> + case filename:extension(File) of + ".html" -> + "text/html"; + ".xhtml" -> + "application/xhtml+xml"; + ".xml" -> + "application/xml"; + ".css" -> + "text/css"; + ".js" -> + "application/x-javascript"; + ".jpg" -> + "image/jpeg"; + ".gif" -> + "image/gif"; + ".png" -> + "image/png"; + ".swf" -> + "application/x-shockwave-flash"; + ".zip" -> + "application/zip"; + ".bz2" -> + "application/x-bzip2"; + ".gz" -> + "application/x-gzip"; + ".tar" -> + "application/x-tar"; + ".tgz" -> + "application/x-gzip"; + ".txt" -> + "text/plain"; + ".doc" -> + "application/msword"; + ".pdf" -> + "application/pdf"; + ".xls" -> + "application/vnd.ms-excel"; + ".rtf" -> + "application/rtf"; + ".mov" -> + "video/quicktime"; + ".mp3" -> + "audio/mpeg"; + ".z" -> + "application/x-compress"; + ".wav" -> + "audio/x-wav"; + ".ico" -> + "image/x-icon"; + ".bmp" -> + "image/bmp"; + ".m4a" -> + "audio/mpeg"; + ".m3u" -> + "audio/x-mpegurl"; + ".exe" -> + "application/octet-stream"; + ".csv" -> + "text/csv"; + _ -> + "text/plain" + end. + +%% @spec parse_header(string()) -> {Type, [{K, V}]} +%% @doc Parse a Content-Type like header, return the main Content-Type +%% and a property list of options. +parse_header(String) -> + %% TODO: This is exactly as broken as Python's cgi module. + %% Should parse properly like mochiweb_cookies. + [Type | Parts] = [string:strip(S) || S <- string:tokens(String, ";")], + F = fun (S, Acc) -> + case lists:splitwith(fun (C) -> C =/= $= end, S) of + {"", _} -> + %% Skip anything with no name + Acc; + {_, ""} -> + %% Skip anything with no value + Acc; + {Name, [$\= | Value]} -> + [{string:to_lower(string:strip(Name)), + unquote_header(string:strip(Value))} | Acc] + end + end, + {string:to_lower(Type), + lists:foldr(F, [], Parts)}. + +unquote_header("\"" ++ Rest) -> + unquote_header(Rest, []); +unquote_header(S) -> + S. + +unquote_header("", Acc) -> + lists:reverse(Acc); +unquote_header("\"", Acc) -> + lists:reverse(Acc); +unquote_header([$\\, C | Rest], Acc) -> + unquote_header(Rest, [C | Acc]); +unquote_header([C | Rest], Acc) -> + unquote_header(Rest, [C | Acc]). + +%% @spec record_to_proplist(Record, Fields) -> proplist() +%% @doc calls record_to_proplist/3 with a default TypeKey of '__record' +record_to_proplist(Record, Fields) -> + record_to_proplist(Record, Fields, '__record'). + +%% @spec record_to_proplist(Record, Fields, TypeKey) -> proplist() +%% @doc Return a proplist of the given Record with each field in the +%% Fields list set as a key with the corresponding value in the Record. +%% TypeKey is the key that is used to store the record type +%% Fields should be obtained by calling record_info(fields, record_type) +%% where record_type is the record type of Record +record_to_proplist(Record, Fields, TypeKey) + when is_tuple(Record), + is_list(Fields), + size(Record) - 1 =:= length(Fields) -> + lists:zip([TypeKey | Fields], tuple_to_list(Record)). + + +shell_quote([], Acc) -> + lists:reverse([$\" | Acc]); +shell_quote([C | Rest], Acc) when C =:= $\" orelse C =:= $\` orelse + C =:= $\\ orelse C =:= $\$ -> + shell_quote(Rest, [C, $\\ | Acc]); +shell_quote([C | Rest], Acc) -> + shell_quote(Rest, [C | Acc]). + +test() -> + test_join(), + test_quote_plus(), + test_unquote(), + test_urlencode(), + test_parse_qs(), + test_urlsplit_path(), + test_urlunsplit_path(), + test_urlsplit(), + test_urlunsplit(), + test_path_split(), + test_guess_mime(), + test_parse_header(), + test_shell_quote(), + test_cmd(), + test_cmd_string(), + test_partition(), + test_safe_relative_path(), + ok. + +test_shell_quote() -> + "\"foo \\$bar\\\"\\`' baz\"" = shell_quote("foo $bar\"`' baz"), + ok. + +test_cmd() -> + "$bling$ `word`!\n" = cmd(["echo", "$bling$ `word`!"]), + ok. + +test_cmd_string() -> + "\"echo\" \"\\$bling\\$ \\`word\\`!\"" = cmd_string(["echo", "$bling$ `word`!"]), + ok. + +test_parse_header() -> + {"multipart/form-data", [{"boundary", "AaB03x"}]} = + parse_header("multipart/form-data; boundary=AaB03x"), + ok. + +test_guess_mime() -> + "text/plain" = guess_mime(""), + "text/plain" = guess_mime(".text"), + "application/zip" = guess_mime(".zip"), + "application/zip" = guess_mime("x.zip"), + "text/html" = guess_mime("x.html"), + "application/xhtml+xml" = guess_mime("x.xhtml"), + ok. + +test_path_split() -> + {"", "foo/bar"} = path_split("/foo/bar"), + {"foo", "bar"} = path_split("foo/bar"), + {"bar", ""} = path_split("bar"), + ok. + +test_urlsplit() -> + {"", "", "/foo", "", "bar?baz"} = urlsplit("/foo#bar?baz"), + {"http", "host:port", "/foo", "", "bar?baz"} = + urlsplit("http://host:port/foo#bar?baz"), + ok. + +test_urlsplit_path() -> + {"/foo/bar", "", ""} = urlsplit_path("/foo/bar"), + {"/foo", "baz", ""} = urlsplit_path("/foo?baz"), + {"/foo", "", "bar?baz"} = urlsplit_path("/foo#bar?baz"), + {"/foo", "", "bar?baz#wibble"} = urlsplit_path("/foo#bar?baz#wibble"), + {"/foo", "bar", "baz"} = urlsplit_path("/foo?bar#baz"), + {"/foo", "bar?baz", "baz"} = urlsplit_path("/foo?bar?baz#baz"), + ok. + +test_urlunsplit() -> + "/foo#bar?baz" = urlunsplit({"", "", "/foo", "", "bar?baz"}), + "http://host:port/foo#bar?baz" = + urlunsplit({"http", "host:port", "/foo", "", "bar?baz"}), + ok. + +test_urlunsplit_path() -> + "/foo/bar" = urlunsplit_path({"/foo/bar", "", ""}), + "/foo?baz" = urlunsplit_path({"/foo", "baz", ""}), + "/foo#bar?baz" = urlunsplit_path({"/foo", "", "bar?baz"}), + "/foo#bar?baz#wibble" = urlunsplit_path({"/foo", "", "bar?baz#wibble"}), + "/foo?bar#baz" = urlunsplit_path({"/foo", "bar", "baz"}), + "/foo?bar?baz#baz" = urlunsplit_path({"/foo", "bar?baz", "baz"}), + ok. + +test_join() -> + "foo,bar,baz" = join(["foo", "bar", "baz"], $,), + "foo,bar,baz" = join(["foo", "bar", "baz"], ","), + "foo bar" = join([["foo", " bar"]], ","), + "foo bar,baz" = join([["foo", " bar"], "baz"], ","), + "foo" = join(["foo"], ","), + "foobarbaz" = join(["foo", "bar", "baz"], ""), + ok. + +test_quote_plus() -> + "foo" = quote_plus(foo), + "1" = quote_plus(1), + "foo" = quote_plus("foo"), + "foo+bar" = quote_plus("foo bar"), + "foo%0A" = quote_plus("foo\n"), + "foo%0A" = quote_plus("foo\n"), + "foo%3B%26%3D" = quote_plus("foo;&="), + ok. + +test_unquote() -> + "foo bar" = unquote("foo+bar"), + "foo bar" = unquote("foo%20bar"), + "foo\r\n" = unquote("foo%0D%0A"), + ok. + +test_urlencode() -> + "foo=bar&baz=wibble+%0D%0A&z=1" = urlencode([{foo, "bar"}, + {"baz", "wibble \r\n"}, + {z, 1}]), + ok. + +test_parse_qs() -> + [{"foo", "bar"}, {"baz", "wibble \r\n"}, {"z", "1"}] = + parse_qs("foo=bar&baz=wibble+%0D%0A&z=1"), + ok. + +test_partition() -> + {"foo", "", ""} = partition("foo", "/"), + {"foo", "/", "bar"} = partition("foo/bar", "/"), + {"foo", "/", ""} = partition("foo/", "/"), + {"", "/", "bar"} = partition("/bar", "/"), + {"f", "oo/ba", "r"} = partition("foo/bar", "oo/ba"), + ok. + +test_safe_relative_path() -> + "foo" = safe_relative_path("foo"), + "foo/" = safe_relative_path("foo/"), + "foo" = safe_relative_path("foo/bar/.."), + "bar" = safe_relative_path("foo/../bar"), + "bar/" = safe_relative_path("foo/../bar/"), + "" = safe_relative_path("foo/.."), + "" = safe_relative_path("foo/../"), + undefined = safe_relative_path("/foo"), + undefined = safe_relative_path("../foo"), + undefined = safe_relative_path("foo/../.."), + undefined = safe_relative_path("foo//"), + ok. diff --git a/deps/mochiweb-r88/src/reloader.erl b/deps/mochiweb-r88/src/reloader.erl new file mode 100644 index 0000000..2ff154b --- /dev/null +++ b/deps/mochiweb-r88/src/reloader.erl @@ -0,0 +1,124 @@ +%% @copyright 2007 Mochi Media, Inc. +%% @author Matthew Dempsky +%% +%% @doc Erlang module for automatically reloading modified modules +%% during development. + +-module(reloader). +-author("Matthew Dempsky "). + +-include_lib("kernel/include/file.hrl"). + +-behaviour(gen_server). +-export([start/0, start_link/0]). +-export([stop/0]). +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). + +-record(state, {last, tref}). + +%% External API + +%% @spec start() -> ServerRet +%% @doc Start the reloader. +start() -> + gen_server:start({local, ?MODULE}, ?MODULE, [], []). + +%% @spec start_link() -> ServerRet +%% @doc Start the reloader. +start_link() -> + gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). + +%% @spec stop() -> ok +%% @doc Stop the reloader. +stop() -> + gen_server:call(?MODULE, stop). + +%% gen_server callbacks + +%% @spec init([]) -> {ok, State} +%% @doc gen_server init, opens the server in an initial state. +init([]) -> + {ok, TRef} = timer:send_interval(timer:seconds(1), doit), + {ok, #state{last = stamp(), tref = TRef}}. + +%% @spec handle_call(Args, From, State) -> tuple() +%% @doc gen_server callback. +handle_call(stop, _From, State) -> + {stop, shutdown, stopped, State}; +handle_call(_Req, _From, State) -> + {reply, {error, badrequest}, State}. + +%% @spec handle_cast(Cast, State) -> tuple() +%% @doc gen_server callback. +handle_cast(_Req, State) -> + {noreply, State}. + +%% @spec handle_info(Info, State) -> tuple() +%% @doc gen_server callback. +handle_info(doit, State) -> + Now = stamp(), + doit(State#state.last, Now), + {noreply, State#state{last = Now}}; +handle_info(_Info, State) -> + {noreply, State}. + +%% @spec terminate(Reason, State) -> ok +%% @doc gen_server termination callback. +terminate(_Reason, State) -> + {ok, cancel} = timer:cancel(State#state.tref), + ok. + + +%% @spec code_change(_OldVsn, State, _Extra) -> State +%% @doc gen_server code_change callback (trivial). +code_change(_Vsn, State, _Extra) -> + {ok, State}. + +%% Internal API + +doit(From, To) -> + [case file:read_file_info(Filename) of + {ok, FileInfo} when FileInfo#file_info.mtime >= From, + FileInfo#file_info.mtime < To -> + reload(Module); + {ok, _} -> + unmodified; + {error, enoent} -> + %% The Erlang compiler deletes existing .beam files if + %% recompiling fails. Maybe it's worth spitting out a + %% warning here, but I'd want to limit it to just once. + gone; + {error, Reason} -> + io:format("Error reading ~s's file info: ~p~n", + [Filename, Reason]), + error + end || {Module, Filename} <- code:all_loaded(), is_list(Filename)]. + +reload(Module) -> + io:format("Reloading ~p ...", [Module]), + code:purge(Module), + case code:load_file(Module) of + {module, Module} -> + io:format(" ok.~n"), + case erlang:function_exported(Module, test, 0) of + true -> + io:format(" - Calling ~p:test() ...", [Module]), + case catch Module:test() of + ok -> + io:format(" ok.~n"), + reload; + Reason -> + io:format(" fail: ~p.~n", [Reason]), + reload_but_test_failed + end; + false -> + reload + end; + {error, Reason} -> + io:format(" fail: ~p.~n", [Reason]), + error + end. + + +stamp() -> + erlang:localtime(). diff --git a/deps/mochiweb-r88/support/include.mk b/deps/mochiweb-r88/support/include.mk new file mode 100644 index 0000000..64b50fe --- /dev/null +++ b/deps/mochiweb-r88/support/include.mk @@ -0,0 +1,46 @@ +## -*- makefile -*- + +###################################################################### +## Erlang + +ERL := erl +ERLC := $(ERL)c + +INCLUDE_DIRS := ../include $(wildcard ../deps/*/include) +EBIN_DIRS := $(wildcard ../deps/*/ebin) +ERLC_FLAGS := -W $(INCLUDE_DIRS:../%=-I ../%) $(EBIN_DIRS:%=-pa %) + +ifndef no_debug_info + ERLC_FLAGS += +debug_info +endif + +ifdef debug + ERLC_FLAGS += -Ddebug +endif + +EBIN_DIR := ../ebin +DOC_DIR := ../doc +EMULATOR := beam + +ERL_SOURCES := $(wildcard *.erl) +ERL_HEADERS := $(wildcard *.hrl) $(wildcard ../include/*.hrl) +ERL_OBJECTS := $(ERL_SOURCES:%.erl=$(EBIN_DIR)/%.$(EMULATOR)) +ERL_DOCUMENTS := $(ERL_SOURCES:%.erl=$(DOC_DIR)/%.html) +ERL_OBJECTS_LOCAL := $(ERL_SOURCES:%.erl=./%.$(EMULATOR)) +APP_FILES := $(wildcard *.app) +EBIN_FILES = $(ERL_OBJECTS) $(ERL_DOCUMENTS) $(APP_FILES:%.app=../ebin/%.app) +EBIN_FILES_NO_DOCS = $(ERL_OBJECTS) $(APP_FILES:%.app=../ebin/%.app) +MODULES = $(ERL_SOURCES:%.erl=%) + +../ebin/%.app: %.app + cp $< $@ + +$(EBIN_DIR)/%.$(EMULATOR): %.erl + $(ERLC) $(ERLC_FLAGS) -o $(EBIN_DIR) $< + +./%.$(EMULATOR): %.erl + $(ERLC) $(ERLC_FLAGS) -o . $< + +$(DOC_DIR)/%.html: %.erl + $(ERL) -noshell -run edoc file $< -run init stop + mv *.html $(DOC_DIR) diff --git a/doc/buildtoc.py b/doc/buildtoc.py new file mode 100644 index 0000000..5bc201c --- /dev/null +++ b/doc/buildtoc.py @@ -0,0 +1,63 @@ +import sys + +wantcounters = False +lines = sys.stdin.readlines() +toc = [] + +class Counter: + def __init__(self): + self.counters = [0] + + def next(self, depth): + global wantcounters + if not wantcounters: + return '' + if depth == 1: + return '' + depth = depth - 1 + while len(self.counters) < depth: + self.counters.append(0) + while len(self.counters) > depth: + self.counters.pop() + self.counters[-1] = self.counters[-1] + 1 + return '.'.join(str(x) for x in self.counters) + +def depthfor(h): + depth = 0 + while h[depth] == '#': + depth = depth + 1 + return depth + +def printtoc(): + c = Counter() + for (depth, h) in toc: + section = c.next(depth) + if depth > 1 and depth <= 3: + label = h[depth:].strip() + sys.stdout.write(' ' * (depth - 1)) + sys.stdout.write('- %s [%s](#%s)\n' % (section, label, anchorfor(h))) + +def anchorfor(line): + result = "" + for c in line: + if c.isalnum(): + result = result + c.lower() + return result + +for line in lines: + if line.startswith('@TOC@'): + wantcounters = True + if line.startswith('#'): + toc.append((depthfor(line), line)) + +c = Counter() +for line in lines: + if line.startswith('@TOC@'): + printtoc() + elif line.startswith('#'): + depth = depthfor(line) + section = c.next(depth) + sys.stdout.write('#' * depth) + sys.stdout.write(' %s %s' % (section, anchorfor(line), line[depth:])) + else: + sys.stdout.write(line) diff --git a/doc/footer.html b/doc/footer.html new file mode 100644 index 0000000..9b151ee --- /dev/null +++ b/doc/footer.html @@ -0,0 +1,4 @@ + + + + diff --git a/doc/header.html b/doc/header.html new file mode 100644 index 0000000..ca889f1 --- /dev/null +++ b/doc/header.html @@ -0,0 +1,12 @@ + + + + @TITLE@ + + + +
+ +
diff --git a/doc/index.md b/doc/index.md new file mode 100644 index 0000000..e21b4bd --- /dev/null +++ b/doc/index.md @@ -0,0 +1,4 @@ +# RabPubSubHub + +## Introduction + diff --git a/priv/www/background-banner.jpg b/priv/www/background-banner.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a1c6084459995e9d7a3dc9195eb77a8e78cdf47f GIT binary patch literal 6668 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 b%0WteAN^rUe83uXK`fws93NFpdp7-FZS@CF literal 0 HcmV?d00001 diff --git a/priv/www/index.html b/priv/www/index.html new file mode 100644 index 0000000..73410bd --- /dev/null +++ b/priv/www/index.html @@ -0,0 +1,16 @@ + + + RabPubSubHub + + + +
+
+

RabPubSubHub

+ +

+

+
+
+ + diff --git a/priv/www/style.css b/priv/www/style.css new file mode 100644 index 0000000..ead831d --- /dev/null +++ b/priv/www/style.css @@ -0,0 +1,106 @@ +/* + * e3e8e2 + * 63d2dd + * 154050 + * 6eaa52 + * ccfafa + */ + +body { + margin: 0; + padding: 0; + background: #154050; + + font-family: Georgia, "Times New Roman", Times, serif; +} + +h1, h2, h3, h4, h5, h6 { + font-weight: bold; +} + +h1 { + margin: -10px; + padding: 0.5em; + text-align: right; + background: #154050; + background-image: url(background-banner.jpg); + background-repeat: repeat-y; + color: #ccfafa; + font-weight: normal; + letter-spacing: 4px; + font-size: 250%; +} + +h2, h3, h4, h5, h6 { + background: #ccfafa; + margin-left: -10px; + margin-right: -10px; + padding-left: 10px; +} + +h3, h4, h5, h6 { + padding-left: 30px; +} + +div#outerContainer { + margin: 0 0 0 60px; + background: white; + width: 700; +} + +div#innerContainer { + padding: 10px; +} + +hr { + border: 0; + border-top: 1px solid #6eaa52; +} + +ul.mainNav { + text-align: right; + float: right: + clear: both; + margin: 0; + color: white; + background: #154050; + border-bottom: 2px solid #6eaa52; + border-right: 2px solid #6eaa52; +} +.mainNav li { + display: inline; + border: none; + font-variant: small-caps; + border-left: 2px solid #6eaa52; + border-top: 2px solid #6eaa52; + padding: 0 1em 0 1em; +} +.mainNav :link, .mainNav :visited { + color: white; + text-decoration: none; +} +.mainNav :hover, .mainNav :active { + color: white; + text-decoration: underline; +} + +th, td { + vertical-align: top; +} + +th { + text-align: left; + border-bottom: 1px solid #6eaa52; +} + +.meta-table { + width: 100%; +} + +pre { + background: #e3e8e2; +} + +.center { + text-align: center; +} diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..f3b0933 --- /dev/null +++ b/src/Makefile @@ -0,0 +1,11 @@ +include ../support/include.mk + +all: $(EBIN_FILES_NO_DOCS) + +docs: $(ERL_DOCUMENTS) + +debug: + $(MAKE) DEBUG=-DDEBUG + +clean: + rm -rf $(EBIN_FILES) diff --git a/src/rabpubsubhub.app b/src/rabpubsubhub.app new file mode 100644 index 0000000..7d2d284 --- /dev/null +++ b/src/rabpubsubhub.app @@ -0,0 +1,14 @@ +{application, rabpubsubhub, + [{description, "rabpubsubhub"}, + {vsn, "0.0.1"}, + {modules, [ + rabpubsubhub, + rabpubsubhub_app, + rabpubsubhub_sup, + rabpubsubhub_web, + rabpubsubhub_deps + ]}, + {registered, []}, + {mod, {rabpubsubhub_app, []}}, + {env, []}, + {applications, [kernel, stdlib, crypto]}]}. diff --git a/src/rabpubsubhub.erl b/src/rabpubsubhub.erl new file mode 100644 index 0000000..307aec9 --- /dev/null +++ b/src/rabpubsubhub.erl @@ -0,0 +1,20 @@ +-module(rabpubsubhub). +-export([start/0, stop/0]). + +ensure_started(App) -> + case application:start(App) of + ok -> + ok; + {error, {already_started, App}} -> + ok + end. + +start() -> + rabpubsubhub_deps:ensure(), + ensure_started(crypto), + application:start(rabpubsubhub). + +stop() -> + Res = application:stop(rabpubsubhub), + application:stop(crypto), + Res. diff --git a/src/rabpubsubhub_app.erl b/src/rabpubsubhub_app.erl new file mode 100644 index 0000000..be9a70f --- /dev/null +++ b/src/rabpubsubhub_app.erl @@ -0,0 +1,12 @@ +-module(rabpubsubhub_app). + +-behaviour(application). +-export([start/2,stop/1]). + + +start(_Type, _StartArgs) -> + rabpubsubhub_deps:ensure(), + rabpubsubhub_sup:start_link(). + +stop(_State) -> + ok. diff --git a/src/rabpubsubhub_deps.erl b/src/rabpubsubhub_deps.erl new file mode 100644 index 0000000..6ea69d1 --- /dev/null +++ b/src/rabpubsubhub_deps.erl @@ -0,0 +1,83 @@ +%% @author Mochiweb team + +%% @doc Ensure that the relatively-installed dependencies are on the code +%% loading path, and locate resources relative +%% to this application's path. + +-module(rabpubsubhub_deps). +-author('author '). + +-export([ensure/0, ensure/1]). +-export([get_base_dir/0, get_base_dir/1]). +-export([local_path/1, local_path/2]). +-export([deps_on_path/0, new_siblings/1]). + +%% @spec deps_on_path() -> [ProjNameAndVers] +%% @doc List of project dependencies on the path. +deps_on_path() -> + F = fun (X, Acc) -> + ProjDir = filename:dirname(X), + case {filename:basename(X), + filename:basename(filename:dirname(ProjDir))} of + {"ebin", "deps"} -> + [filename:basename(ProjDir) | Acc]; + _ -> + Acc + end + end, + ordsets:from_list(lists:foldl(F, [], code:get_path())). + +%% @spec new_siblings(Module) -> [Dir] +%% @doc Find new siblings paths relative to Module that aren't already on the +%% code path. +new_siblings(Module) -> + Existing = deps_on_path(), + SiblingEbin = filelib:wildcard(local_path(["deps", "*", "ebin"], Module)), + Siblings = [filename:dirname(X) || X <- SiblingEbin, + ordsets:is_element( + filename:basename(filename:dirname(X)), + Existing) =:= false], + lists:filter(fun filelib:is_dir/1, + lists:append([[filename:join([X, "ebin"]), + filename:join([X, "include"])] || + X <- Siblings])). + + +%% @spec ensure(Module) -> ok +%% @doc Ensure that all ebin and include paths for dependencies +%% of the application for Module are on the code path. +ensure(Module) -> + code:add_paths(new_siblings(Module)), + code:clash(), + ok. + +%% @spec ensure() -> ok +%% @doc Ensure that the ebin and include paths for dependencies of +%% this application are on the code path. Equivalent to +%% ensure(?Module). +ensure() -> + ensure(?MODULE). + +%% @spec get_base_dir(Module) -> string() +%% @doc Return the application directory for Module. It assumes Module is in +%% a standard OTP layout application in the ebin or src directory. +get_base_dir(Module) -> + {file, Here} = code:is_loaded(Module), + filename:dirname(filename:dirname(Here)). + +%% @spec get_base_dir() -> string() +%% @doc Return the application directory for this application. Equivalent to +%% get_base_dir(?MODULE). +get_base_dir() -> + get_base_dir(?MODULE). + +%% @spec local_path([string()], Module) -> string() +%% @doc Return an application-relative directory from Module's application. +local_path(Components, Module) -> + filename:join([get_base_dir(Module) | Components]). + +%% @spec local_path(Components) -> string() +%% @doc Return an application-relative directory for this application. +%% Equivalent to local_path(Components, ?MODULE). +local_path(Components) -> + local_path(Components, ?MODULE). diff --git a/src/rabpubsubhub_sup.erl b/src/rabpubsubhub_sup.erl new file mode 100644 index 0000000..4e6d7bd --- /dev/null +++ b/src/rabpubsubhub_sup.erl @@ -0,0 +1,50 @@ +%% @doc Supervisor for the rabpubsubhub application. + +-module(rabpubsubhub_sup). + +-behaviour(supervisor). + +%% External exports +-export([start_link/0, upgrade/0]). + +%% supervisor callbacks +-export([init/1]). + +%% @spec start_link() -> ServerRet +%% @doc API for starting the supervisor. +start_link() -> + supervisor:start_link({local, ?MODULE}, ?MODULE, []). + +%% @spec upgrade() -> ok +%% @doc Add processes if necessary. +upgrade() -> + {ok, {_, Specs}} = init([]), + + Old = sets:from_list( + [Name || {Name, _, _, _} <- supervisor:which_children(?MODULE)]), + New = sets:from_list([Name || {Name, _, _, _, _, _} <- Specs]), + Kill = sets:subtract(Old, New), + + sets:fold(fun (Id, ok) -> + supervisor:terminate_child(?MODULE, Id), + supervisor:delete_child(?MODULE, Id), + ok + end, ok, Kill), + + [supervisor:start_child(?MODULE, Spec) || Spec <- Specs], + ok. + +%% @spec init([]) -> SupervisorTree +%% @doc supervisor callback. +init([]) -> + Ip = case os:getenv("MOCHIWEB_IP") of false -> "0.0.0.0"; Any -> Any end, + WebConfig = [ + {ip, Ip}, + {port, 8000}, + {docroot, rabpubsubhub_deps:local_path(["priv", "www"])}], + Web = {rabpubsubhub_web, + {rabpubsubhub_web, start, [WebConfig]}, + permanent, 5000, worker, dynamic}, + + Processes = [Web], + {ok, {{one_for_one, 10, 10}, Processes}}. diff --git a/src/rabpubsubhub_web.erl b/src/rabpubsubhub_web.erl new file mode 100644 index 0000000..7e4e4ba --- /dev/null +++ b/src/rabpubsubhub_web.erl @@ -0,0 +1,39 @@ +%% @doc Web server for rabpubsubhub. + +-module(rabpubsubhub_web). + +-export([start/1, stop/0, loop/2]). + +%% External API + +start(Options) -> + {DocRoot, Options1} = get_option(docroot, Options), + Loop = fun (Req) -> + ?MODULE:loop(Req, DocRoot) + end, + mochiweb_http:start([{name, ?MODULE}, {loop, Loop} | Options1]). + +stop() -> + mochiweb_http:stop(?MODULE). + +loop(Req, DocRoot) -> + "/" ++ Path = Req:get(path), + case Req:get(method) of + Method when Method =:= 'GET'; Method =:= 'HEAD' -> + case Path of + _ -> + Req:serve_file(Path, DocRoot) + end; + 'POST' -> + case Path of + _ -> + Req:not_found() + end; + _ -> + Req:respond({501, [], []}) + end. + +%% Internal API + +get_option(Option, Options) -> + {proplists:get_value(Option, Options), proplists:delete(Option, Options)}. diff --git a/start-dev.sh b/start-dev.sh new file mode 100755 index 0000000..207f0de --- /dev/null +++ b/start-dev.sh @@ -0,0 +1,3 @@ +#!/bin/sh +cd `dirname $0` +exec erl -pa $PWD/ebin $PWD/deps/*/ebin -boot start_sasl -s reloader -s rabpubsubhub "$@" diff --git a/start.sh b/start.sh new file mode 100755 index 0000000..caaea41 --- /dev/null +++ b/start.sh @@ -0,0 +1,3 @@ +#!/bin/sh +cd `dirname $0` +exec erl -pa $PWD/ebin $PWD/deps/*/ebin -boot start_sasl -s rabpubsubhub "$@" diff --git a/support/include.mk b/support/include.mk new file mode 100644 index 0000000..20b8ad2 --- /dev/null +++ b/support/include.mk @@ -0,0 +1,46 @@ +## -*- makefile -*- + +###################################################################### +## Erlang + +ERL := erl +ERLC := $(ERL)c + +INCLUDE_DIRS := ../include $(wildcard ../deps/*/include) +EBIN_DIRS := $(wildcard ../deps/*/ebin) +ERLC_FLAGS := -W $(INCLUDE_DIRS:../%=-I ../%) $(EBIN_DIRS:%=-pa %) + +ifndef no_debug_info + ERLC_FLAGS += +debug_info +endif + +ifdef debug + ERLC_FLAGS += -Ddebug +endif + +EBIN_DIR := ../ebin +DOC_DIR := ../doc/edoc +EMULATOR := beam + +ERL_SOURCES := $(wildcard *.erl) +ERL_HEADERS := $(wildcard *.hrl) $(wildcard ../include/*.hrl) +ERL_OBJECTS := $(ERL_SOURCES:%.erl=$(EBIN_DIR)/%.$(EMULATOR)) +ERL_DOCUMENTS := $(ERL_SOURCES:%.erl=$(DOC_DIR)/%.html) +ERL_OBJECTS_LOCAL := $(ERL_SOURCES:%.erl=./%.$(EMULATOR)) +APP_FILES := $(wildcard *.app) +EBIN_FILES = $(ERL_OBJECTS) $(ERL_DOCUMENTS) $(APP_FILES:%.app=../ebin/%.app) +EBIN_FILES_NO_DOCS = $(ERL_OBJECTS) $(APP_FILES:%.app=../ebin/%.app) +MODULES = $(ERL_SOURCES:%.erl=%) + +../ebin/%.app: %.app + cp $< $@ + +$(EBIN_DIR)/%.$(EMULATOR): %.erl + $(ERLC) $(ERLC_FLAGS) -o $(EBIN_DIR) $< + +./%.$(EMULATOR): %.erl + $(ERLC) $(ERLC_FLAGS) -o . $< + +$(DOC_DIR)/%.html: %.erl + $(ERL) -noshell -run edoc file $< -run init stop + mv *.html $(DOC_DIR)