From e122e7a6ae21afacac17803e9f2243b489cc1b42 Mon Sep 17 00:00:00 2001 From: sysadmin Date: Sat, 29 Jul 2023 20:32:17 +0200 Subject: [PATCH] Initial commit --- .dirimages/archive.png | Bin 0 -> 6768 bytes .dirimages/audio.png | Bin 0 -> 6539 bytes .dirimages/bad.png | Bin 0 -> 7008 bytes .dirimages/css.png | Bin 0 -> 8800 bytes .dirimages/directory.png | Bin 0 -> 3652 bytes .dirimages/diskimage.png | Bin 0 -> 23257 bytes .dirimages/fifo.png | Bin 0 -> 4144 bytes .dirimages/font.png | Bin 0 -> 9131 bytes .dirimages/html.png | Bin 0 -> 10044 bytes .dirimages/hwdevice.png | Bin 0 -> 5965 bytes .dirimages/image.png | Bin 0 -> 6519 bytes .dirimages/important.png | Bin 0 -> 6696 bytes .dirimages/javascript.png | Bin 0 -> 7218 bytes .dirimages/other.png | Bin 0 -> 3133 bytes .dirimages/php.png | Bin 0 -> 8400 bytes .dirimages/return.png | Bin 0 -> 6874 bytes .dirimages/socket.png | Bin 0 -> 4205 bytes .dirimages/text.png | Bin 0 -> 5965 bytes .dirimages/video.png | Bin 0 -> 7234 bytes .gitignore | 2 + LICENSE | 21 + config.json | 86 + favicon.ico | Bin 0 -> 15406 bytes hexstrbase64/fail.png | Bin 0 -> 243 bytes hexstrbase64/hexstrbase64/base64.js | 124 + hexstrbase64/hexstrbase64/base64_browser.js | 123 + hexstrbase64/hexstrbase64/main.js | 129 + hexstrbase64/hexstrbase64/main_browser.js | 126 + hexstrbase64/index.js | 11 + hexstrbase64/ok.png | Bin 0 -> 252 bytes hexstrbase64/readme.css | 8 + hexstrbase64/readme.html | 31 + hexstrbase64/test/base64.html | 13 + hexstrbase64/test/base64.js | 56 + hexstrbase64/test/hex.html | 13 + hexstrbase64/test/hex.js | 56 + hexstrbase64/test/index.html | 16 + hviews.txt | 1 + index.html | 130 + lib/hexstrbase64/fail.png | Bin 0 -> 243 bytes lib/hexstrbase64/hexstrbase64/base64.js | 124 + .../hexstrbase64/base64_browser.js | 123 + lib/hexstrbase64/hexstrbase64/main.js | 129 + lib/hexstrbase64/hexstrbase64/main_browser.js | 126 + lib/hexstrbase64/index.js | 11 + lib/hexstrbase64/ok.png | Bin 0 -> 252 bytes lib/hexstrbase64/readme.css | 8 + lib/hexstrbase64/readme.html | 31 + lib/hexstrbase64/test/base64.html | 13 + lib/hexstrbase64/test/base64.js | 56 + lib/hexstrbase64/test/hex.html | 13 + lib/hexstrbase64/test/hex.js | 56 + lib/hexstrbase64/test/index.html | 16 + licenses/.index.html.kate-swp | Bin 0 -> 48 bytes licenses/asap.txt | 21 + licenses/asn1.js-rfc2560.txt | 0 licenses/asn1.js-rfc5280.txt | 0 licenses/asn1.js.txt | 0 licenses/async.txt | 19 + licenses/bn.js.txt | 19 + licenses/call-bind.txt | 21 + licenses/chownr.txt | 15 + licenses/dezalgo.txt | 15 + licenses/formidable.txt | 21 + licenses/fs-minipass.txt | 15 + licenses/function-bind.txt | 20 + licenses/get-intrinsic.txt | 21 + licenses/graceful-fs.txt | 15 + licenses/has-symbols.txt | 21 + licenses/has.txt | 22 + licenses/hexoid.txt | 9 + licenses/index.html | 357 ++ licenses/inherits.txt | 16 + licenses/mime-db.txt | 22 + licenses/mime-types.txt | 23 + licenses/minimalistic-assert.txt | 13 + licenses/minipass.txt | 15 + licenses/minizlib.txt | 26 + licenses/mkdirp.txt | 21 + licenses/object-inspect.txt | 21 + licenses/ocsp.txt | 21 + licenses/once.txt | 15 + licenses/pretty-bytes.txt | 9 + licenses/qs.txt | 29 + licenses/side-channel.txt | 21 + licenses/simple-lru-cache.txt | 19 + licenses/tar.txt | 15 + licenses/wrappy.txt | 15 + licenses/yallist.txt | 15 + loghighlight.js | 50 + logo.png | Bin 0 -> 31852 bytes logviewer.js | 175 + mods/primitiveanalytics.tar.gz | Bin 0 -> 1175 bytes powered.png | Bin 0 -> 7901 bytes serverSideScript.js | 90 + svr.js | 4828 +++++++++++++++++ svr_new.js | 1 + svrpasswd.js | 346 ++ testdir/.personalized/FOOT.html | 6 + testdir/.personalized/HEAD.html | 39 + testdir/.personalized/folder/FOOT.html | 6 + testdir/.personalized/folder/HEAD.html | 39 + testdir/.personalized/html.html | 0 testdir/.personalized/js.js | 0 testdir/.personalized/text.txt | 0 testdir/html.html | 0 testdir/js.js | 0 testdir/text.txt | 0 tests.html | 41 + views.txt | 1 + 110 files changed, 8201 insertions(+) create mode 100644 .dirimages/archive.png create mode 100644 .dirimages/audio.png create mode 100644 .dirimages/bad.png create mode 100644 .dirimages/css.png create mode 100644 .dirimages/directory.png create mode 100644 .dirimages/diskimage.png create mode 100644 .dirimages/fifo.png create mode 100644 .dirimages/font.png create mode 100644 .dirimages/html.png create mode 100644 .dirimages/hwdevice.png create mode 100644 .dirimages/image.png create mode 100644 .dirimages/important.png create mode 100644 .dirimages/javascript.png create mode 100644 .dirimages/other.png create mode 100644 .dirimages/php.png create mode 100644 .dirimages/return.png create mode 100644 .dirimages/socket.png create mode 100644 .dirimages/text.png create mode 100644 .dirimages/video.png create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 config.json create mode 100644 favicon.ico create mode 100644 hexstrbase64/fail.png create mode 100644 hexstrbase64/hexstrbase64/base64.js create mode 100644 hexstrbase64/hexstrbase64/base64_browser.js create mode 100644 hexstrbase64/hexstrbase64/main.js create mode 100644 hexstrbase64/hexstrbase64/main_browser.js create mode 100644 hexstrbase64/index.js create mode 100644 hexstrbase64/ok.png create mode 100644 hexstrbase64/readme.css create mode 100644 hexstrbase64/readme.html create mode 100644 hexstrbase64/test/base64.html create mode 100644 hexstrbase64/test/base64.js create mode 100644 hexstrbase64/test/hex.html create mode 100644 hexstrbase64/test/hex.js create mode 100644 hexstrbase64/test/index.html create mode 100644 hviews.txt create mode 100644 index.html create mode 100644 lib/hexstrbase64/fail.png create mode 100644 lib/hexstrbase64/hexstrbase64/base64.js create mode 100644 lib/hexstrbase64/hexstrbase64/base64_browser.js create mode 100644 lib/hexstrbase64/hexstrbase64/main.js create mode 100644 lib/hexstrbase64/hexstrbase64/main_browser.js create mode 100644 lib/hexstrbase64/index.js create mode 100644 lib/hexstrbase64/ok.png create mode 100644 lib/hexstrbase64/readme.css create mode 100644 lib/hexstrbase64/readme.html create mode 100644 lib/hexstrbase64/test/base64.html create mode 100644 lib/hexstrbase64/test/base64.js create mode 100644 lib/hexstrbase64/test/hex.html create mode 100644 lib/hexstrbase64/test/hex.js create mode 100644 lib/hexstrbase64/test/index.html create mode 100644 licenses/.index.html.kate-swp create mode 100644 licenses/asap.txt create mode 100644 licenses/asn1.js-rfc2560.txt create mode 100644 licenses/asn1.js-rfc5280.txt create mode 100644 licenses/asn1.js.txt create mode 100644 licenses/async.txt create mode 100644 licenses/bn.js.txt create mode 100644 licenses/call-bind.txt create mode 100644 licenses/chownr.txt create mode 100644 licenses/dezalgo.txt create mode 100644 licenses/formidable.txt create mode 100644 licenses/fs-minipass.txt create mode 100644 licenses/function-bind.txt create mode 100644 licenses/get-intrinsic.txt create mode 100644 licenses/graceful-fs.txt create mode 100644 licenses/has-symbols.txt create mode 100644 licenses/has.txt create mode 100644 licenses/hexoid.txt create mode 100644 licenses/index.html create mode 100644 licenses/inherits.txt create mode 100644 licenses/mime-db.txt create mode 100644 licenses/mime-types.txt create mode 100644 licenses/minimalistic-assert.txt create mode 100644 licenses/minipass.txt create mode 100644 licenses/minizlib.txt create mode 100644 licenses/mkdirp.txt create mode 100644 licenses/object-inspect.txt create mode 100644 licenses/ocsp.txt create mode 100644 licenses/once.txt create mode 100644 licenses/pretty-bytes.txt create mode 100644 licenses/qs.txt create mode 100644 licenses/side-channel.txt create mode 100644 licenses/simple-lru-cache.txt create mode 100644 licenses/tar.txt create mode 100644 licenses/wrappy.txt create mode 100644 licenses/yallist.txt create mode 100644 loghighlight.js create mode 100644 logo.png create mode 100644 logviewer.js create mode 100644 mods/primitiveanalytics.tar.gz create mode 100644 powered.png create mode 100644 serverSideScript.js create mode 100644 svr.js create mode 100644 svr_new.js create mode 100644 svrpasswd.js create mode 100644 testdir/.personalized/FOOT.html create mode 100644 testdir/.personalized/HEAD.html create mode 100644 testdir/.personalized/folder/FOOT.html create mode 100644 testdir/.personalized/folder/HEAD.html create mode 100644 testdir/.personalized/html.html create mode 100644 testdir/.personalized/js.js create mode 100644 testdir/.personalized/text.txt create mode 100644 testdir/html.html create mode 100644 testdir/js.js create mode 100644 testdir/text.txt create mode 100644 tests.html create mode 100644 views.txt diff --git a/.dirimages/archive.png b/.dirimages/archive.png new file mode 100644 index 0000000000000000000000000000000000000000..3ebc9954ee1722049cf7c24323d7a4ff28c52c0b GIT binary patch literal 6768 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 literal 0 HcmV?d00001 diff --git a/.dirimages/audio.png b/.dirimages/audio.png new file mode 100644 index 0000000000000000000000000000000000000000..5b6d4c56a09e3b0b6a01504e1766be18d5347394 GIT binary patch literal 6539 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 aJG!rH8w`&3P8a`idNqi)QkBB1cmD%OR6TV7 literal 0 HcmV?d00001 diff --git a/.dirimages/bad.png b/.dirimages/bad.png new file mode 100644 index 0000000000000000000000000000000000000000..115fb0e329d77d42a19cb35011e4582b78ca7ce9 GIT binary patch literal 7008 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 JwTMCB{{TI8(G36q literal 0 HcmV?d00001 diff --git a/.dirimages/directory.png b/.dirimages/directory.png new file mode 100644 index 0000000000000000000000000000000000000000..9dcd620911ef936b83085ca857dc5a730ef62d31 GIT binary patch literal 3652 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 literal 0 HcmV?d00001 diff --git a/.dirimages/diskimage.png b/.dirimages/diskimage.png new file mode 100644 index 0000000000000000000000000000000000000000..4306ca2ac7d50729f7d390a7b25c7e9d1067eed1 GIT binary patch literal 23257 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

Qq_bub5@3NOnD;Y)6Aun$PB$ zHGYRyOF+$DquzHw1&G`lYAHqZl7@<$<`$UCJflf#2PUgHnM6x>dWIT};Lh&kC$8@`ww)Oz)>T)O%_KaH*G?9a_S-w{^*%!pL5&8NdtbE2%lf;bCFK!s)S+n z(9R|@AgfY0C8~IhVo%L!6s!1@kEK82&fAV0fyUHeM_-U-l?U@c&_L1vX@N>V0)>fj8qF<(}wSP%b- z8Pq5zbe2e=yr^S2gT<=1bVZ91DMGommO|SmftfKeI}68g4|gUfKKkS{&-`6HLJmI? z-o&Y=p7z;OPd)9Y2=sgsTvbW%SaQC)SGXqbR3kjravH7hvz{cBBZPo8C!ri|w|drZ zIS^VzM9}JXvF*eYZ=9T&eeqe(ea?(dk~iWJbO?{YbN%(#p9A1L5IqA{BWB$sRKq_D zgd!ORAK9jv0tH+xP8M2TvJK$rkUNOvVVx=LczykQb=7Ni>$fo)UL(N7^fXAR%NjxR zy{A0+v`+!R%{SaYC!KtK>nJ3kend1NngE zk^%BufX-riQ;P^B^YV38<1adjwMG<0GP`jjDcAjK&}v_s#K{GxopJi@kK}9oh-QFH z8#n--Eh2A);G9COQ$(AYXzuQmy5?% zK0t`l2CnQiO|}5C&;Z4DP@PGCs$Du06YzX50@0s)p8KuEh2Gbm_N=FyN6U46)G$DP zzO?I$bS$AS?%?F|2xWYEOM=>AAM9W&6w&!^&8jtVv z8;vbtIKH;gXq=Wr@lNG9Pa$Bp(R!2Xc`iH7_e7iD4qDBd4(!|8>`qSwC!BENodB@7 zxJXk|Q-byL$UWb^;=A6BH{SUCn{K+P@q-`!;0e2T-+bZ~mw&gpd-v{V0>JK@Z$1YA zZn@>ArvSiBKfGbf?R)l2T=Ks6@t#|6J#PI9uAlX@e%8e_r_n9%{C9*#H0l07*qoM6N<$g2ITS+yDRo literal 0 HcmV?d00001 diff --git a/.dirimages/fifo.png b/.dirimages/fifo.png new file mode 100644 index 0000000000000000000000000000000000000000..79a24f7d91f22139b3ea768abe8e3d448c2e95d8 GIT binary patch literal 4144 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 literal 0 HcmV?d00001 diff --git a/.dirimages/font.png b/.dirimages/font.png new file mode 100644 index 0000000000000000000000000000000000000000..58942e9f4ca55e83be1733c090fdbe0e2d9c3429 GIT binary patch literal 9131 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 literal 0 HcmV?d00001 diff --git a/.dirimages/html.png b/.dirimages/html.png new file mode 100644 index 0000000000000000000000000000000000000000..0bd299325bb2ce03ada2a8824645f9cc523efafd GIT binary patch literal 10044 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 literal 0 HcmV?d00001 diff --git a/.dirimages/image.png b/.dirimages/image.png new file mode 100644 index 0000000000000000000000000000000000000000..5c9d72684927e91f99a116c7b1a3055df4b43b30 GIT binary patch literal 6519 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 JTHGk;zW{hWX2t*j literal 0 HcmV?d00001 diff --git a/.dirimages/important.png b/.dirimages/important.png new file mode 100644 index 0000000000000000000000000000000000000000..b68e876f6263682530b06fe34b5c6c20d4c5a3fa GIT binary patch literal 6696 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Ŀzu9ZAA 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 S!MgOH^))s$N7WcOMgJRN5{qU4 literal 0 HcmV?d00001 diff --git a/.dirimages/php.png b/.dirimages/php.png new file mode 100644 index 0000000000000000000000000000000000000000..93e9834a18ecc583d4b5bdaee2cfcbe69bfba4e7 GIT binary patch literal 8400 zcmbVSMN}L>lOEjN-92~+?lxG^;O;KL-3NDfx8MW`4#9#C1b25IT>t!UZ+qCo9;&PA zb(eG<`m0y3qg1}iq9GF_0{{RtIiQrKMk@@A#O9 ztQ&6oiGFNs>^yibPD0Q|=%%ZC11Ha20(X60g%Zy%>vL|rM$$&a5_mV@Z+)6}Bc250 z>N;OK&=P`AIs$<$dy7>bp1tuSaaPO;AE!c|hWDI50(k2QBnNNDH6A zNQxf6f#LelK#A8IdmSu_(5{D1zV*KDthChGb)Cg*5PUL!5Q(7V6Qpra z>)_+h^poGWp0W8l@v%dmx~av6d4Z;;I5dwH%Qk~U_oaT^P`&tEzC#e?P?t(}9<5#o z&VTuwYdNPU@#l8>b^iDTKY?$)-ZTV0CU>!U?c`JFa>~0?&xEqm`*_B(FQe2w^RzZ6 zdqsyO@mOw;D*;mDOH!K`^y~c{@S`A5zJ?+6I;_2+&*RQFlKi}2<%VH8|Ao5V6GbQG z&(53=q$85`EG{Z*W$3LWw0@^aCjmQqb>!Cq$Edi7Va{Idl|373X)|%-Objq z=SE~tjWxqiFpBmj-6Rn?r;Hv>Y&$c8X{N|IVrm-4`h=^d$h1$JXt#WrX!e!=Fxk#= zC_}|mjxIyZ)@J@22K;cgZC%;IN;Y>K0?)AR+UBoWg9qW;CH3j<+m+QqKN&)o(j-|T z_vYjBg1$jT?rE-5`|)fzwVH+5`px@KKb`mT| z_p(}jVC49I{KLClv6i7hI2~2yZrwDKwcZjb&z0w_#kK93{Otz~=v-4dAJ}TpU6m5N z=K8vzV=>8ncisBjHk9G1xcl7Z&2+!t78biTLy z{CJ}E(CO97+kGIhPGvopf9uctqp0@W$9wJeuQ0zy=MJi6M!Q~nfzX(z^1FvjeDHnU zC~;h1dpDy8#j1S@@<~P=evYSgNo&&g`sxWg_SK_~2CFL=jmWx-c8?oB-f9bA-DI`u z`ddyXIqw~p59HlekZ%LoWP{tzOnMhVKjkD#vX)7GG-kA%%T?UyehSX&uFg|ZNWeYD z;f49K+;+tb%rUM|(&g*#fi`)+!aH536KfvOVe&P*`^S41vig%4VxSM$o5yHk>)hS1 z<`$RG`_5L-q@9tkH+fd$$bcL1N-7>ovg-LzV1P8`OHUupQlmwO~X?Gs41iT+~L>w3fStlHkb6+lsrN!UK`T|hzdjU(HNSvbPrOjYFxg+>r zq8OX%AWB!^_NOJy1G5Sv3b8k`@&Sxagu9I+D>x*91LQ33(pj}$o$uEKaIeI5ZG4Q# z&>zY}FBhPUhd{)?9TSFAmO(ADH$-~yQ0NFvQdbfKDe4(Ba#ZS#wQaFmk!)L3Gr}Ts z`E(i1i4GYXp#=*LrTIBj=OVW3c-T^JHNaE?Q&?E*TEia9I1Scl_-g~%U^@rxavjSm zhW%seC5~r#*DzhN2ex%83&xD30XP!gHdV`IE{MUWI2y&fo?1!SG&>EBr>yWO$S2z6 zhIS)3GFcb)V8ihV>-{<9J4>At!LM8l;~f35M=XMR2gmv$5Mzo?s%Jb}W=|$Gs#Muq zax4$5G;JN$aJ(AWT|sDQ%M`utw? zXESSRMe3%ycmV1;?r`p^4<4{%Oq&2}g%&$FKAP$Rt4b`B5nT{0qTYftf;`O}^6bfL z6bfx1M%=5#G>X$g#(`V99c%q5@+C<3oVhztI^cVS4An-=#i|Xt6r%SWAHm(*eb3V~ zh<000YO7VfRRX#6n9VKo?sm0BMnbS+rF`STB0HE(pa|9+eq$6J`!8>};4zyleIiYR z0jqYlK(GOFpH)jsMfzWfg4o9Wc;uqJ-3GE1Kb`^om|>E?&0|%0hE|X& zwpIyjS(i8(Rb=L*hM7OqHw}e-H!Zh+EklZx;HC9DWf6kSJEQLxqy@cb1ImDnFhjZd zKUx(d+$8w6pAQTfq7;Uwa6Gers2Sj4MOR5asWlPGgVcFL_4>ktS;e4#>;wUD6=n?Uuw350lEb_&=4&qiDHLTJCkn796pPU(qV33_ri_age*Fs0Nl`(wI zntb%OxVVjVk;sLUIs2LjH%Wvoye)IiRL)0GS3)s-skwO?eV-$cknue}rr(u;#^@=m5R`c7^ zHp`Xu(UouNSm}V)kFUA>w1QT&Z608#Yoe4bnut86er%hOm2@yDfsr`01F+}>RtmX8 zUo9T+J0kF72+T_2It#DCLUu~NA%^Vbqsmp`txbl7Z;wN;=49Ev;lbC&DcQlq3=ufA zlhWa=SAXLEC?D_1usGOsTYhMo=;{n!!k@$c)w3r8Q@hCA75VvQ>ypnR`YESh7nQLV z^Qz<4q?!KrZb?cRXRrlOffTKG=qnqNc=5+grXcX1bX1=}w;I(*&Fv@%Wm0;Skkca# zy{;@Yn%1Klx#%3KQ;Q(cU{K0jq0>SaQDCfSfz$&tR|OL>#1^EdJ~Rrv8j!dvL=^Ow z8aoEs=EmDAxJvNw)8`idB3#78c+_Rb>QRc@p>vyCn^AXYK1aS!j!LTI->KP)tkcQ! zZiAI+ZGcYbQvYI1?mQcbkR>%gRZv2~Hk5&#HV!>ghtQPQ-I_ZbL{YhE*nG+lYp)_V zWC-=1Guo$*l8oTI{o|)N#+zh+l>Ili{X+FfROxfF(;{vXTf7-WEe%@W-LP3j?ju5( z?5!46`Br5$8?>>CNy+x9L)dZQ7Ld`>nWvQXMwoqv1j_4ZEN%`;$ro8x=xNRjs`lMe&`qALW`G5YuX{k2md+dJtdg{#luoe=uwyC>(b9 zQ9cs{)d>|A&L=+4k{E>cHac>Y7pew_7+m2p^A0UliA>cn_g$i9mgjly%h4Dv=BKS! z!X6!{8#pyY*0-c)G~<-Z>tzO(I%x+^lFYEW5eim`rcfw|n97d=Y)^EbPu(`6nOGoo z5AdhYCWJUnGv7+W)4qZ+x>ATWUS=IF^#NHE!uGQXO|VnyhUCg&o$*I1J?zc!nx#@&7;vufg^!NyyeSI{=}GCyaNQV)I%rDl5`X*DnM1h!S*K)p^_-Adk< zaN!$3*_DP7BpTj0?j#9VC`&JH*V86>$%%cRl!h2KhfG5r-zx0UzE>ST7Shv6*faAh zs_%(Bx)r}$`)~)#=!SR9IEwbmEXv9giR)l~|N0qrUA&l**vBKr9;$!FLBz-Q83iM2 z(wZYXB{YYxwKl)+vnmTYpRrBQ_k=-%4cGM*|5baA6wn1WvdcQS3*qjZ$@cqrl!kh(j7{(i|}G`rc>^_Tf-BYlGd zTWrjsB_(arX2Xa$gOo;5Zxmv`VXOv~0l*tmmN+Hk=7;u@pl!RB%pI!xpBvP3vb$gr zDSvPh!T6+2R^k1;%>|<>HZBN3H1SoIuLTQ!^0&R|S<`bH&-bxNvcEAcL34(U#b{QM zW+f8dqy{G(!&g-6K|g{QFtM&kyAssStftT44LaJBMJd>c+!47-4!n(YBrWP3s=g~) zCiBa!TnE$B-wW3M=?3-$>88`ll7<%Ig_kBN&WU^>t2eYtD-C88w|88_w3q9B#&SW7 z{~KOfpNL|c4vmnbksV+Jvd5D+b>5vYP00_J1mI;=wl<1q4s$UWqTmGgFQ$`e`vetk zAV<6#9+c5Z?hvRovGH{Z$m%n(Vx%iK34b|2c~E#(C&6?n(1vAURb*oX>JpX&Zlkd& zDCplT&wUT=tLV~=!*`TY=>vkfVftVGSmkM{*C`ytl=EVZGfXDKX{=N`e|T&&eLXVKWgn7u$*9y+gjz#MdwZyhsm_yAI^Sd?Vwsi=diA)ljnAwmyQ~ibjs8}Go`QnQ!z1W|dk@@@_+xL2q2>6xk z*eps_@ucjwd-`%v{U%hZIfL$4reKh-!S3Grx{Q2%yM1L1gHc*&kXUTS3^ZKjyRuGG z5nLSz^VL_O)==&y7|2%8I%)kwlBlZW%R2@ zwMGrh^AL0x^U^OiO31C%ki0JhYE{jV9$a8Wl`0Rsws@)+GuJIcB{>6MF88E1ZJe)Q zD(%N0Jq~<889HwwZZV3rBriHJE`5(T-|Ar*OK)-67ID^wU!t5D+aLB`jNZMO@_B_} zz(GuWF&{E`wjamIinCmsK3{G`V^8Z6?wf-G7AY6~JHBY|MLhvDl2o!Ajn8HKjrfH2 zOIulInURkO<)W2nvQQT$_+59LNq>DGFFoU)sH@f zeCoi2g^PS^k_`|DI|Sn7EN&*I$p@1B$S<05#3>cJn7OEoQxckl-F-{0&c;CP+Kz%p zq?q~=9|_xHWKKJFLq&_$?Pgxe_GVr3K}|cUj3C^=v~uhzD{;^{fWAV05!BWvn0@q_ zaUzCN3C0B2u->0UlTO_v6S4}!k}9*fkx-H8jGIngxXH%2@EcY0xu`hY)_6<2nX?9J zvu9I{q5(gL;pEnNtp=xS49%W~#(_ ztJ>mt5B3YT5RJzZ>7ia_u`|h)r{QHSrH~Aza@SxcHRT);T?d?WKUVvSGxZDUAf_zo1`GW`?SRh!HEdH9zIN$mH?lnLoG-f3HeXI zg+E%v>O7`BBYPRtPttLjqS7IJUbP{M)KAxMoiY>k(R&2-2jkRF0TuVF16+_r=%;;? ztf%MZv&9ID=@pLNKX3K61FwgGolOs!K-k1BaV>_To-?@5H>-n9CKM&{LLCJt;z@2Q zR*)KjlH)m_3-^`PZPOiuxdI@4kO=X{I>v%6vMPXwSj?I9Muv*ZzE zn)M6BE~AfQiIhvA9~Pi)1H-?~CtD~z_b=0B#!wcS2>a9rwH6~T?NC8tKK_%9OZ2gx z)Lq8n!yQD!|At5n!dX6Xz6zI4!DaK6xi{*#S0n8ybyN^5*ncK)V>k zzxKUcgMb*`f=}(sVe_%X{<-;G@4XeK@6qP21r$m}3W-YorPnjp6qe6ClhYA`0>cL= z>(oQ$;yNN}9>id5Gd=Rkek{AIhr@oZ2>>Ab%1Mc9c;%kw+Sucbko5(=_=)%p8M`hK zl2lIaJHtaWJ5ug5(#1t$W6uCt({6&l*ihd|;$D}jVbB@rR>}XR8H*^VqC>Mmv7yvQ z`Xy5&#;3?3i#ABw3PsXz{bt{L9?^KWfWPFlne%Um!!2sF@7=%+2^8#I&&%T#ES$0a zOdKa8jhYqyE!$ETkPmMNX9-m+mVM@9nlvRD0bpcdiK)IkN%;om{F{p(^nJ@fY8R%o zL~K{DswTT9a1xZ z9E%0p8cK*3pB4=56}$LPHU+TLfuRQx4dn{qBB{C(#ZV}rvIYtr75=Ho=Elu?T_4S{$WNNEa|P1I-wZFXqw=h%SLsA!QBP zv4+BxMJeQ?v1RMZ6*eLXbbQjQcwAkE3w8v44VlBest6Koxpdl0hhXl- zg}>@&Cc-d{8U_klMe0GV2o{}S>sJP^=oQb9WS^zY5Mk=6k6*nU=!Ru#!u~A(UHSMp z8VKC66cGc5W$RYffW|)hhwLy>SJnDMxAX% z#3oQw)Dd3d`x=?89TMtl=F6@Q9;Bb=9U9)uZ?ErCM3C{PLedf>RP(#Ozo^DwrA9U(W5k^)aAeJ&V%ysuAVrpZBmTh z*W`+BnW9Y5%AbEMwVs9TqnJ1lmA>RvvlE+21Z@~D1>%aVU^q0<&!Cyw)9(q>&Kd|i z_iSYO)U4Tsk|CIUV0l@>)o8;aAx$mXAxy(nI)!Zp0*n^<^`?c!myN!mtanc>Se4bn zY`pPmVfUq6dC;6sL@V4Q7|{Mm5ZX zm(?mc6;0F_!;p**c!a6(nC$>k0Xk42Z-6r50bCtA?0HSlgJ_G^{g}?x0%ZF#@DaPK z2`~Fos0>LjQqE%Z?U0`H6*TShVEPsgnkXj;c|PMTz=-$v$>vy$31hwy4LM!}^#wu* z+v4v9;d@AJKA!xgD9X!JG>lz#aU*?PFkJY~gpy(qow~_5O2c~sc!u?6k`*!Sx?lD2 zl;EM>&JX|WiRdxK_I^FbsA#DJ!SZYPD0(a4Yt55 zz}J53$8~td$%y3Zt1)>mvy8{yb`ue(HEWQaXQ6rS<659j3f6!MZ0OEo*R}e?2$KGB zfJQE6@4_Bl7yrf1FsrIxE}_CjS?y*-M`BZ6D8@GfZK?fX@i$ z{hiBMKM3cLdfKl0A)owUBxexzai{~_Coj$81`F>2h`=+pW%MsL0w8fNQDyJqA;o(_ zv+GBFDliH2yuZ7ASRvL{p?$BdMljr@yXY-@D)NuO?&E+rgo-yxA_&c!8G>Mu*8^Sj zg9Im&_Hz>H*eU?Q6(_QKEwc{mBSZ_RF?+0JQH zgRe0b#hGKOeCF{CCkpP zNp(vNTC>kI-18IGBqiuO40Rtr+iw(nK7chGY;bIUNs-jAskORf5Bov$1DCvJ zAxFWXgmQKsN5a9{(Ix}7PV}Kc<9~nmc4~BS@!&2rzQ`wBd^ip_*hcZO8E*dofS^SE zpwp-PuS6E0(zfB9a(~wnrcZ@a)Z95rFnV#Yt{sLdoth}7XA%o%o-+)&o!;VdeoUdP zMQ)E;T%E~ZZlYY>-d)}7US%W~9P0rk%t%)#__2@e=fsLIEq=A)E=o7Es{TYmNwJ*<3wR z&x|K{_yg!*k19G5;`4*MKk!gk2TADsS;9Ug|B{GQgVsJaqB$D>c`=>dC@DiM#D$~D z3i^$7Gp?l>6c9qMUxmXG_DwT}C3MyGsP@rsN3p;F)m6TbL%AONfcZSn`93*Q)@YTo z6}Tyi7?Eq=C}SK@_c-cNK7I;o3-!COn&gD!Srgd-lU>&hb`ZHJ&j3aX4Gor(-+Sw% zuB9Q|eNh0&Nq?29lQ0hX53^PKbpQYW literal 0 HcmV?d00001 diff --git a/.dirimages/return.png b/.dirimages/return.png new file mode 100644 index 0000000000000000000000000000000000000000..83e9a2a6243c0374fb96cfba590038f53847cfb6 GIT binary patch literal 6874 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 B{}=!O literal 0 HcmV?d00001 diff --git a/.dirimages/socket.png b/.dirimages/socket.png new file mode 100644 index 0000000000000000000000000000000000000000..19bc9cac198994838241d18acd3b12f93bba3f6e GIT binary patch literal 4205 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 literal 0 HcmV?d00001 diff --git a/.dirimages/text.png b/.dirimages/text.png new file mode 100644 index 0000000000000000000000000000000000000000..fc4cd51fbc8d2adcd50af572899e248bdf2f61ab GIT binary patch literal 5965 zcmdUz)n5}1^!GQqq&pOlPAMe>X&4gH-MulSJ0}v-t%Quh=+e@jJ>F(Td`n6B`zu>0-7rl=y#3+(NBs6XS!XEy)MLp4zC`s zu9ZtWSA5Y9)(Uc>iit5{CKi4dk4RXwMU5~ba*UOE4ZaE)cW)J!u%jI0-6P_8*cwf< z;&@Oscr)(Yj~uCPyNXvtNmJz*qRR~65}29DBRGoU@Oeek7$s=Dx#kr_q1ro0;%>+Gd+ceY^>ks=p&a<@td4gk@Gf^Na z!68wwD~7rnPEuOYo(Hb3?uun*@u;mV%Xe>X+p=^!E^acfex1rMnhD3GyANx_-MwR24e6lrl?F@7o6P2;%tWXKz@9Sjx2#g)jS zD-~33vVga_S7Be3#S|04vomFgouOh~!QX95ve1PB92G84XpK3-K7gUvc5}fx8>C$F zMBU`BxW`YNrR2b#ifIIfyPX+dmDd?v`!pBt1&guD)ArH48W@)&={sd5jX#)3?f1@J zgJoaFB0hYHY};kstmjaM;CxHV*b~q%!?7!KP>x==)0nv=*r#T`56^R$8*U6Yma5#! z20!TH@prTEtsnj4o7fO*Rsw18*LRxxP;DJ(z{MEj{kd@z5lvooCB|uamD}{KVbZ~& zFVRSW#H==UttmrL*8GZ9e*6~rb!|bab(XewpBeSg>}Ad%FHIrDmP^%=uxO1o`gy_Oj(0* z&%PjG`FTmd6y>tnvG0vx--d3teptNNZqMU?%tblZX8^yD@2wdWuf;#9dU?fF@N6dk z>-@T7Z6*-dgp7CUMfEBkud7z_ZxH(CMX@dD?Vq`QvsUGBz$4*?B=@ois=nh?HPeli zHX|}~&re~R+aH>!5os_ehvUsDf()cjw{KH5DQua?{BO)mvgwc4NJArsLqmtRRBnNcyjA^er)i~o*?To zv=e+_guLc+vo0PywdXa4jDQBflN|H0;$%{>O~no-T%XKTrQKq!b-fID1#^lKkZ!pK zq=g-_AP-539GM>oJZk1{R8(7H>rY93%p(kgDfK~ z%F$BW6&$rYtqksK7x^6<1l2Z7uG=!d+Q57$2;2qrU@JKHoOmZ!<~N)Y~42gp4S#2zM$qI{>TSebALk$ zyM0qU<6<}5VWLZwD3aDsXbJSTS)`=aGzTR-21U8fj^jMa9C9<#ZdY$mr<7=Q+L@I{ zr_oC{ww1AGBn9{F*27zwlL#3`h@Y5y6Q(H-R2F97QyLMq)76S&QR&6{m92(?bP#z& zuUSKxThBG58v3}}>8$PReB*#?kJHdTo!ri}Qy#q!4q{5|1fW*p$6n~3l}0Gtit%=sUh1*SkJ(91-;u(UW+>qtLMRo8ThUv z3HyRX8lH?PSUF>F~=nFM16Zn{{S1tZ4n~Wo9K_bt?koGkIc-)Nlf^z z0NgeJsF1~f;9&L@+NZ(gQv|MZB8+1u#7twv;^%vZqAdqG8-}>m$avEvM2sA(g=V?z z+}2}w90FB?`!3V`ruzh2cz$y<3y66}i#eH&_}jyZj^gxl;sGK!^LQHJy3sa+)DJo3 zbNHaDyFS`Io^Dw(KK*mBBm9DNrXO9~N(JBXv}n4__BVkML_D87_^RzF8b$`zqP*?a z%%lv|Ov_||g6@dHuS6+sodf&XW58bT0a+@+SRBDBJ6KWLqIkZDSkpnYC2Wd!dMpLHQB@jo;hz8UmxdfinJo4ut$zBcOqE$Ybv@~`^*N1; z_T9WXAC4oLdfjO)(GRE+r#}2E4T+>e_rC@oVJ^qrn4gBt9mWE(Wk1PA+-F^imZy}a zdzr%pPL;*upLY3)zBjMTAh)JfLVYG?{LQ*;&6}(%@O%lVj{=rwE73J1Ea13Lgin4=o9@$+!5cguTSDY`@lm7G!Y?1eO;r6ys5_&SZQigQT&%Jvpth4mxbdY^d=Z{}B z>UR!j^Dp*xdf^BWLX2Ggvam*2Qi%Hm{`C9seVY=1{vq6oj^0@jX+;|sMrw(po7ew9d*A-` zyGx$H3|4F??&%F91TRs7NL^V{M2d%4!Sr(e1t^#Do?C)~c@pjHe- zgf0h{+pruB7X$`K&b3h_7>O;+vVZ>2@C6^B!0TOhj`zZvyGY>LYMEw_;vs0ogss^T z@w%FP34t4K=|4};hFz-aZ|444DJ$f#0C{;1HW}s?Y&S*8ikM?M^Nc4Sz@>X0OT0Ng z3R~XFz$O)(V;`v+%tv9nb(kCA4-i4i^XuNEgr992`4;%T0_w;Z)~%nLI^J|r7Ih^Y zpDTw=V%Ubp185y!R>t;E@-*07I{DC!#!(na$df=jl%IgF@w3aR{i{5wsax8M5P6d( z;FXM=cK676x9$POnZ(GOt*)OemT03d)GAUsob+T-6(iISA2E2HAPmpc(TkC?F?W;TiBr_dRDOF#Jkf8T?jai)5n=(CWS;I?;l zbJgSDjQu)o{B7zG#-*nmue2iG#di+#Q}X5-vU{(87tFc(O{X7h$kr98Ts~gHtH!DT zx<6m!>-_lX;w~N^ioV=?On(}#d+RS%IwwKD;@Y@#mNa!4qQ&1W(HQa$S*il!Qwn2S(hLG>#gVF)YZg_7_~ zz`WMvd#!eC_TEr%zEk4B0ia&qD6u8H0`pTcgiGR({nPGkdFw zhKd3jJn`I}pI-%O-LyzLjLfnd4C04UXJeg!b%2Wu$oj$WCW3U@qN(FByQow(+#CE- zoTJoM{dnERv5x9Hy48tm)+V{<&h!1Z*0IW82R1LwQd0XE;nx=^!nn5E$7P_6no3gG z{eCYfd(ALCy3T}2fsP3a{-0tT&u`m~tZ=TPF1G^vMcuzsRSiNVANZ`x!}Ix1(^=eb zDynJPor^>Hi-;^yCw(wMGS$Zx3HADn)SFyzM%?zku}ZGjM}@%?)Ba%Q;~S&JEW>R2 zb$u(gJm#K9c@l~ZCcchaEC%>*p|b?ffyvs(Uy3bAKT^;MxxL-!x7r;E7`hu{6%AZ) zaubh(Cnsv(7wW8C78^!qx!?#C*^ zl2fHp^$Yc~UKbpIkKyO-=$YlM1<~`(5zoPteOEa?tL&OBgun)!_tCyI5s zI@u}EGma@UtL8Rhb-|c#$Y{`!-aU^YO$sIG*Sl<~6RJf60i1k-Lm*NRvu1C#Y9w3{ z-~VD}q7{_Ap-lu}`)2=CyB6>2(F9tF;4aNa3jhGVbyrb&1yWJ@zjWyzQz{5gm)Go9 zWF53MR1>D7l8cvis(_(k74an|#YA*kwdkrhqexqHDF?{go7ReyJVMBX&8ooP--`~f zmx5A*msgE}|2PsMLd>a&a=zUI3x$9h($F>nwWv(vpkVay`_|qPpJXQlhl>{1_ z-p&}sdpu~%mT->-e&qzUm3(j1PfZEtA7bVmNUDbyq_}3 zY18#(^cHd;2F0i8s}Vb!#ZOkHJ8-I9i&5OCZRlmZh*tmJ+?@*T@W#0)iWaQ>RX-j5L2nWwEF6Y+3dLoZ6V_SA6$y;$ z<+T>)()cq)`zLtyHmQrBliv6bAG^D{ z|H3PMa`X|rI<1mC<@X{HwJN1$3BiUB={kaECH>^Ai-K0U7!)#*AL`SM#|1KAkfI9w zd!D+yhE?Y5Oc+JnqP7HQXBC2B5b%NXY1Ce;i3_ zpT2Xcj{gMkH;Uz>k*^vhT@RGug)5#u@Q3fHulhf!lPmY16tdbapgJuP<+VNh{qX|7 zvl)dl!b6HsLE6ZS`Xr)(e$R1mu|aqQ=wl)>_y(0~IAf!i*zn8ygB2?_r;8_=#1sE- zc3N`SkeJ!SBMrNk5j!PoKuq;q4!GjrnjKT`;&?GJk`eGfeiB7j15|+%CaITX9TJn- zy*0gGuq9vhl6DFYm$$|vXeh<^BGQhQLGPW-&(9OBkZ5VFa(SuAuabD(EEY{{YxB^Q zP4S}^g!a9gTUuCfEBpjo=kFwYC{`h%0YMaD#32RW%f^ISQ%#mh6q;TW%q=dxjyl}D z_*ekMlg1%mgs$Qq31fT%sm(q)cZFh&qsSBq*m2W<*i=?LZhbK2CR2~FA_08dhuTbx zH(DIP^xp!UvjC50jq8=?KX`)g`@dpBjkn4Q-vF|P@cH^szgIoT>}e6v*B`X8(+55e zOnw()5ei6+PS`UVzzZ@81sq1jslReMegwEiK4b^e-cZfs+5iKiEqjmwhhVGW$N<1| j$VAQcLvm0C*V`-Nbik?)-i6A44+8+H>8jQ#+rj=1{Ffuq literal 0 HcmV?d00001 diff --git a/.dirimages/video.png b/.dirimages/video.png new file mode 100644 index 0000000000000000000000000000000000000000..8d44d54433ea715c8d4eae2f64b533ee5ced2fb6 GIT binary patch literal 7234 zcmV-I9KGX-P) zaB^>EX>4U6ba`-PAZ2)IW&i+q+O3*vcH=sXg#U9Dvjl=~E{D%^W(TwU`9Rr@lQ{15 zy|>e`6G^5>piroyV%Got?>7I#znDTXA?A{5N|t}2hUywW6np)v=T&Stujk+W6u*Bu zZXPEbhdl58`Zc%P^^N24`#{n^A2;Q8#+BPZw-fgPz24dJ!+f43$K7>Y!fl|st{+k@ z`FX0{UGL*|;hD5&`+Ny!*Sg&jI=rHpkK0GCVSHt@;2Nu-gDSJ<|605 z_1ter;m%XI)Cq&hN&gRD=H1D^`7(PC4UyRLh!yj4ifg8!nbTLUA_cNW_pTks|LykG+oJ+2`6<3^(NF$Fj>S)tVKcR`4W}ao%*_N0EQ>?J!N-M9j z>S`OTZNI~gJMFy7uDd<5cFXFwufNEe+_L6Qru4e}$QnmY&i5fi=_G2-$e0U4#j`d;CBspDw-{#2 zs|lfVu7IfwOuc_ zi{4$6W0w_YFuTm%%5~>ae!BCIGja^HcTHk+eCzfC>^`>HCJ5?Qj~2;0v{Bngxz|;u z-Afu(7xoN(zzWgTJ=ad2SF>8C$+c-4=k5q-|S1P1Us9pkbD$LizOms2Ymxxo#d+vlz*| zyI@X=y(<2^0#ox5(T*=~XkN_V{yx4UJq1Qyy-Fu67Qp$HG);xtHG=?8z+l8SI<(VMh(p<_w^mm2Qbt1Y&0M3YUCS&x-|JK!rZ3KnI#h;gqfsXvLAH8a zA-haeV`ai?jN7q6kC5G#(L$mZ%r=mUqe6E8qOA;GXVm<`ce@A0(fiH~vwl5-9X z!~`VNW^gjo^*e)2`^P%@p}UwzJVQ+n?R@!?YxsDe^xv}9BO1u%p&^nxv= zZb*j0+dFo@)?vtE_12XJlY0UMbuln4JoEV^te34$moZH*E`834t&MF&H{*d0>|9c_ek8COoamv3!j z$ZHlb-^lqD3w-J$uesU$4d~us&in~<=$+82rS&*pS@LSB2-E2ui3(6U^IhmwLkx=+ zVMf7qt-~})Xz9o}I-4J@=Y;H-BX`RORUMA9P@rjxB|J{^Z(}qTDI$}cZR=-|1;#l~ zz%{pHZp35qYalB}U7ohtI_h=BT@_GQyn4Zk=JBmedy9-bAQx$gkH-igNzp=eo+M10 z5J#sPTw8S_Zao7{L2|`yk)P${Z?R>9}s&nPS|)Y6hrV=ptVB$x;qzyc;hu zw0%QojQUM4m=D1K`_8cg+8)BZM#KV+3k(O367#0O!^X;oUw2kI#kUBY5*-ZTS~jDT-p>W z

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 literal 0 HcmV?d00001 diff --git a/hexstrbase64/hexstrbase64/base64.js b/hexstrbase64/hexstrbase64/base64.js new file mode 100644 index 0000000..a995d36 --- /dev/null +++ b/hexstrbase64/hexstrbase64/base64.js @@ -0,0 +1,124 @@ +//Base64.js for hexstrbase64 library for Node.js +/* + * Copyright (c) 2012 Miles Shang + * + * 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. + */ + +/* + * Usage: + * Set your settings: + * base64.settings.char62 = "-"; + * base64.settings.char63 = "_"; + * etc. + * + * Then: + * base64.encode(str) takes a string and returns the base64 encoding of it. + * base64.decode(str) does the reverse. + */ + +/* TODO: + * Add a "padding_mandatory" flag to check for bad padding in the decoder. + * Add test cases that throw errors. + */ + +var base64 = new Object(); + +base64.settings = { // defaults + "char62" : "+", + "char63" : "/", + "pad" : "=", + "ascii" : false +}; + +/* + * Settings: + * If "pad" is not null or undefined, then it will be used for encoding. + * + * If "ascii" is set to true, then the encoder + * will assume that plaintext is in 8-bit chars (the standard). + * In this case, for every 3 chars in plaintext, you get 4 chars of base64. + * Any non-8-bit chars will cause an error. + * Otherwise, assume that all plaintext can be in the full range + * of Javascript chars, i.e. 16 bits. Get 8 chars of base64 for 3 chars + * of plaintext. Any possible JS string can be encoded. + */ + +base64.encode = function (str) { + this.char_set = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" + + this.settings.char62 + this.settings.char63; + + var output = ""; // final output + var buf = ""; // binary buffer + for (var i = 0; i < str.length; ++i) { + var c_num = str.charCodeAt(i); + if (this.settings.ascii) + if (c_num >= 256) + throw "Not an 8-bit char."; + var c_bin = c_num.toString(2); + while (c_bin.length < (this.settings.ascii ? 8 : 16)) + c_bin = "0" + c_bin; + buf += c_bin; + + while (buf.length >= 6) { + var sextet = buf.slice(0, 6); + buf = buf.slice(6); + output += this.char_set.charAt(parseInt(sextet, 2)); + } + } + + if (buf) { // not empty + while (buf.length < 6) buf += "0"; + output += this.char_set.charAt(parseInt(buf, 2)); + } + + if (this.settings.pad) + while (output.length % (this.settings.ascii ? 4 : 8) != 0) + output += this.settings.pad; + + return output; +} + +base64.decode = function (str) { + this.char_set = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" + + this.settings.char62 + this.settings.char63; + + var output = ""; // final output + var buf = ""; // binary buffer + var bits = (this.settings.ascii ? 8 : 16); + for (var i = 0; i < str.length; ++i) { + if (str[i] == this.settings.pad) break; + var c_num = this.char_set.indexOf(str.charAt(i)); + if (c_num == -1) throw "Not base64."; + var c_bin = c_num.toString(2); + while (c_bin.length < 6) c_bin = "0" + c_bin; + buf += c_bin; + + while (buf.length >= bits) { + var octet = buf.slice(0, bits); + buf = buf.slice(bits); + output += String.fromCharCode(parseInt(octet, 2)); + } + } + return output; +} +module.exports = base64; \ No newline at end of file diff --git a/hexstrbase64/hexstrbase64/base64_browser.js b/hexstrbase64/hexstrbase64/base64_browser.js new file mode 100644 index 0000000..44dec66 --- /dev/null +++ b/hexstrbase64/hexstrbase64/base64_browser.js @@ -0,0 +1,123 @@ +//Base64.js for hexstrbase64 library for browser +/* + * Copyright (c) 2012 Miles Shang + * + * 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. + */ + +/* + * Usage: + * Set your settings: + * base64.settings.char62 = "-"; + * base64.settings.char63 = "_"; + * etc. + * + * Then: + * base64.encode(str) takes a string and returns the base64 encoding of it. + * base64.decode(str) does the reverse. + */ + +/* TODO: + * Add a "padding_mandatory" flag to check for bad padding in the decoder. + * Add test cases that throw errors. + */ + +var base64 = new Object(); + +base64.settings = { // defaults + "char62" : "+", + "char63" : "/", + "pad" : "=", + "ascii" : false +}; + +/* + * Settings: + * If "pad" is not null or undefined, then it will be used for encoding. + * + * If "ascii" is set to true, then the encoder + * will assume that plaintext is in 8-bit chars (the standard). + * In this case, for every 3 chars in plaintext, you get 4 chars of base64. + * Any non-8-bit chars will cause an error. + * Otherwise, assume that all plaintext can be in the full range + * of Javascript chars, i.e. 16 bits. Get 8 chars of base64 for 3 chars + * of plaintext. Any possible JS string can be encoded. + */ + +base64.encode = function (str) { + this.char_set = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" + + this.settings.char62 + this.settings.char63; + + var output = ""; // final output + var buf = ""; // binary buffer + for (var i = 0; i < str.length; ++i) { + var c_num = str.charCodeAt(i); + if (this.settings.ascii) + if (c_num >= 256) + throw "Not an 8-bit char."; + var c_bin = c_num.toString(2); + while (c_bin.length < (this.settings.ascii ? 8 : 16)) + c_bin = "0" + c_bin; + buf += c_bin; + + while (buf.length >= 6) { + var sextet = buf.slice(0, 6); + buf = buf.slice(6); + output += this.char_set.charAt(parseInt(sextet, 2)); + } + } + + if (buf) { // not empty + while (buf.length < 6) buf += "0"; + output += this.char_set.charAt(parseInt(buf, 2)); + } + + if (this.settings.pad) + while (output.length % (this.settings.ascii ? 4 : 8) != 0) + output += this.settings.pad; + + return output; +} + +base64.decode = function (str) { + this.char_set = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" + + this.settings.char62 + this.settings.char63; + + var output = ""; // final output + var buf = ""; // binary buffer + var bits = (this.settings.ascii ? 8 : 16); + for (var i = 0; i < str.length; ++i) { + if (str[i] == this.settings.pad) break; + var c_num = this.char_set.indexOf(str.charAt(i)); + if (c_num == -1) throw "Not base64."; + var c_bin = c_num.toString(2); + while (c_bin.length < 6) c_bin = "0" + c_bin; + buf += c_bin; + + while (buf.length >= bits) { + var octet = buf.slice(0, bits); + buf = buf.slice(bits); + output += String.fromCharCode(parseInt(octet, 2)); + } + } + return output; +} \ No newline at end of file diff --git a/hexstrbase64/hexstrbase64/main.js b/hexstrbase64/hexstrbase64/main.js new file mode 100644 index 0000000..4f3d12d --- /dev/null +++ b/hexstrbase64/hexstrbase64/main.js @@ -0,0 +1,129 @@ +//Requires base64 +var base64 = require('./base64.js'); +var HexStrBase64Buffer = { + from: function(s, e) { + var type = e; + var value = ''; + if (e == 'base64') { + value = base64.decode(s); + } else if (e == 'hex') { + try { + var escaped = ""; + var hex = ""; + if(s.length%4 > 0) { + for(i=0;i<(4-(s.length%4));i++) { + hex += "0"; + } + } + hex += s; + for(var i = 0;i 4) { + result += hex.substring(hex.length-5,hex.length-1); + } else if(hex.length < 4) { + for(var j=0;j<=4-(hex.length%4);j++) { + result += "0"; + } + result += hex; + } + } + return result.split("undefined").join("").split("%").join(""); + } else { + //return Buffer.from(value, type).toString(en); + return Buffer.from(value,"utf8").toString(en); + } + } + //} + //function toString(en) { + //return toStringE(en,type); + //} + return { type: type, value: value, toString: toString }; + } +}; +var hexstrbase64 = { + strtobase64: function(s) { + return HexStrBase64Buffer.from(s, 'utf8').toString('base64'); + }, + base64tostr: function(b) { + return HexStrBase64Buffer.from(b, 'base64').toString('utf8'); + }, + strtohex: function(s) { + return HexStrBase64Buffer.from(s, 'utf8').toString('hex'); + }, + hextostr: function(h) { + return HexStrBase64Buffer.from(h, 'hex').toString('utf8'); + }, + hextobase64: function(h) { + return hexstrbase64.strtobase64(hexstrbase64.hextostr(h)); + }, + base64tohex: function(b) { + return hexstrbase64.strtohex(hexstrbase64.base64tostr(b)); + }, + cipher: function() { + this.strtobase64 = hexstrbase64.strtobase64; + this.base64tostr = hexstrbase64.base64tostr; + this.strtohex = hexstrbase64.strtohex; + this.hextostr = hexstrbase64.hextostr; + this.hextobase64 = hextobase64.hextobase64; + this.base64tohex = hextobase64.base64tohex; + }, + native: { + btoa: function(b) { + return hexstrbase64.strtobase64(b); + }, + atob: function(a) { + return hexstrbase64.base64tostr(a); + } + } +}; +module.exports = hexstrbase64; \ No newline at end of file diff --git a/hexstrbase64/hexstrbase64/main_browser.js b/hexstrbase64/hexstrbase64/main_browser.js new file mode 100644 index 0000000..a2b4531 --- /dev/null +++ b/hexstrbase64/hexstrbase64/main_browser.js @@ -0,0 +1,126 @@ +//Requires base64_browser.js +var HexStrBase64Buffer = { + from: function(s, e) { + var type = e; + var value = ''; + if (e == 'base64') { + value = base64.decode(s); + } else if (e == 'hex') { + try { + var escaped = ""; + var hex = ""; + if(s.length%4 > 0) { + for(i=0;i<(4-(s.length%4));i++) { + hex += "0"; + } + } + hex += s; + for(var i = 0;i 4) { + result += hex.substring(hex.length-5,hex.length-1); + } else if(hex.length < 4) { + for(var j=0;j<=4-(hex.length%4);j++) { + result += "0"; + } + result += hex; + } + } + return result.split("undefined").join("").split("%").join(""); + } else { + //return new TextDecoder(en).decode(new TextEncoder(type).encode(value)); + return new TextDecoder(en).decode(new TextEncoder("utf8").encode(value)); + } + } + //function toString(en) { + //return toStringE(en,type); + //} + return { type: type, value: value, toString: toString }; + } +}; +var hexstrbase64 = { + strtobase64: function(s) { + return HexStrBase64Buffer.from(s, 'utf8').toString('base64'); + }, + base64tostr: function(b) { + return HexStrBase64Buffer.from(b, 'base64').toString('utf8'); + }, + strtohex: function(s) { + return HexStrBase64Buffer.from(s, 'utf8').toString('hex'); + }, + hextostr: function(h) { + return HexStrBase64Buffer.from(h, 'hex').toString('utf8'); + }, + hextobase64: function(h) { + return hexstrbase64.strtobase64(hexstrbase64.hextostr(h)); + }, + base64tohex: function(b) { + return hexstrbase64.strtohex(hexstrbase64.base64tostr(b)); + }, + cipher: function() { + this.strtobase64 = hexstrbase64.strtobase64; + this.base64tostr = hexstrbase64.base64tostr; + this.strtohex = hexstrbase64.strtohex; + this.hextostr = hexstrbase64.hextostr; + this.hextobase64 = hextobase64.hextobase64; + this.base64tohex = hextobase64.base64tohex; + }, + native: { + btoa: function(b) { + return hexstrbase64.strtobase64(b); + }, + atob: function(a) { + return hexstrbase64.base64tostr(a); + } + } +}; \ No newline at end of file diff --git a/hexstrbase64/index.js b/hexstrbase64/index.js new file mode 100644 index 0000000..4ec8879 --- /dev/null +++ b/hexstrbase64/index.js @@ -0,0 +1,11 @@ +let hexstrbase64 = require("./hexstrbase64/main.js"); +function decodeBase(b) { + return hexstrbase64.native.atob(b); +} +function encodeStr(s) { + return hexstrbase64.native.btoa(s); +} +var m = Object.create(hexstrbase64); +m.decodeBase = decodeBase; +m.encodeBase = encodeStr; +module.exports = m; \ No newline at end of file diff --git a/hexstrbase64/ok.png b/hexstrbase64/ok.png new file mode 100644 index 0000000000000000000000000000000000000000..1293b272bc7cea83026d363415f1d94b4124cb88 GIT binary patch literal 252 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc1SFYWcSQjy#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=DjSL74G){)!Z!;6zUs#}EtuqZ7{ZHYo78aWAeYkc>U9IpaMwGc|o`R%!&GqXG_-9aTc80 z9iQ4^tn8T{w)#-AcuizbTHc#} + + + HexStrBase64 Readme + + + +

hexstrbase64

+

How to install?

+

Node.js

+
    +
  1. Download library from here.
  2. +
  3. Modify index.js
  4. +
  5. Write header in program (replace ./hexstrbase64 with your path to hexstrbase64 library):
  6. +
    + var hexstrbase64 = require("./hexstrbase64/index.js");
    +
    +
  7. Now hexstrbase64 library added to program!
  8. +
+

Browser Javascript

+
    +
  1. Download library from here.
  2. +
  3. Write this code in <head> element (replace hexstrbase64/ with your path to hexstrbase64 library):
  4. +
    + <script src="hexstrbase64/hexstrbase64/base64_browser.js"></script>
    + <script src="hexstrbase64/hexstrbase64/main_browser.js"></script> +
    +
  5. Now hexstrbase64 library added to HTML!
  6. +
+ + \ No newline at end of file diff --git a/hexstrbase64/test/base64.html b/hexstrbase64/test/base64.html new file mode 100644 index 0000000..18a02a0 --- /dev/null +++ b/hexstrbase64/test/base64.html @@ -0,0 +1,13 @@ + + + + + hexstrbase64 base64 test + + + + + + \ No newline at end of file diff --git a/hexstrbase64/test/base64.js b/hexstrbase64/test/base64.js new file mode 100644 index 0000000..c425403 --- /dev/null +++ b/hexstrbase64/test/base64.js @@ -0,0 +1,56 @@ +function nameObj(n, v) { + return {name:n, value:v}; +} + +function testStart() { + document.write(''); + document.write('hexstrbase64 base64 test'); + console.log("TESTING STARTED"); + for (var i = 0; i < 3; i++) { + document.write('

' + enccases[i].name + '

'); + console.log("SWITCH " + enccases[i].name); + for (var j = 0; j < 2; j++) { + document.write('

' + enccases[i].value[j].name + '

'); + console.log("CASE " + enccases[i].value[j].name); + console.log("MUST BE: " + enccases[i].value[j].value); + console.log("IS : " + hexstrbase64.strtobase64(enccases[i].value[j].name)); + if (hexstrbase64.strtobase64(enccases[i].value[j].name) == enccases[i].value[j].value) { + document.write('

Equals

'); + console.log("EQUALS"); + } else { + document.write('

Not Equals

'); + console.log("NOT EQUALS"); + } + console.log("CASE " + enccases[i].value[j].name + " ENDED"); + } + console.log("SWITCH " + enccases[i].name + " ENDED"); + } + console.log("TESTING ENDED"); +} + +var enccases = [ + nameObj('Ascii', [ + nameObj('Hello world!', 'AEgAZQBsAGwAbwAgAHcAbwByAGwAZAAh'), + nameObj('Lorem ipsum', 'AEwAbwByAGUAbQAgAGkAcABzAHUAbQ==') + ]), + nameObj('Ascii More Than 64 Bytes', [ + nameObj( + 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum fermentum ac nisl a sollicitudin. Cras interdum dui turpis, non scelerisque.', + 'AEwAbwByAGUAbQAgAGkAcABzAHUAbQAgAGQAbwBsAG8AcgAgAHMAaQB0ACAAYQBtAGUAdAAsACAAYwBvAG4AcwBlAGMAdABlAHQAdQByACAAYQBkAGkAcABpAHMAYwBpAG4AZwAgAGUAbABpAHQALgAgAFYAZQBzAHQAaQBiAHUAbAB1AG0AIABmAGUAcgBtAGUAbgB0AHUAbQAgAGEAYwAgAG4AaQBzAGwAIABhACAAcwBvAGwAbABpAGMAaQB0AHUAZABpAG4ALgAgAEMAcgBhAHMAIABpAG4AdABlAHIAZAB1AG0AIABkAHUAaQAgAHQAdQByAHAAaQBzACwAIABuAG8AbgAgAHMAYwBlAGwAZQByAGkAcwBxAHUAZQAu' + ), + nameObj( + 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus dapibus volutpat ligula at pulvinar. Etiam consequat mi fringilla facilisis eleifend.', + 'AEwAbwByAGUAbQAgAGkAcABzAHUAbQAgAGQAbwBsAG8AcgAgAHMAaQB0ACAAYQBtAGUAdAAsACAAYwBvAG4AcwBlAGMAdABlAHQAdQByACAAYQBkAGkAcABpAHMAYwBpAG4AZwAgAGUAbABpAHQALgAgAFAAaABhAHMAZQBsAGwAdQBzACAAZABhAHAAaQBiAHUAcwAgAHYAbwBsAHUAdABwAGEAdAAgAGwAaQBnAHUAbABhACAAYQB0ACAAcAB1AGwAdgBpAG4AYQByAC4AIABFAHQAaQBhAG0AIABjAG8AbgBzAGUAcQB1AGEAdAAgAG0AaQAgAGYAcgBpAG4AZwBpAGwAbABhACAAZgBhAGMAaQBsAGkAcwBpAHMAIABlAGwAZQBpAGYAZQBuAGQALg==' + ) + ]), + nameObj('Unicode UTF8', [ + nameObj( + 'DorianTech Hex String Base64转换器', + 'AEQAbwByAGkAYQBuAFQAZQBjAGgAIABIAGUAeAAgAFMAdAByAGkAbgBnACAAQgBhAHMAZQA2ADSPbGNiVmg=====' + ), + nameObj( + 'DorianTech Hex String Base64转换器,用于由DorianTech提供的Node.js', + 'AEQAbwByAGkAYQBuAFQAZQBjAGgAIABIAGUAeAAgAFMAdAByAGkAbgBnACAAQgBhAHMAZQA2ADSPbGNiVmj/DHUoTo51MQBEAG8AcgBpAGEAbgBUAGUAYwBoY9BPm3aEAE4AbwBkAGUALgBqAHM=====' + ) + ]) +]; \ No newline at end of file diff --git a/hexstrbase64/test/hex.html b/hexstrbase64/test/hex.html new file mode 100644 index 0000000..713dd6c --- /dev/null +++ b/hexstrbase64/test/hex.html @@ -0,0 +1,13 @@ + + + + + hexstrbase64 hex test + + + + + + \ No newline at end of file diff --git a/hexstrbase64/test/hex.js b/hexstrbase64/test/hex.js new file mode 100644 index 0000000..8a57a8a --- /dev/null +++ b/hexstrbase64/test/hex.js @@ -0,0 +1,56 @@ +function nameObj(n, v) { + return {name:n, value:v}; +} + +function testStart() { + document.write(''); + document.write('hexstrbase64 hex test'); + console.log("TESTING STARTED"); + for (var i = 0; i < 3; i++) { + document.write('

' + enccases[i].name + '

'); + console.log("SWITCH " + enccases[i].name); + for (var j = 0; j < 2; j++) { + document.write('

' + enccases[i].value[j].name + '

'); + console.log("CASE " + enccases[i].value[j].name); + console.log("MUST BE: " + enccases[i].value[j].value); + console.log("IS : " + hexstrbase64.strtohex(enccases[i].value[j].name)); + if (hexstrbase64.strtohex(enccases[i].value[j].name) == enccases[i].value[j].value) { + document.write('

Equals

'); + console.log("EQUALS"); + } else { + document.write('

Not Equals

'); + console.log("NOT EQUALS"); + } + console.log("CASE " + enccases[i].value[j].name + " ENDED"); + } + console.log("SWITCH " + enccases[i].name + " ENDED"); + } + console.log("TESTING ENDED"); +} + +var enccases = [ + nameObj('Ascii', [ + nameObj('Hello world!', '00480065006c006c006f00200077006f0072006c00640021'), + nameObj('Lorem ipsum', '004c006f00720065006d00200069007000730075006d') + ]), + nameObj('Ascii More Than 64 Bytes', [ + nameObj( + 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum fermentum ac nisl a sollicitudin. Cras interdum dui turpis, non scelerisque.', + '004c006f00720065006d00200069007000730075006d00200064006f006c006f0072002000730069007400200061006d00650074002C00200063006f006e00730065006300740065007400750072002000610064006900700069007300630069006e006700200065006c00690074002e00200056006500730074006900620075006c0075006d0020006600650072006d0065006e00740075006d0020006100630020006e00690073006c0020006100200073006f006c006c0069006300690074007500640069006e002e0020004300720061007300200069006e00740065007200640075006d00200064007500690020007400750072007000690073002C0020006e006f006e0020007300630065006c0065007200690073007100750065002e' + ), + nameObj( + 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus dapibus volutpat ligula at pulvinar. Etiam consequat mi fringilla facilisis eleifend.', + '004c006f00720065006d00200069007000730075006d00200064006f006c006f0072002000730069007400200061006d00650074002C00200063006f006e00730065006300740065007400750072002000610064006900700069007300630069006e006700200065006c00690074002e002000500068006100730065006c006c007500730020006400610070006900620075007300200076006f006c007500740070006100740020006c006900670075006c0061002000610074002000700075006c00760069006e00610072002e00200045007400690061006d00200063006f006e0073006500710075006100740020006d00690020006600720069006e00670069006c006c006100200066006100630069006c006900730069007300200065006c0065006900660065006e0064002e' + ) + ]), + nameObj('Unicode UTF8', [ + nameObj( + 'DorianTech Hex String Base64转换器', + '0044006f007200690061006e0054006500630068002000480065007800200053007400720069006e006700200042006100730065003600348F6C63625668' + ), + nameObj( + 'DorianTech Hex String Base64转换器,用于由DorianTech提供的Node.js', + '0044006f007200690061006e0054006500630068002000480065007800200053007400720069006e006700200042006100730065003600348F6C63625668FF0C75284E8E75310044006f007200690061006e005400650063006863D04F9B7684004e006f00640065002e006a0073' + ) + ]) +]; \ No newline at end of file diff --git a/hexstrbase64/test/index.html b/hexstrbase64/test/index.html new file mode 100644 index 0000000..b0ae622 --- /dev/null +++ b/hexstrbase64/test/index.html @@ -0,0 +1,16 @@ + + + + hexstrbase64 test + + + + + + + \ No newline at end of file diff --git a/hviews.txt b/hviews.txt new file mode 100644 index 0000000..573541a --- /dev/null +++ b/hviews.txt @@ -0,0 +1 @@ +0 diff --git a/index.html b/index.html new file mode 100644 index 0000000..8bb0e4c --- /dev/null +++ b/index.html @@ -0,0 +1,130 @@ + + + + SVR.JS 3.4.17 + + + + + +

Welcome to SVR.JS 3.4.17

+
+ +
+

If you see this page, that means, that the server is properly working. You can further configure the server and replace index.html and tests.html pages with custom ones.

+

Default config.json looks like this:

+
+ + {
+   "users": [],
+   "port": 80,
+   "pubport": 80,
+   "page404": "404.html",
+   "timestamp": 1680954429282,
+   "blacklist": [],
+   "nonStandardCodes": [],
+   "enableCompression": true,
+   "customHeaders": {},
+   "enableHTTP2": false,
+   "enableLogging": true,
+   "enableDirectoryListing": true,
+   "enableDirectoryListingWithDefaultHead": false,
+   "serverAdministratorEmail": "[no contact information]",
+   "stackHidden": false,
+   "enableRemoteLogBrowsing": true,
+   "exposeServerVersion": true,
+   "disableServerSideScriptExpose": false,
+   "rewriteMap": [
+     {
+       "definingRegex": "/\\/invoke500\\/\\?/",
+       "replacements": dorians[
+         {
+           "regex": "/\\/invoke500\\/\\?/",
+           "replacement": "/invoke500.svr?"
+         }
+       ]
+     },
+     {
+       "definingRegex": "/\\/invoke500\\/.+\\//",
+       "replacements": [
+         {
+           "regex": "/\\/\\?/",
+           "replacement": "&"
+         },
+         {
+           "regex": "/invoke500\\//",
+           "replacement": "invoke500.svr?"
+         },
+         {
+           "regex": "/\\/(?!invoke500.svr?)/",
+           "replacement": ""
+         }
+       ]
+     },
+     {
+       "definingRegex": "/\\/invoke500\\/.+/",
+       "replacements": [
+         {
+           "regex": "/\\?/",
+           "replacement": "&"
+         },
+         {
+           "regex": "/invoke500\\//",
+           "replacement": "invoke500.svr?"
+         }
+       ]
+     },
+     {
+       "definingRegex": "/\\/invoke500\\//",
+       "replacements": [
+         {
+           "regex": "/\\/invoke500\\//",
+           "replacement": "/invoke500.svr"
+         }
+       ]
+     },
+     {
+       "definingRegex": "/\\/invoke500$/",
+       "replacements": [
+         {
+           "regex": "/\\/invoke500/",
+           "replacement": "/invoke500.svr"
+         }
+       ]
+     }
+   ],
+   "allowStatus": true,
+   "dontCompress": ["/.*\\.ipxe$/","/.*\\.img$/","/.*\\.iso$/"],
+   "enableIPSpoofing": false,
+   "secure": false,
+   "sni": {},
+   "disableNonEncryptedServer": false,
+   "disableToHTTPSRedirect": false
+ } +
+
+

Changes:

+
    +
  • Improved URL sanitizer.
  • +
  • Fixed bug with formidable wrapper.
  • +
+

Bugs:

+
    +
  • Some very old mods requiring hexstrbase64 will fail to load.
  • +
  • On first load server-side JavaScript will fail to load when SVR.JS is running on Bun.
  • +
+
+ Tests
+ Licenses
+ SVR.JS status page
+ More Information +
+
+ + + diff --git a/lib/hexstrbase64/fail.png b/lib/hexstrbase64/fail.png new file mode 100644 index 0000000000000000000000000000000000000000..bbebb5d51d6fef7b2c11b04f96e93f9228834c30 GIT binary patch literal 243 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc1SFYWcSQjymUKs7M+SzC{oH>NS%G}c0*}aI z1_r*vAk26?e?9 literal 0 HcmV?d00001 diff --git a/lib/hexstrbase64/hexstrbase64/base64.js b/lib/hexstrbase64/hexstrbase64/base64.js new file mode 100644 index 0000000..a995d36 --- /dev/null +++ b/lib/hexstrbase64/hexstrbase64/base64.js @@ -0,0 +1,124 @@ +//Base64.js for hexstrbase64 library for Node.js +/* + * Copyright (c) 2012 Miles Shang + * + * 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. + */ + +/* + * Usage: + * Set your settings: + * base64.settings.char62 = "-"; + * base64.settings.char63 = "_"; + * etc. + * + * Then: + * base64.encode(str) takes a string and returns the base64 encoding of it. + * base64.decode(str) does the reverse. + */ + +/* TODO: + * Add a "padding_mandatory" flag to check for bad padding in the decoder. + * Add test cases that throw errors. + */ + +var base64 = new Object(); + +base64.settings = { // defaults + "char62" : "+", + "char63" : "/", + "pad" : "=", + "ascii" : false +}; + +/* + * Settings: + * If "pad" is not null or undefined, then it will be used for encoding. + * + * If "ascii" is set to true, then the encoder + * will assume that plaintext is in 8-bit chars (the standard). + * In this case, for every 3 chars in plaintext, you get 4 chars of base64. + * Any non-8-bit chars will cause an error. + * Otherwise, assume that all plaintext can be in the full range + * of Javascript chars, i.e. 16 bits. Get 8 chars of base64 for 3 chars + * of plaintext. Any possible JS string can be encoded. + */ + +base64.encode = function (str) { + this.char_set = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" + + this.settings.char62 + this.settings.char63; + + var output = ""; // final output + var buf = ""; // binary buffer + for (var i = 0; i < str.length; ++i) { + var c_num = str.charCodeAt(i); + if (this.settings.ascii) + if (c_num >= 256) + throw "Not an 8-bit char."; + var c_bin = c_num.toString(2); + while (c_bin.length < (this.settings.ascii ? 8 : 16)) + c_bin = "0" + c_bin; + buf += c_bin; + + while (buf.length >= 6) { + var sextet = buf.slice(0, 6); + buf = buf.slice(6); + output += this.char_set.charAt(parseInt(sextet, 2)); + } + } + + if (buf) { // not empty + while (buf.length < 6) buf += "0"; + output += this.char_set.charAt(parseInt(buf, 2)); + } + + if (this.settings.pad) + while (output.length % (this.settings.ascii ? 4 : 8) != 0) + output += this.settings.pad; + + return output; +} + +base64.decode = function (str) { + this.char_set = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" + + this.settings.char62 + this.settings.char63; + + var output = ""; // final output + var buf = ""; // binary buffer + var bits = (this.settings.ascii ? 8 : 16); + for (var i = 0; i < str.length; ++i) { + if (str[i] == this.settings.pad) break; + var c_num = this.char_set.indexOf(str.charAt(i)); + if (c_num == -1) throw "Not base64."; + var c_bin = c_num.toString(2); + while (c_bin.length < 6) c_bin = "0" + c_bin; + buf += c_bin; + + while (buf.length >= bits) { + var octet = buf.slice(0, bits); + buf = buf.slice(bits); + output += String.fromCharCode(parseInt(octet, 2)); + } + } + return output; +} +module.exports = base64; \ No newline at end of file diff --git a/lib/hexstrbase64/hexstrbase64/base64_browser.js b/lib/hexstrbase64/hexstrbase64/base64_browser.js new file mode 100644 index 0000000..44dec66 --- /dev/null +++ b/lib/hexstrbase64/hexstrbase64/base64_browser.js @@ -0,0 +1,123 @@ +//Base64.js for hexstrbase64 library for browser +/* + * Copyright (c) 2012 Miles Shang + * + * 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. + */ + +/* + * Usage: + * Set your settings: + * base64.settings.char62 = "-"; + * base64.settings.char63 = "_"; + * etc. + * + * Then: + * base64.encode(str) takes a string and returns the base64 encoding of it. + * base64.decode(str) does the reverse. + */ + +/* TODO: + * Add a "padding_mandatory" flag to check for bad padding in the decoder. + * Add test cases that throw errors. + */ + +var base64 = new Object(); + +base64.settings = { // defaults + "char62" : "+", + "char63" : "/", + "pad" : "=", + "ascii" : false +}; + +/* + * Settings: + * If "pad" is not null or undefined, then it will be used for encoding. + * + * If "ascii" is set to true, then the encoder + * will assume that plaintext is in 8-bit chars (the standard). + * In this case, for every 3 chars in plaintext, you get 4 chars of base64. + * Any non-8-bit chars will cause an error. + * Otherwise, assume that all plaintext can be in the full range + * of Javascript chars, i.e. 16 bits. Get 8 chars of base64 for 3 chars + * of plaintext. Any possible JS string can be encoded. + */ + +base64.encode = function (str) { + this.char_set = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" + + this.settings.char62 + this.settings.char63; + + var output = ""; // final output + var buf = ""; // binary buffer + for (var i = 0; i < str.length; ++i) { + var c_num = str.charCodeAt(i); + if (this.settings.ascii) + if (c_num >= 256) + throw "Not an 8-bit char."; + var c_bin = c_num.toString(2); + while (c_bin.length < (this.settings.ascii ? 8 : 16)) + c_bin = "0" + c_bin; + buf += c_bin; + + while (buf.length >= 6) { + var sextet = buf.slice(0, 6); + buf = buf.slice(6); + output += this.char_set.charAt(parseInt(sextet, 2)); + } + } + + if (buf) { // not empty + while (buf.length < 6) buf += "0"; + output += this.char_set.charAt(parseInt(buf, 2)); + } + + if (this.settings.pad) + while (output.length % (this.settings.ascii ? 4 : 8) != 0) + output += this.settings.pad; + + return output; +} + +base64.decode = function (str) { + this.char_set = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" + + this.settings.char62 + this.settings.char63; + + var output = ""; // final output + var buf = ""; // binary buffer + var bits = (this.settings.ascii ? 8 : 16); + for (var i = 0; i < str.length; ++i) { + if (str[i] == this.settings.pad) break; + var c_num = this.char_set.indexOf(str.charAt(i)); + if (c_num == -1) throw "Not base64."; + var c_bin = c_num.toString(2); + while (c_bin.length < 6) c_bin = "0" + c_bin; + buf += c_bin; + + while (buf.length >= bits) { + var octet = buf.slice(0, bits); + buf = buf.slice(bits); + output += String.fromCharCode(parseInt(octet, 2)); + } + } + return output; +} \ No newline at end of file diff --git a/lib/hexstrbase64/hexstrbase64/main.js b/lib/hexstrbase64/hexstrbase64/main.js new file mode 100644 index 0000000..4f3d12d --- /dev/null +++ b/lib/hexstrbase64/hexstrbase64/main.js @@ -0,0 +1,129 @@ +//Requires base64 +var base64 = require('./base64.js'); +var HexStrBase64Buffer = { + from: function(s, e) { + var type = e; + var value = ''; + if (e == 'base64') { + value = base64.decode(s); + } else if (e == 'hex') { + try { + var escaped = ""; + var hex = ""; + if(s.length%4 > 0) { + for(i=0;i<(4-(s.length%4));i++) { + hex += "0"; + } + } + hex += s; + for(var i = 0;i 4) { + result += hex.substring(hex.length-5,hex.length-1); + } else if(hex.length < 4) { + for(var j=0;j<=4-(hex.length%4);j++) { + result += "0"; + } + result += hex; + } + } + return result.split("undefined").join("").split("%").join(""); + } else { + //return Buffer.from(value, type).toString(en); + return Buffer.from(value,"utf8").toString(en); + } + } + //} + //function toString(en) { + //return toStringE(en,type); + //} + return { type: type, value: value, toString: toString }; + } +}; +var hexstrbase64 = { + strtobase64: function(s) { + return HexStrBase64Buffer.from(s, 'utf8').toString('base64'); + }, + base64tostr: function(b) { + return HexStrBase64Buffer.from(b, 'base64').toString('utf8'); + }, + strtohex: function(s) { + return HexStrBase64Buffer.from(s, 'utf8').toString('hex'); + }, + hextostr: function(h) { + return HexStrBase64Buffer.from(h, 'hex').toString('utf8'); + }, + hextobase64: function(h) { + return hexstrbase64.strtobase64(hexstrbase64.hextostr(h)); + }, + base64tohex: function(b) { + return hexstrbase64.strtohex(hexstrbase64.base64tostr(b)); + }, + cipher: function() { + this.strtobase64 = hexstrbase64.strtobase64; + this.base64tostr = hexstrbase64.base64tostr; + this.strtohex = hexstrbase64.strtohex; + this.hextostr = hexstrbase64.hextostr; + this.hextobase64 = hextobase64.hextobase64; + this.base64tohex = hextobase64.base64tohex; + }, + native: { + btoa: function(b) { + return hexstrbase64.strtobase64(b); + }, + atob: function(a) { + return hexstrbase64.base64tostr(a); + } + } +}; +module.exports = hexstrbase64; \ No newline at end of file diff --git a/lib/hexstrbase64/hexstrbase64/main_browser.js b/lib/hexstrbase64/hexstrbase64/main_browser.js new file mode 100644 index 0000000..a2b4531 --- /dev/null +++ b/lib/hexstrbase64/hexstrbase64/main_browser.js @@ -0,0 +1,126 @@ +//Requires base64_browser.js +var HexStrBase64Buffer = { + from: function(s, e) { + var type = e; + var value = ''; + if (e == 'base64') { + value = base64.decode(s); + } else if (e == 'hex') { + try { + var escaped = ""; + var hex = ""; + if(s.length%4 > 0) { + for(i=0;i<(4-(s.length%4));i++) { + hex += "0"; + } + } + hex += s; + for(var i = 0;i 4) { + result += hex.substring(hex.length-5,hex.length-1); + } else if(hex.length < 4) { + for(var j=0;j<=4-(hex.length%4);j++) { + result += "0"; + } + result += hex; + } + } + return result.split("undefined").join("").split("%").join(""); + } else { + //return new TextDecoder(en).decode(new TextEncoder(type).encode(value)); + return new TextDecoder(en).decode(new TextEncoder("utf8").encode(value)); + } + } + //function toString(en) { + //return toStringE(en,type); + //} + return { type: type, value: value, toString: toString }; + } +}; +var hexstrbase64 = { + strtobase64: function(s) { + return HexStrBase64Buffer.from(s, 'utf8').toString('base64'); + }, + base64tostr: function(b) { + return HexStrBase64Buffer.from(b, 'base64').toString('utf8'); + }, + strtohex: function(s) { + return HexStrBase64Buffer.from(s, 'utf8').toString('hex'); + }, + hextostr: function(h) { + return HexStrBase64Buffer.from(h, 'hex').toString('utf8'); + }, + hextobase64: function(h) { + return hexstrbase64.strtobase64(hexstrbase64.hextostr(h)); + }, + base64tohex: function(b) { + return hexstrbase64.strtohex(hexstrbase64.base64tostr(b)); + }, + cipher: function() { + this.strtobase64 = hexstrbase64.strtobase64; + this.base64tostr = hexstrbase64.base64tostr; + this.strtohex = hexstrbase64.strtohex; + this.hextostr = hexstrbase64.hextostr; + this.hextobase64 = hextobase64.hextobase64; + this.base64tohex = hextobase64.base64tohex; + }, + native: { + btoa: function(b) { + return hexstrbase64.strtobase64(b); + }, + atob: function(a) { + return hexstrbase64.base64tostr(a); + } + } +}; \ No newline at end of file diff --git a/lib/hexstrbase64/index.js b/lib/hexstrbase64/index.js new file mode 100644 index 0000000..4ec8879 --- /dev/null +++ b/lib/hexstrbase64/index.js @@ -0,0 +1,11 @@ +let hexstrbase64 = require("./hexstrbase64/main.js"); +function decodeBase(b) { + return hexstrbase64.native.atob(b); +} +function encodeStr(s) { + return hexstrbase64.native.btoa(s); +} +var m = Object.create(hexstrbase64); +m.decodeBase = decodeBase; +m.encodeBase = encodeStr; +module.exports = m; \ No newline at end of file diff --git a/lib/hexstrbase64/ok.png b/lib/hexstrbase64/ok.png new file mode 100644 index 0000000000000000000000000000000000000000..1293b272bc7cea83026d363415f1d94b4124cb88 GIT binary patch literal 252 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc1SFYWcSQjy#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=DjSL74G){)!Z!;6zUs#}EtuqZ7{ZHYo78aWAeYkc>U9IpaMwGc|o`R%!&GqXG_-9aTc80 z9iQ4^tn8T{w)#-AcuizbTHc#} + + + HexStrBase64 Readme + + + +

hexstrbase64

+

How to install?

+

Node.js

+
    +
  1. Download library from here.
  2. +
  3. Modify index.js
  4. +
  5. Write header in program (replace ./hexstrbase64 with your path to hexstrbase64 library):
  6. +
    + var hexstrbase64 = require("./hexstrbase64/index.js");
    +
    +
  7. Now hexstrbase64 library added to program!
  8. +
+

Browser Javascript

+
    +
  1. Download library from here.
  2. +
  3. Write this code in <head> element (replace hexstrbase64/ with your path to hexstrbase64 library):
  4. +
    + <script src="hexstrbase64/hexstrbase64/base64_browser.js"></script>
    + <script src="hexstrbase64/hexstrbase64/main_browser.js"></script> +
    +
  5. Now hexstrbase64 library added to HTML!
  6. +
+ + \ No newline at end of file diff --git a/lib/hexstrbase64/test/base64.html b/lib/hexstrbase64/test/base64.html new file mode 100644 index 0000000..18a02a0 --- /dev/null +++ b/lib/hexstrbase64/test/base64.html @@ -0,0 +1,13 @@ + + + + + hexstrbase64 base64 test + + + + + + \ No newline at end of file diff --git a/lib/hexstrbase64/test/base64.js b/lib/hexstrbase64/test/base64.js new file mode 100644 index 0000000..c425403 --- /dev/null +++ b/lib/hexstrbase64/test/base64.js @@ -0,0 +1,56 @@ +function nameObj(n, v) { + return {name:n, value:v}; +} + +function testStart() { + document.write(''); + document.write('hexstrbase64 base64 test'); + console.log("TESTING STARTED"); + for (var i = 0; i < 3; i++) { + document.write('

' + enccases[i].name + '

'); + console.log("SWITCH " + enccases[i].name); + for (var j = 0; j < 2; j++) { + document.write('

' + enccases[i].value[j].name + '

'); + console.log("CASE " + enccases[i].value[j].name); + console.log("MUST BE: " + enccases[i].value[j].value); + console.log("IS : " + hexstrbase64.strtobase64(enccases[i].value[j].name)); + if (hexstrbase64.strtobase64(enccases[i].value[j].name) == enccases[i].value[j].value) { + document.write('

Equals

'); + console.log("EQUALS"); + } else { + document.write('

Not Equals

'); + console.log("NOT EQUALS"); + } + console.log("CASE " + enccases[i].value[j].name + " ENDED"); + } + console.log("SWITCH " + enccases[i].name + " ENDED"); + } + console.log("TESTING ENDED"); +} + +var enccases = [ + nameObj('Ascii', [ + nameObj('Hello world!', 'AEgAZQBsAGwAbwAgAHcAbwByAGwAZAAh'), + nameObj('Lorem ipsum', 'AEwAbwByAGUAbQAgAGkAcABzAHUAbQ==') + ]), + nameObj('Ascii More Than 64 Bytes', [ + nameObj( + 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum fermentum ac nisl a sollicitudin. Cras interdum dui turpis, non scelerisque.', + 'AEwAbwByAGUAbQAgAGkAcABzAHUAbQAgAGQAbwBsAG8AcgAgAHMAaQB0ACAAYQBtAGUAdAAsACAAYwBvAG4AcwBlAGMAdABlAHQAdQByACAAYQBkAGkAcABpAHMAYwBpAG4AZwAgAGUAbABpAHQALgAgAFYAZQBzAHQAaQBiAHUAbAB1AG0AIABmAGUAcgBtAGUAbgB0AHUAbQAgAGEAYwAgAG4AaQBzAGwAIABhACAAcwBvAGwAbABpAGMAaQB0AHUAZABpAG4ALgAgAEMAcgBhAHMAIABpAG4AdABlAHIAZAB1AG0AIABkAHUAaQAgAHQAdQByAHAAaQBzACwAIABuAG8AbgAgAHMAYwBlAGwAZQByAGkAcwBxAHUAZQAu' + ), + nameObj( + 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus dapibus volutpat ligula at pulvinar. Etiam consequat mi fringilla facilisis eleifend.', + 'AEwAbwByAGUAbQAgAGkAcABzAHUAbQAgAGQAbwBsAG8AcgAgAHMAaQB0ACAAYQBtAGUAdAAsACAAYwBvAG4AcwBlAGMAdABlAHQAdQByACAAYQBkAGkAcABpAHMAYwBpAG4AZwAgAGUAbABpAHQALgAgAFAAaABhAHMAZQBsAGwAdQBzACAAZABhAHAAaQBiAHUAcwAgAHYAbwBsAHUAdABwAGEAdAAgAGwAaQBnAHUAbABhACAAYQB0ACAAcAB1AGwAdgBpAG4AYQByAC4AIABFAHQAaQBhAG0AIABjAG8AbgBzAGUAcQB1AGEAdAAgAG0AaQAgAGYAcgBpAG4AZwBpAGwAbABhACAAZgBhAGMAaQBsAGkAcwBpAHMAIABlAGwAZQBpAGYAZQBuAGQALg==' + ) + ]), + nameObj('Unicode UTF8', [ + nameObj( + 'DorianTech Hex String Base64转换器', + 'AEQAbwByAGkAYQBuAFQAZQBjAGgAIABIAGUAeAAgAFMAdAByAGkAbgBnACAAQgBhAHMAZQA2ADSPbGNiVmg=====' + ), + nameObj( + 'DorianTech Hex String Base64转换器,用于由DorianTech提供的Node.js', + 'AEQAbwByAGkAYQBuAFQAZQBjAGgAIABIAGUAeAAgAFMAdAByAGkAbgBnACAAQgBhAHMAZQA2ADSPbGNiVmj/DHUoTo51MQBEAG8AcgBpAGEAbgBUAGUAYwBoY9BPm3aEAE4AbwBkAGUALgBqAHM=====' + ) + ]) +]; \ No newline at end of file diff --git a/lib/hexstrbase64/test/hex.html b/lib/hexstrbase64/test/hex.html new file mode 100644 index 0000000..713dd6c --- /dev/null +++ b/lib/hexstrbase64/test/hex.html @@ -0,0 +1,13 @@ + + + + + hexstrbase64 hex test + + + + + + \ No newline at end of file diff --git a/lib/hexstrbase64/test/hex.js b/lib/hexstrbase64/test/hex.js new file mode 100644 index 0000000..8a57a8a --- /dev/null +++ b/lib/hexstrbase64/test/hex.js @@ -0,0 +1,56 @@ +function nameObj(n, v) { + return {name:n, value:v}; +} + +function testStart() { + document.write(''); + document.write('hexstrbase64 hex test'); + console.log("TESTING STARTED"); + for (var i = 0; i < 3; i++) { + document.write('

' + enccases[i].name + '

'); + console.log("SWITCH " + enccases[i].name); + for (var j = 0; j < 2; j++) { + document.write('

' + enccases[i].value[j].name + '

'); + console.log("CASE " + enccases[i].value[j].name); + console.log("MUST BE: " + enccases[i].value[j].value); + console.log("IS : " + hexstrbase64.strtohex(enccases[i].value[j].name)); + if (hexstrbase64.strtohex(enccases[i].value[j].name) == enccases[i].value[j].value) { + document.write('

Equals

'); + console.log("EQUALS"); + } else { + document.write('

Not Equals

'); + console.log("NOT EQUALS"); + } + console.log("CASE " + enccases[i].value[j].name + " ENDED"); + } + console.log("SWITCH " + enccases[i].name + " ENDED"); + } + console.log("TESTING ENDED"); +} + +var enccases = [ + nameObj('Ascii', [ + nameObj('Hello world!', '00480065006c006c006f00200077006f0072006c00640021'), + nameObj('Lorem ipsum', '004c006f00720065006d00200069007000730075006d') + ]), + nameObj('Ascii More Than 64 Bytes', [ + nameObj( + 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum fermentum ac nisl a sollicitudin. Cras interdum dui turpis, non scelerisque.', + '004c006f00720065006d00200069007000730075006d00200064006f006c006f0072002000730069007400200061006d00650074002C00200063006f006e00730065006300740065007400750072002000610064006900700069007300630069006e006700200065006c00690074002e00200056006500730074006900620075006c0075006d0020006600650072006d0065006e00740075006d0020006100630020006e00690073006c0020006100200073006f006c006c0069006300690074007500640069006e002e0020004300720061007300200069006e00740065007200640075006d00200064007500690020007400750072007000690073002C0020006e006f006e0020007300630065006c0065007200690073007100750065002e' + ), + nameObj( + 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus dapibus volutpat ligula at pulvinar. Etiam consequat mi fringilla facilisis eleifend.', + '004c006f00720065006d00200069007000730075006d00200064006f006c006f0072002000730069007400200061006d00650074002C00200063006f006e00730065006300740065007400750072002000610064006900700069007300630069006e006700200065006c00690074002e002000500068006100730065006c006c007500730020006400610070006900620075007300200076006f006c007500740070006100740020006c006900670075006c0061002000610074002000700075006c00760069006e00610072002e00200045007400690061006d00200063006f006e0073006500710075006100740020006d00690020006600720069006e00670069006c006c006100200066006100630069006c006900730069007300200065006c0065006900660065006e0064002e' + ) + ]), + nameObj('Unicode UTF8', [ + nameObj( + 'DorianTech Hex String Base64转换器', + '0044006f007200690061006e0054006500630068002000480065007800200053007400720069006e006700200042006100730065003600348F6C63625668' + ), + nameObj( + 'DorianTech Hex String Base64转换器,用于由DorianTech提供的Node.js', + '0044006f007200690061006e0054006500630068002000480065007800200053007400720069006e006700200042006100730065003600348F6C63625668FF0C75284E8E75310044006f007200690061006e005400650063006863D04F9B7684004e006f00640065002e006a0073' + ) + ]) +]; \ No newline at end of file diff --git a/lib/hexstrbase64/test/index.html b/lib/hexstrbase64/test/index.html new file mode 100644 index 0000000..b0ae622 --- /dev/null +++ b/lib/hexstrbase64/test/index.html @@ -0,0 +1,16 @@ + + + + hexstrbase64 test + + + + + + + \ No newline at end of file diff --git a/licenses/.index.html.kate-swp b/licenses/.index.html.kate-swp new file mode 100644 index 0000000000000000000000000000000000000000..d9b63208857c1223ac41563cb3d78a893fad735d GIT binary patch literal 48 zcmZQzU=Z?7EJ;-eE>A2_aLdd|RWQ;sU|?VnDSMD0Ws DJmU{c literal 0 HcmV?d00001 diff --git a/licenses/asap.txt b/licenses/asap.txt new file mode 100644 index 0000000..ba18c61 --- /dev/null +++ b/licenses/asap.txt @@ -0,0 +1,21 @@ + +Copyright 2009–2014 Contributors. All rights reserved. + +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/licenses/asn1.js-rfc2560.txt b/licenses/asn1.js-rfc2560.txt new file mode 100644 index 0000000..e69de29 diff --git a/licenses/asn1.js-rfc5280.txt b/licenses/asn1.js-rfc5280.txt new file mode 100644 index 0000000..e69de29 diff --git a/licenses/asn1.js.txt b/licenses/asn1.js.txt new file mode 100644 index 0000000..e69de29 diff --git a/licenses/async.txt b/licenses/async.txt new file mode 100644 index 0000000..8f29698 --- /dev/null +++ b/licenses/async.txt @@ -0,0 +1,19 @@ +Copyright (c) 2010-2014 Caolan McMahon + +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/licenses/bn.js.txt b/licenses/bn.js.txt new file mode 100644 index 0000000..c328f04 --- /dev/null +++ b/licenses/bn.js.txt @@ -0,0 +1,19 @@ +Copyright Fedor Indutny, 2015. + +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/licenses/call-bind.txt b/licenses/call-bind.txt new file mode 100644 index 0000000..48f05d0 --- /dev/null +++ b/licenses/call-bind.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Jordan Harband + +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/licenses/chownr.txt b/licenses/chownr.txt new file mode 100644 index 0000000..19129e3 --- /dev/null +++ b/licenses/chownr.txt @@ -0,0 +1,15 @@ +The ISC License + +Copyright (c) Isaac Z. Schlueter and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/licenses/dezalgo.txt b/licenses/dezalgo.txt new file mode 100644 index 0000000..19129e3 --- /dev/null +++ b/licenses/dezalgo.txt @@ -0,0 +1,15 @@ +The ISC License + +Copyright (c) Isaac Z. Schlueter and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/licenses/formidable.txt b/licenses/formidable.txt new file mode 100644 index 0000000..5e7ad11 --- /dev/null +++ b/licenses/formidable.txt @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2011-present Felix Geisendörfer, and contributors. + +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/licenses/fs-minipass.txt b/licenses/fs-minipass.txt new file mode 100644 index 0000000..19129e3 --- /dev/null +++ b/licenses/fs-minipass.txt @@ -0,0 +1,15 @@ +The ISC License + +Copyright (c) Isaac Z. Schlueter and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/licenses/function-bind.txt b/licenses/function-bind.txt new file mode 100644 index 0000000..62d6d23 --- /dev/null +++ b/licenses/function-bind.txt @@ -0,0 +1,20 @@ +Copyright (c) 2013 Raynos. + +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/licenses/get-intrinsic.txt b/licenses/get-intrinsic.txt new file mode 100644 index 0000000..48f05d0 --- /dev/null +++ b/licenses/get-intrinsic.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Jordan Harband + +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/licenses/graceful-fs.txt b/licenses/graceful-fs.txt new file mode 100644 index 0000000..9d2c803 --- /dev/null +++ b/licenses/graceful-fs.txt @@ -0,0 +1,15 @@ +The ISC License + +Copyright (c) Isaac Z. Schlueter, Ben Noordhuis, and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/licenses/has-symbols.txt b/licenses/has-symbols.txt new file mode 100644 index 0000000..df31cbf --- /dev/null +++ b/licenses/has-symbols.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2016 Jordan Harband + +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/licenses/has.txt b/licenses/has.txt new file mode 100644 index 0000000..ae7014d --- /dev/null +++ b/licenses/has.txt @@ -0,0 +1,22 @@ +Copyright (c) 2013 Thiago de Arruda + +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/licenses/hexoid.txt b/licenses/hexoid.txt new file mode 100644 index 0000000..fa6089f --- /dev/null +++ b/licenses/hexoid.txt @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) Luke Edwards (lukeed.com) + +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/licenses/index.html b/licenses/index.html new file mode 100644 index 0000000..64063b5 --- /dev/null +++ b/licenses/index.html @@ -0,0 +1,357 @@ + + + + SVR.JS 3.4.17 Licenses + + + + + +

SVR.JS 3.4.17 Licenses

+

SVR.JS 3.4.17

+
+ MIT License
+
+ Copyright (c) 2020 DorianTech S.A.
+
+ 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.
+
+

Packages used by SVR.JS 3.4.17 and utilities

+
+
License: MIT
+
+ asap +
+
+ High-priority task queue for Node.js and browsers +
+
+
+
License: MIT
+
+ asn1.js (by Fedor Indutny) +
+
+ ASN.1 encoder and decoder +
+
+
+
License: MIT
+
+ asn1.js-rfc2560 (by Fedor Indutny) +
+
+ RFC2560 structures for asn1.js +
+
+
+
License: MIT
+
+ asn1.js-rfc5280 (by Felix Hanley) +
+
+ RFC5280 extension structures for asn1.js +
+
+
+
License: MIT
+
+ async (by Caolan McMahon) +
+
+ Higher-order functions and common patterns for asynchronous code +
+
+
+
License: MIT
+
+ bn.js (by Fedor Indutny) +
+
+ Big number implementation in pure javascript +
+
+
+
License: MIT
+
+ call-bind (by Jordan Harband) +
+
+ Robustly `.call.bind()` a function +
+
+
+
License: ISC
+
+ chownr (by Isaac Z. Schlueter) +
+
+ like `chown -R` +
+
+
+
License: ISC
+
+ dezalgo (by Isaac Z. Schlueter) +
+
+ Contain async insanity so that the dark pony lord doesn't eat souls +
+
+
+
License: MIT
+ +
+ A node.js module for parsing form data, especially file uploads.
+ Required by SVR.JS. Patched to work with Node.JS 8.x +
+
+
+
License: ISC
+
+ fs-minipass (by Isaac Z. Schlueter) +
+
+ fs read and write streams based on minipass
+ Patched to work with Bun +
+
+
+
License: MIT
+
+ function-bind (by Raynos) +
+
+ Implementation of Function.prototype.bind +
+
+
+
License: MIT
+
+ get-intrinsic (by Jordan Harband) +
+
+ Get and robustly cache all JS language-level intrinsics at first require time +
+
+
+
License: ISC
+ +
+ A drop-in replacement for fs, making various improvements.
+ Required by SVR.JS. +
+
+
+
License: MIT
+
+ has (by Thiago de Arruda) +
+
+ Object.prototype.hasOwnProperty.call shortcut +
+
+
+
License: MIT
+
+ has-symbols (by Jordan Harband) +
+
+ Determine if the JS environment has Symbol support. Supports spec, or shams. +
+
+
+
License: MIT
+
+ hexoid (by Luke Edwards) +
+
+ A tiny (190B) and extremely fast utility to generate random IDs of fixed length +
+
+
+
License: ISC
+
+ inherits +
+
+ Browser-friendly inheritance fully compatible with standard node.js inherits() +
+
+
+
License: MIT
+
+ mime-db +
+
+ Media Type Database +
+
+
+
License: MIT
+ +
+ The ultimate javascript content-type utility.
+ Required by SVR.JS. +
+
+
+
License: ISC
+ +
+ minimalistic-assert === +
+
+
+
License: ISC
+
+ minipass (by Isaac Z. Schlueter) +
+
+ minimal implementation of a PassThrough stream +
+
+
+
License: MIT
+
+ minizlib (by Isaac Z. Schlueter) +
+
+ A small fast zlib stream built on minipass and Node.js's zlib binding. +
+
+
+
License: MIT
+
+ mkdirp +
+
+ Recursively mkdir, like `mkdir -p` +
+
+
+
License: MIT
+
+ object-inspect (by James Halliday) +
+
+ string representations of objects in node and the browser +
+
+
+
License: MIT
+
+ ocsp (by Fedor Indutny) +
+
+ OCSP Stapling implementation
+ Required by SVR.JS. +
+
+
+
License: ISC
+
+ once (by Isaac Z. Schlueter) +
+
+ Run a function exactly one time +
+
+
+
License: MIT
+
+ pretty-bytes (by Sindre Sorhus) +
+
+ Convert bytes to a human readable string: 1337 → 1.34 kB
+ Required by SVR.JS. +
+
+
+
License: BSD-3
+
+ qs +
+
+ A querystring parser that supports nesting and arrays, with a depth limit +
+
+
+
License: MIT
+
+ side-channel (by Jordan Harband) +
+
+ Store information about any JS value in a side channel. Uses WeakMap if available. +
+
+
+
License: MIT
+
+ simple-lru-cache (by Gabriel Eisbruch) +
+
+ node-simple-lru-cache ===================== +
+
+
+
License: ISC
+
+ tar (by Isaac Z. Schlueter) +
+
+ tar for node
+ Required by SVR.JS. +
+
+
+
License: ISC
+
+ wrappy (by Isaac Z. Schlueter) +
+
+ Callback wrapping utility +
+
+
+
License: ISC
+
+ yallist (by Isaac Z. Schlueter) +
+
+ Yet Another Linked List +
+
+
+ + + diff --git a/licenses/inherits.txt b/licenses/inherits.txt new file mode 100644 index 0000000..dea3013 --- /dev/null +++ b/licenses/inherits.txt @@ -0,0 +1,16 @@ +The ISC License + +Copyright (c) Isaac Z. Schlueter + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. + diff --git a/licenses/mime-db.txt b/licenses/mime-db.txt new file mode 100644 index 0000000..a7ae8ee --- /dev/null +++ b/licenses/mime-db.txt @@ -0,0 +1,22 @@ + +The MIT License (MIT) + +Copyright (c) 2014 Jonathan Ong me@jongleberry.com + +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/licenses/mime-types.txt b/licenses/mime-types.txt new file mode 100644 index 0000000..0616607 --- /dev/null +++ b/licenses/mime-types.txt @@ -0,0 +1,23 @@ +(The MIT License) + +Copyright (c) 2014 Jonathan Ong +Copyright (c) 2015 Douglas Christopher Wilson + +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/licenses/minimalistic-assert.txt b/licenses/minimalistic-assert.txt new file mode 100644 index 0000000..adca66b --- /dev/null +++ b/licenses/minimalistic-assert.txt @@ -0,0 +1,13 @@ +Copyright 2015 Calvin Metcalf + +Permission to use, copy, modify, and/or distribute this software for any purpose +with or without fee is hereby granted, provided that the above copyright notice +and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. \ No newline at end of file diff --git a/licenses/minipass.txt b/licenses/minipass.txt new file mode 100644 index 0000000..20a4762 --- /dev/null +++ b/licenses/minipass.txt @@ -0,0 +1,15 @@ +The ISC License + +Copyright (c) npm, Inc. and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/licenses/minizlib.txt b/licenses/minizlib.txt new file mode 100644 index 0000000..ffce738 --- /dev/null +++ b/licenses/minizlib.txt @@ -0,0 +1,26 @@ +Minizlib was created by Isaac Z. Schlueter. +It is a derivative work of the Node.js project. + +""" +Copyright Isaac Z. Schlueter and Contributors +Copyright Node.js contributors. All rights reserved. +Copyright Joyent, Inc. and other Node contributors. All rights reserved. + +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/licenses/mkdirp.txt b/licenses/mkdirp.txt new file mode 100644 index 0000000..13fcd15 --- /dev/null +++ b/licenses/mkdirp.txt @@ -0,0 +1,21 @@ +Copyright James Halliday (mail@substack.net) and Isaac Z. Schlueter (i@izs.me) + +This project is free software released under the MIT license: + +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/licenses/object-inspect.txt b/licenses/object-inspect.txt new file mode 100644 index 0000000..ca64cc1 --- /dev/null +++ b/licenses/object-inspect.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2013 James Halliday + +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/licenses/ocsp.txt b/licenses/ocsp.txt new file mode 100644 index 0000000..8aa2645 --- /dev/null +++ b/licenses/ocsp.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) [year] [fullname] + +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/licenses/once.txt b/licenses/once.txt new file mode 100644 index 0000000..19129e3 --- /dev/null +++ b/licenses/once.txt @@ -0,0 +1,15 @@ +The ISC License + +Copyright (c) Isaac Z. Schlueter and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/licenses/pretty-bytes.txt b/licenses/pretty-bytes.txt new file mode 100644 index 0000000..e7af2f7 --- /dev/null +++ b/licenses/pretty-bytes.txt @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) Sindre Sorhus (sindresorhus.com) + +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/licenses/qs.txt b/licenses/qs.txt new file mode 100644 index 0000000..fecf6b6 --- /dev/null +++ b/licenses/qs.txt @@ -0,0 +1,29 @@ +BSD 3-Clause License + +Copyright (c) 2014, Nathan LaFreniere and other [contributors](https://github.com/ljharb/qs/graphs/contributors) +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/licenses/side-channel.txt b/licenses/side-channel.txt new file mode 100644 index 0000000..3900dd7 --- /dev/null +++ b/licenses/side-channel.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Jordan Harband + +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/licenses/simple-lru-cache.txt b/licenses/simple-lru-cache.txt new file mode 100644 index 0000000..1b60e29 --- /dev/null +++ b/licenses/simple-lru-cache.txt @@ -0,0 +1,19 @@ +Copyright (c) 2013 Mercadolibre.com + +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/licenses/tar.txt b/licenses/tar.txt new file mode 100644 index 0000000..19129e3 --- /dev/null +++ b/licenses/tar.txt @@ -0,0 +1,15 @@ +The ISC License + +Copyright (c) Isaac Z. Schlueter and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/licenses/wrappy.txt b/licenses/wrappy.txt new file mode 100644 index 0000000..19129e3 --- /dev/null +++ b/licenses/wrappy.txt @@ -0,0 +1,15 @@ +The ISC License + +Copyright (c) Isaac Z. Schlueter and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/licenses/yallist.txt b/licenses/yallist.txt new file mode 100644 index 0000000..19129e3 --- /dev/null +++ b/licenses/yallist.txt @@ -0,0 +1,15 @@ +The ISC License + +Copyright (c) Isaac Z. Schlueter and Contributors + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR +IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/loghighlight.js b/loghighlight.js new file mode 100644 index 0000000..a934a27 --- /dev/null +++ b/loghighlight.js @@ -0,0 +1,50 @@ +//SVR.JS LOG HIGHLIGHTER + +var readline = require("readline"); +var process = require("process"); + +var args = process.argv; +for (var i = (process.argv[0].indexOf("node") > -1 || process.argv[0].indexOf("bun") > -1 ? 2 : 1); i < args.length; i++) { + if (args[i] == "-h" || args[i] == "--help" || args[i] == "-?" || args[i] == "/h" || args[i] == "/?") { + console.log("SVR.JS log highlighter usage:"); + console.log(" | node loghighlight.js [-h] [--help] [-?] [/h] [/?]"); + console.log("-h -? /h /? --help -- Displays help"); + process.exit(0); + } else { + console.log("Unrecognized argument: " + args[i]); + console.log("SVR.JS log highlighter usage:"); + console.log(" | node loghighlight.js [-h] [--help] [-?] [/h] [/?]"); + console.log("-h -? /h /? --help -- Displays help"); + process.exit(1); + } +} + +var rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, + terminal: false, + prompt: '' +}); +rl.prompt(); +rl.on('line', (line) => { + viewLog([line]); +}); + +function viewLog(log) { + if(log[log.length-1] == "") log.pop(); + if(log[0] == "") log.shift(); + for(var i=0;i& z1eF969b9qF8+r+WAicH}7gvUfi&Ho_*_&J1m_ZRUZ)xmLMyh8&I)n7#4AQj&rr|TkG z?A-%a!TBD~Q$xgfLQCC$;{GewB?-zz>F&{-i%VD&)&9#OfS(%PIrnCyG5~QIz#?U z#DU0y$M)i4s&Gol62gkYbm8ajGFdlf0P>VZ3g_*O| zk>`gtQn1?hnAytL21}g7M=>a*t8`nDaBu?mpO}?-H#fDp+ufYFtev978aq|XRZsMR zbwM|kla>I#!Oxj^lRx-%%RxrR83JLULi|JW$Q5=27cpF53X&M>Xh^r|>4qrs&%vdK zE|S_V;`X+-W_B(RaVIkq7qjOS?v^eV6w)vSWvxIQA_#;60+SF`_n7`M=k1|(nznlp z0qu`B>*t{q2)~6F;wYaMppeR4it;)q@C{Cqo7cVRB$7Pa~|zXh@ay10Kwr z^xM{HrI*_L8hE{c2NK2d5$!|&4+lxBudT^-E-*$QpU^@A?5{fOwhZ}CN06f-koNDs zHuH5jZAWtnp)h28BIA49ZH)~-HmQ`_B=12W5}xEfJ!?ZkSL69bGKkx^^$h2n+&@Ky zy2~I#ATd8ucBc+DL(4QTF<=2p$TG=wCi8n=ud`8+AOSk3<8KdLTsl=I0?J8f?;68v zTWf2y35Ydc(=kIJiL>Bqjm-=5=hK>l!GUJOwd|JG{EoH! z3R@1u*m^prhpyH}7G8^J2)WHKeqFk58~!@SokA?h@(0%w~c>z7v!9ioXAv>NxQ39wh`4u)VZ_Z5lwuQh#)QxE0?-4^%PMXW(pJ z9cOmGkNV+l?d=jjN7uc$2*V^PpoOuRbx4Y4hQHYWBT_Ul|6QJ|q|MG3!Grfu5jN2M z$Yt;%L%2Wr9zKx-p8XvZ2t;bJnMhe*#1aVtiT&qIW-~B=EH(s^*$lxaQq~1huA=>Y zL&us~A20%RrelSCdX0D^;u%=O_qM$?PnaVO@mV_tJbkEpwY2s9TUHfV8i?u7AHzBb zIU}JWjvWNG&5f%+wS>IPh+$ylDv#A{tvp*#*Zw(ZVDHk^8@Db`pk;e8@4`QGuA;ep z()(nyvPdo@j}Ww2$5^h~Kw!}Vh**vl-WaeXhPa&B+-~Pllz7LXo&a2b6FB$PX(u2X zF&3P63po@!7Y!Kn0J-UVi78?2PQr{GEx6F0jJ_Y>W^G1d3r2A3P$8OyNb56M%?(YK zP$OcJ`LnY5;X=nr&)5(;n&Rpi&VXRpWte|EennIuBFUf{ zi1o_^tU6Vgm3qt8ctLmKV8!uZ3AIv*7d$)zla57`C_9^(nn9%^G%lOO4}|EH_B&nm zeExAbx0|@b7+UuC6%>7~s8Dsd>MBD58}M5czP_(c%#%I?S~YNuGB2?}`eAeC=ksqo zJEbooq*hyzu?qB88Pq8|P#}0c#ABJV8Vm)-M-zg7W3Mo?URyJEo~BkATO;qu*$Nfd zjWQ#};Hq&}>i1g&R(DM6vB*U%@21?CIqT%?b~4w+TQ|g8K18gI&qvyfc*`VkVb)1H zzYchbU33q5cCW7FtBBfPtCOnC8m$Mt#1P2)LYTsI*_D-=9{x18wZtD z1PA9!phUatDx(ohb_9uZm@kbAPvVX}K~;dlO{JwHRIXo@ITyrYfMZJ5LtS`opPHe> z?u)~sDE}hC;+LKFns`}tZ_idIiH^_^0gxEW!Jj=hP5P}aY~PLj{r3>LRHy!dC_)H? zjXEKsSz`N@ud+T~@Xt}-)DT<a{-*Q>Zj+%75DHOkZs z6&i{tC@5Q7Ta8QH3H{U=LsR|y!JQ_qZf=Io-dvpKLBEH>PX5ZQYK?GrUlTu~}+!mEO2gJxpQg05$iRRf7uVVNSnS7Q z!t_bnj6RLh$zy(8KMB;fa83b~GCIwi$0cL3dfj7_lljVYE}v{!66BJSlHRa}&dkh| zHO%dv8XeADVH2@*9mz;bNxivu@18tI&-nDr%pJZZC#u52!UGS%=6HxobSlZj2T5{x z`CdJ8U>(ho{mYIac$P^x4r!y#)2E8&H5<(WHEPFj9=B|Lo!j_C&NJuJ6HEDir|Kmt zGZ&4mrTVL?`XAKwSqz(b7hj(Z4-a##XY-PT!$`SgV6dqE)h7ca%k(TP3D#q60uw`~ z78Vxz_P6Afl-56EBmi=d-=X0^_zUtg~5>gsN>JD-v?9(2fEq$4~zbZ2g@ z_G|`gQmFC-SFnHSkv#!NMcrVtnYJap%3E8xcRIOwdBmFSBO@bB!aW`@(kwdaef-qi z-A@yxVO~5Oh;qj7Ir1o>O4rd4 z2COWr{z3O)?R%Bg%|7&f?9=-7$H<5LnqD?9Rc5@DaTzoUYX__>EM%^(uC!*|&Y-qb zcW`lw^7Ge|XIc!)C~1Qtj(sIAN)*2)W+N_UQPI z@%owzf5XxPJS1!)w~fP$vQg%`9~?e2Iywr;LUXwt9WGovv_0d+nyIG|XDe!HTkrMm zACtw*k4dFF?uQLIzC}S}P!4y(M6O-m_lS*ta(=6zrKJTG^+9oUwZS{0Nd|$7l8`xR zw-Y_x=6cQC+#N@bB7*f6-Eb%MOyqQEX1%d#sJaAZNYO!bVxq^u&dZlC?>0$r<3+3u zwiFy3oUWX<-QT}BxL8o}Jo8Q#IQ_8mqGqYNSQu>Cl+m$W_JVls-Cpm$Ti8>fu<^Yk z;<1{puFH1wAI)ni`Sk9QtuUC3R4x!JHV2x{1Sa!Yvm~RW`1#(IMUIoD?RM8o0>(j$ ziDI4)er;9eEm`2xl$$2wU_U=9KGBBs&9d~HRG6?{)vf!EJ&O+Sy(&=W8PMeo6cyVD znLK#RPuq+;n)-S->*j!cc&4`_+b+fzD(rIW+uu*K)RsIk72x2CVM7YVHcqUptUB;| zcIrrMxJ$0C={Dg8)_LBdOnlHt@P=+kBote|fO_m;uF#H3@7qC$(rsxU!q~5}p`*QW zWNaGF>)hJr=!^qapFDdysUj-lY{#ZIyaiF{S=h3S?HxMQ>!R`ngCz8cx1m0v*lb+X z)TFX<-mGiRIk_$Q(a|_FU%->J^Z^No}G)KX7`i`Lb-f5~Ph z!(I1AQj1HUw@T6}<^+or_Q`9d>-(+rSNKFz8lkF7t<^3rE_)*76%M?`;J=OzQCD~O z2puK^mW1f&UwE9x@aPylCXRP;<_LF?2JVn`FHQ!tJXV>G^CyV!xK63%!!xNXeIm&H zWv8^dzamzWi8AfIvDd3p^`b9cJ-8dYf0-m>xtQbA{y9>L}OaBH_|^?>ZORE@f5m6FxgN&E0Q(zQ4CuuFsN? zs+d1($77F;^0=UeE3;Yf@QZ1JP6_X>#3U6c3n#~OSv*&%F}2@JZpY2+&|tRbD5mZe z)lUij=M2WEgN_vykz$}mWs|qH&EpqX@9_yA96W=U3{($*WlcB+1qPdX)ATJQh;hNQ z9xP^#m;Y%+lOQad7-UGhjE2VO_?)lzm!EA-7j7CF3cr2a9eMk1I&WtE_w&I`k_2^R z$OCvTwMsDV0zuVV@xFyWqk+V5-U)R+(HI388n%1j1`)xv@NLR z$6HR8KCDdxJ~J;<<~0u2Oy>lHb}MdkQbHiN-S>^?!6 zS>KLF>k@RR4}rW9aAVSli#r^jIN1zY(9)a9*U;fJ>Iu)C_5<^KzjVb^B2?CA6S`yd zU62SOBB8GJV)J{)y_#A-Zqw7R)8{&*g4!Rpwupx0fhNBovMJjdWyIqJ z$OHfwwL5TeC0W_Cjz?2JOy+ozkToOZ=kpqe3^292yx&@nv5MKHdXJ^jaS$_xwW0|2pDADC*vWdq( z5U$b%gX-sQmHY01*LF!twZ+$bb!`v*z;?a+=cgUtUyKj4NeAQ16{nNYR_S`o6V?={ zgbC6c(+&Ew6=6N}QuTc+Tdm)3`$)^kC_^l3n4qpfpT*4FoY+fv(LsA>XVMS3d|@}C zb1~%eX;frn=KJ^W6W1+|NOpF1u!1|aX5-V-S?Xp64#~^kUS6`SuGi7?)F~t-iRR{1 z_wCrlL+{*Pffq6Ak&+^D@m!3Lt!SyKVO32(pMN@?tvgQKw#Zy;u>4S3e@UXmip%IQ zu1NnQK!lQNYJH!BJkhJVzcvq8h09xt&Mw1E9dmTY5#UMN9$*dUBCQtUF%f{eC>|4! zwZqQ9@;-=ztIJRZfv>#5qR)}lxQF`qjkEreyt1f9p=^Kk7tIANRn=(l^LXJXO;I6* zd*EOp_~F{;zP_bfw;Aw|=ghpCF5}=Ae*kW%4sN_Zv2H`>6nOydIZcn{q3~zo9wC8( zJ>b@v2>C_;PJx2$E0EP6@1_nqu7CzA+0I{OiLsm4-fz0x@JH2t_zofoL3jrIT9JF^~_9U~JpVK*372P!?DnJV_DG zS)>D^sb%XH3O-Sy(bZ?Ix3cQRroh3J0kopb&5D%?e28cG0GEYl`#r46f)YGj;a0-!gg zlyR=kb3hII)~A_%fUhi~(-*BIluIWGCPv2P03gy-`Q?_Df4$xuP|K}GAP8%#c#vOr zTvh~F_qsaV2Pizc=zP(R@(`>#gbuVX0XB66SO303LP0?f`TMF2LLaQd62Pl}UqKil zE@gty`{%VimKqP~ePoFZ02fGTvAIm3a0yk7Es&tzV?%*Z(4cb__#GF?8T=NAu^@AB zGxd)=?bp$ggjLO`9GAxcRIB#xTGFcE}L|IbibSPUi| zvs_IqqKD9J`^Ma&FrGol^LKmdczweI7#%is5Un0cf@hoU`J zU+7>dJ_Qjv&u8;5c<+*fc+zP-0OC?%N|4a5_hh}+wP=zf!@YgGpr~j&bOvmbK0?og z-_elZ6jfCnCeMtHj;=cJmew=lV_8~Q(3vOw@(}_laP*5~&ETNo>&``b`-N4f-bDvG zO3K9;VMxFRgUZ_=tOGvK6KUr~LQ~IsGUICmawVv0v}U;l1xcbV8qfW@pKx-5TH?{I zgf{=GFYLPEQuql)Z9Ms-g)1x1Jq6RP`=AgCr#|&OfHgCN5K83%406ybjaOa*N|w@6 zmgA$N&+CW1J8Qm^BE>7~?)1WK3JndmeP3=fQ0HpcxmQ6gjVZJY48Cn`U=}odE6by4 zOHNjm=dw0kvV^Jn-^i;CSWVdY9u(@YdKr`*ewUWm+m@+00*JpP!%O zW4$h|A6Rvw=inIKlXUd>Q--v6A+SVQS42;y0&HerNhPfo)!&MzdT3%~U^%pg& zwZGw4#Fy^6a<{M-B#v*Xav7*!SqL@UySL(^XtU17$>}n1Mu!vpOjY_03lTlA85sg? z9h!7AL;>i^<0JR|(>tP7rv}~wWQmTeQ~!dcC91y9iL;(KV6VeMogYbaXHhqpBb{zK zU8DPQ{uBD5M4f_S&U4-1ZpOIPw|F?G((1wf3OoG=jC<&3cGvtFr~4nsYkGK#f3jEF7DGd79M5Y{B#MmTr%g2K^E~k z@5^6Uq`Z-3F|;jr6OZfAht4HCU)1Q<`rAaD6lm6T+b2@7I1Wgn(zup!KWJ#cIL-te zk(v58Z{Cb8Ecg$Ya`Ewf8@_l*My9ExG{jZs-C`&n+ONGJUA?e{AI9h9p7P>j(pL6OL>a0tP8_(GsH6SLD z*K&5Q46OPY)?REhdbF8xI|bOlaDX;rOZ~+q%1;H`6&<3sUnB{<%9+R@!&4Z0lSN3= z(`RP{0B|iQ63`(lH&f%`SW;QF5IW`i{6$;tr+3yGy3=;U#c_92YephIN6c`PQPAD7V8)n#o*PD1j2in=n;gVX(|WL!cUZQD&+$U?%xO?Y4b!su)R zQ9kc>o3zPG$#BXm*vUjb(FrV~%u6J3X1A=al@9HhJ~b8Bt@<^aZ5VU2Is`xH>v-@7>( zgtpuLEr6sUV&W6A2FE@7|I!fPK#hz9DPF*pCglBC)Zv zOnQO`T>LsYs|=G@Eq_12?#*&HFinrX1%JZn471PUT0W=RK~>23eO8ubd=fRub<$hI z89(zP8=VNmJ}%W-&zB|(x-iV90fAm#PD~tf?x(m49NQk|6{@u&#ma%UHp(v@uk>*8 zN9oHZtv9$k7xxlr{X0oWaDtl+%LK`A?Qv0VpX~R1lU2BCu{3YkFRttYF6s7U`!1-w zwn6csPecUzb-&_r=vbmw-I{29dY2X#O~4kxm!{Y-XE#rXE}?8~E#m39mh_1Q0nFc) zN`r!<6x0X@eR6o^x639O>t+=20$^#pi&~Bqp5_((2!Q-y`))gC>ayuG%Ga0xOeIcF z%e%S_Mx2qPr5$SMrSOI85G5Kk8xy)}!>{w&5;>fl{~&y<;&-}3wXDovcbS?qK# z%%%Q{=qMOYP=PIt?SBHgViH|FZ<`wkEh2*7;|Q)*VzmlG3B|ViQLT6%jO&1bOSo}) z=KaCVe|=~n`y~c;9-Fle{tM=mBSe_HBCz9^TWJ)ikbuQ-QaKH?RiBy~U;d?20K_ac zGzTXuo)xO49&@N99&@NABlP|qe0=b1Q1^#~Y;7?ifY-#tkkk($T|MEVml$&D25Dx) z#j7AC%_33~^ERFYHt01w`);X$qBIj36g&?~^Jvs|c5-zqq2h?nnafO1MA_e#ne|?= z(O4>98<)of>sEW9YNt8K580OL+2>j zf1gZmyZiC=Yj{zUxcBKiNrz6G(39;ZAbn|+&U%k;ZFY$;@B*3~l`t!ncTTYY9R!4Y zYdSS=Ii64ed!`x#lVGXH_mW#z7sh*4Ru+lb;=y*BN)a5gRi$J}HBLcQ@eyCn&q9W* zYHX+rC7G?%sg$rD^ogR?w_G*0K0cu6$9FKRL%KTa<|UGwaQBhH?c@jUB20Qr2#XW*L(3t4oaXY-kuzCDnZQ+{2b?)D1HMm>U=ATz+UL#iP zT`gS$=kn{+({AeK2(RyYt2=Jhy2hy00P3}n(7e3ZD73@d?*`A0KHprUeNz;vN~TiN z#XGHsYm*deasfw%OgsYqHoK)!hR%ftsH%_IMl$9 zMX*)fS69}6k33@|u`%dT6aNPw{CPve6S!SHvMFl;o_eggv+D^9L4$mvz>^_V16G>9iJXt1?X#vOe4z2+9zl^dUmqnt1Y%Px$qZK`U2h3mLbisJ zo-rr~f^rbFEWj}k?vVni@gl=8O&SK$f6f9}L4iKT5kxgoTnsQ+a=vGB$v3bY9()$( z1yCF`U|e$3_dF7j zAb9s*605hJgBICqeAT9D0(Iqe7sV1QY6_scKVX#v2S#lMCgcZVrO5zPZg;_Mkux&% zBv2Nq;<|{rnJ6+$o3Kn9zC}8&a$FeEZfu3~n5_`&PKskOGC_fiB!e=NFgTrI1Cjw8 zv@OOh<|z?n@a|)r_u~IxY&L+gvx1_^e#30I26GnBeVa}-N>&4|zj_b2q$7nipKj@D za@iSNvt~;}i4Fu$^FPd-31FIoo#Kk(bPIh}i&W#V7-SIl^$D%A`qXJMd_YW!q%x{! zpkWuBfsDGmg7^X5Nx+B3%RFbzXGC% z^5oz0Y|F!9%++($5g6#`W8>qlzlkHEjEW4&`#8bl-@bVc>}+msI`NWd%@Sh021pFQ zO5R5(1AspSj(PDSA7sJUx?QJ+6$jqf$jH53a)^jaT!Dlr+F2?f#Enw}kQvmHc3R7R zcaJ+5?H-Ylg@5)n@D+ackeHbGbRq0j|3aJ2+5={3s?h%Ng_O}vbtk7vL9!t(vwILc zJ~M_<(0EZ6MJT1FRq6ydQOqe#keao$5$SNp!GtV}6OdE+<2eZs+U#|Xc!uFlB0X<@f* zt?q(|ML%RSD*XNX+ki<-G!HMTsjGYB)U_(5s;0KO@8O|<^a0ApS1kEXNZn-75e+%+ z{&`)8aRV{&+Qx8J%J}H=bIo^yO=+*Sp+s z08E=qjaYKvrEwkm?iBT;pJaK_sX^)~xq0zDK7j8!>rf4wJyci{-chlN;LrNs1pnDe z-D!_^Q37SrkO737&uwfJs0Pi6NOH{@KR>Cbb6t?tfySL7j>Q3ulVlYS)n;gUpN`t>=)SVIxA!efHf>AGCPLQIu+JaQD}VfuWj53=8W}nH-BLDW zmgWOF<9BD6U6SE3YL|v5OkTcu?WCilBgEkGqY!E5bwhPPY8_nMJY70nJ-uoA3#@*V zR%TbApKv=W2kN&RSRa#`D!8=+IOta>51#*qRGI`e7b)B&vVigW-S8HOM<$?y!31>x zB>DAaR8UN0_TkP!fAj~ZGcyXZVr@sMD)XJU=U0QtHPVclsTDg2BH&LkXEQ*9+C>`N z$-bpA?@{!C%@x2V-SY2qpt1x|ih(wN;ZJ3W=)NlhJ_`?I=D&Q_BU85oRfz#s9EYzd?dH8a@STSMJZBKUM6>w{v_U%l|@C{pB;_E5T4Yy66}f(#*^x zj!1bl0sUMSbuUnASQm|;;pYZ+59Fr7`TSO+E>De;KP=HLRlsI zn*ZR55Y@V@itKDce0Vqx874O&PA~yZFc&Xx0T6@zU2gxXs_H%RAar?vCADZ$R_mGn zD@+uCO3$kVZQuPtdtECPFjP0Y+#^-({#7cvRN zP*P))|38|cnFeCnJbOv}etW@*H%-wK^dIUT;so~yo;_Oo?!dcp?1e}T-Vcn+BZLPv zt>FKHT>ihnMl2x+Y=o_@mtvXnpMck`27x@3d%_8H|FWFFmF`3(fC8A21&8MqH3_(3 z|M>Y+>>ncP&!T?;r5fTJy1MsVf9)BtH{Emw@2bklec1VrwJ^~ghZ`P)V^rP6j zg=+oTer@@R%#)=C95w<7Dmu#W7&PPsEkWdjff4`#!+w76Ku!}jBCNR(@Nbm|h-eBy zD*bWqrB{l02%H3{CU~V&>-4k`;HRyuFWxgSu5V9OsLvqi9i3pX^^22?tANoYBwStN zambidtgSt}erDIy=wsl<7c}xS+Wi(1G%`A3j|-K;te}{Hry3i3ZnZwBN-BIU*;(g% z`HQH=`9-0X)stlet>z6XIJlCpAj~)s^>KL{IBq0@h6+mc5YWEj_jGFg*>ML@zTS7f(84rd7PAnpVu;B7$pji#b9}&ve>bVRop0`7>zvN1B zf#X9AS`AnlG87Oce{2|7eX{SM@{O(pZIw{|V7nr8C-k+gGETA^~C z=_Yq*aNxpOMlGd=t^!T2UmaB_rDf8>6+Aaa7G4f_~ z<QdNJr~O&raT)t};xLio4UX z;YHTX6fH&s6P(lK+~3U@w)Xc33qX4R?j@!?T&(j|fN-t(SK>L&3mU$BK3B@spsl&% z^OEH{RFw9SR^CfVf;;&1*^J_ghbF!*OH?f$86xyv9pXmLLP!;g{a!Uyvp6_jIHybRzH67eL>a#! zH9mx`T`RZ^E+FP!aPz#)uVsA_zFka(ABs)K0?sZyUh`bH9PiU`n%^2rDYW0Z(;`X- z!4n0D@6@naYca;8rS_1EbVo>*4j~KJRV}@t{2-!)8vL!cz<-7>balcEym}*YT>?SM>;|YDDFqvIg;mMJw#&%cJKTy z?S*OnepP!?jWJ!dFO6O|qcI)K+Rbe+n^U^rYPnrn@=DYZlxz4z+a7CjL!Xc7iPp;i zR{l0zB`V%VK5|$ltOQyx2$Qh8n>hOQ6^8Xbx`hH+nyPv_#Bh1RhW6%<&}#={FO_=L zetfN(e*y0Ay$zdtG_E&URC3d&_xX>Z?ulFfG{{crvs0DSMFWVv+mOfI?yIy0`Q|4c zBPd9qh_6WuX&tnE7$L}=zB`pwcg!6KQu7AcT(q5A@#&#RemHTf$5Obt__1SAN zg>~afXLs9dy&s_X#PXhx5u3GYb(V9&B~-sh1TFa8%{Zn0iuNo8SPIxP*vz&sCaiz$ zm2>aT%Kui4jwP{C8jDP2Kh(73@GylP60mP*;VtUgv~|?qZFCPE94Y?}Mecg35MdX6 zGjtV82odQjJ}ip?@28LGq>ln+GHa3vr1fv_du%Qz*SHXBVWU7K#^VQ>`D<=s{dBL? zKC*FYPn7Pzr(!8D87Shh_E=>Dhkx;02`mYS4f%=(iuiWX*sw~*tIIq{9FvO2-Agq) z4v;l+)U+Xxin`ThW~u=dcmy&FhKlE8UL|Si^s3h>B?G zT@UrwxP(qg&maL$CzjIm#`enYoUc>eAQnX*!=B(}uHadFO!9v2Ltya$TlE|bi|z#T zjb4m&=xq>n0v>kb&RB85)qHPp!3I9nzm_uqJ|4}&*D+B5-0G_ps@15GteO5ftq3|a zdf<2I^aovAUmHdfuq|nSt=+X9jj+3la^rPFoL8Cvzc-}ou4QIg>%!OqiX4JsbR3;p zN)F4G_d#<}fVH|M^X828M(sj)TIoT8qm!ThatOA(<@ipK%gxpW_-loL z`kKKu#nZA@qar&a$p>J6GC!L7*rR@w`?!<@9TMUVmYv~THHu8{*8{Nii=nc`?8f>^ zkG8QHY^zP)2feQ7iTu*ZBUbrI_r6wf&;^2+mxC;h7LQ&{>4^d&XHS|NYOic;DPm1n zLJ9hl0oqBUQofnf-+D#m3vAr=o}fDi8zY&ZXq73;Jw;qui5+ zsq1{;b9WS2`2dRx#c%Gh4RkT!96~#4pWNC9`9V*R6++^B2!--~@3QqUmnoew1 z`6gG@F#P~*Sve(*zH#PH+1}mX2Z5J!r}q)(cA$>WAc$g-9Hth_!k<1Hil8onwBwg3 z8I%c-H>%0m>&ha8k@Eg-jk~u9{>J11H~=k%FmDjB659x3N%(L0gFt^+WczZvddGVP zQS-H9>XFaD+Xk-mz#R(YYwlC~n&XNRRDkl?2oU*n_luV_=vD&7A7^Tmc)geq@UkNy zX9vXz8@-ayUQY5lmv!(n3}87tQ2`QxF~Kq&a2blIyX3pF-31v=U(EjX6NwJDNw)^! zDmRA&?Ii&LidoLfSImz8E(-H(j2dYouz*BW#vXa^A1uHF3ct3X0OlLtYu<@HTx(gdEY;`-@@c zut6n6rF{w?4kQxVSsUxJy97jQuP(va2X5Y1M0NkRw$`SDzBgr;S0fC%A8Q=N#- z<@80168P@a@I}@9B)A|3tk`}os};Kib`gYPcUpe<=;SiWB1YDpN%o$!kT%9ZO!-Uz zS)h7to(myM@pfKi%2Dk+!Y^!o*tV2_jqNdlUQtkb`YV=UgaIr^0IzbCW>!9#Pbe@^ z;IT*O(^sH0zVHLuA9{i9c0*?u@y9|UB%Tu&#p!1w9EgFLjv#0FxOagWQ9zzboZ>L@ zl6n-wgZOJEM~i0%Qi#8cLAG}A?UMvEmlJ~Wk*zFWQ!p1q;O{AGDLr4N>vj+Te++z^ zJDVg1z81iRg!me*(CI6XT4=$Q%;UoCLgtG#NM+Rm&OAWl z0Hq&;vn3jJWxq8e`y}_yRVEnWXFf}jIE5aT?1+Ut>uK6^7 zSD?*4!rok4H8@8TW*f~SW0inU(omwOnvgU6+5b@HK&~%xC1xcq_A$MJYI@F(sGD@Y zg9QlWf`Bwa%qC}`WY-AN*bq5YdQJ(CgRl)tWkPI5cWFS zJnTp0hn=9DK}?%dLKx1wtIJ?$+Pii)4kuf;W3Q8z>LU9E$%SSc$F zpM+l3ATK5Vsgb`nH}&lL=?nXAxlQE{EmR-5p|^y+ z!WL=G@un)zLHTs7$WSU=T`Ej>|qS^c-&e=H~n2 ztBvIqADL)Hvy3OF0pzgrvS>mXU6V@E!EreC)o$Zae9?`ZYRP;5luxTh0zOp8Gf#~> zF!tJ{j8lvVdUEo&dZ>mbnPC1@g>Plrg}OK|@C3>p0uS;nF={Xb>oLHmzy5iUMr&k} zI;~v0UO2W|dBojyvmoHBJ*1fynd2s`*ws%eCC5Ls-A&HZmTfF1dQEnkyD|iID!9v! z{0R(v@jdN8yCH4hn6W9fGPPnqFqA^1znRV0Bpz(2-id{@KC-UKShuAl6 z3F8$1LOCtup#6z3L86n-E^U%;!pd>q4%MIl+*{{4@98D5W9xj!RdnQ#N+8uL1+7&h zVNmYdi{TFgT$P`>ljG)_NZtnTYI)d(6llVX9(bk)hoJlrH?74@>XWmdo=IL$>sh`7 z8V5u0xT5OOicvunF4=Pq*drT~^NJ|aEpvl&rhvk8c~tYuh8$7s=igX2SAmn*x%f6Jtv;%iiavPqO~F}G&W(72&@3((>&iF8U;>ek=0WLpY`&GU_FyfN za3r@#OJTM1zCpC6v~g`G;b+!+9M|+^cT`Rnpd|@o;s|MueLv$jC!Obk z=RGfhG@rdK&cwaM?GvBvH}Ho0yD{D4zL6c_BnxM+(o`j25Ac;4JEhb#k^?#7LSRYY-V9Qs>*iHM&o3RB#v>gR z;o%j(Sd%($r-^E71p_I&!#tA%IH7jIe8Ty|)%Kom?1 zJ&*SLejV8AGm~&!bn2Nw3sU@4&TnZ%qY^O=krIGC*0d3)HIKv zSDGUW66{4cG@HG*u2Yg4j|#uubhh(d-<I$a8HCHi$5iQ$Qd8HtKXp>#KrA`|no zxLi&A<+Y!*B|bDD-9Dix#sU(rEcr~WO>P7YldWN8~fbm zaJL+qA$u?ieL>w&v~ucb72T#-ImdsQlFrFZ#pq;wlMQXNee;3AL-3Pu_tK4lgq9aP?9q}#xS`mjV=K=EZ?vmH`OckDl>z4nV z1=xv6PWu>6-P%0`mF5x1^jC0l>ODSpobZhZ;)!)2IaQ~t$A7wGsvq5AwfD)_)fsPu z#dl!X!$0~_bn0Z#q@ZwE*s)ZjT{fx+<9S>oqh#cR%uZ zQ438k&uAis!5hZOXWu7cR;Gd?o=HW2BKQSiRXT`3t2?7D#ok^WAAmSp_Ldz!%9SD? z84=1UeRdm_h<~VjzjOL@=`>+@ttr}vb0_VHSE#obDjh#|%{sZf@WUwPu&$I$zG6t{ zN2-KylgZQ13puj{>14|0z{tDeTfE)C$cKV0-$r-5_cP*$*Q%jZ49Zg#u)Kt^6VmN{ z`&v0C{X2sTjpvdbJ6Gbw{mGTr*ABsD#l8-9s;2)s#w_nj_eryZKd~GK;bsoBNvojV zJ2zFKt#z^(JY5tNu{;7Tn$cdk=3KKT+Eku=C3aisSzOMOOTB0U+1cYG$`T+B=WlU* zF3Tg5UB|UA7Ual1lmQ0eV=PjBY7v4z6z^K#o}ukv$YLR!Y&7_{F8xUw8(uvj+4`QiUO?>H3c z4VHL_V0L~3i>Q${%pmGrwGVin8+Kj${aHiZ9WG`s*eq^-OMiwNyi(I*R4ohFQi)u( zt^z#>J8f}lE@I%+t97y!h3lB9#l)lT#F;Lg*4^q`rP3@at_hei(Gk197!zcP+p?n3)@ zwfkG4H@e2&_Hva))?)O1jmwYgGjT1AKya~RkoG@+%ffE8tpWy!qOLxZfj4cRDZ)xw z%UcF`zAxCNbq@|3v6Q3uu=Tbw!&mL2FYDV+gc6@ef|1Y>i%B{eJm&Z%@Vi*cvCYKj zJjztgD{g(t`H$b7+&gwizk4YsSH?4wl~x-lk(+)&V%I3P zW+Ja>ZoyFQo;@9+*7-hL8uuapok4szeyJv%(`aw@$S>-cXckT&+hh>-Io>lIJpIN# zHSu(-U8Gp{y~LE2o1ErN`n2cz(J|OIyYZZP4so`Q(eJQqc0IIORO9x)w5P-p_=d1! zV6qsj{uFhuJB9S=+HO?ASQIO#`TQiM!O|Hp^R8WB+wF!4q31F@i)osqWBfbQi{{2< zl?JZ5+Ea?|oaTluTH2&&CpXQXP~ch@qS)bQC&)AW+`UCUw`ElR9@gqea*JAw=lR#=?&F<=!-P^-lGiBe zhh9)#05?;T6`XjC(LpX>Su$#r+{#Jj#FC70_%--}cfj`%1N5-s&vxE3wBMROm7OVB z&YDf}^->OWj7P)>#&?g!+f|uGE8$Pg=fct%H?`Z0jP-UMN$JK*V6CbSDYQIg4>NJu zPXcIo7Mt4J%R`Hgd7-^Jwj>hs6PFh+=c1c=!ou*~Var5k%%f*Rq9jQx*l0D!#?eOk zzkJN)bX zosDm%IrDDWq&&9?_Eif_XF2J=7bF)oG?*IuoN+R6iIoXvCx7r0Du-=%5wzxtd_>tz zMp5sQpz%^mW8Iir!O1Q9RI6IseI+8nWsWZlgZor*S*vCz8D%kx>q^8XeWe;Yify$k zU|D>kwo>+9Qb@r0y|qYIJWT^nuP{xLf!M_iHJ@nGU(>~D;^>0^V~Use;m4O$H+*vv^h#u&d%0FA~*q1zZv;4xy+Sf{YAZeJs@|Lhg z)1Ua!K?$sqDXh@DP3hnepJ!k%S3l}`>NYoUOS|EtJ`{PBM5IbnMKj89SW0hNhq&6mp)FZKB>xTy8&4=#e!AemV?7_lKx- zIb55_qjvq<7U`G?jAgss)Stc=Bbp@g`8z1N>@v{`d=2&{wACn9`Nho1$LX_8t84 z@XKfDjX>H>(Kl=b*=IUSqoY$%4W!(1Pa4`!e$r)zHK36V&G?=&+73Z^a`%fG(lLEy zuFfO*Bc9^v7nHu%(cS@N@Cpa4b^JB$`Xy(4Uo)L$zonO6psg?Sqt=+C2yFg%70WA} znc@~pm>q1H4-MF|4!7`d3T!G?x{+w(!k;@}*9^Vlu<+fFF-Pu}mtJn-?~L4c*C^UB z>&!aIC=8-jZtz{0@(F*n(haIu&L#;w4clbN#4U6uS3Ui0jw((rFA=*0&b9q(@939J zKO_c}4)QGp-+zPO36{>Sc`>Xbo3V&d zit9RHI7-isW65dwD*NYef=3c%N8!|a??_gRs*rBn6h(X3Sb7axrQ*uN8#6eI%VIGP++YF!|Xo% zVHj?fZ5T0^L!9yThQ%a6f=XPZ4z%faFj;T5UV-S;$Q7Yg z5%Q6{zL=OSakq zXAlO4SM53fbMIR3-7oKbeavEYS69`pT~)jG{?+cP>Z#H>Gru~<`Z$(Kvq0UMGS()& z;FI+ioO2|-$JmuM3sY|8q*E=1?||`NC7e5Flx`-Mn9_L<6`{O!fQUqR6?i>9dB{kY zx31G{apHn)yf7Nz_>5_zs#lM*U?FPTMLN+kH`0@r=}KQKkkSUYQ4GwSv74xg_?aJf z2bMUQEl6K?g!tE#`WPh>jaUR2xadTz39#xiS=gte>?1QP1V zLdttsOTvHmHm5sT1|Z{5LC8txDq6K2+<>v6M`eu|F2Yj{&c*+QE53?&^q zql+q3-AcH^{Pjd>I&QI4r_Rd}n-VQ)Bx`t*I`dlh*U6Y>&(nZv>O$)}(MdG-ZdS>9 zOZ^<$&j441Ntk*rojK@7^{!J(K#xT(>BUiB^pSM9R~NEsp@N0CWc8;se zp3Sp&FEboVh5sUj~0p3HBmTa>K#0xUr{;q#p#il&SSAL+FM1 z0Ht7#0}aL|>zKvnyL-l*%5VEx*3>K}_Cxi1&x&4KazuG-3OIx~Vw0m=%PTN(JB<4r z45Q7$^~e^r^CNeQd@&dO6!T$)PDU^CXMEVxO&jfbp^w7{*Aw39Y>g^kzsrteww2`H zGP~Dy*c!Je8M@JYckOHs;|pv(cQ|E)8c)VC0egLhT@(kZ$)R|pi-QIoE zD&Z@(s?>U^vuI0O`$Wa-T8y7C&Xuh(_z&9^M&tgHLa&gMRWx}+>oaK&$EWw(^*M2= z0NXB&1N(oYx|mf2Kli4`j=)w^npv5qvgLqlGwr9{X;QI>d_ljYY+>6?v)he89#&Vh zL?Ig3%6+_eLeILd%Y^|Is%M%+>ySsa>a5OKb=*<>e#T-_3YSV=;XWUFgo$K7cuL~P zGS4-rLlC9ZlQ+yPS6_y~PIJ(%u-)g{V8`##txwk6SKLHYEo!=N8Xey}86PL7O}0(T z;6xW5^(j$Tto^5$vkvd6R!bkf@iCb>h^?CXLC2Y!($N>6xZyi=xo52eWioqf(1Y-< zCwty2V-LmQhJf>?3?QYVrds*SZ3v zY-(@gTFA-cm?zgOubJLY!UJF!RlCo6>rmm>(aq}OwO6||_L7RDv~-!jOf8F4U9`eg zKNqr<4?iD0Vo03Hydn*`KB`CAEsVZCfj46R=r9Aum`j6=z)}miKUP*A`bQ*_Z9=Afp z(Jz!4CdUPHq-ox{H89oF?)C=vm(tlT!+n2faM0Pl$NV`-*=G9{AR@?occL_?t;rC3 zirQvg0mV+=(_QDA!#!IA*y)AYiCe3~?!a<4Ysd+dz zJdEvMyU137Y|8W)n*HhQr^ri)s{9Qoa@M;EPiQ$EuI=m=YZC3Zn{V#2d)5Ed(j#{s!c4E>@X&%RcSqqx};?}i5fa_MDR@AcH{El z>!~k?2|c12z*ixOYg4kp_+#-o^)=(y?6ltuvl4?$hFT112NH7i_K!uU)XvjdVQTc|Xs+Jal*x(<(%F zatk$}W0GvFkyB=$G=YESSU8_qzBkM~sA?LrdgY*RP z`zDH)J+L0tKT(Gwf&Ny?VaXMMwC zRH34a9%{9zD16;f9ZOfSSfLOj>73y$;YIzrRHqdwgJ^SsYJ(qq_WS zzOn()h|AoZ^49nG9_;4Lq7fnhfZ6Wuh8$hh$&Ecl3sujA!bj8kVVcpB zQG=3Gbz-<7fYRI_h7`H#kW;K9IlO$Xne40i1#{;Y%#$7>tE~-V#Hi9HuP2+Nx=)VI zHX_n^#LHJwTDkr>jEY#3;}VzywSA0dowd?j&1V7A7AKDB$ZleG+IgG-bt-n>ap0+xo$SIjTDZgjXpxY?A z}I3GWYY2C*)_{ZtLpHyxMWUw;HipI_08q z?*px$ViqBaNPhfGF!+XB2nVzOdkMUDm-@n?o%*{dO^;Wxvd9HVgl(rtt z&&aRFSy~&Ee%alZ^MJSm;q#N6{2RM2G#3%?)X*zwCYz{)P$xvfs!EB|7GK@P6GFc z*J5fN-`Bn$0)9r6ja5aNCB3oInZN`qMF_yHQG@9QGtOgMtZ8ORre&44A$Fa^%kfb} z6-T)C+x9cObA>}~rtkXK;`$D*Jtj7*+KykC{^VlCh%wq>HSP=6@(2TtD=Fj6&zBGM zCfoA7oB`Sqz&$qhG$*p+hWe^QA9FYX^>fjpWKY7&54D>5wzY8fl(9yeD59oDZlXi9 z?}xhyORuosx3)v+SZ?O(FT3YJ`#M&Yf0040_SXOM&j?lhhUxv$@rMdcq#xm(>(K0t z$vy+l?X@31eeoiDDslZ=J&3BTMhxx4tf%t}QjXq!0Hu%v*Pzwz(ZGQ8=SSEhJVFLF zJ&4IUtV1-ZZWZ?LuiaO}?gM}8XyOaHC5!F3`5GQH;=q@FU^@^YI05zPi-i|;nKTW4(tKka&coT`~IqivNY|1tH!MdKmuxQSrB=~K=+(Y$t&9Sht z4n8H?mm4>4=TdK~Vj)UM*=OcM5BD77W~xa?k)JW;v3n>BQP($R`dBrQqJZ^+1p(%X>krc2nTy8v!zSTi zW=U-hN$1ywxzZfu66WXa&_L@MdN-SOEOBNs`nqZKG;>Vr(UDKzFX62ox48*~d>1+Z zjb-wavA0#PMbrP%@sV>aK3xCU-JcU;tI|?i$u1+ZAQ#5DKsC6!sG%nF%k98^IqnZn z#-^~4$C zT^)Sw9o$!5=?MPJ61$;%djkbOMK6mpHqV&Za&z(TCXbr}mI&M7sZMSW8&Z{~SzCOt zM>oR7yFQf~gITDT1!F#U1J^*ZAcI%Z^ChiEH@bTbY zp!}Ti@OtWgjgC|)mzQyHW6h(a0RGCqluWT!D4nbl zjqfbP!`^C2@n7Ttx3Fioy-2twk|Ldmt7)ySXwAf-%K6m&DnV4h_K8g9LE6{ou&^AH zUDjopSg1Lu)Ohzr{?^`5gOrR>QCg&(gL;`9>B1`$e`lfibUMx-quYB%AjPHUv6}=X z`lK>TRa(D2^&qh0cQ^yjoG7k5cbn`v^{4MAicL4cGRSjOd|IO``RMtDXCE$;LVETJ zbk|dcb@4h>Wb_8;wXGU)8{EA!Bv3)ci`eV$YC7h*;M^4}M5{Mo1r-u@3~3gnCWahe z1i3xsFA_Mcmj*O1lQswK{n*4YT_%C;Winxkrtun_5he{L*O;ejh#F>+K2hR2J=ln;rU0l zw7r56bR*$#rQgVNI_L}-jl#Tj_6cy)kikvkHI$5&Q-T&;G<8&PAdmwWU^v;}2gtON z!er)+yj0nX;tS3}Jwf1_>{=+P02dZa^dW#6hmpXXcGqkrCpR291VMp(E_+*L7 zGyHYB^*>TU>FboBV#syEywwQS7PPgo8MunC6Lc=fIcH?2{=!`pzfH+W2Duf#a8ZYY zFq?i|)m9H9yq2y4lC&P3nr)V?yh3mjdDl%RBDa1L>9|3Tbh`ZjF^rLG@O{8)z$9^? zs%*L57<83HPvN#lS5?5SV-Vm^D0xpUXSQ>`TZ;->xPg|&SgR6p`e16^g3j}SH=)2r zCNlcG)viUmWLGEZedp!jY$+>Uf2U%6I{El!*pF?BfxNyzX?Z( z5Ot?rTTbc1J)N@J+0oru&IZbW>}=rbZVOVc-Kg$in;XBw&#{-5dk#{+1+MX4qMe#R z7HiwsFh*D=mXXJ-Q2Ij;Uz;`~mo_%IAu!?=gA56KVVo9_I%QvKLadN=N(Sg9w;W3U z4OvxvS0HAxK`Etk?<6-~P{MUu6J9RVA3mDsD%14PI1A;br<7`nG-0+oYwcsr0T1Ks z0j@66hlEHjGDxdOA>ib@3USU7)aMx@;do$J+~;5z)aNqXk3xj}G9c{aM6+(JxV~q{ z7nPN?+^di}2|N&f@=^<-ao1Zw#`&bD+hJi%PQL7x&&?s(YmdIYghSS2l5)W0PPHWO z?v}ZXO}BwwF8}^Ck|nOJ(LWaS+2;=N-S?V8jv>d!P&!jKZ$hjlzl1grFEhun{bPbl ztE)&2X`qh8bd8Y9IT*qIU%)ZZ<)}5cd?02MbN%P{LgwES&$9 z*l};)c1W8{2I!;bYLdi|WdFrQ&=sWeVlcCQKiz;#l0GsO8$V-C#iw4r+~}WTg(XS9 z(HE=I;cnWb3iwaA5tV!@w^qAUsN(05XAXU)hzDbE%zePP=4Z&YOQ)L2+wTyF!wH!2 zdKG;Ec>%~w3S8N?3zOo8_whR5J@D{O)#}n{hd%J1Ba2TQ2k3BH#iaJQK;+BaC$EAg0Z|WED1gH@mlKEXKASZ^SjHM zTm3X@HGR*gNVT7Cs_}In+0PqtF+hpg(n#U$qLlT7D`fO+>2`d_Em<}5?LH0~r)7td zDlHAxYPOj3gMkohcQpoh6sEcRW%DN&UWMGU7O8XQ5A}yebI23D-=)8oj(*^tn*~9P zEbuo1NaStAPG+{ZgB%;9IJPs*p`OiFD@(6)5F#x93_|SgpGjdsiic5Ex=FsSE3FG}Ua0>RdmSZlPi* z$O{K*fP7s#W%7zLIj{ZeLD9ldG#)=YYLr^%T4fHYBQFZFwn_OX&+89z&N9@U;iKXc zcJDS#i=JHFtn;<~z|TnmRS1*>n72-fN~8Ubd0K$yOF8?^V|x%NID7Ct3Ra{oQ2Lc_ z6Mv6aM>el}NS77uK5=1;262r^Ye^O|o(_%m1997m%#Y7OT6DpXYT&)jGmJ2c=w0ig=;1A)X*muQ~re5)O??pS8hl38)&apU$R@;)ZDJq zJ>jFLtJM>LZg-*lIINC}TIYeL)C$wMkWpeU>e?1m=sahIw*J|~*9j})Q-K;Preadm zC+-h7>x5p`8KRTRW_@2E3>SFX=uk1|X&A zR)4AUjW1}lKxQd^)hAzv-6smgSJPBA&w`~d^3EAX0lQ00?L>vOv=ijs$z0CC$LG+- z8`}1J;&UG@T-t;&Goww1mCva_A3hJgS>D}7ckEf|-#2*BnVt6dEM($6R&QwlTdahq z9cMv5!oQLEohC7EvMj_h^4T^&I?q#UsX#f2{cYYD}o=s{bvdRI)UWR?^~`SSAGk?udq!(z(Cs zmig`dz-I>x`Qw|R{y%|oDE%&X%?ha0e=6se7FoY}Yj`%$SkNiC?hd3a1ccqg*QM>~ zaL;ren`Zp}#&c@;hjN+wj<%jzyS3f-C?GN4r%>lypop0F)WY@hnRK4I0R3zeDeZee zl)=xWJ@wXJXNdw!Qn<&+?J5TsW&d~yihA7TDD3{iNJG%?vv(&Gq8vOOL}H~Vt0HPec~r*S;v|n zW9JXAHF`t3A<42#(5p181=Awa6y$of(4^M_^36Y)o~-+H5lZK!eXM`{`oqh+EXQ>^ z>YA(16Wy!n+}EQ{ZN&~a;jZ8v-o`W;CV;7G*JV1Rj5|O8L5sO>Y<`ta1?yfUu7wZZ z4prdmmXce;ft4URcVs^`SWGbu+wYn%G&r~&((lZN1gCuidi?fTkTZ1ZK8Lu+zD3z~ zArjp`_@TEPwnp_Vnp%1 zQI_+~$oZdmX@`}O77>l-c8sEy$pGJ8y~bLtH7fG8>M>Z{ZE=e%&h8r0cLlEvj#&9Y z)tuE^$yhNUp9j`uJG`9Iu(c3ja82NXbA30ndc{?S5QJo3WJT4bBEh+zfm5VlnOZei zh5Z~H`58F%MOngb=P?YlFEU(&OJPM`FAWB{TOYJO(4(z6X$$3)9?HaynLvrnJ4qD}!Dn3sHQnACE9=)&1X6<@U9} zw(|JkLIiFwyp9i&&2g;?qW>8Icn2uCV0%y6Nk$ud^}u~WQWu`>IF3bU3$VWvlw^+U9%&kq&ZIQFhCnaHJ-PG;U@*b4R<>ebssz{+$9ycyjRbTZoDWsKB#|!i?KNQ$=pF>c);9?Qw#cZbZ zGl0mMlm~yUs&jOT{rsq}*2(zc0wl}U%5IoeY&O`24%a?SMo;|afp!$V)8y26AA(ky zqR)=-ZQ~Q1#zLmPbZpd9Lp@JY?SZcKBmoSP(Z2)-wq^j;`4em(Gm!m1KmHu73{syn z$ZZUNmtMzzXOw0{l?e*mtsGBZg=w^lo+KYgaX9#WA&miZ(t*=3J4EsAAP{F@0rQmt z_Z}CRaelG|6}huKUDldM_UR_;_N3PuIpo>WZ+;fFTr>TJ#rX9Ea;{+sf{xzqM|c(W z+#mflwb?u7M*ip8mArS6^#{WHGLBL3tajmaEkSQfOSkEaU#z(?34(8_hSZ z9Z7)+h{WVU5i)=-BPeh4nPp1K9)C`X#IbH)PCl4G>9L$F^{FNiGZyFb!MufOwm(36 zH*A|lA|Ou*lIanOc5Nq8a1-%EFF}Hw0feJorNBGX-k<)0co|BpYEgm zR=z{U+aE*6mxbKFHs81yR)_yp0?G{~Or4%*_H8BLe}yOkNbW7R8OqUp&~9NmtiT4P zPf2@>UR*zFZ)+?q0@1RkyPN(x8h7&B86-5;{i*|)-hMInyTWV~6YcPH|9*;hlO0Hv zbUckAKK($B=My9DD+dH@fqk3aU#Gd5@zMEdonP&lb*@7a-Q6!R-PNqqrCl42f*!wq zq&W%O<%hO92+ox@1?*4m=hFfe5Oddcu9HOjhco!1z@bDSt(i91s3hx50=7W?*LeTm zLS*oHz<=uk5cfZXeiQgN?tcjVS5a`v|0(ouQU4cAP07>fkOr;^dvukC>6als5K?iu z!peg0ou0nhn7-+n>K%sJyexU=$3SSkrU@GuEIoBmy2+Qu!GcH(X*rDH7l z3(+pk7YY&#uyVUwZ@2qC%yieX^^-S`VL@V2#xbTUyNtmT0qEe0iI#oB0Y^^2 z_QHnW)>JW*3g;=%QVV3r0&j`BBF{TWo|Cu;FSVqC8qOkPZ%vcByIyX82D_YWjBhRU z@FqI|rUFOEV+YjGc&mLY!e!ItmHnC{)6%b7GgV(@qH1pKfBPUub`?(6K3eMJWm2_5 zT&Z=i)@@i{)%E2aEmCA=98Nm9TH|wtmhtCT*ubdEJLwUZL9(tGzp2K{9>yX<)4I&@ z1W|Dw8J3bzI&x4WS#5{9!-SQxl|qNQzg$`rr|%JuQ)_q)KS}UmBF2!BG{Y*7Vd)yN zO}iX9SE20G{;iRPyOsLX_?r$P?}6qTOn;K={07q{G9WnV10u`|9VGbft~I<#=2d+su08qK|)< zoPG(~osOWEt+?NjfQqC+P~7L*tMS-itOGdcunbi;2TNyxmCb<$)>!NPtcUF&Gu&qt&hIbe zN&$;6Yel01Jg6_U-aAqV<4Ir)z<=IAI>yp4#-`3O37BV)ntoHC&6y7#|6KNFO4b|< z*%x923cOU|C!VQITBP%2AX$1TsOOs>Hl=SUMDa8*CDcR~SvP$5yOadftK0`xG`6X? zKo15M?xEP6BVY=7PlW!tgQ*wllFy%ksV&hyCjIA5MCYG7xCHyWsbmr$PFmyVmHu;g z<)6FiiNBIbs&AhvZe00uZ46G;-~3%^>i3=2c=x||x7nQ6{x16U1u+cVMA}0i$hw(e zrvJu5;L+cJ2+;T&zJR9vZ*l(>st;>cl8xz17I#&_4sj$z%?P7Blmw-5@rVythvZC! ztlXG3pMms=p)3;Z^qUk|$v8_}EIC+7GxHWfhp0i~_`Q96=x<+qPW?{ww%ASFD7Ic4 z=g&n7@#k^iisvSOAbjc-(+)cjqHG6`ot)Mylpf~zk)$*IFf$>Yl^F?_PyDF z6Lj=c#{wcvb&E4i2+B>q6~aqjZqAmmv@(64A!Bv}F{$KI+d~S)2v-m5 z2ILQ8UQFHB4T_yWm@04S3_yVfp7z)xVS;n6Tkorzq#fyq=)?d>w!tB3rb7tbeLZca z4A>$+rTYWb)A!G3SZyQH=nbIsJ$AMO-!doQe~qB#O?zZ9dk0Dy&Z^1(BN0>Bk+6}OTsNP&yFAVdkq*npru zsHCJc?is*M2=QWZ5tzV1I`}|cD+TaZ4|XO9(J7nvaY9oEv`!J{0a6j1_y7)I5&&Kx z?R`Er0CvyA^b&;7%x}TCg&>nZE4xZasrbsrN8f?tY;-`DrHKNNr37S&5eA(ofZEeM z4NY0C3|$uroVq_+Zvsw@v;eEu^PW713qA`99=C@x!!H1qJT$=e?s)JvIqzB#T=FSU zGCJto-_P2wW&P(_Rmne@e-EB(3*sfeMI zZg9G9C*W@2`2IiKaeV?Gvbl$a@!5u;|L||#4aYOdihlw_ok`Wrbd?*V5An|=Q~z@c z<0|jG_F0bv;MQIs?~d3uEmD!ryzsOBDaZxINa40KfSuF2TqF+hF)to+KX;{Ur2=(d&xeK*o|=GWdf!;jbvP=^SNpWtF)wp@!c8%ZnFG)s zR?Dla4LmqUE|O*RGt*U25KonG8wDVXGSwi}lTo8#PSC#gNd0x9e#Q5flER*u_X7n; zfb0Pd2^EN;T(W|+oZz*ORNV}2&+QX`3rab4birc^J&YRdxX$@xq1-y*t`eZKv<$%@ z+eJy|Tqs@xU?TaZ7+M{ZW*si3+ZqMi_SXSYMV%DV569w49@O(0N?uRz8Sqz+0SEwJ4iST9kdb~nqrnw9ul8zf_A6bAxNo^&+Mikttu%&QY} zlAi<&j_&vHK2hGvnwT)58)mKk%uNYLnoks@G``Taty1y`6O9aB zPe13DXv$v0iHiiIq)3|S3$@tr?@BN_xKU5V3%&ay#0Om-xNc*51z+#*ns_%m z_%tt!1lV51UBs%WTCnj9b|oUBrTR#FSpD8#fV50qKhC7F4?hqdY%fF6mU>@>QJxQJ zryR=TaY)?maw_MrHUlIP`q3N`?!6urAdX72J9^uQFuh^`NtDxRkp_A*#$yjqmD(W) zoQ0HTX^J4^#s}Ofc6s9JCvMmnW209wC~4ku$n9PeP?Ec%ci76JkF2oG##^yNh=ZzB zPVvEEN>Hs_)4-aWxH|0F#h$8a30#bn=9V=28jMJZBy*jJG?h~Q;QA~y*0&L@lr2#Q z*D8g`tl#M{%GHnWHr0?kdC%nJi< z;yKH16&E+K%s5PGdN{t6@aoy!RBiplnV$&VBvNql$p8QQe`|prPNIy~gA_Tg?A1P) O5=vh6LE(L~=l=@~+j_tN literal 0 HcmV?d00001 diff --git a/logviewer.js b/logviewer.js new file mode 100644 index 0000000..582b65e --- /dev/null +++ b/logviewer.js @@ -0,0 +1,175 @@ +//SVR.JS LOG VIEWER + +var fs = require("fs"); +var readline = require("readline"); +var process = require("process"); + +var args = process.argv; +for (var i = (process.argv[0].indexOf("node") > -1 || process.argv[0].indexOf("bun") > -1 ? 2 : 1); i < args.length; i++) { + if (args[i] == "-h" || args[i] == "--help" || args[i] == "-?" || args[i] == "/h" || args[i] == "/?") { + console.log("SVR.JS log viewer usage:"); + console.log("node logviewer.js [-h] [--help] [-?] [/h] [/?]"); + console.log("-h -? /h /? --help -- Displays help"); + process.exit(0); + } else { + console.log("Unrecognized argument: " + args[i]); + console.log("SVR.JS log viewer usage:"); + console.log("node logviewer.js [-h] [--help] [-?] [/h] [/?]"); + console.log("-h -? /h /? --help -- Displays help"); + process.exit(1); + } +} + +var logo = ["","",""," \u001b[38;5;002m&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&"," &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&%"," &&\u001b[38;5;243m(((((\u001b[38;5;242m(\u001b[38;5;243m(((\u001b[38;5;241m##(###\u001b[38;5;243m(\u001b[38;5;241m##\u001b[38;5;243m((((((((((((((((((((((\u001b[38;5;143m*\u001b[38;5;243m(((\u001b[38;5;101m/\u001b[38;5;143m/\u001b[38;5;243m(((\u001b[38;5;185m*\u001b[38;5;243m((\u001b[38;5;242m(\u001b[38;5;002m&&"," \u001b[38;5;m&\u001b[38;5;002m&\u001b[38;5;243m((((((\u001b[38;5;242m(\u001b[38;5;243m(((((((\u001b[38;5;241m#\u001b[38;5;243m(((((((((((((((((((((((((\u001b[38;5;011m***\u001b[38;5;244m(\u001b[38;5;185m*\u001b[38;5;011m***\u001b[38;5;243m(\u001b[38;5;011m***\u001b[38;5;243m((\u001b[38;5;002m&&"," \u001b[38;5;m&\u001b[38;5;002m&&&\u001b[38;5;243m((((((((((((((((((((((((((((((((((((((((((((((((((\u001b[38;5;002m&&&&"," \u001b[38;5;m&\u001b[38;5;002m&&\u001b[38;5;243m((((((((((((((((((((((((((((((((((((((((((((((((((((\u001b[38;5;002m&&&"," \u001b[38;5;m&\u001b[38;5;002m&\u001b[38;5;243m((((((\u001b[38;5;241m###\u001b[38;5;243m(((((\u001b[38;5;241m#\u001b[38;5;243m((\u001b[38;5;241m##\u001b[38;5;243m(((((((((((((((((((((\u001b[38;5;011m****\u001b[38;5;015m \u001b[38;5;243m(\u001b[38;5;011m***\u001b[38;5;243m((\u001b[38;5;002m&&"," \u001b[38;5;m&\u001b[38;5;002m&&\u001b[38;5;243m((((((((((((((((((((((((((((((((((((((((((((((((((((\u001b[38;5;002m&&&"," \u001b[38;5;m&\u001b[38;5;002m&&&%\u001b[38;5;243m((((((((((((((((((((((((((((((((((((((((((((((((\u001b[38;5;065m#\u001b[38;5;002m&&&&"," \u001b[38;5;m&\u001b[38;5;002m&\u001b[38;5;243m((((((\u001b[38;5;242m(\u001b[38;5;243m(((((((\u001b[38;5;241m(\u001b[38;5;243m((((\u001b[38;5;242m(\u001b[38;5;243m((((((((((((((((((((\u001b[38;5;015m \u001b[38;5;143m/\u001b[38;5;187m.\u001b[38;5;015m \u001b[38;5;243m(\u001b[38;5;015m \u001b[38;5;243m((\u001b[38;5;002m&&"," \u001b[38;5;m&\u001b[38;5;002m&&\u001b[38;5;243m(((((\u001b[38;5;242m(\u001b[38;5;243m(((((((\u001b[38;5;241m#\u001b[38;5;243m(((((((((((((((((((((((((((((((((((((((\u001b[38;5;002m&&"," \u001b[38;5;m&\u001b[38;5;002m&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&"," \u001b[38;5;m&\u001b[38;5;002m&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&"," \u001b[38;5;m&\u001b[38;5;002m&&&&&&&&\u001b[38;5;010m########################################\u001b[38;5;071m#\u001b[38;5;002m&&&&&&&&"," \u001b[38;5;m&\u001b[38;5;002m&&&&&\u001b[38;5;010m##############################################\u001b[38;5;002m&&&&&&"," \u001b[38;5;m&\u001b[38;5;002m&&&\u001b[38;5;010m##################################################\u001b[38;5;002m&&&&"," \u001b[38;5;m&\u001b[38;5;002m&&\u001b[38;5;010m####\u001b[38;5;016m@@@@@@\u001b[38;5;010m#\u001b[38;5;016m@@\u001b[38;5;010m####\u001b[38;5;016m@@\u001b[38;5;040m#\u001b[38;5;010m#\u001b[38;5;016m@@@@@@@\u001b[38;5;010m###########\u001b[38;5;016m@@\u001b[38;5;010m##\u001b[38;5;016m@@@@@@\u001b[38;5;010m###\u001b[38;5;002m&&&"," \u001b[38;5;m&\u001b[38;5;002m&\u001b[38;5;083m/\u001b[38;5;010m###\u001b[38;5;016m@@\u001b[38;5;010m#######\u001b[38;5;016m@@\u001b[38;5;010m###\u001b[38;5;016m@\u001b[38;5;233m@\u001b[38;5;010m##\u001b[38;5;016m@@\u001b[38;5;010m####\u001b[38;5;016m@@\u001b[38;5;010m##########\u001b[38;5;016m@@\u001b[38;5;010m#\u001b[38;5;016m@@\u001b[38;5;010m#########\u001b[38;5;002m&&"," \u001b[38;5;m&\u001b[38;5;002m&\u001b[38;5;010m#######\u001b[38;5;022m@\u001b[38;5;016m@@@@\u001b[38;5;010m##\u001b[38;5;016m@@\u001b[38;5;010m#\u001b[38;5;016m@@\u001b[38;5;010m###\u001b[38;5;016m@@@@@@@\u001b[38;5;010m#######\u001b[38;5;016m@\u001b[38;5;010m###\u001b[38;5;016m@@\u001b[38;5;010m####\u001b[38;5;040m#\u001b[38;5;016m@@@@\u001b[38;5;010m###\u001b[38;5;002m&&"," \u001b[38;5;m&\u001b[38;5;002m&\u001b[38;5;114m*\u001b[38;5;010m###\u001b[38;5;016m@@\u001b[38;5;010m###\u001b[38;5;002m&\u001b[38;5;016m@@\u001b[38;5;010m###\u001b[38;5;016m@@@\u001b[38;5;010m####\u001b[38;5;016m@@\u001b[38;5;010m####\u001b[38;5;016m@@\u001b[38;5;010m##\u001b[38;5;016m@@\u001b[38;5;010m#\u001b[38;5;034m%\u001b[38;5;010m#\u001b[38;5;016m@@@@\u001b[38;5;010m##\u001b[38;5;016m@@\u001b[38;5;034m%\u001b[38;5;010m###\u001b[38;5;016m@@\u001b[38;5;010m###\u001b[38;5;002m&&"," \u001b[38;5;m&\u001b[38;5;002m&&\u001b[38;5;010m####################################################\u001b[38;5;002m&&&"," \u001b[38;5;m&\u001b[38;5;002m&&&\u001b[38;5;010m##################################################\u001b[38;5;002m&&&&"," \u001b[38;5;m&\u001b[38;5;002m&&&&&\u001b[38;5;010m##############################################\u001b[38;5;002m&&&&&&"," \u001b[38;5;m&\u001b[38;5;002m&&&&&&&&\u001b[38;5;010m########################################\u001b[38;5;002m&&&&&&&&&"," \u001b[38;5;m#\u001b[38;5;002m&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&"," &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&"," &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&"," \u001b[38;5;247m*\u001b[38;5;246m///\u001b[38;5;247m*\u001b[38;5;246m///"," ////\u001b[38;5;247m*\u001b[38;5;246m///"," \u001b[38;5;208m////////"," \u001b[38;5;m&\u001b[38;5;208m(((((/(((("," \u001b[38;5;246m/\u001b[38;5;247m*\u001b[38;5;246m///\u001b[38;5;247m*\u001b[38;5;246m///\u001b[38;5;247m*\u001b[38;5;246m///\u001b[38;5;247m*\u001b[38;5;246m///\u001b[38;5;247m*\u001b[38;5;246m/\u001b[38;5;208m/(/(((/(((/\u001b[38;5;137m/\u001b[38;5;246m//\u001b[38;5;247m*\u001b[38;5;246m///\u001b[38;5;247m*\u001b[38;5;246m///\u001b[38;5;247m*\u001b[38;5;246m///\u001b[38;5;247m*\u001b[38;5;246m///"," //\u001b[38;5;247m*\u001b[38;5;246m///////\u001b[38;5;247m*\u001b[38;5;246m///////\u001b[38;5;247m*\u001b[38;5;246m/\u001b[38;5;208m/(((((/((((\u001b[38;5;137m/\u001b[38;5;246m//\u001b[38;5;247m*\u001b[38;5;246m///////\u001b[38;5;247m*\u001b[38;5;246m///////\u001b[38;5;247m*"," *\u001b[38;5;246m/\u001b[38;5;247m*\u001b[38;5;246m/\u001b[38;5;247m*\u001b[38;5;246m/\u001b[38;5;247m*\u001b[38;5;246m/\u001b[38;5;247m*\u001b[38;5;246m/\u001b[38;5;247m*\u001b[38;5;246m/\u001b[38;5;247m*\u001b[38;5;246m/\u001b[38;5;247m*\u001b[38;5;246m/\u001b[38;5;247m*\u001b[38;5;246m/\u001b[38;5;247m*\u001b[38;5;246m/\u001b[38;5;247m*\u001b[38;5;208m(/(/(/(/(/\u001b[38;5;246m/\u001b[38;5;247m*\u001b[38;5;246m/\u001b[38;5;247m*\u001b[38;5;246m/\u001b[38;5;247m*\u001b[38;5;246m/\u001b[38;5;247m*\u001b[38;5;246m/\u001b[38;5;247m*\u001b[38;5;246m/\u001b[38;5;247m*\u001b[38;5;246m/\u001b[38;5;247m*\u001b[38;5;246m/\u001b[38;5;247m*\u001b[38;5;246m/\u001b[38;5;247m*\u001b[38;5;246m/\u001b[38;5;247m*"," \u001b[38;5;208m((((/(((","","","","\u001b[0m"]; + +for(var i=0;i ' +}); +console.log("Options:"); +for(var i=0;i { + var op = line.trim(); + if(op == "") { + rl.prompt(); + return; + } + var opn = parseInt(op); + rl.close(); + if(options[op]) { + options[op].callback(); + } else { + console.log("Invalid option."); + prompt(options); + } +}); +} + + +function viewLog(log) { + if(log[log.length-1] == "") log.pop(); + if(log[0] == "") log.shift(); + for(var i=0;i ' +}); +console.log("Input filter:"); +rl2.prompt(); +rl2.on('line', (line) => { + rl2.close(); + viewFilteredWorkerLogs(line); +}); +} + +var mainOptions = [ + {name: "View latest master log", callback: viewMasterLogs}, + {name: "View 5 latest worker logs", callback: viewWorkerLogs}, + {name: "View filtered worker logs (latest 20 logs)", callback: viewFilteredWorkerLogsPrompt}, + {name: "Exit log viewer", callback: function(){console.log("Bye!");process.exit(0);}} +] + +prompt(mainOptions); diff --git a/mods/primitiveanalytics.tar.gz b/mods/primitiveanalytics.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..af6c34f55f5c6f1725425acf00d711ba5ad56bbf GIT binary patch literal 1175 zcmV;I1ZevoiwFSnDnVZW1MOE`Z`(E$&2xbMhYLO=65>d*oh(EAF?4B%uE4q>+4hp; zL6#|toh+&(727EM-*+fmt)0eph9L=t=_Iga-b>yOo_k4&Y{;kHN4@ui2!OM*v+^6b z|9yHAwDy|qvsTb*owiTgdqK0=YPa_&cuJ=#6xxJ}XirILw(0GX|3`6$;A2faQv6dP z6n9vxncI1_Zv}EqAQytT$RZg0WbP-e3Lt0e-*QCqQvisO+nw&KHS=h{=H$ z)awyT%s6FqaDa4qd@8ijSFMrFeNM#-ndqV#tQtr5})tV?_xc$A* zyc2w)Ju@||K~5ej&412}13iVH9#X?9N7QsZBd?4S*$B&a)_wnXuD-#xM@Fid<9-|W zx;mbiFUnq0=E;TAc^XFC@o(_wj~dK}zt0r5rBAtMeqyJwwLQHLqh{O03z96oeg~JF zRuD9ZUGhlqJ3gcfnHip0?{}r{lEW_3bCyHEpf3 zv5k7nwMK>?FN73m_1FccSQI)f3LNP$d7i%fbu4SY+YEIcf-WI(% zTAhg!+cvhc&ce3X$aXC3Ihv)jLW2m$k{rLQWIZZSTd~NHr61_}mdOlc86y;Vvo zu%@#APm032)bHAf{jL!v<$c(o2Q75{g+kX~s1M|DhN;bMGa&8e{nyKaq9h!G2Yedw z+<*vt%A?#KS|0t#Q#rvAY3#8pV=@URZr328NhflWom1D(``t(mdEfSJH0n}&rBADy z41JndccA?KS_j`Wt{xtza`+${=)A-1=CiI}8b&a`r5s!-OFfJXO@v8sqTj)P(8v#m zW0B#AQ6Y|Vj1qHB8AuqWog3C) zu$$P^vAaVb5MWh8xC`0-wrat(#FuLmB@m>U{6^R zyZiBD`j*9d-v)o~_w6_osjeD!HhNsco^KW{E?*nw&dbZ&<{r9wwc8Zh(l);EKbFJ7 z#x>2QGPdF3+KOo}eFJ}4ASs^6V)>LdcD(-u&GvWI`(OL) pYpX_$8Z~Ovs8ORvjT$v-)TmLTMvWRZYSefs{0-u5YWx5w008pTT7dun literal 0 HcmV?d00001 diff --git a/powered.png b/powered.png new file mode 100644 index 0000000000000000000000000000000000000000..56a8049d2c98f078843dfa006c740bbaba85a4cb GIT binary patch literal 7901 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 literal 0 HcmV?d00001 diff --git a/serverSideScript.js b/serverSideScript.js new file mode 100644 index 0000000..747a3c8 --- /dev/null +++ b/serverSideScript.js @@ -0,0 +1,90 @@ +//Server-side Javascript (Node.js) +//This implementation uses Node.js, which powers SVR.JS. +//This implementation contains elements specific for SVR.JS mods: +// req - A server request instance +// res - A server response instance +// serverconsole - A console output object for SVR.JS +// responseEnd - Response ending method of SVR.JS +// href - Request URL without query +// ext - File extension of requested file +// uobject - Request URL object +// search - Request URL queries +// defaultPage - An index page location (deprecated, always returns 'index.json') +// users - A list of users (deprecated) +// page404 - 404 Not Found page location +// head - A head of server response +// foot - A foot of server response +// fd - A response body used by responseEnd method +// elseCallback - Method summoning SVR.JS internal callbacks +// callServerError - Method to end with server error +// getCustomHeaders - Method to get headers defined in config.json file +// origHref - Original request URL without query (before URL rewriting) +// redirect - Method to redirect. +// parsePostData - Method to parse POST data. +//Along with elements added by this implementation: +// disableEndElseCallbackExecute - Determines execution of elseCallback on end +// filterHeaders - Removes invalid HTTP/1.0 headers +// customvar1, customvar2, customvar3, customvar4 - Custom variables +//Built-in libraries: +// http +// https +// readline +// os +// url +// hexstrbase64 +// fs +// path +// crypto +// stream +//If you send response remember and don't use disableEndElseCallbackExecute, use "return;", or else SVR.JS will crash. +//If you use proxy, use filterHeaders to remove HTTP/2.0 headers, which are invalid in HTTP/1.0. +//If you type no code, elseCallback is executed. +//Below we have example script, which serves dynamic content. +disableEndElseCallbackExecute = true; //Avoid crashing on async. +var headers = getCustomHeaders(); //Headers +if(!fs.existsSync(__dirname + "/../temp/requestCounter")) { + fs.writeFileSync(__dirname + "/../temp/requestCounter","0"); //Reset counter +} +headers["Content-Type"] = 'text/html; charset=utf-8' //HTML output +if(href == "/hello.svr") { + fs.readFile(__dirname + "/../temp/requestCounter", (err,data) => { + if(err) throw err; + var requestCounter = parseInt(data.toString()); //Counter + fs.writeFile(__dirname + "/../temp/requestCounter",(requestCounter + 1).toString(),() => { + //Increase value of counter + }); + res.writeHead(200, "OK", headers); //Write Head + res.end("SVR.JS ServerSide Test

Hello World!

This is a test from server-side JavaScript. This test is executed " + requestCounter.toString() + " times from taking server up." + (req.headers.origin == undefined ? "" : " This request is done from a proxy.") + "

SVR.JS/" + configJSON.version + ' (' + os.platform()[0].toUpperCase() + os.platform().slice(1) + ')' + (req.headers.host == undefined ? "" : " on " + req.headers.host) + "

"); //Write response + serverconsole.resmessage("Client successfully recieved content."); //Log into SVR.JS + return; //Prevent SVR.JS from crashing + }); +} else if(href == "/proxy.svr") { + callServerError(403,"SVR.JS-exampleproxy"); //Server error + serverconsole.errmessage("Client fails to recieve content."); //Log into SVR.JS +} else if(href.indexOf("/proxy.svr/") == 0) { + var hdrs = req.headers; + hdrs["Host"] = (href.split("/")[2] == "this" ? req.headers.host : href.split("/")[2]); + hdrs["Origin"] = (req.headers.host == undefined ? "" : req.headers.host); + var options = { + hostname: (href.split("/")[2] == "this" ? req.headers.host.split(":")[0] : href.split("/")[2].split(":")[0]), + port: (href.split("/")[2] == "this" ? req.headers.host.split(":")[1] : (href.split("/")[2].split(":")[1] == undefined ? 80 : href.split("/")[2].split(":")[1])), + path: req.url.replace("/proxy.svr/" + href.split("/")[2],""), + method: req.method, + headers: filterHeaders(hdrs) + }; + var proxy = http.request(options, function (sres) { + res.writeHead(sres.statusCode, sres.headers) + sres.pipe(res, { + end: true + }); + }); + proxy.on("error",(ex) => { + callServerError(500,"SVR.JS-exampleproxy",ex.stack); //Server error + serverconsole.errmessage("Client fails to recieve content."); //Log into SVR.JS + }); + req.pipe(proxy, { + end: true + }); +} else { + elseCallback(); //Load SVR.JS internal callbacks +} diff --git a/svr.js b/svr.js new file mode 100644 index 0000000..0223eaf --- /dev/null +++ b/svr.js @@ -0,0 +1,4828 @@ +//DorianTech HTTP Server +//Uses Content-Type and Content-Length +//Events calling: +//| +//+--request +//| +//+--connect +//| +//+--error +// +//APIs: +// - https +// - readline +// - os +// - http +// - url +// - fs +// - path +// - hexstrbase64 +// - crypto +// - svrmodpack +// - graceful-fs +// - formidable +if (typeof require === "undefined") { + if (typeof ActiveXObject !== "undefined" && typeof WScript !== "undefined") { + var shell = new ActiveXObject("WScript.Shell"); + shell.Popup("SVR.JS doesn't work on Windows Script Host. SVR.JS requires use of Node.JS (or compatible JS runtime).", undefined, "Can't start SVR.JS", 16); + WScript.quit(); + } else { + if (typeof alert !== "undefined" && typeof document !== "undefined") { + alert("SVR.JS doesn't work on web browser. SVR.JS requires use of Node.JS (or compatible JS runtime)."); + } + if (typeof document !== "undefined") { + throw new Error("SVR.JS doesn't work on web browser. SVR.JS requires use of Node.JS (or compatible JS runtime)."); + } else { + throw new Error("SVR.JS doesn't work on Deno/QuickJS. SVR.JS requires use of Node.JS (or compatible JS runtime)."); + } + } +} +var secure = false; +var disableMods = false; +var logo = ["", "", "", " \x1b[38;5;002m&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&", " &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&", " &&&\x1b[38;5;243m(((((((((((((((((((((((((((((((((((((((((((((((((((\x1b[38;5;002m&&&", " \x1b[38;5;002m&&\x1b[38;5;243m((((((\x1b[38;5;241m###########\x1b[38;5;243m(((((((((((((((((((((((\x1b[38;5;011m***\x1b[38;5;243m(\x1b[38;5;011m***\x1b[38;5;243m(\x1b[38;5;011m***\x1b[38;5;243m((\x1b[38;5;002m&&", " \x1b[38;5;002m&&&\x1b[38;5;243m(((((((((((((((((((((((((((((((((((((((((((((((((((\x1b[38;5;002m&&&", " \x1b[38;5;002m&&&\x1b[38;5;243m(((((((((((((((((((((((((((((((((((((((((((((((((((\x1b[38;5;002m&&&", " \x1b[38;5;002m&&\x1b[38;5;243m((((((\x1b[38;5;241m###########\x1b[38;5;243m(((((((((((((((((((((((\x1b[38;5;011m***\x1b[38;5;243m(\x1b[38;5;015m \x1b[38;5;243m(\x1b[38;5;011m***\x1b[38;5;243m((\x1b[38;5;002m&&", " \x1b[38;5;002m&&&\x1b[38;5;243m(((((((((((((((((((((((((((((((((((((((((((((((((((\x1b[38;5;002m&&&", " \x1b[38;5;002m&&&\x1b[38;5;243m(((((((((((((((((((((((((((((((((((((((((((((((((((\x1b[38;5;002m&&&", " \x1b[38;5;002m&&\x1b[38;5;243m((((((\x1b[38;5;241m###########\x1b[38;5;243m(((((((((((((((((((((((\x1b[38;5;015m \x1b[38;5;243m(\x1b[38;5;015m \x1b[38;5;243m(\x1b[38;5;015m \x1b[38;5;243m((\x1b[38;5;002m&&", " \x1b[38;5;002m&&&\x1b[38;5;243m(((((((((((((((((((((((((((((((((((((((((((((((((((\x1b[38;5;002m&&&", " \x1b[38;5;002m&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&", " \x1b[38;5;002m&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&", " \x1b[38;5;002m&&&&&&&&\x1b[38;5;010m#########################################\x1b[38;5;002m&&&&&&&&", " \x1b[38;5;002m&&&&&\x1b[38;5;010m###############################################\x1b[38;5;002m&&&&&", " \x1b[38;5;002m&&&\x1b[38;5;010m###################################################\x1b[38;5;002m&&&", " \x1b[38;5;002m&&\x1b[38;5;010m####\x1b[38;5;016m@@@@@@\x1b[38;5;010m#\x1b[38;5;016m@@@\x1b[38;5;010m###\x1b[38;5;016m@@@\x1b[38;5;010m#\x1b[38;5;016m@@@@@@@\x1b[38;5;010m###########\x1b[38;5;016m@@\x1b[38;5;010m##\x1b[38;5;016m@@@@@@\x1b[38;5;010m####\x1b[38;5;002m&&", " \x1b[38;5;002m&&\x1b[38;5;010m###\x1b[38;5;016m@@\x1b[38;5;010m#######\x1b[38;5;016m@@\x1b[38;5;010m###\x1b[38;5;016m@@\x1b[38;5;010m##\x1b[38;5;016m@@\x1b[38;5;010m####\x1b[38;5;016m@@\x1b[38;5;010m##########\x1b[38;5;016m@@\x1b[38;5;010m#\x1b[38;5;016m@@\x1b[38;5;010m#########\x1b[38;5;002m&&", " \x1b[38;5;002m&&\x1b[38;5;010m######\x1b[38;5;040m#\x1b[38;5;016m@@@@\x1b[38;5;010m##\x1b[38;5;016m@@\x1b[38;5;010m#\x1b[38;5;016m@@\x1b[38;5;010m###\x1b[38;5;016m@@@@@@@\x1b[38;5;010m#######\x1b[38;5;016m@@\x1b[38;5;010m##\x1b[38;5;016m@@\x1b[38;5;010m####\x1b[38;5;040m#\x1b[38;5;016m@@@@\x1b[38;5;010m###\x1b[38;5;002m&&", " \x1b[38;5;002m&&\x1b[38;5;010m###\x1b[38;5;016m@@\x1b[38;5;034m%\x1b[38;5;010m###\x1b[38;5;016m@@\x1b[38;5;010m###\x1b[38;5;016m@@@\x1b[38;5;010m####\x1b[38;5;016m@@\x1b[38;5;010m####\x1b[38;5;016m@@\x1b[38;5;010m##\x1b[38;5;016m@@\x1b[38;5;010m###\x1b[38;5;016m@@@@\x1b[38;5;010m##\x1b[38;5;016m@@\x1b[38;5;034m%\x1b[38;5;010m###\x1b[38;5;016m@@\x1b[38;5;010m###\x1b[38;5;002m&&", " \x1b[38;5;002m&&\x1b[38;5;010m#####################################################\x1b[38;5;002m&&", " \x1b[38;5;002m&&&\x1b[38;5;010m###################################################\x1b[38;5;002m&&&", " \x1b[38;5;002m&&&&&\x1b[38;5;010m###############################################\x1b[38;5;002m&&&&&", " \x1b[38;5;002m&&&&&&&&\x1b[38;5;010m#########################################\x1b[38;5;002m&&&&&&&&", " \x1b[38;5;002m&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&", " &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&", " &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&", " \x1b[38;5;246m///////", " ///////", " \x1b[38;5;208m((((/))))", " \x1b[38;5;208m(((((/)))))", " \x1b[38;5;246m/\x1b[38;5;247m*\x1b[38;5;246m///\x1b[38;5;247m*\x1b[38;5;246m///\x1b[38;5;247m*\x1b[38;5;246m///\x1b[38;5;247m*\x1b[38;5;246m///\x1b[38;5;247m*\x1b[38;5;246m/\x1b[38;5;247m/\x1b[38;5;208m(((((/)))))\x1b[38;5;246m//\x1b[38;5;247m*\x1b[38;5;246m///\x1b[38;5;247m*\x1b[38;5;246m///\x1b[38;5;247m*\x1b[38;5;246m///\x1b[38;5;247m*\x1b[38;5;246m///\x1b[38;5;247m*\x1b[38;5;246m/", " //\x1b[38;5;247m*\x1b[38;5;246m///////\x1b[38;5;247m*\x1b[38;5;246m///////\x1b[38;5;247m*\x1b[38;5;246m/\x1b[38;5;247m/\x1b[38;5;208m(((((/)))))\x1b[38;5;246m//\x1b[38;5;247m*\x1b[38;5;246m///////\x1b[38;5;247m*\x1b[38;5;246m///////\x1b[38;5;247m*\x1b[38;5;246m//", " *\x1b[38;5;246m/\x1b[38;5;247m*\x1b[38;5;246m/\x1b[38;5;247m*\x1b[38;5;246m/\x1b[38;5;247m*\x1b[38;5;246m/\x1b[38;5;247m*\x1b[38;5;246m/\x1b[38;5;247m*\x1b[38;5;246m/\x1b[38;5;247m*\x1b[38;5;246m/\x1b[38;5;247m*\x1b[38;5;246m/\x1b[38;5;247m*\x1b[38;5;246m/\x1b[38;5;247m*\x1b[38;5;246m/\x1b[38;5;247m*\x1b[38;5;208m(((((/)))))\x1b[38;5;247m*\x1b[38;5;246m/\x1b[38;5;247m*\x1b[38;5;246m/\x1b[38;5;247m*\x1b[38;5;246m/\x1b[38;5;247m*\x1b[38;5;246m/\x1b[38;5;247m*\x1b[38;5;246m/\x1b[38;5;247m*\x1b[38;5;246m/\x1b[38;5;247m*\x1b[38;5;246m/\x1b[38;5;247m*\x1b[38;5;246m/\x1b[38;5;247m*\x1b[38;5;246m/\x1b[38;5;247m*\x1b[38;5;246m/\x1b[38;5;247m*", " \x1b[38;5;208m((((/))))", "", "", "", "\x1b[0m"]; + +var fs = require("fs"); + +function factoryReset() { + console.log("Removing logs..."); + deleteFolderRecursive(__dirname + "/log"); + fs.mkdirSync(__dirname + "/log"); + console.log("Removing temp folder..."); + deleteFolderRecursive(__dirname + "/temp"); + fs.mkdirSync(__dirname + "/temp"); + console.log("Removing configuration file..."); + fs.unlinkSync("config.json"); + console.log("Done!"); + process.exit(0); +} + +function deleteFolderRecursive(path) { + if (fs.existsSync(path)) { + fs.readdirSync(path).forEach(function (file) { + var curPath = path + "/" + file; + if (fs.statSync(curPath).isDirectory()) { // recurse + deleteFolderRecursive(curPath); + } else { // delete file + fs.unlinkSync(curPath); + } + }); + fs.rmdirSync(path); + } +} + +var os = require("os"); +var version = "3.4.17"; +var singlethreaded = false; + +if (process.versions) process.versions.svrjs = version; //Inject SVR.JS into process.versions + +var args = process.argv; +for (var i = (process.argv[0].indexOf("node") > -1 || process.argv[0].indexOf("bun") > -1 ? 2 : 1); i < args.length; i++) { + if (args[i] == "-h" || args[i] == "--help" || args[i] == "-?" || args[i] == "/h" || args[i] == "/?") { + console.log("SVR.JS usage:"); + console.log("node svr.js [-h] [--help] [-?] [/h] [/?] [--secure] [--reset] [--clean] [--disable-mods] [--single-threaded] [-v] [--version]"); + console.log("-h -? /h /? --help -- Displays help"); + console.log("--clean -- Cleans files, that SVR.JS created"); + console.log("--reset -- Resets SVR.JS to factory settings (WARNING: DANGEROUS)"); + console.log("--secure -- Runs HTTPS server"); + console.log("--disable-mods -- Disables mods (safe mode)"); + console.log("--single-threaded -- Run single-threaded"); + console.log("-v --version -- Display server version"); + process.exit(0); + } else if (args[i] == "--secure") { + secure = true; + } else if (args[i] == "-v" || args[i] == "--version") { + console.log("SVR.JS/" + version + " (" + getOS() + "; " + (process.isBun ? ("Bun/v" + process.versions.bun + "; like Node.JS/" + process.version) : ("Node.JS/" + process.version)) + ")"); + process.exit(0); + } else if (args[i] == "--clean") { + console.log("Removing logs..."); + deleteFolderRecursive(__dirname + "/log"); + fs.mkdirSync(__dirname + "/log"); + console.log("Removing temp folder..."); + deleteFolderRecursive(__dirname + "/temp"); + fs.mkdirSync(__dirname + "/temp"); + console.log("Done!"); + process.exit(0); + } else if (args[i] == "--reset") { + factoryReset(); + } else if (args[i] == "--disable-mods") { + disableMods = true; + } else if (args[i] == "--single-threaded") { + singlethreaded = true; + } else { + console.log("Unrecognized argument: " + args[i]); + console.log("SVR.JS usage:"); + console.log("node svr.js [-h] [--help] [-?] [/h] [/?] [--secure] [--reset] [--clean] [--disable-mods] [--single-threaded] [-v] [--version]"); + console.log("-h -? /h /? --help -- Displays help"); + console.log("--clean -- Cleans files, that SVR.JS created"); + console.log("--reset -- Resets SVR.JS to factory settings (WARNING: DANGEROUS)"); + console.log("--secure -- Runs HTTPS server"); + console.log("--disable-mods -- Disables mods (safe mode)"); + console.log("--single-threaded -- Run single-threaded"); + console.log("-v --version -- Display server version"); + process.exit(1); + } +} + +var readline = require("readline"); +var net = require("net"); +var child_process = require("child_process"); +var cluster = {}; +if (!singlethreaded) { + try { + // Import cluster module + var cluster = require("cluster"); + } catch (ex) { + // Clustering is not supported! + } + + // Cluster & IPC shim for Bun + if (process.isBun && cluster.isMaster === undefined) { + cluster.isMaster = !process.env.NODE_UNIQUE_ID; + cluster.isWorker = !cluster.isMaster; + + if (cluster.isWorker) { + // Shim the cluster.worker object for worker processes + cluster.worker = { + id: parseInt(process.env.NODE_UNIQUE_ID), + process: process, + isDead: function () { + return false; + }, + send: function (message, b, c, d) { + process.send(message, b, c, d); + } + }; + + if (!process.send) { + // Shim the process.send function for worker processes + var net = require("net"); + var os = require("os"); + var path = require("path"); + + // Create a fake IPC server to receive messages + var fakeIPCServer = net.createServer(function (socket) { + var receivedData = ""; + + socket.on("data", function (data) { + receivedData += data.toString(); + }); + + socket.on("end", function () { + process.emit("message", receivedData); + }); + }).listen(os.platform() === "win32" ? path.join("\\\\?\\pipe", __dirname, "temp/.W" + process.pid + ".ipc") : (__dirname + "/temp/.W" + process.pid + ".ipc")); + + process.send = function (message) { + // Create a fake IPC connection to send messages + var fakeIPCConnection = net.createConnection(os.platform() === "win32" ? path.join("\\\\?\\pipe", __dirname, "temp/.P" + process.pid + ".ipc") : (__dirname + "/temp/.P" + process.pid + ".ipc"), function () { + fakeIPCConnection.end(message); + }); + }; + } + } + + // Custom implementation for cluster.fork() + cluster._workersCounter = 1; + cluster.workers = {}; + cluster.fork = function (env) { + var child_process = require("child_process"); + var newEnvironment = JSON.parse(JSON.stringify(env ? env : process.env)); + newEnvironment.NODE_UNIQUE_ID = cluster._workersCounter; + var newArguments = JSON.parse(JSON.stringify(process.argv)); + var command = newArguments.shift(); + var newWorker = child_process.spawn(command, newArguments, { + env: newEnvironment, + stdio: ["inherit", "inherit", "inherit", "ipc"] + }); + + newWorker.process = newWorker; + newWorker.isDead = function () { + return newWorker.exitCode !== null || newWorker.killed; + }; + newWorker.id = newEnvironment.NODE_UNIQUE_ID; + + function checkSendImplementation(worker) { + var sendImplemented = true; + + if (!worker.send) { + sendImplemented = false; + } + + try { + eval("'use strict';(" + String(worker.send).replace(/console\.log *\( *(["'])ChildProcess\.prototype\.send\(\) - Sorry! Not implemented yet\1 *\) *;?/, "throw new Error(\"NOT IMPLEMENTED\"); //") + ")(undefined)"); + } catch (ex) { + if (ex.message === "NOT IMPLEMENTED") { + sendImplemented = false; + } + } + + return sendImplemented; + } + + if (!checkSendImplementation(newWorker)) { + // Create a fake IPC server for worker process to receive messages + var fakeWorkerIPCServer = net.createServer(function (socket) { + var receivedData = ""; + + socket.on("data", function (data) { + receivedData += data.toString(); + }); + + socket.on("end", function () { + newWorker.emit("message", receivedData); + }); + }).listen(os.platform() === "win32" ? path.join("\\\\?\\pipe", __dirname, "temp/.P" + newWorker.process.pid + ".ipc") : (__dirname + "/temp/.P" + newWorker.process.pid + ".ipc")); + + // Cleanup when worker process exits + newWorker.on("exit", function () { + fakeWorkerIPCServer.close(); + delete cluster.workers[newWorker.id]; + }); + + newWorker.send = function (message, fakeParam2, fakeParam3, fakeParam4, tries) { + if (!tries) tries = 0; + + try { + // Create a fake IPC connection to send messages to worker process + var fakeWorkerIPCConnection = net.createConnection(os.platform() === "win32" ? path.join("\\\\?\\pipe", __dirname, "temp/.W" + newWorker.process.pid + ".ipc") : (__dirname + "/temp/.W" + newWorker.process.pid + ".ipc"), function () { + fakeWorkerIPCConnection.end(message); + }); + } catch (ex) { + if (tries > 25) throw ex; + + setTimeout(function () { + newWorker.send(message, undefined, undefined, tries + 1); + }, 10); + } + }; + } + + cluster.workers[newWorker.id] = newWorker; + cluster._workersCounter++; + return newWorker; + }; + } +} + +var bruteForceDb = {}; + +var SVRJSInitialized = false; +var exiting = false; +var crashed = false; + +function SVRJSFork() { + //Log + if (SVRJSInitialized) serverconsole.locmessage("Starting next thread, because previous one hung up/crashed..."); + //Fork new worker + var newWorker = cluster.fork(); + newWorker.on("error", function (err) { + serverconsole.locwarnmessage("There was a problem when handling SVR.JS worker! (from master process side) Reason: " + err.message); + }); + newWorker.on("exit", function () { + if (!exiting && Object.keys(cluster.workers).length == 0) { + crashed = true; + SVRJSFork(); + } + }); + newWorker.on("message", bruteForceListenerWrapper(newWorker)); + newWorker.on("message", listenConnListener); +} +var http = require("http"); +http.STATUS_CODES[497] = "HTTP Request Sent to HTTPS Port"; +http.STATUS_CODES[598] = "Network Read Timeout Error"; +http.STATUS_CODES[599] = "Network Connect Timeout Error"; +var url = require("url"); +if (url.URL && typeof URL == "undefined") URL = url.URL; +try { + var gracefulFs = require("graceful-fs"); + if (!process.isBun) gracefulFs.gracefulify(fs); //Bun + graceful-fs + SVR.JS + requests about static content = 500 Internal Server Error! +} catch (ex) { + //Don't use graceful-fs +} +var path = require("path"); +var hexstrbase64 = undefined; +try { + hexstrbase64 = require("./hexstrbase64/index.js"); +} catch (ex) { + //Don't use hexstrbase64 +} +var svrmodpack = undefined; +try { + svrmodpack = require("svrmodpack"); +} catch (ex) { + svrmodpack = { + _errored: ex + }; +} +var zlib = require("zlib"); +var tar = undefined; +try { + tar = require("tar"); +} catch (ex) { + tar = { + _errored: ex + }; +} +var formidable = undefined; +try { + formidable = require("formidable"); +} catch (ex) { + formidable = { + _errored: ex + }; +} +var ocsp = undefined; +var ocspCache = undefined; +try { + ocsp = require("ocsp"); + ocspCache = new ocsp.Cache(); +} catch (ex) { + ocsp = { + _errored: ex + }; +} +var prettyBytes = undefined; +try { + prettyBytes = require("pretty-bytes"); +} catch (ex) { + prettyBytes = { + _errored: ex + }; +} +var http2 = {}; +try { + http2 = require("http2"); + if (process.isBun) { + try { + http2.Http2ServerRequest(); + } catch (ex) { + if (ex.name == "NotImplementedError" || ex.code == "ERR_NOT_IMPLEMENTED") throw ex; + } + } +} catch (ex) { + http2.__disabled__ = null; + http2.createServer = function () { + throw new Error("HTTP/2 support not present"); + }; + http2.createSecureServer = function () { + throw new Error("HTTP/2 support not present"); + }; + http2.connect = function () { + throw new Error("HTTP/2 support not present"); + }; + http2.get = function () { + throw new Error("HTTP/2 support not present"); + }; +} +var crypto = {}; +var https = {}; +try { + crypto = require("crypto"); + https = require("https"); +} catch (ex) { + crypto = {}; + https = {}; + crypto.__disabled__ = null; + https.createServer = function () { + throw new Error("Crypto support not present"); + }; + http2.createSecureServer = function () { + throw new Error("Crypto support not present"); + }; + https.connect = function () { + throw new Error("Crypto support not present"); + }; + https.get = function () { + throw new Error("Crypto support not present"); + }; +} +var mime = require("mime-types"); +var pubip = ""; +var pubport = 80; +var port = 80; +var domain = ""; +var spubport = 443; +var sport = 443; +var mods = []; + +if (!fs.existsSync(__dirname + "/log")) fs.mkdirSync(__dirname + "/log"); +if (!fs.existsSync(__dirname + "/mods")) fs.mkdirSync(__dirname + "/mods"); +if (!fs.existsSync(__dirname + "/temp")) fs.mkdirSync(__dirname + "/temp"); + +var modFiles = fs.readdirSync(__dirname + "/mods").sort(); +var modInfos = []; + +function sizify(x) { + try { + if (prettyBytes._errored) throw prettyBytes._errored; + return prettyBytes(parseInt(x), { + minimumFractionDigits: 0, + maximumFractionDigits: 2 + }).replace(/ /g, "").replace(/B/ig, "").replace(/k/g, "K"); + } catch (ex) { + if (x < 1000) return x.toString(); + if (x < 10000) return (Math.round(x / 10) / 100).toString() + "K"; + if (x < 100000) return (Math.round(x / 100) / 10).toString() + "K"; + if (x < 1000000) return (Math.round(x / 1000)).toString() + "K"; + if (x < 10000000) return (Math.round(x / 10000) / 100).toString() + "M"; + if (x < 100000000) return (Math.round(x / 100000) / 10).toString() + "M"; + if (x < 1000000000) return (Math.round(x / 1000000)).toString() + "M"; + if (x < 10000000000) return (Math.round(x / 10000000) / 100).toString() + "G"; + if (x < 100000000000) return (Math.round(x / 100000000) / 10).toString() + "G"; + if (x < 1000000000000) return (Math.round(x / 1000000000)).toString() + "G"; + if (x < 10000000000000) return (Math.round(x / 10000000000) / 100).toString() + "T"; + if (x < 100000000000000) return (Math.round(x / 100000000000) / 10).toString() + "T"; + if (x < 1000000000000000) return (Math.round(x / 1000000000000)).toString() + "T"; + if (x < 10000000000000000) return (Math.round(x / 10000000000000) / 100).toString() + "P"; + if (x < 100000000000000000) return (Math.round(x / 100000000000000) / 10).toString() + "P"; + return (Math.round(x / 1000000000000000)).toString() + "P"; + } +} + +function getOS() { + var osType = os.type(); + var platform = os.platform(); + if (platform == "android") { + return "Android"; + } else if (osType == "Windows_NT" || osType == "WindowsNT") { + var arch = os.arch(); + if (arch == "ia32") { + return "Win32"; + } else if (arch == "x64") { + return "Win64"; + } else { + return "Win" + arch.toUpperCase(); + } + } else if (osType.indexOf("CYGWIN") == 0) { + return "Cygwin"; + } else if (osType.indexOf("MINGW") == 0) { + return "MinGW"; + } else if (osType.indexOf("MSYS") == 0) { + return "MSYS"; + } else if (osType.indexOf("UWIN") == 0) { + return "UWIN"; + } else if (osType == "GNU") { + return "GNU Hurd"; + } else { + return osType; + } +} + +function createRegex(regex) { + var regexObj = regex.split("/"); + if (regexObj.length == 0) throw new Error("Invalid regex!"); + var modifiers = regexObj.pop(); + regexObj.shift(); + var searchString = regexObj.join("/"); + return new RegExp(searchString, modifiers); +} + +//IP Block list object +function ipBlockList(rawBlockList) { + function normalizeIPv4Address(address) { + return address.replace(/(^|\.)(?:0(?!\.|$))+/g, ""); + } + + function expandIPv6Address(address) { + var fullAddress = ""; + var expandedAddress = ""; + var validGroupCount = 8; + var validGroupSize = 4; + + var ipv4 = ""; + var extractIpv4 = /([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})/; + var validateIpv4 = /((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})/; + + if (validateIpv4.test(address)) { + groups = address.match(extractIpv4); + for (var i = 1; i < groups.length; i++) { + ipv4 += ("00" + (parseInt(groups[i], 10).toString(16))).slice(-2) + (i == 2 ? ":" : ""); + } + address = address.replace(extractIpv4, ipv4); + } + + if (address.indexOf("::") == -1) { + fullAddress = address; + } else { + var sides = address.split("::"); + var groupsPresent = 0; + for (var i = 0; i < sides.length; i++) { + groupsPresent += sides[i].split(":").length; + } + fullAddress += sides[0] + ":"; + for (var i = 0; i < validGroupCount - groupsPresent; i++) { + fullAddress += "0000:"; + } + fullAddress += sides[1]; + } + var groups = fullAddress.split(":"); + for (var i = 0; i < validGroupCount; i++) { + while (groups[i].length < validGroupSize) { + groups[i] = "0" + groups[i]; + } + expandedAddress += (i != validGroupCount - 1) ? groups[i] + ":" : groups[i]; + } + return expandedAddress; + } + + function ipv4ToInt(ip) { + var ips = ip.split("."); + return parseInt(ips[0]) * 16777216 + parseInt(ips[1]) * 65536 + parseInt(ips[2]) * 256 + parseInt(ips[3]); + } + + function getIPv4CIDRLimits(ip, cidrMask) { + var ipInt = ipv4ToInt(ip); + var exp = Math.pow(2, 32 - cidrMask); + var ipMin = Math.floor(ipInt / exp) * exp; + var ipMax = ipMin + exp - 1; + return { + min: ipMin, + max: ipMax + }; + } + + function ipv6ToBlocks(ip) { + var ips = ip.split(":"); + var ip2s = []; + for (var i = 0; i < ips.length; i++) { + ip2s.push(parseInt(ips[i])); + } + return ip2s; + } + + function getIPv6CIDRLimits(ip, cidrMask) { + var ipBlocks = ipv6ToBlocks(ip); + var fieldsToDelete = Math.floor((128 - cidrMask) / 16); + var fieldMaskModify = (128 - cidrMask) % 16; + var ipBlockMin = []; + var ipBlockMax = []; + for (var i = 0; i < 8; i++) { + ipBlockMin.push((i < 8 - fieldsToDelete) ? ((i < 7 - fieldsToDelete) ? ipBlocks[i] : (ipBlocks[i] >> fieldMaskModify << fieldMaskModify)) : 0); + } + for (var i = 0; i < 8; i++) { + ipBlockMax.push((i < 8 - fieldsToDelete) ? ((i < 7 - fieldsToDelete) ? ipBlocks[i] : ((ipBlocks[i] >> fieldMaskModify << fieldMaskModify) + Math.pow(2, fieldMaskModify) - 1)) : 65535); + } + return { + min: ipBlockMin, + max: ipBlockMax + }; + } + + function checkIfIPv4CIDRMatches(ipInt, cidrObject) { + if (cidrObject.v6) return false; + return ipInt >= cidrObject.min && ipInt <= cidrObject.max; + } + + function checkIfIPv6CIDRMatches(ipBlock, cidrObject) { + if (!cidrObject.v6) return false; + for (var i = 0; i < 8; i++) { + if (ipBlock[i] < cidrObject.min[i] || ipBlock[i] > cidrObject.max[i]) return true; + } + return false; + } + if (rawBlockList === undefined) rawBlockList = []; + var instance = {}; + instance.raw = []; + instance.rawtoPreparedMap = []; + instance.prepared = []; + instance.cidrs = []; + instance.add = function (rawValue) { + instance.raw.push(rawValue); + var beginIndex = instance.prepared.length; + var cidrIndex = instance.cidrs.length; + var cidrMask = null; + var isIPv6 = false; + if (rawValue.indexOf("/") > -1) { + var rwArray = rawValue.split("/"); + cidrMask = rwArray.pop(); + rawValue = rwArray.join("/"); + } + rawValue = rawValue.toLowerCase(); + if (rawValue.indexOf("::ffff:") == 0) rawValue = rawValue.substr(7); + if (rawValue.indexOf(":") > -1) { + isIPv6 = true; + rawValue = expandIPv6Address(rawValue); + } else { + rawValue = normalizeIPv4Address(rawValue); + } + if (cidrMask) { + var cidrLimits = {}; + if (isIPv6) { + cidrLimits = getIPv6CIDRLimits(rawValue, cidrMask); + cidrLimits.v6 = true; + } else { + cidrLimits = getIPv4CIDRLimits(rawValue, cidrMask); + cidrLimits.v6 = false; + } + instance.cidrs.push(cidrLimits); + instance.rawtoPreparedMap.push({ + cidr: true, + index: cidrIndex + }); + } else { + instance.prepared.push(rawValue); + instance.rawtoPreparedMap.push({ + cidr: false, + index: beginIndex + }); + } + }; + instance.remove = function (ip) { + var index = instance.raw.indexOf(ip); + if (index == -1) return false; + var map = instance.rawtoPreparedMap[index]; + instance.raw.splice(index, 1); + instance.rawtoPreparedMap.splice(index, 1); + if (map.cidr) { + instance.cidrs.splice(map.index, 1); + } else { + instance.prepared.splice(map.index, 1); + } + return true; + }; + instance.check = function (rawValue) { + if (instance.raw.length == 0) return false; + var isIPv6 = false; + rawValue = rawValue.toLowerCase(); + if (rawValue == "localhost") rawValue = "127.0.0.1"; + if (rawValue.indexOf("::ffff:") == 0) rawValue = rawValue.substr(7); + if (rawValue.indexOf(":") > -1) { + isIPv6 = true; + rawValue = expandIPv6Address(rawValue); + } else { + rawValue = normalizeIPv4Address(rawValue); + } + if (instance.prepared.indexOf(rawValue) > -1) return true; + if (instance.cidrs.length == 0) return false; + var ipParsedObject = (!isIPv6 ? ipv4ToInt : ipv6ToBlocks)(rawValue); + var checkMethod = (!isIPv6 ? checkIfIPv4CIDRMatches : checkIfIPv6CIDRMatches); + for (var i = 0; i < instance.cidrs.length; i++) { + if (checkMethod(ipParsedObject, instance.cidrs[i])) return true; + } + return false; + }; + for (var i = 0; i < rawBlockList.length; i++) { + instance.add(rawBlockList[i]); + } + return instance; +} + +//Error stack generator from Error objects. +function generateErrorStack(ex) { + var errStack = ex.stack ? ex.stack.split("\n") : []; + for (var i = 0; i < errStack.length; i++) { + if (errStack[i].indexOf(ex.name) == 0) return ex.stack; + } + var nErrStack = [ex.name + (ex.code ? ": " + ex.code : "") + (ex.message == "" ? "" : ": " + ex.message)]; + for (var i = 0; i < errStack.length; i++) { + if (errStack[i] != "") { + var ef = errStack[i].split("@"); + var location = ""; + if (ef.length > 1) location = ef.pop(); + var func = ef.join("@"); + nErrStack.push(" at " + (func == "" ? (!location || location == "" ? "" : location) : (func + (!location || location == "" ? "" : " (" + location + ")")))); + } + } + return nErrStack.join("\n"); +} + +var ifaces = {}; +var ifaceEx = null; +try { + ifaces = os.networkInterfaces(); +} catch (ex) { + ifaceEx = ex; +} +var ips = []; +var attmts = 5; +var attmtsRedir = 5; +var errors = os.constants.errno; +var timestamp = new Date().getTime(); +var wwwredirect = false; +Object.keys(ifaces).forEach(function (ifname) { + var alias = 0; + ifaces[ifname].forEach(function (iface) { + if (iface.family !== "IPv4" || iface.internal !== false) { + return; + } + if (alias >= 1) { + ips.push(ifname + ":" + alias, iface.address); + } else { + ips.push(ifname, iface.address); + } + ++alias; + }); +}); +if (ips.length == 0) { + Object.keys(ifaces).forEach(function (ifname) { + var alias = 0; + ifaces[ifname].forEach(function (iface) { + if (iface.family !== "IPv6" || iface.internal !== false) { + return; + } + if (alias >= 1) { + ips.push(ifname + ":" + alias, iface.address); + } else { + ips.push(ifname, iface.address); + } + ++alias; + }); + }); +} +var host = ips[(ips.length) - 1]; +if (!host) host = "[offline]"; +var hp = host + ":" + port.toString(); + +var ipRequestCompleted = false; +var ipRequestGotError = false; +if (host != "[offline]" || ifaceEx) { + var ipRequest = (crypto.__disabled__ !== undefined ? http : https).get({ + host: "api64.ipify.org", + port: (crypto.__disabled__ !== undefined ? 80 : 443), + path: "/", + headers: { + "User-Agent": (exposeServerVersion ? "SVR.JS/" + version + " (" + getOS() + "; " + (process.isBun ? ("Bun/v" + process.versions.bun + "; like Node.JS/" + process.version) : ("Node.JS/" + process.version)) + ")" : "SVR.JS") + }, + timeout: 5000 + }, function (res) { + ipRequest.removeAllListeners("timeout"); + res.on("data", function (d) { + if (res.statusCode != 200) { + ipRequestCompleted = true; + process.emit("ipRequestCompleted"); + return; + } + pubip = d.toString(); + if (!domain) { + if (pubip.indexOf(":") == -1) { + var parts = pubip.split("."); + var p1 = parseInt(parts[0]).toString(16); + var p2 = parseInt(parts[1]).toString(16); + var p3 = parseInt(parts[2]).toString(16); + var p4 = parseInt(parts[3]).toString(16); + var pp = parseInt(pubport).toString(16); + domain = p1 + p2 + p3 + p4 + pp + ".nodesvr.doriantech.com"; + } else { + domain = pubip.replace(/[^0-9a-zA-Z]/gi, "").toLowerCase() + ".nodesvrip6.doriantech.com"; + } + } + ipRequestCompleted = true; + process.emit("ipRequestCompleted"); + }); + }); + ipRequest.on("error", function () { + if (crypto.__disabled__ || ipRequestGotError) { + ipRequestCompleted = true; + process.emit("ipRequestCompleted"); + } else { + ipRequestGotError = true; + } + }); + ipRequest.on("timeout", function () { + if (crypto.__disabled__ || ipRequestGotError) { + ipRequestCompleted = true; + process.emit("ipRequestCompleted"); + } else { + ipRequestGotError = true; + } + }); + + if (!crypto.__disabled) { + var ipRequest2 = https.get({ + host: "api.seeip.org", + port: 443, + path: "/", + headers: { + "User-Agent": (exposeServerVersion ? "SVR.JS/" + version + " (" + getOS() + "; " + (process.isBun ? ("Bun/v" + process.versions.bun + "; like Node.JS/" + process.version) : ("Node.JS/" + process.version)) + ")" : "SVR.JS") + }, + timeout: 5000 + }, function (res) { + ipRequest2.removeAllListeners("timeout"); + res.on("data", function (d) { + if (res.statusCode != 200) { + ipRequestCompleted = true; + process.emit("ipRequestCompleted"); + return; + } + pubip = d.toString(); + if (!domain) { + if (pubip.indexOf(":") == -1) { + var parts = pubip.split("."); + var p1 = parseInt(parts[0]).toString(16); + var p2 = parseInt(parts[1]).toString(16); + var p3 = parseInt(parts[2]).toString(16); + var p4 = parseInt(parts[3]).toString(16); + var pp = parseInt(pubport).toString(16); + domain = p1 + p2 + p3 + p4 + pp + ".nodesvr.doriantech.com"; + } else { + domain = pubip.replace(/[^0-9a-zA-Z]/gi, "").toLowerCase() + ".nodesvrip6.doriantech.com"; + } + } + ipRequestCompleted = true; + process.emit("ipRequestCompleted"); + }); + }); + ipRequest2.on("error", function () { + if (crypto.__disabled__ || ipRequestGotError) { + ipRequestCompleted = true; + process.emit("ipRequestCompleted"); + } else { + ipRequestGotError = true; + } + }); + ipRequest2.on("timeout", function () { + if (crypto.__disabled__ || ipRequestGotError) { + ipRequestCompleted = true; + process.emit("ipRequestCompleted"); + } else { + ipRequestGotError = true; + } + }); + } +} else { + ipRequestCompleted = true; +} + +function ipStatusCallback(callback) { + if (ipRequestCompleted) { + callback(); + } else { + process.once("ipRequestCompleted", callback); + } +} + +var configJSON = {}; +if (fs.existsSync(__dirname + "/config.json")) { + var configJSONf = ""; + try { + configJSONf = fs.readFileSync(__dirname + "/config.json"); //Read JSON File + } catch (ex) { + throw new Error("Cannot read JSON file."); + } + try { + configJSON = JSON.parse(configJSONf); //Parse JSON + } catch (ex) { + throw new Error("JSON Parse error."); + } +} + +var rawBlackList = []; +var users = []; +var page404 = "404.html"; +var serverAdmin = "[no contact information]"; +var stackHidden = false; +var exposeServerVersion = true; +var rewriteMap = []; +var allowStatus = true; +var dontCompress = []; +var enableIPSpoofing = false; +var sni = {}; +var disableNonEncryptedServer = false; +var disableToHTTPSRedirect = false; +var nonStandardCodesRaw = []; +if (configJSON.blacklist != undefined) rawBlackList = configJSON.blacklist; +if (configJSON.wwwredirect != undefined) wwwredirect = configJSON.wwwredirect; +if (configJSON.port != undefined) port = configJSON.port; +if (configJSON.pubport != undefined) pubport = configJSON.pubport; +if (configJSON.domian != undefined) domain = configJSON.domian; +if (configJSON.domain != undefined) domain = configJSON.domain; +if (configJSON.sport != undefined) sport = configJSON.sport; +if (configJSON.spubport != undefined) spubport = configJSON.spubport; +if (configJSON.page404 != undefined) page404 = configJSON.page404; +if (configJSON.serverAdministratorEmail != undefined) serverAdmin = configJSON.serverAdministratorEmail; +if (configJSON.nonStandardCodes != undefined) nonStandardCodesRaw = configJSON.nonStandardCodes; +if (configJSON.stackHidden != undefined) stackHidden = configJSON.stackHidden; +if (configJSON.users != undefined) users = configJSON.users; +if (configJSON.exposeServerVersion != undefined) exposeServerVersion = configJSON.exposeServerVersion; +if (configJSON.rewriteMap != undefined) rewriteMap = configJSON.rewriteMap; +if (configJSON.allowStatus != undefined) allowStatus = configJSON.allowStatus; +if (configJSON.dontCompress != undefined) dontCompress = configJSON.dontCompress; +if (configJSON.enableIPSpoofing != undefined) enableIPSpoofing = configJSON.enableIPSpoofing; +if (configJSON.secure != undefined) secure = secure || configJSON.secure; +if (configJSON.sni != undefined) sni = configJSON.sni; +if (configJSON.disableNonEncryptedServer != undefined) disableNonEncryptedServer = configJSON.disableNonEncryptedServer; +if (configJSON.disableToHTTPSRedirect != undefined) disableToHTTPSRedirect = configJSON.disableToHTTPSRedirect; +if (configJSON.wwwroot != undefined) { + var wwwroot = configJSON.wwwroot; + if (cluster.isMaster || cluster.isMaster === undefined) process.chdir(wwwroot); +} else { + if (cluster.isMaster || cluster.isMaster === undefined) process.chdir(__dirname); +} + +//Compability for older mods +configJSON.version = version; +configJSON.productName = "SVR.JS"; + +var blacklist = ipBlockList(rawBlackList); + +var nonStandardCodes = []; +for (var i = 0; i < nonStandardCodesRaw.length; i++) { + var nO = {}; + var nsKeys = Object.keys(nonStandardCodesRaw[i]); + for (var j = 0; j < nsKeys.length; j++) { + if (nsKeys[j] != "users") { + nO[nsKeys[j]] = nonStandardCodesRaw[i][nsKeys[j]]; + } else { + nO["users"] = ipBlockList(nonStandardCodesRaw[i].users); + } + } + nonStandardCodes.push(nO); +} + +var customHeaders = (configJSON.customHeaders == undefined ? {} : JSON.parse(JSON.stringify(configJSON.customHeaders))); +if (exposeServerVersion) { + customHeaders["Server"] = "SVR.JS/" + version + " (" + getOS() + "; " + (process.isBun ? ("Bun/v" + process.versions.bun + "; like Node.JS/" + process.version) : ("Node.JS/" + process.version)) + ")"; +} else { + customHeaders["Server"] = "SVR.JS"; +} + +function getCustomHeaders() { + return JSON.parse(JSON.stringify(customHeaders)); +} + +var vnum = 0; +try { + vnum = process.config.variables.node_module_version; +} catch (ex) { + //Version number not retrieved +} +if (vnum === undefined) vnum = 0; +if (process.isBun) vnum = 59; +// NOTE: One of regexes used here is reported as vulnerable according to Devina ReDOS checker, but it's actually just FALSE POSITIVE. + +function sanitizeURL(resource) { + if (resource == "*") return "*"; + if (resource == "") return ""; + // Remove null characters + resource = resource.replace(/%00/ig, "").replace(/\0/g,""); + // Check if URL is malformed (e.g. %c0%af or %u002f or simply %as) + if (resource.match(/%(?:c[01]|f[ef]|(?![0-9a-f]{2}).{2}|.{0,1}$)/gi)) throw new URIError("URI malformed"); + // Decode URL-encoded characters while preserving certain characters + resource = resource.replace(/%([0-9a-f]{2})/gi, function (match, hex) { + var decodedChar = String.fromCharCode(parseInt(hex, 16)); + return /(?![;?:@&=+$,#%])[!-~]/.test(decodedChar) ? decodedChar : "%" + hex; + }); + var sanitizedResource = resource; + // Ensure the resource starts with a slash + if (resource[0] != "/") sanitizedResource = "/" + sanitizedResource; + // Convert backslashes to slashes and remove duplicate slashes + sanitizedResource = sanitizedResource.replace(/\\/g, "/").replace(/\/+/g, "/"); + // Handle relative navigation (e.g., "/./", "/../", "../", "./"), also remove trailing dots in paths + sanitizedResource = sanitizedResource.replace(/\/\.(?:\.{2,})?(?=($|\/))/g, "$1").replace(/([^.\/])\.+(?=($|\/))/g, "$1$2").replace(/\/+/g, "/"); + while (sanitizedResource.match(/\/(?!\.\.\/)[^\/]+\/\.\.(?=(\/|$))/g)) { + sanitizedResource = sanitizedResource.replace(/\/(?!\.\.\/)[^\/]+\/\.\.(?=(\/|$))/g, "$1").replace(/\/+/g, "/"); + } + sanitizedResource = sanitizedResource.replace(/\/\.\.(?=(\/|$))/g, "$1").replace(/\/+/g, "/"); + if (sanitizedResource.length == 0) return "/"; + else return sanitizedResource; +} + +var key = ""; +var cert = ""; + +if (secure) { + if (!configJSON.key) configJSON.key = "cert/key.key"; + if (!configJSON.cert) configJSON.cert = "cert/cert.crt"; +} else { + key = "SSL DISABLED"; + cert = "SSL DISABLED"; + configJSON.cert = "SSL DISABLED"; + configJSON.key = "SSL DISABLED"; +} + +if (!fs.existsSync(__dirname + "/config.json")) { + saveConfig(); +} + +if (secure) { + key = fs.readFileSync((configJSON.key[0] != "/" && !configJSON.key.match(/^[A-Z0-9]:\\/)) ? __dirname + "/" + configJSON.key : configJSON.key).toString(); + cert = fs.readFileSync((configJSON.cert[0] != "/" && !configJSON.cert.match(/^[A-Z0-9]:\\/)) ? __dirname + "/" + configJSON.cert : configJSON.cert).toString(); + var sniNames = Object.keys(sni); + var sniCredentials = []; + for (var i = 0; i < sniNames.length; i++) { + sniCredentials.push({ + name: sniNames[i], + cert: fs.readFileSync((sni[sniNames[i]].cert[0] != "/" && !sni[sniNames[i]].cert.match(/^[A-Z0-9]:\\/)) ? __dirname + "/" + sni[sniNames[i]].cert : sni[sniNames[i]].cert).toString(), + key: fs.readFileSync((sni[sniNames[i]].key[0] != "/" && !sni[sniNames[i]].key.match(/^[A-Z0-9]:\\/)) ? __dirname + "/" + sni[sniNames[i]].key : sni[sniNames[i]].key).toString() + }); + } +} + +var logFile = undefined; +var logSync = false; + +function LOG(s) { + try { + if (configJSON.enableLogging || configJSON.enableLogging == undefined) { + if (logSync) { + fs.appendFileSync(__dirname + "/log/" + (cluster.isMaster ? "master" : (cluster.isMaster === undefined ? "singlethread" : "worker")) + "-" + timestamp + ".log", "[" + new Date().toISOString() + "] " + s + "\r\n"); + } else { + if (!logFile) { + logFile = fs.createWriteStream(__dirname + "/log/" + (cluster.isMaster ? "master" : (cluster.isMaster === undefined ? "singlethread" : "worker")) + "-" + timestamp + ".log", { + flags: "a", + autoClose: false + }); + } + if (logFile.writable) { + logFile.write("[" + new Date().toISOString() + "] " + s + "\r\n"); + } else { + throw new Error("Log file stream is closed."); + } + } + } + } catch (ex) { + if (!s.match(/^SERVER WARNING MESSAGE(?: \[Request Id: [0-9a-f]{6}\])?: There was a problem while saving logs! Logs will not be kept in log file\. Reason: /)) serverconsole.locwarnmessage("There was a problem while saving logs! Logs will not be kept in log file. Reason: " + ex.message); + } +} + +var serverconsole = { + climessage: function (msg) { + if (msg.indexOf("\n") != -1) { + var nmsg = msg.split("\n"); + for (var i = 0; i < nmsg.length; i++) { + serverconsole.climessage(nmsg[i]); + } + return; + } + console.log("SERVER CLI MESSAGE: " + msg); + LOG("SERVER CLI MESSAGE: " + msg); + return; + }, + reqmessage: function (msg) { + if (msg.indexOf("\n") != -1) { + var nmsg = msg.split("\n"); + for (var i = 0; i < nmsg.length; i++) { + serverconsole.reqmessage(nmsg[i]); + } + return; + } + console.log("\x1b[34mSERVER REQUEST MESSAGE: " + msg + "\x1b[37m\x1b[0m"); + LOG("SERVER REQUEST MESSAGE: " + msg); + return; + }, + resmessage: function (msg) { + if (msg.indexOf("\n") != -1) { + var nmsg = msg.split("\n"); + for (var i = 0; i < nmsg.length; i++) { + serverconsole.resmessage(nmsg[i]); + } + return; + } + console.log("\x1b[32mSERVER RESPONSE MESSAGE: " + msg + "\x1b[37m\x1b[0m"); + LOG("SERVER RESPONSE MESSAGE: " + msg); + return; + }, + errmessage: function (msg) { + if (msg.indexOf("\n") != -1) { + var nmsg = msg.split("\n"); + for (var i = 0; i < nmsg.length; i++) { + serverconsole.errmessage(nmsg[i]); + } + return; + } + console.log("\x1b[31mSERVER RESPONSE ERROR MESSAGE: " + msg + "\x1b[37m\x1b[0m"); + LOG("SERVER RESPONSE ERROR MESSAGE: " + msg); + return; + }, + locerrmessage: function (msg) { + if (msg.indexOf("\n") != -1) { + var nmsg = msg.split("\n"); + for (var i = 0; i < nmsg.length; i++) { + serverconsole.locerrmessage(nmsg[i]); + } + return; + } + console.log("\x1b[41mSERVER ERROR MESSAGE: " + msg + "\x1b[40m\x1b[0m"); + LOG("SERVER ERROR MESSAGE: " + msg); + return; + }, + locwarnmessage: function (msg) { + if (msg.indexOf("\n") != -1) { + var nmsg = msg.split("\n"); + for (var i = 0; i < nmsg.length; i++) { + serverconsole.locwarnmessage(nmsg[i]); + } + return; + } + console.log("\x1b[43mSERVER WARNING MESSAGE: " + msg + "\x1b[40m\x1b[0m"); + LOG("SERVER WARNING MESSAGE: " + msg); + return; + }, + locmessage: function (msg) { + if (msg.indexOf("\n") != -1) { + var nmsg = msg.split("\n"); + for (var i = 0; i < nmsg.length; i++) { + serverconsole.locmessage(nmsg[i]); + } + return; + } + console.log("SERVER MESSAGE: " + msg); + LOG("SERVER MESSAGE: " + msg); + return; + } +}; + +//Wrap around process.exit, so log contents can flush. +process.unsafeExit = process.exit; +process.exit = function (code) { + if (logFile && logFile.writable && !logFile.pending) { + try { + logFile.close(function () { + logFile = undefined; + logSync = true; + process.unsafeExit(code); + if (process.isBun) { + setInterval(function () { + if (!logFile.writable) { + logFile = undefined + logSync = true; + process.unsafeExit(code); + } + }, 50); //Interval + } + }); + setTimeout(function () { + logFile = undefined; + logSync = true; + process.unsafeExit(code); + }, 10000); //timeout + } catch (ex) { + logFile = undefined; + logSync = true; + process.unsafeExit(code); + } + } else { + logSync = true; + process.unsafeExit(code); + } +}; + +if (!disableMods) { + var modloaderFolderName = "modloader"; + if (cluster.isMaster === false) { + modloaderFolderName = ".modloader_w" + Math.floor(Math.random() * 65536); + } + for (var i = 0; i < modFiles.length; i++) { + var modFile = __dirname + "/mods/" + modFiles[i]; + try { + try { + fs.mkdirSync(__dirname + "/temp/" + modloaderFolderName); + } catch (ex) { + if (ex.code != "EEXIST") { + fs.mkdirSync(__dirname + "/temp"); + try { + fs.mkdirSync(__dirname + "/temp/" + modloaderFolderName); + } catch (ex) { + if (ex.code != "EEXIST") throw ex; + } + } + } + fs.mkdirSync(__dirname + "/temp/" + modloaderFolderName + "/" + modFiles[i]); + } catch (ex) { + if (ex.code != "EEXIST" && ex.code != "ENOENT") throw ex; + //Some other SVR.JS process may have create files. + } + if (fs.statSync(modFile).isFile()) { + try { + function extract() { + if (modFile.indexOf(".tar.gz") == modFile.length - 7) { + if (tar._errored) throw tar._errored; + tar.x({ + file: modFile, + sync: true, + C: __dirname + "/temp/" + modloaderFolderName + "/" + modFiles[i] + }); + } else { + if (svrmodpack._errored) throw svrmodpack._errored; + svrmodpack.unpack(modFile, __dirname + "/temp/" + modloaderFolderName + "/" + modFiles[i]); + } + } + extract(); + var Mod = undefined; + var mod = undefined; + var modSloaded = false; + for (var j = 0; j < 3; j++) { + try { + Mod = require("./temp/" + modloaderFolderName + "/" + modFiles[i] + "/index.js"); + mod = new Mod(); + break; + } catch (ex) { + if (j >= 2 || ex.name == "SyntaxError") throw ex; + var now = Date.now(); + while (Date.now() - now < 2); + //Try reloading mod + } + } + mods.push(mod); + for (var j = 0; j < 3; j++) { + try { + modInfos.push(JSON.parse(fs.readFileSync(__dirname + "/temp/" + modloaderFolderName + "/" + modFiles[i] + "/mod.info"))); + break; + } catch (ex) { + if (j >= 2) { + modInfos.push({ + name: "Unknown mod (" + modFiles[i] + ";" + ex.message + ")", + version: "ERROR" + }); + } + //Try reloading mod info + } + } + } catch (ex) { + if (cluster.isMaster || cluster.isMaster === undefined) { + serverconsole.locwarnmessage("There was a problem while loading a \"" + modFiles[i] + "\" mod."); + serverconsole.locwarnmessage("Stack:"); + serverconsole.locwarnmessage(generateErrorStack(ex)); + } + } + } + } + if (fs.existsSync("./serverSideScript.js") && fs.statSync("./serverSideScript.js").isFile()) { + try { + var modhead = "var readline = require('readline');\r\nvar os = require('os');\r\nvar http = require('http');\r\nvar url = require('url');\r\nvar fs = require('fs');\r\nvar path = require('path');\r\n" + (hexstrbase64 === undefined ? "" : "var hexstrbase64 = require('../hexstrbase64/index.js');\r\n") + (crypto.__disabled__ === undefined ? "var crypto = require('crypto');\r\nvar https = require('https');\r\n" : "") + "var stream = require('stream');\r\nvar customvar1;\r\nvar customvar2;\r\nvar customvar3;\r\nvar customvar4;\r\n\r\nfunction Mod() {}\r\nMod.prototype.callback = function callback(req, res, serverconsole, responseEnd, href, ext, uobject, search, defaultpage, users, page404, head, foot, fd, elseCallback, configJSON, callServerError, getCustomHeaders, origHref, redirect, parsePostData) {\r\nreturn function () {\r\nvar disableEndElseCallbackExecute = false;\r\nfunction filterHeaders(headers){for(var jsn=JSON.stringify(headers,null,2).split('\\n'),njsn=[\"{\"],i=1;i= 7 || ex.name == "SyntaxError") throw ex; + var now = Date.now(); + while (Date.now() - now < 2); + //Try reloading mod + } + } + mods.push(amod); + } catch (ex) { + if (cluster.isMaster || cluster.isMaster === undefined) { + serverconsole.locwarnmessage("There was a problem while loading server side JavaScript."); + serverconsole.locwarnmessage("Stack:"); + serverconsole.locwarnmessage(generateErrorStack(ex)); + } + } + } +} + +function sha256(s) { + if (crypto.__disabled__ === undefined) { + var hash = crypto.createHash("SHA256"); + hash.update(s); + return hash.digest("hex"); + } else { + + var chrsz = 8; + var hexcase = 0; + + function safeAdd(x, y) { + var lsw = (x & 0xFFFF) + (y & 0xFFFF); + var msw = (x >> 16) + (y >> 16) + (lsw >> 16); + return (msw << 16) | (lsw & 0xFFFF); + } + + function S(X, n) { + return (X >>> n) | (X << (32 - n)); + } + + function R(X, n) { + return (X >>> n); + } + + function Ch(x, y, z) { + return ((x & y) ^ ((~x) & z)); + } + + function Maj(x, y, z) { + return ((x & y) ^ (x & z) ^ (y & z)); + } + + function Sigma0256(x) { + return (S(x, 2) ^ S(x, 13) ^ S(x, 22)); + } + + function Sigma1256(x) { + return (S(x, 6) ^ S(x, 11) ^ S(x, 25)); + } + + function Gamma0256(x) { + return (S(x, 7) ^ S(x, 18) ^ R(x, 3)); + } + + function Gamma1256(x) { + return (S(x, 17) ^ S(x, 19) ^ R(x, 10)); + } + + function coreSha256(m, l) { + var K = new Array(0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5, 0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5, 0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3, 0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174, 0xE49B69C1, 0xEFBE4786, 0xFC19DC6, 0x240CA1CC, 0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA, 0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7, 0xC6E00BF3, 0xD5A79147, 0x6CA6351, 0x14292967, 0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13, 0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85, 0xA2BFE8A1, 0xA81A664B, 0xC24B8B70, 0xC76C51A3, 0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070, 0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5, 0x391C0CB3, 0x4ED8AA4A, 0x5B9CCA4F, 0x682E6FF3, 0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208, 0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2); + var HASH = new Array(0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19); + var W = new Array(64); + var a, b, c, d, e, f, g, h, i, j; + var T1, T2; + + m[l >> 5] |= 0x80 << (24 - l % 32); + m[((l + 64 >> 9) << 4) + 15] = l; + + for (var i = 0; i < m.length; i += 16) { + a = HASH[0]; + b = HASH[1]; + c = HASH[2]; + d = HASH[3]; + e = HASH[4]; + f = HASH[5]; + g = HASH[6]; + h = HASH[7]; + + for (var j = 0; j < 64; j++) { + if (j < 16) W[j] = m[j + i]; + else W[j] = safeAdd(safeAdd(safeAdd(Gamma1256(W[j - 2]), W[j - 7]), Gamma0256(W[j - 15])), W[j - 16]); + + T1 = safeAdd(safeAdd(safeAdd(safeAdd(h, Sigma1256(e)), Ch(e, f, g)), K[j]), W[j]); + T2 = safeAdd(Sigma0256(a), Maj(a, b, c)); + + h = g; + g = f; + f = e; + e = safeAdd(d, T1); + d = c; + c = b; + b = a; + a = safeAdd(T1, T2); + } + + HASH[0] = safeAdd(a, HASH[0]); + HASH[1] = safeAdd(b, HASH[1]); + HASH[2] = safeAdd(c, HASH[2]); + HASH[3] = safeAdd(d, HASH[3]); + HASH[4] = safeAdd(e, HASH[4]); + HASH[5] = safeAdd(f, HASH[5]); + HASH[6] = safeAdd(g, HASH[6]); + HASH[7] = safeAdd(h, HASH[7]); + } + return HASH; + } + + function str2binb(str) { + var bin = Array(); + var mask = (1 << chrsz) - 1; + for (var i = 0; i < str.length * chrsz; i += chrsz) { + bin[i >> 5] |= (str.charCodeAt(i / chrsz) & mask) << (24 - i % 32); + } + return bin; + } + + function Utf8Encode(string) { + string = string.replace(/\r\n/g, "\n"); + var utftext = ""; + + for (var n = 0; n < string.length; n++) { + + var c = string.charCodeAt(n); + + if (c < 128) { + utftext += String.fromCharCode(c); + } else if ((c > 127) && (c < 2048)) { + utftext += String.fromCharCode((c >> 6) | 192); + utftext += String.fromCharCode((c & 63) | 128); + } else { + utftext += String.fromCharCode((c >> 12) | 224); + utftext += String.fromCharCode(((c >> 6) & 63) | 128); + utftext += String.fromCharCode((c & 63) | 128); + } + + } + + return utftext; + } + + function binb2hex(binarray) { + var hexTab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef"; + var str = ""; + for (var i = 0; i < binarray.length * 4; i++) { + str += hexTab.charAt((binarray[i >> 2] >> ((3 - i % 4) * 8 + 4)) & 0xF) + + hexTab.charAt((binarray[i >> 2] >> ((3 - i % 4) * 8)) & 0xF); + } + return str; + } + + s = Utf8Encode(s); + return binb2hex(coreSha256(str2binb(s), s.length * chrsz)); + } +} + +function getInitializePath(to) { + var cwd = process.cwd(); + if (os.platform() == "win32") { + to = to.replace(/\//g, "\\"); + if (to[0] == "\\") to = cwd.split("\\")[0] + to; + } + var absoluteTo = path.isAbsolute(to) ? to : (__dirname + (os.platform() == "win32" ? "\\" : "/") + to); + if (os.platform() == "win32" && cwd[0] != absoluteTo[0]) return ""; + var relative = path.relative(cwd, absoluteTo); + if (os.platform() == "win32") { + return "/" + relative.replace(/\\/g, "/"); + } else { + return "/" + relative; + } +} + +function checkIfForbiddenPath(decodedHref, match) { + var mo = forbiddenPaths[match]; + if (!mo) return false; + if (typeof mo == "string") return decodedHref == mo || (os.platform() == "win32" && decodedHref.toLowerCase() == mo.toLowerCase()); + if (typeof mo == "object") { + for (var i = 0; i < mo.length; i++) { + if (decodedHref == mo[i] || (os.platform() == "win32" && decodedHref.toLowerCase() == mo[i].toLowerCase())) return true; + } + } + return false; +} + +function checkIfIndexOfForbiddenPath(decodedHref, match) { + var mo = forbiddenPaths[match]; + if (!mo) return false; + if (typeof mo == "string") return decodedHref == mo || decodedHref.indexOf(mo + "/") == 0 || (os.platform() == "win32" && (decodedHref.toLowerCase() == mo.toLowerCase() || decodedHref.toLowerCase().indexOf(mo.toLowerCase()) == 0)); + if (typeof mo == "object") { + for (var i = 0; i < mo.length; i++) { + if (decodedHref == mo || decodedHref.indexOf(mo[i] + "/") == 0 || (os.platform() == "win32" && (decodedHref.toLowerCase() == mo[i].toLowerCase() || decodedHref.toLowerCase().indexOf(mo[i].toLowerCase()) == 0))) return true; + } + } + return false; +} + +var forbiddenPaths = {}; + +forbiddenPaths.config = getInitializePath("./config.json"); +forbiddenPaths.certificates = []; +if (secure) { + forbiddenPaths.certificates.push(getInitializePath(configJSON.cert)); + forbiddenPaths.certificates.push(getInitializePath(configJSON.key)); + for (var i = 0; i < Object.keys(sni).length; i++) { + forbiddenPaths.certificates.push(getInitializePath(sni[Object.keys(sni)[i]].cert)); + forbiddenPaths.certificates.push(getInitializePath(sni[Object.keys(sni)[i]].key)); + } +} +forbiddenPaths.svrjs = getInitializePath("./" + ((__dirname[__dirname.length - 1] != "/") ? __filename.replace(__dirname + "/", "") : __filename.replace(__dirname, ""))); +forbiddenPaths.serverSideScripts = []; +forbiddenPaths.serverSideScripts.push("/serverSideScript.js"); +forbiddenPaths.serverSideScripts.push(getInitializePath("./temp/serverSideScript.js")); +forbiddenPaths.serverSideScriptDirectories = []; +forbiddenPaths.serverSideScriptDirectories.push(getInitializePath("./temp/modloader")); +forbiddenPaths.serverSideScriptDirectories.push(getInitializePath("./node_modules")); +forbiddenPaths.serverSideScriptDirectories.push(getInitializePath("./mods")); +forbiddenPaths.log = getInitializePath("./log"); + +//Create server +if (!cluster.isMaster) { + var reqcounter = 0; + var server = {}; + var server2 = {}; + try { + server2 = http.createServer({ + requireHostHeader: false + }); + } catch (ex) { + server2 = http.createServer(); + } + if (!disableToHTTPSRedirect) { + server2.on("request", redirhandler); + server2.on("connect", function (request, socket) { + var reqIdInt = Math.round(Math.random() * 16777216); + var reqId = "0".repeat(6 - reqIdInt.toString(16).length) + reqIdInt.toString(16); + var serverconsole = { + climessage: function (msg) { + if (msg.indexOf("\n") != -1) { + var nmsg = msg.split("\n"); + for (var i = 0; i < nmsg.length; i++) { + serverconsole.climessage(nmsg[i]); + } + return; + } + console.log("SERVER CLI MESSAGE [Request Id: " + reqId + "]: " + msg); + LOG("SERVER CLI MESSAGE [Request Id: " + reqId + "]: " + msg); + return; + }, + reqmessage: function (msg) { + if (msg.indexOf("\n") != -1) { + var nmsg = msg.split("\n"); + for (var i = 0; i < nmsg.length; i++) { + serverconsole.reqmessage(nmsg[i]); + } + return; + } + console.log("\x1b[34mSERVER REQUEST MESSAGE [Request Id: " + reqId + "]: " + msg + "\x1b[37m\x1b[0m"); + LOG("SERVER REQUEST MESSAGE [Request Id: " + reqId + "]: " + msg); + return; + }, + resmessage: function (msg) { + if (msg.indexOf("\n") != -1) { + var nmsg = msg.split("\n"); + for (var i = 0; i < nmsg.length; i++) { + serverconsole.resmessage(nmsg[i]); + } + return; + } + console.log("\x1b[32mSERVER RESPONSE MESSAGE [Request Id: " + reqId + "]: " + msg + "\x1b[37m\x1b[0m"); + LOG("SERVER RESPONSE MESSAGE [Request Id: " + reqId + "]: " + msg); + return; + }, + errmessage: function (msg) { + if (msg.indexOf("\n") != -1) { + var nmsg = msg.split("\n"); + for (var i = 0; i < nmsg.length; i++) { + serverconsole.errmessage(nmsg[i]); + } + return; + } + console.log("\x1b[31mSERVER RESPONSE ERROR MESSAGE [Request Id: " + reqId + "]: " + msg + "\x1b[37m\x1b[0m"); + LOG("SERVER RESPONSE ERROR MESSAGE [Request Id: " + reqId + "]: " + msg); + return; + }, + locerrmessage: function (msg) { + if (msg.indexOf("\n") != -1) { + var nmsg = msg.split("\n"); + for (var i = 0; i < nmsg.length; i++) { + serverconsole.locerrmessage(nmsg[i]); + } + return; + } + console.log("\x1b[41mSERVER ERROR MESSAGE [Request Id: " + reqId + "]: " + msg + "\x1b[40m\x1b[0m"); + LOG("SERVER ERROR MESSAGE [Request Id: " + reqId + "]: " + msg); + return; + }, + locwarnmessage: function (msg) { + if (msg.indexOf("\n") != -1) { + var nmsg = msg.split("\n"); + for (var i = 0; i < nmsg.length; i++) { + serverconsole.locwarnmessage(nmsg[i]); + } + return; + } + console.log("\x1b[43mSERVER WARNING MESSAGE [Request Id: " + reqId + "]: " + msg + "\x1b[40m\x1b[0m"); + LOG("SERVER WARNING MESSAGE [Request Id: " + reqId + "]: " + msg); + return; + }, + locmessage: function (msg) { + if (msg.indexOf("\n") != -1) { + var nmsg = msg.split("\n"); + for (var i = 0; i < nmsg.length; i++) { + serverconsole.locmessage(nmsg[i]); + } + return; + } + console.log("SERVER MESSAGE [Request Id: " + reqId + "]: " + msg); + LOG("SERVER MESSAGE [Request Id: " + reqId + "]: " + msg); + return; + } + }; + socket.on("close", function (hasError) { + if (!hasError) serverconsole.locmessage("Client disconnected."); + else serverconsole.locmessage("Client disconnected due to error."); + }); + socket.on("error", function () {}); + var reqip = socket.remoteAddress; + var reqport = socket.remotePort; + serverconsole.locmessage("Somebody connected to port " + port + "..."); + serverconsole.reqmessage("Client " + ((!reqip || reqip == "") ? "[unknown client]" : (reqip + ((reqport && reqport !== 0) && reqport != "" ? ":" + reqport : ""))) + " wants to proxy " + request.url + " through this server"); + if (request.headers["user-agent"] != undefined) serverconsole.reqmessage("Client uses " + request.headers["user-agent"]); + serverconsole.errmessage("This server will never be a proxy."); + if (!socket.destroyed) socket.end("HTTP/1.1 501 Not Implemented\n\n"); + }); + server2.on("clientError", function (err, socket) { + reqerrhandler(err, socket, false); + }); + server2.on("checkExpectation", redirhandler); + } else { + server2.on("request", function (req, res) { + reqhandler(req, res, false); + }); + server2.on("checkExpectation", reqhandler); + server2.on("connect", connhandler); + server2.on("clientError", function (err, socket) { + reqerrhandler(err, socket, false); + }); + + } + server2.on("error", function (err) { + if (err.code == "EADDRINUSE" || err.code == "EADDRNOTAVAIL" || err.code == "EACCES") { + attmtsRedir--; + if (cluster.isMaster === undefined) { + if (err.code == "EADDRINUSE") { + serverconsole.locerrmessage("Address in use by another process."); + } else if (err.code == "EADDRNOTAVAIL") { + serverconsole.locerrmessage("Address not available."); + } else if (err.code == "EACCES") { + serverconsole.locerrmessage("Access denied."); + } + serverconsole.locmessage(attmtsRedir + " attempts left."); + } else { + process.send("\x12ERRLIST" + attmtsRedir + err.code); + } + if (attmtsRedir > 0) { + server2.close(); + setTimeout(start, 900); + } else { + if (cluster.isMaster !== undefined) process.send("\x12" + err.code); + process.exit(errors[err.code]); + } + } else { + serverconsole.locerrmessage("There was a problem starting SVR.JS!!!"); + serverconsole.locerrmessage("Stack:"); + serverconsole.locerrmessage(generateErrorStack(err)); + if (cluster.isMaster !== undefined) process.send("\x12CRASH"); + process.exit(err.code ? errors[err.code] : 1); + } + }); + + server2.once("listening", function () { + listeningMessage(); + }); + + if (configJSON.enableHTTP2 == true) { + if (secure) { + server = http2.createSecureServer({ + allowHTTP1: true, + requireHostHeader: false, + key: key, + cert: cert + }); + } else { + server = http2.createServer({ + allowHTTP1: true, + requireHostHeader: false + }); + } + } else { + if (secure) { + server = https.createServer({ + key: key, + cert: cert, + requireHostHeader: false + }); + } else { + try { + server = http.createServer({ + requireHostHeader: false + }); + } catch (ex) { + server = http.createServer(); + } + } + } + if (secure) { + for (var i = 0; i < sniCredentials.length; i++) { + server.addContext(sniCredentials[i].name, { + cert: sniCredentials[i].cert, + key: sniCredentials[i].key + }); + } + } + server.on("request", reqhandler); + server.on("checkExpectation", reqhandler); + server.on("connect", connhandler); + server.on("clientError", reqerrhandler); + + if (secure) { + server.prependListener("connection", function (sock) { + sock.reallyDestroy = sock.destroy; + sock.destroy = function () { + sock.toDestroy = true; + }; + }); + + server.prependListener("tlsClientError", function (err, sock) { + if (err.code == "ERR_SSL_HTTP_REQUEST" || err.message.indexOf("http request") != -1) { + sock._parent.destroy = sock._parent.reallyDestroy; + sock._readableState = sock._parent._readableState; + sock._writableState = sock._parent._writableState; + sock._parent.toDestroy = false; + sock.pipe = function (a, b, c) { + sock._parent.pipe(a, b, c); + }; + sock.write = function (a, b, c) { + sock._parent.write(a, b, c); + }; + sock.end = function (a, b, c) { + sock._parent.end(a, b, c); + }; + sock.destroyed = sock._parent.destroyed; + sock.readable = sock._parent.readable; + sock.writable = sock._parent.writable; + sock.remoteAddress = sock._parent.remoteAddress; + sock.remotePort = sock._parent.remoteAddress; + sock.destroy = function (a, b, c) { + try { + sock._parent.destroy(a, b, c); + sock.destroyed = sock._parent.destroyed; + } catch (ex) { + //Socket is probably already destroyed. + } + }; + } else { + sock._parent.destroy = sock._parent.reallyDestroy; + try { + if (sock._parent.toDestroy) sock._parent.destroy(); + } catch (ex) { + //Socket is probably already destroyed. + } + } + }); + + server.prependListener("secureConnection", function (sock) { + sock._parent.destroy = sock._parent.reallyDestroy; + delete sock._parent.reallyDestroy; + }); + + if (configJSON.enableOCSPStapling && !ocsp._errored) { + server.on("OCSPRequest", function (cert, issuer, callback) { + ocsp.getOCSPURI(cert, function (err, uri) { + if (err) return callback(err); + + var req = ocsp.request.generate(cert, issuer); + var options = { + url: uri, + ocsp: req.data + }; + + ocspCache.request(req.id, options, callback); + }); + }); + } + } + + //Patches from Node.JS v18.0.0 + if (server.requestTimeout !== undefined && server.requestTimeout === 0) server.requestTimeout = 300000; + if (server2.requestTimeout !== undefined && server2.requestTimeout === 0) server2.requestTimeout = 300000; + + function redirhandler(req, res) { + if (req.headers["force-insecure"] == "true" || req.headers["x-force-insecure"] == "true" || req.headers["x-svr-js-force-insecure"] == "true") { + reqhandler(req, res, false); + } else { + var reqIdInt = Math.round(Math.random() * 16777216); + var reqId = "0".repeat(6 - reqIdInt.toString(16).length) + reqIdInt.toString(16); + var serverconsole = { + climessage: function (msg) { + if (msg.indexOf("\n") != -1) { + var nmsg = msg.split("\n"); + for (var i = 0; i < nmsg.length; i++) { + serverconsole.climessage(nmsg[i]); + } + return; + } + console.log("SERVER CLI MESSAGE [Request Id: " + reqId + "]: " + msg); + LOG("SERVER CLI MESSAGE [Request Id: " + reqId + "]: " + msg); + return; + }, + reqmessage: function (msg) { + if (msg.indexOf("\n") != -1) { + var nmsg = msg.split("\n"); + for (var i = 0; i < nmsg.length; i++) { + serverconsole.reqmessage(nmsg[i]); + } + return; + } + console.log("\x1b[34mSERVER REQUEST MESSAGE [Request Id: " + reqId + "]: " + msg + "\x1b[37m\x1b[0m"); + LOG("SERVER REQUEST MESSAGE [Request Id: " + reqId + "]: " + msg); + return; + }, + resmessage: function (msg) { + if (msg.indexOf("\n") != -1) { + var nmsg = msg.split("\n"); + for (var i = 0; i < nmsg.length; i++) { + serverconsole.resmessage(nmsg[i]); + } + return; + } + console.log("\x1b[32mSERVER RESPONSE MESSAGE [Request Id: " + reqId + "]: " + msg + "\x1b[37m\x1b[0m"); + LOG("SERVER RESPONSE MESSAGE [Request Id: " + reqId + "]: " + msg); + return; + }, + errmessage: function (msg) { + if (msg.indexOf("\n") != -1) { + var nmsg = msg.split("\n"); + for (var i = 0; i < nmsg.length; i++) { + serverconsole.errmessage(nmsg[i]); + } + return; + } + console.log("\x1b[31mSERVER RESPONSE ERROR MESSAGE [Request Id: " + reqId + "]: " + msg + "\x1b[37m\x1b[0m"); + LOG("SERVER RESPONSE ERROR MESSAGE [Request Id: " + reqId + "]: " + msg); + return; + }, + locerrmessage: function (msg) { + if (msg.indexOf("\n") != -1) { + var nmsg = msg.split("\n"); + for (var i = 0; i < nmsg.length; i++) { + serverconsole.locerrmessage(nmsg[i]); + } + return; + } + console.log("\x1b[41mSERVER ERROR MESSAGE [Request Id: " + reqId + "]: " + msg + "\x1b[40m\x1b[0m"); + LOG("SERVER ERROR MESSAGE [Request Id: " + reqId + "]: " + msg); + return; + }, + locwarnmessage: function (msg) { + if (msg.indexOf("\n") != -1) { + var nmsg = msg.split("\n"); + for (var i = 0; i < nmsg.length; i++) { + serverconsole.locwarnmessage(nmsg[i]); + } + return; + } + console.log("\x1b[43mSERVER WARNING MESSAGE [Request Id: " + reqId + "]: " + msg + "\x1b[40m\x1b[0m"); + LOG("SERVER WARNING MESSAGE [Request Id: " + reqId + "]: " + msg); + return; + }, + locmessage: function (msg) { + if (msg.indexOf("\n") != -1) { + var nmsg = msg.split("\n"); + for (var i = 0; i < nmsg.length; i++) { + serverconsole.locmessage(nmsg[i]); + } + return; + } + console.log("SERVER MESSAGE [Request Id: " + reqId + "]: " + msg); + LOG("SERVER MESSAGE [Request Id: " + reqId + "]: " + msg); + return; + } + }; + + function getCustomHeaders() { + var ph = JSON.parse(JSON.stringify(customHeaders)); + var phk = Object.keys(ph); + for (var i = 0; i < phk.length; i++) { + if (typeof ph[phk[i]] == "string") ph[phk[i]] = ph[phk[i]].replace(/\{path\}/g, req.url); + } + return ph; + } + if (req.headers["x-svr-js-from-main-thread"] == "true") { + res.writeHead(204, "No Content", getCustomHeaders()); + res.end(); + return; + } + try { + req.url = encodeURI(Buffer.from(req.url, "latin1").toString("utf8")).replace(/%25/gi, "%"); + } catch (ex) { + //URL not converted... + } + res.writeHeadNative = res.writeHead; + res.writeHead = function (a, b, c) { + if (parseInt(a) >= 400 && parseInt(a) <= 599) { + serverconsole.errmessage("Server responded with " + a.toString() + " code."); + } else { + serverconsole.resmessage("Server responded with " + a.toString() + " code."); + } + res.writeHeadNative(a, b, c); + }; + var finished = false; + res.on("finish", function () { + if (!finished) { + finished = true; + serverconsole.locmessage("Client disconnected."); + } + }); + res.on("close", function () { + if (!finished) { + finished = true; + serverconsole.locmessage("Client disconnected."); + } + }); + + serverconsole.locmessage("Somebody connected to port " + port + "..."); + + if (req.socket == null) { + serverconsole.errmessage("Client socket is null!!!"); + return; + } + + var isProxy = false; + if (req.url.indexOf("/") != 0 && req.url != "*") isProxy = true; + + var head = fs.existsSync("./.head") ? fs.readFileSync("./.head").toString() : (fs.existsSync("./head.html") ? fs.readFileSync("./head.html").toString() : ""); // header + var foot = fs.existsSync("./.foot") ? fs.readFileSync("./.foot").toString() : (fs.existsSync("./foot.html") ? fs.readFileSync("./foot.html").toString() : ""); // footer + var fd = ""; + + function responseEnd(d) { + if (d === undefined) d = fd; + res.write(head + d + foot); + res.end(); + } + + //Error descriptions + var serverErrorDescs = { + 400: "The request you made is invalid.", + 417: "Expectation in Expect property couldn't be satisfied.", + 500: "The server had an unexpected error. Below, the error stack is shown:

{stack}

Please contact with developer/administrator at {contact}.", + 501: "The request requires use of a function, which isn't implemented (yet) by the server." + }; + + //Server error calling method + function callServerError(errorCode, extName, stack, ch) { + var errorFile = errorCode.toString() + ".html"; + var errorFile2 = "." + errorCode.toString(); + if (fs.existsSync(errorFile2)) errorFile = errorFile2; + if (errorCode == 404 && fs.existsSync(page404)) errorFile = page404; + if (Object.prototype.toString.call(stack) === "[object Error]") stack = generateErrorStack(stack); + if (stack === undefined) stack = generateErrorStack(new Error("Unknown error")); + if (errorCode == 500 || errorCode == 502) { + serverconsole.errmessage("There was an error while processing the request!"); + serverconsole.errmessage("Stack:"); + serverconsole.errmessage(stack); + } + if (stackHidden) stack = "[error stack hidden]"; + if (serverErrorDescs[errorCode] === undefined) { + callServerError(501, extName, stack); + } else { + var cheaders = getCustomHeaders(); + if (ch) { + var chon = Object.keys(cheaders); + var chn = Object.keys(ch); + for (var i = 0; i < chn.length; i++) { + var nhn = chn[i]; + for (var j = 0; j < chon.length; j++) { + if (chon[j].toLowerCase() == chn[i].toLowerCase()) { + nhn = chon[j]; + break; + } + } + if (ch[chn[i]]) cheaders[nhn] = ch[chn[i]]; + } + } + cheaders["Content-Type"] = "text/html; charset=utf-8"; + if (errorCode == 405 && !cheaders["Allow"]) cheaders["Allow"] = "GET, POST, HEAD, OPTIONS"; + fs.readFile(errorFile, function (err, data) { + try { + if (err) throw err; + res.writeHead(errorCode, http.STATUS_CODES[errorCode], cheaders); + fd += data.toString().replace(/{errorMessage}/g, errorCode.toString() + " " + http.STATUS_CODES[errorCode]).replace(/{errorDesc}/g, serverErrorDescs[errorCode]).replace(/{stack}/g, stack.replace(/&/g, "&").replace(//g, ">").replace(/\r\n/g, "
").replace(/\n/g, "
").replace(/\r/g, "
").replace(/ {2}/g, "  ")).replace(/{path}/g, req.url.replace(/&/g, "&").replace(//g, ">")).replace(/{server}/g, "" + (exposeServerVersion ? "SVR.JS/" + version + " (" + getOS() + "; " + (process.isBun ? ("Bun/v" + process.versions.bun + "; like Node.JS/" + process.version) : ("Node.JS/" + process.version)) + ")" : "SVR.JS") + (extName == undefined ? "" : " " + extName) + ((req.headers.host == undefined || isProxy) ? "" : " on " + String(req.headers.host).replace(/&/g, "&").replace(//g, ">"))).replace(/{contact}/g, serverAdmin.replace(/\./g, "[dot]").replace(/@/g, "[at]")); + responseEnd(); + } catch (ex) { + var additionalError = 500; + if (ex.code == "ENOENT") { + additionalError = 404; + } else if (ex.code == "EACCES") { + additionalError = 403; + } else if (ex.code == "EMFILE") { + additionalError = 429; + } else if (ex.code == "ELOOP") { + additionalError = 508; + } + res.writeHead(errorCode, http.STATUS_CODES[errorCode], cheaders); + res.write(("{errorMessage}

{errorMessage}

{errorDesc}

" + ((additionalError == 404) ? "" : "

Additionally, a {additionalError} error occurred while loading an error page.

") + "

{server}

").replace(/{errorMessage}/g, errorCode.toString() + " " + http.STATUS_CODES[errorCode]).replace(/{errorDesc}/g, serverErrorDescs[errorCode]).replace(/{stack}/g, stack.replace(/&/g, "&").replace(//g, ">").replace(/\r\n/g, "
").replace(/\n/g, "
").replace(/\r/g, "
").replace(/ {2}/g, "  ")).replace(/{path}/g, req.url.replace(/&/g, "&").replace(//g, ">")).replace(/{server}/g, "" + (exposeServerVersion ? "SVR.JS/" + version + " (" + getOS() + "; " + (process.isBun ? ("Bun/v" + process.versions.bun + "; like Node.JS/" + process.version) : ("Node.JS/" + process.version)) + ")" : "SVR.JS") + (extName == undefined ? "" : " " + extName) + ((req.headers.host == undefined || isProxy) ? "" : " on " + String(req.headers.host).replace(/&/g, "&").replace(//g, ">"))).replace(/{contact}/g, serverAdmin.replace(/\./g, "[dot]").replace(/@/g, "[at]")).replace(/{additionalError}/g, additionalError.toString())); + res.end(); + } + }); + + } + } + + var reqport = ""; + var reqip = ""; + var oldport = ""; + var oldip = ""; + if (req.headers["x-svr-js-client"] != undefined && enableIPSpoofing) { + var kl = req.headers["x-svr-js-client"].split(":"); + reqport = kl.pop(); + reqip = kl.join(":"); + try { + oldport = req.socket.remotePort; + oldip = req.socket.remoteAddress; + req.socket.realRemotePort = reqport; + req.socket.realRemoteAddress = reqip; + req.socket.originalRemotePort = oldport; + req.socket.originalRemoteAddress = oldip; + res.socket.realRemotePort = reqport; + res.socket.realRemoteAddress = reqip; + res.socket.originalRemotePort = oldport; + res.socket.originalRemoteAddress = oldip; + } catch (ex) { + //Nevermind... + } + } else if (req.headers["x-forwarded-for"] != undefined && enableIPSpoofing) { + reqport = null; + reqip = req.headers["x-forwarded-for"].split(",")[0].replace(/ /g, ""); + if (reqip.indexOf(":") == -1) reqip = "::ffff:" + reqip; + try { + oldport = req.socket.remotePort; + oldip = req.socket.remoteAddress; + req.socket.realRemotePort = reqport; + req.socket.realRemoteAddress = reqip; + req.socket.originalRemotePort = oldport; + req.socket.originalRemoteAddress = oldip; + res.socket.realRemotePort = reqport; + res.socket.realRemoteAddress = reqip; + res.socket.originalRemotePort = oldport; + res.socket.originalRemoteAddress = oldip; + } catch (ex) { + //Nevermind... + } + } else { + reqip = req.socket.remoteAddress; + reqport = req.socket.remotePort; + } + + if (!isProxy) serverconsole.reqmessage("Client " + ((!reqip || reqip == "") ? "[unknown client]" : (reqip + ((reqport && reqport !== 0) && reqport != "" ? ":" + reqport : ""))) + " wants " + (req.method == "GET" ? "content in " : (req.method == "POST" ? "to post content in " : (req.method == "PUT" ? "to add content in " : (req.method == "DELETE" ? "to delete content in " : (req.method == "PATCH" ? "to patch content in " : "to access content using " + req.method + " method in "))))) + (req.headers.host == undefined ? "" : req.headers.host) + req.url); + else serverconsole.reqmessage("Client " + ((!reqip || reqip == "") ? "[unknown client]" : (reqip + ((reqport && reqport !== 0) && reqport != "" ? ":" + reqport : ""))) + " wants " + (req.method == "GET" ? "content in " : (req.method == "POST" ? "to post content in " : (req.method == "PUT" ? "to add content in " : (req.method == "DELETE" ? "to delete content in " : (req.method == "PATCH" ? "to patch content in " : "to access content using " + req.method + " method in "))))) + req.url); + if (req.headers["user-agent"] != undefined) serverconsole.reqmessage("Client uses " + req.headers["user-agent"]); + + try { + if (req.headers["expect"] && req.headers["expect"] != "100-continue") { + serverconsole.errmessage("Expectation not satified!"); + callServerError(417); + return; + } + var hostx = req.headers.host; + if (hostx === undefined) { + serverconsole.errmessage("Bad request!"); + callServerError(400); + return; + } + + if (req.method == "CONNECT") { + callServerError(501); + serverconsole.errmessage("CONNECT requests aren't supported. Your JS runtime probably doesn't support 'connect' handler for HTTP library."); + return; + } + + if (isProxy) { + callServerError(501); + serverconsole.errmessage("This server will never be a proxy."); + return; + } + + function urlParse(uri) { + if (typeof URL != "undefined" && url.Url) { + try { + var uobject = new URL(uri, "http" + (req.socket.encrypted ? "s" : "") + "://" + (req.headers.host ? req.headers.host : (domain ? domain : "unknown.invalid"))); + var nuobject = new url.Url(); + if (uri.indexOf("/") != -1) nuobject.slashes = true; + if (uobject.protocol != "") nuobject.protocol = uobject.protocol; + if (uobject.username != "" && uobject.password != "") nuobject.auth = uobject.username + ":" + uobject.password; + if (uobject.host != "") nuobject.host = uobject.host; + if (uobject.hostname != "") nuobject.hostname = uobject.hostname; + if (uobject.port != "") nuobject.port = uobject.port; + if (uobject.pathname != "") nuobject.pathname = uobject.pathname; + if (uobject.search != "") nuobject.search = uobject.search; + if (uobject.hash != "") nuobject.hash = uobject.hash; + if (uobject.href != "") nuobject.href = uobject.href; + if (uri.indexOf("/") != 0) { + if (nuobject.pathname) { + nuobject.pathname = nuobject.pathname.substr(1); + nuobject.href = nuobject.pathname + (nuobject.search ? nuobject.search : ""); + } + } + if (nuobject.pathname) { + nuobject.path = nuobject.pathname + (nuobject.search ? nuobject.search : ""); + } + //if(nuobject.path != "" && uobject.password != "") nuobject.path = nuobject.pathname + nuobject.href; + nuobject.query = {}; + uobject.searchParams.forEach(function (value, key) { + nuobject.query[key] = value; + }); + return nuobject; + } catch (ex) { + return url.parse(uri, true); + } + } else { + return url.parse(uri, true); + } + } + var urlp = urlParse("http://" + hostx); + try { + if (urlp.path.indexOf("//") == 0) { + urlp = urlParse("http:" + url.path); + } + } catch (ex) { + //URL parse error... + } + if (urlp.host == "localhost" || urlp.host == "localhost:" + port.toString() || urlp.host == "127.0.0.1" || urlp.host == "127.0.0.1:" + port.toString() || urlp.host == "::1" || urlp.host == "::1:" + port.toString()) { + urlp.protocol = "https:"; + if (sport == 443) { + urlp.host = urlp.hostname; + } else { + urlp.host = urlp.hostname + ":" + sport.toString(); + urlp.port = sport.toString(); + } + } else if (urlp.host == host || urlp.host == host + ":" + port.toString()) { + urlp.protocol = "https:"; + if (sport == 443) { + urlp.host = urlp.hostname; + } else { + urlp.host = urlp.hostname + ":" + sport.toString(); + urlp.port = sport.toString(); + } + } else if (urlp.host == pubip || urlp.host == pubip + ":" + pubport.toString()) { + urlp.protocol = "https:"; + if (spubport == 443) { + urlp.host = urlp.hostname; + } else { + urlp.host = urlp.hostname + ":" + spubport.toString(); + urlp.port = spubport.toString(); + } + } else if (urlp.hostname == domain || urlp.hostname.indexOf(domain) != -1) { + urlp.protocol = "https:"; + if (spubport == 443) { + urlp.host = urlp.hostname; + } else { + urlp.host = urlp.hostname + ":" + spubport.toString(); + urlp.port = spubport.toString(); + } + } else { + urlp.protocol = "https:"; + } + urlp.path = null; + urlp.pathname = null; + var lloc = url.format(urlp); + var requestURL = req.url; + try { + if (requestURL.split("/")[1].indexOf(".onion") != -1) { + requestURL = requestURL.split("/"); + requestURL.shift(); + requestURL.shift(); + requestURL.unshift(""); + requestURL = requestURL.join("/"); + } + } catch (ex) { + //Leave URL as it is... + } + var rheaders = getCustomHeaders(); + rheaders["Location"] = lloc + requestURL; + res.writeHead(301, "Redirect to HTTPS", rheaders); + res.end(); + } catch (ex) { + serverconsole.errmessage("There was an error while processing the request!"); + serverconsole.errmessage("Stack:"); + serverconsole.errmessage(generateErrorStack(ex)); + callServerError(500, undefined, generateErrorStack(ex)); + } + + } + } + + function reqerrhandler(err, socket, fromMain) { + if (fromMain === undefined) fromMain = true; + //Define response object similar to Node.JS native one + var res = {}; + res.socket = socket; + res.write = function (x) { + if (err.code === "ECONNRESET" || !socket.writable) { + return; + } + socket.write(x); + }; + res.end = function (x) { + if (err.code === "ECONNRESET" || !socket.writable) { + return; + } + socket.end(x, function () { + try { + socket.destroy(); + } catch (ex) { + //Socket is probably already destroyed + } + }); + }; + res.writeHead = function (code, name, headers) { + var head = ("HTTP/1.1 " + code.toString() + " " + name + "\r\n"); + var headers = JSON.parse(JSON.stringify(headers)); + headers["Date"] = (new Date()).toGMTString(); + headers["Connection"] = "close"; + var headernames = Object.keys(headers); + for (var i = 0; i < headernames.length; i++) { + if (headernames[i].toLowerCase() == "set-cookie") { + for (var j = 0; j < headers[headernames[i]]; j++) { + if (headernames[i].match(/[^\x09\x20-\x7e\x80-\xff]|.:/) || headers[headernames[i]][j].match(/[^\x09\x20-\x7e\x80-\xff]/)) throw new Error("Invalid header!!! (" + headernames[i] + ")"); + head += (headernames[i] + ": " + headers[headernames[i]][j]); + } + } else { + if (headernames[i].match(/[^\x09\x20-\x7e\x80-\xff]|.:/) || headers[headernames[i]].match(/[^\x09\x20-\x7e\x80-\xff]/)) throw new Error("Invalid header!!! (" + headernames[i] + ")"); + head += (headernames[i] + ": " + headers[headernames[i]]); + } + head += "\r\n"; + } + head += ("\r\n"); + res.write(head); + }; + + var reqIdInt = Math.round(Math.random() * 16777216); + var reqId = "0".repeat(6 - reqIdInt.toString(16).length) + reqIdInt.toString(16); + var serverconsole = { + climessage: function (msg) { + if (msg.indexOf("\n") != -1) { + var nmsg = msg.split("\n"); + for (var i = 0; i < nmsg.length; i++) { + serverconsole.climessage(nmsg[i]); + } + return; + } + console.log("SERVER CLI MESSAGE [Request Id: " + reqId + "]: " + msg); + LOG("SERVER CLI MESSAGE [Request Id: " + reqId + "]: " + msg); + return; + }, + reqmessage: function (msg) { + if (msg.indexOf("\n") != -1) { + var nmsg = msg.split("\n"); + for (var i = 0; i < nmsg.length; i++) { + serverconsole.reqmessage(nmsg[i]); + } + return; + } + console.log("\x1b[34mSERVER REQUEST MESSAGE [Request Id: " + reqId + "]: " + msg + "\x1b[37m\x1b[0m"); + LOG("SERVER REQUEST MESSAGE [Request Id: " + reqId + "]: " + msg); + return; + }, + resmessage: function (msg) { + if (msg.indexOf("\n") != -1) { + var nmsg = msg.split("\n"); + for (var i = 0; i < nmsg.length; i++) { + serverconsole.resmessage(nmsg[i]); + } + return; + } + console.log("\x1b[32mSERVER RESPONSE MESSAGE [Request Id: " + reqId + "]: " + msg + "\x1b[37m\x1b[0m"); + LOG("SERVER RESPONSE MESSAGE [Request Id: " + reqId + "]: " + msg); + return; + }, + errmessage: function (msg) { + if (msg.indexOf("\n") != -1) { + var nmsg = msg.split("\n"); + for (var i = 0; i < nmsg.length; i++) { + serverconsole.errmessage(nmsg[i]); + } + return; + } + console.log("\x1b[31mSERVER RESPONSE ERROR MESSAGE [Request Id: " + reqId + "]: " + msg + "\x1b[37m\x1b[0m"); + LOG("SERVER RESPONSE ERROR MESSAGE [Request Id: " + reqId + "]: " + msg); + return; + }, + locerrmessage: function (msg) { + if (msg.indexOf("\n") != -1) { + var nmsg = msg.split("\n"); + for (var i = 0; i < nmsg.length; i++) { + serverconsole.locerrmessage(nmsg[i]); + } + return; + } + console.log("\x1b[41mSERVER ERROR MESSAGE [Request Id: " + reqId + "]: " + msg + "\x1b[40m\x1b[0m"); + LOG("SERVER ERROR MESSAGE [Request Id: " + reqId + "]: " + msg); + return; + }, + locwarnmessage: function (msg) { + if (msg.indexOf("\n") != -1) { + var nmsg = msg.split("\n"); + for (var i = 0; i < nmsg.length; i++) { + serverconsole.locwarnmessage(nmsg[i]); + } + return; + } + console.log("\x1b[43mSERVER WARNING MESSAGE [Request Id: " + reqId + "]: " + msg + "\x1b[40m\x1b[0m"); + LOG("SERVER WARNING MESSAGE [Request Id: " + reqId + "]: " + msg); + return; + }, + locmessage: function (msg) { + if (msg.indexOf("\n") != -1) { + var nmsg = msg.split("\n"); + for (var i = 0; i < nmsg.length; i++) { + serverconsole.locmessage(nmsg[i]); + } + return; + } + console.log("SERVER MESSAGE [Request Id: " + reqId + "]: " + msg); + LOG("SERVER MESSAGE [Request Id: " + reqId + "]: " + msg); + return; + } + }; + socket.on("close", function (hasError) { + if (!hasError || err.code == "ERR_SSL_HTTP_REQUEST" || err.message.indexOf("http request") != -1) serverconsole.locmessage("Client disconnected."); + else serverconsole.locmessage("Client disconnected due to error."); + }); + socket.on("error", function () {}); + + var head = fs.existsSync("./.head") ? fs.readFileSync("./.head").toString() : (fs.existsSync("./head.html") ? fs.readFileSync("./head.html").toString() : ""); // header + var foot = fs.existsSync("./.foot") ? fs.readFileSync("./.foot").toString() : (fs.existsSync("./foot.html") ? fs.readFileSync("./foot.html").toString() : ""); // footer + + var fd = ""; + + function responseEnd(d) { + if (d === undefined) d = fd; + res.write(head + d + foot); + res.end(); + } + + var serverErrorDescs = { + 400: "The request you made is invalid.", + 405: "Method used to access the requested file isn't allowed.", + 408: "You have timed out.", + 414: "URL you sent is too long.", + 431: "The request you sent contains headers, that are too large.", + 451: "The requested file isn't accessible for legal reasons.", + 497: "You sent non-TLS request to the HTTPS server." + }; + + //Server error calling method + function callServerError(errorCode, extName, stack, ch) { + var errorFile = errorCode.toString() + ".html"; + var errorFile2 = "." + errorCode.toString(); + if (fs.existsSync(errorFile2)) errorFile = errorFile2; + if (errorCode == 404 && fs.existsSync(page404)) errorFile = page404; + if (Object.prototype.toString.call(stack) === "[object Error]") stack = generateErrorStack(stack); + if (stack === undefined) stack = generateErrorStack(new Error("Unknown error")); + if (errorCode == 500 || errorCode == 502) { + serverconsole.errmessage("There was an error while processing the request!"); + serverconsole.errmessage("Stack:"); + serverconsole.errmessage(stack); + } + if (stackHidden) stack = "[error stack hidden]"; + if (serverErrorDescs[errorCode] === undefined) { + callServerError(501, extName, stack); + } else { + var cheaders = getCustomHeaders(); + if (ch) { + var chon = Object.keys(cheaders); + var chn = Object.keys(ch); + for (var i = 0; i < chn.length; i++) { + var nhn = chn[i]; + for (var j = 0; j < chon.length; j++) { + if (chon[j].toLowerCase() == chn[i].toLowerCase()) { + nhn = chon[j]; + break; + } + } + if (ch[chn[i]]) cheaders[nhn] = ch[chn[i]]; + } + } + cheaders["Content-Type"] = "text/html; charset=utf-8"; + if (errorCode == 405 && !cheaders["Allow"]) cheaders["Allow"] = "GET, POST, HEAD, OPTIONS"; + fs.readFile(errorFile, function (err, data) { + try { + if (err) throw err; + res.writeHead(errorCode, http.STATUS_CODES[errorCode], cheaders); + fd += data.toString().replace(/{errorMessage}/g, errorCode.toString() + " " + http.STATUS_CODES[errorCode]).replace(/{errorDesc}/g, serverErrorDescs[errorCode]).replace(/{stack}/g, stack.replace(/&/g, "&").replace(//g, ">").replace(/\r\n/g, "
").replace(/\n/g, "
").replace(/\r/g, "
").replace(/ {2}/g, "  ")).replace(/{server}/g, "" + (exposeServerVersion ? "SVR.JS/" + version + " (" + getOS() + "; " + (process.isBun ? ("Bun/v" + process.versions.bun + "; like Node.JS/" + process.version) : ("Node.JS/" + process.version)) + ")" : "SVR.JS") + (extName == undefined ? "" : " " + extName)).replace(/{contact}/g, serverAdmin.replace(/\./g, "[dot]").replace(/@/g, "[at]")); + responseEnd(); + } catch (ex) { + var additionalError = 500; + if (ex.code == "ENOENT") { + additionalError = 404; + } else if (ex.code == "EACCES") { + additionalError = 403; + } else if (ex.code == "EMFILE") { + additionalError = 429; + } else if (ex.code == "ELOOP") { + additionalError = 508; + } + res.writeHead(errorCode, http.STATUS_CODES[errorCode], cheaders); + res.write(("{errorMessage}

{errorMessage}

{errorDesc}

" + ((additionalError == 404) ? "" : "

Additionally, a {additionalError} error occurred while loading an error page.

") + "

{server}

").replace(/{errorMessage}/g, errorCode.toString() + " " + http.STATUS_CODES[errorCode]).replace(/{errorDesc}/g, serverErrorDescs[errorCode]).replace(/{stack}/g, stack.replace(/&/g, "&").replace(//g, ">").replace(/\r\n/g, "
").replace(/\n/g, "
").replace(/\r/g, "
").replace(/ {2}/g, "  ")).replace(/{server}/g, "" + (exposeServerVersion ? "SVR.JS/" + version + " (" + getOS() + "; " + (process.isBun ? ("Bun/v" + process.versions.bun + "; like Node.JS/" + process.version) : ("Node.JS/" + process.version)) + ")" : "SVR.JS") + (extName == undefined ? "" : " " + extName)).replace(/{contact}/g, serverAdmin.replace(/\./g, "[dot]").replace(/@/g, "[at]")).replace(/{additionalError}/g, additionalError.toString())); + res.end(); + } + }); + + } + } + var reqip = socket.remoteAddress; + var reqport = socket.remotePort; + serverconsole.locmessage("Somebody connected to port " + (secure && fromMain ? sport : port) + "..."); + serverconsole.reqmessage("Client " + ((!reqip || reqip == "") ? "[unknown client]" : (reqip + ((reqport && reqport !== 0) && reqport != "" ? ":" + reqport : ""))) + " sent invalid request."); + try { + if ((err.code && (err.code.indexOf("ERR_SSL_") == 0 || err.code.indexOf("ERR_TLS_") == 0)) || (!err.code && err.message.indexOf("SSL routines") != -1)) { + if (err.code == "ERR_SSL_HTTP_REQUEST" || err.message.indexOf("http request") != -1) { + serverconsole.errmessage("Client sent HTTP request to HTTPS port."); + callServerError(497); + return; + } else { + serverconsole.errmessage("An SSL error occured: " + (err.code ? err.code : err.message)); + callServerError(400); + return; + } + } + + if (err.code && err.code == "ERR_HTTP_REQUEST_TIMEOUT") { + serverconsole.errmessage("Client timed out."); + callServerError(408); + return; + } + + if (!err.rawPacket) { + serverconsole.errmessage("Connection ended prematurely."); + callServerError(400); + return; + } + + var packetLines = err.rawPacket.toString().split("\r\n"); + if (packetLines.length == 0) { + serverconsole.errmessage("Invalid request."); + callServerError(400); + return; + } + + function checkHeaders(beginsFromFirst) { + for (var i = (beginsFromFirst ? 0 : 1); i < packetLines.length; i++) { + var header = packetLines[i]; + if (header == "") return false; //Beginning of body + else if (header.indexOf(":") < 1) { + serverconsole.errmessage("Invalid header."); + callServerError(400); + return true; + } else if (header.length > 8192) { + serverconsole.errmessage("Header too large."); + callServerError(431); //Headers too large + return true; + } + } + return false; + } + var packetLine1 = packetLines[0].split(" "); + var method = "GET"; + var httpVersion = "HTTP/1.1"; + if (String(packetLine1[0]).indexOf(":") > 0) { + if (!checkHeaders(true)) { + serverconsole.errmessage("The request is invalid."); + callServerError(400); //Also malformed Packet + return; + } + } + if (String(packetLine1[0]).length < 50) method = packetLine1.shift(); + if (String(packetLine1[packetLine1.length - 1]).length < 50) httpVersion = packetLine1.pop(); + if (packetLine1.length != 1) { + serverconsole.errmessage("The head of request is invalid."); + callServerError(400); //Malformed Packet + } else if (!httpVersion.toString().match(/^HTTP[\/]/i)) { + serverconsole.errmessage("Invalid protocol."); + callServerError(400); //bad protocol version + } else if (http.METHODS.indexOf(method) == -1) { + serverconsole.errmessage("Invalid method."); + callServerError(405); //Also malformed Packet + } else { + if (checkHeaders(false)) return; + if (packetLine1[0].length > 255) { + serverconsole.errmessage("URI too long."); + callServerError(414); //Also malformed Packet + } else { + serverconsole.errmessage("Bad request."); + callServerError(400); //Also malformed Packet + } + } + } catch (ex) { + serverconsole.errmessage("There was an error while determining type of malformed request."); + callServerError(400); + } + } + + function connhandler(request, socket, head) { + var reqIdInt = Math.round(Math.random() * 16777216); + var reqId = "0".repeat(6 - reqIdInt.toString(16).length) + reqIdInt.toString(16); + var serverconsole = { + climessage: function (msg) { + if (msg.indexOf("\n") != -1) { + var nmsg = msg.split("\n"); + for (var i = 0; i < nmsg.length; i++) { + serverconsole.climessage(nmsg[i]); + } + return; + } + console.log("SERVER CLI MESSAGE [Request Id: " + reqId + "]: " + msg); + LOG("SERVER CLI MESSAGE [Request Id: " + reqId + "]: " + msg); + return; + }, + reqmessage: function (msg) { + if (msg.indexOf("\n") != -1) { + var nmsg = msg.split("\n"); + for (var i = 0; i < nmsg.length; i++) { + serverconsole.reqmessage(nmsg[i]); + } + return; + } + console.log("\x1b[34mSERVER REQUEST MESSAGE [Request Id: " + reqId + "]: " + msg + "\x1b[37m\x1b[0m"); + LOG("SERVER REQUEST MESSAGE [Request Id: " + reqId + "]: " + msg); + return; + }, + resmessage: function (msg) { + if (msg.indexOf("\n") != -1) { + var nmsg = msg.split("\n"); + for (var i = 0; i < nmsg.length; i++) { + serverconsole.resmessage(nmsg[i]); + } + return; + } + console.log("\x1b[32mSERVER RESPONSE MESSAGE [Request Id: " + reqId + "]: " + msg + "\x1b[37m\x1b[0m"); + LOG("SERVER RESPONSE MESSAGE [Request Id: " + reqId + "]: " + msg); + return; + }, + errmessage: function (msg) { + if (msg.indexOf("\n") != -1) { + var nmsg = msg.split("\n"); + for (var i = 0; i < nmsg.length; i++) { + serverconsole.errmessage(nmsg[i]); + } + return; + } + console.log("\x1b[31mSERVER RESPONSE ERROR MESSAGE [Request Id: " + reqId + "]: " + msg + "\x1b[37m\x1b[0m"); + LOG("SERVER RESPONSE ERROR MESSAGE [Request Id: " + reqId + "]: " + msg); + return; + }, + locerrmessage: function (msg) { + if (msg.indexOf("\n") != -1) { + var nmsg = msg.split("\n"); + for (var i = 0; i < nmsg.length; i++) { + serverconsole.locerrmessage(nmsg[i]); + } + return; + } + console.log("\x1b[41mSERVER ERROR MESSAGE [Request Id: " + reqId + "]: " + msg + "\x1b[40m\x1b[0m"); + LOG("SERVER ERROR MESSAGE [Request Id: " + reqId + "]: " + msg); + return; + }, + locwarnmessage: function (msg) { + if (msg.indexOf("\n") != -1) { + var nmsg = msg.split("\n"); + for (var i = 0; i < nmsg.length; i++) { + serverconsole.locwarnmessage(nmsg[i]); + } + return; + } + console.log("\x1b[43mSERVER WARNING MESSAGE [Request Id: " + reqId + "]: " + msg + "\x1b[40m\x1b[0m"); + LOG("SERVER WARNING MESSAGE [Request Id: " + reqId + "]: " + msg); + return; + }, + locmessage: function (msg) { + if (msg.indexOf("\n") != -1) { + var nmsg = msg.split("\n"); + for (var i = 0; i < nmsg.length; i++) { + serverconsole.locmessage(nmsg[i]); + } + return; + } + console.log("SERVER MESSAGE [Request Id: " + reqId + "]: " + msg); + LOG("SERVER MESSAGE [Request Id: " + reqId + "]: " + msg); + return; + } + }; + var req = request; + socket.on("close", function (hasError) { + if (!hasError) serverconsole.locmessage("Client disconnected."); + else serverconsole.locmessage("Client disconnected due to error."); + }); + socket.on("error", function () {}); + + var reqip = socket.remoteAddress; + var reqport = socket.remotePort; + serverconsole.locmessage("Somebody connected to port " + (secure ? sport : port) + "..."); + serverconsole.reqmessage("Client " + ((!reqip || reqip == "") ? "[unknown client]" : (reqip + ((reqport && reqport !== 0) && reqport != "" ? ":" + reqport : ""))) + " wants to proxy " + request.url + " through this server"); + if (request.headers["user-agent"] != undefined) serverconsole.reqmessage("Client uses " + request.headers["user-agent"]); + + function modExecute(mods, ffinals) { + var proxyMods = []; + for (var i = 0; i < mods.length; i++) { + if (mods[i].proxyCallback !== undefined) proxyMods.push(mods[i]); + } + + var modFunction = ffinals; + for (var i = proxyMods.length - 1; i >= 0; i--) { + modFunction = proxyMods[i].callback(req, socket, head, configJSON, serverconsole, modFunction); + } + modFunction(); + } + + function vres(req, socket, head, serverconsole) { + return function () { + serverconsole.errmessage("SVR.JS doesn't support proxy without proxy mod."); + if (!socket.destroyed) socket.end("HTTP/1.1 501 Not Implemented\n\n"); + }; + } + modExecute(mods, vres(req, socket, head, serverconsole)); + } + + function reqhandler(request, response, fromMain) { + if (fromMain === undefined) fromMain = true; + var reqIdInt = Math.round(Math.random() * 16777216); + var reqId = "0".repeat(6 - reqIdInt.toString(16).length) + reqIdInt.toString(16); + var serverconsole = { + climessage: function (msg) { + if (msg.indexOf("\n") != -1) { + var nmsg = msg.split("\n"); + for (var i = 0; i < nmsg.length; i++) { + serverconsole.climessage(nmsg[i]); + } + return; + } + console.log("SERVER CLI MESSAGE [Request Id: " + reqId + "]: " + msg); + LOG("SERVER CLI MESSAGE [Request Id: " + reqId + "]: " + msg); + return; + }, + reqmessage: function (msg) { + if (msg.indexOf("\n") != -1) { + var nmsg = msg.split("\n"); + for (var i = 0; i < nmsg.length; i++) { + serverconsole.reqmessage(nmsg[i]); + } + return; + } + console.log("\x1b[34mSERVER REQUEST MESSAGE [Request Id: " + reqId + "]: " + msg + "\x1b[37m\x1b[0m"); + LOG("SERVER REQUEST MESSAGE [Request Id: " + reqId + "]: " + msg); + return; + }, + resmessage: function (msg) { + if (msg.indexOf("\n") != -1) { + var nmsg = msg.split("\n"); + for (var i = 0; i < nmsg.length; i++) { + serverconsole.resmessage(nmsg[i]); + } + return; + } + console.log("\x1b[32mSERVER RESPONSE MESSAGE [Request Id: " + reqId + "]: " + msg + "\x1b[37m\x1b[0m"); + LOG("SERVER RESPONSE MESSAGE [Request Id: " + reqId + "]: " + msg); + return; + }, + errmessage: function (msg) { + if (msg.indexOf("\n") != -1) { + var nmsg = msg.split("\n"); + for (var i = 0; i < nmsg.length; i++) { + serverconsole.errmessage(nmsg[i]); + } + return; + } + console.log("\x1b[31mSERVER RESPONSE ERROR MESSAGE [Request Id: " + reqId + "]: " + msg + "\x1b[37m\x1b[0m"); + LOG("SERVER RESPONSE ERROR MESSAGE [Request Id: " + reqId + "]: " + msg); + return; + }, + locerrmessage: function (msg) { + if (msg.indexOf("\n") != -1) { + var nmsg = msg.split("\n"); + for (var i = 0; i < nmsg.length; i++) { + serverconsole.locerrmessage(nmsg[i]); + } + return; + } + console.log("\x1b[41mSERVER ERROR MESSAGE [Request Id: " + reqId + "]: " + msg + "\x1b[40m\x1b[0m"); + LOG("SERVER ERROR MESSAGE [Request Id: " + reqId + "]: " + msg); + return; + }, + locwarnmessage: function (msg) { + if (msg.indexOf("\n") != -1) { + var nmsg = msg.split("\n"); + for (var i = 0; i < nmsg.length; i++) { + serverconsole.locwarnmessage(nmsg[i]); + } + return; + } + console.log("\x1b[43mSERVER WARNING MESSAGE [Request Id: " + reqId + "]: " + msg + "\x1b[40m\x1b[0m"); + LOG("SERVER WARNING MESSAGE [Request Id: " + reqId + "]: " + msg); + return; + }, + locmessage: function (msg) { + if (msg.indexOf("\n") != -1) { + var nmsg = msg.split("\n"); + for (var i = 0; i < nmsg.length; i++) { + serverconsole.locmessage(nmsg[i]); + } + return; + } + console.log("SERVER MESSAGE [Request Id: " + reqId + "]: " + msg); + LOG("SERVER MESSAGE [Request Id: " + reqId + "]: " + msg); + return; + } + }; + + function getCustomHeaders() { + var ph = JSON.parse(JSON.stringify(customHeaders)); + var phk = Object.keys(ph); + for (var i = 0; i < phk.length; i++) { + if (typeof ph[phk[i]] == "string") ph[phk[i]] = ph[phk[i]].replace(/\{path\}/g, request.url); + } + return ph; + } + + //Make HTTP/1.x API-based scripts compatible with HTTP/2.0 API + if (configJSON.enableHTTP2 == true && request.httpVersion == "2.0") { + try { + //Set HTTP/1.x methods (to prevent process warnings) + response.writeHeadNodeApi = response.writeHead; + response.setHeaderNodeApi = response.setHeader; + response.writeHead = function (a, b, c) { + var table = c; + if (typeof (b) == "object") table = b; + if (table == undefined) table = this.tHeaders; + table = JSON.parse(JSON.stringify(table)); + if (table["content-type"] != undefined && table["Content-Type"] != undefined) { + delete table["content-type"]; + } + delete table["transfer-encoding"]; + delete table["connection"]; + delete table["keep-alive"]; + delete table["upgrade"]; + return response.writeHeadNodeApi(a, table); + }; + + response.setHeader = function (a, b) { + if (a != "transfer-encoding" && a != "connection" && a != "keep-alive" && a != "upgrade") return response.setHeaderNodeApi(a, b); + return false; + }; + //Set HTTP/1.x headers + if (!request.headers.host) request.headers.host = request.headers[":authority"]; + (request.headers[":path"] == undefined ? (function () {})() : request.url = request.headers[":path"]); + request.protocol = request.headers[":scheme"]; + var headers = [":path" || ":method"]; + for (var i = 0; i < headers.length; i++) { + if (request.headers[headers[i]] == undefined) { + var cheaders = getCustomHeaders(); + cheaders["Content-Type"] = "text/html; charset=utf-8"; + response.writeHead(400, "Bad Request", cheaders); + response.write("400 Bad Request

400 Bad Request

The request you sent is invalid.

" + (exposeServerVersion ? "SVR.JS/" + version + " (" + getOS() + "; " + (process.isBun ? ("Bun/v" + process.versions.bun + "; like Node.JS/" + process.version) : ("Node.JS/" + process.version)) + ")" : "SVR.JS") + (request.headers[":authority"] == undefined ? "" : " on " + request.headers[":authority"]) + "

"); + response.end(); + return; + } + } + } catch (ex) { + var cheaders = getCustomHeaders(); + cheaders["Content-Type"] = "text/html; charset=utf-8"; + cheaders[":status"] = "500"; + response.stream.respond(cheaders); + response.stream.write("500 Internal Server Error

500 Internal Server Error

The server had an unexpected error. Below, error stack is shown:

" + (stackHidden ? "[error stack hidden]" : generateErrorStack(ex)).replace(/\r\n/g, "
").replace(/\n/g, "
").replace(/\r/g, "
").replace(/ {2}/g, "  ") + "

Please contact with developer/administrator of the website.

" + (exposeServerVersion ? "SVR.JS/" + version + " (" + getOS() + "; " + (process.isBun ? ("Bun/v" + process.versions.bun + "; like Node.JS/" + process.version) : ("Node.JS/" + process.version)) + ")" : "SVR.JS") + (request.headers[":authority"] == undefined ? "" : " on " + request.headers[":authority"]) + "

"); + response.stream.end(); + return; + } + } + + if (request.headers["x-svr-js-from-main-thread"] == "true") { + response.writeHead(204, "No Content", getCustomHeaders()); + var lastStatusCode = null; + response.end(); + return; + } + + try { + request.url = encodeURI(Buffer.from(request.url, "latin1").toString("utf8")).replace(/%25/gi, "%"); + } catch (ex) { + //Request URL not modified... + } + + var headWritten = false; + response.writeHeadNative = response.writeHead; + response.writeHead = function (a, b, c) { + if (!(headWritten && process.isBun && a === lastStatusCode && b === undefined && c === undefined)) { + if (headWritten) { + process.emitWarning("res.writeHead called multiple times.", { + code: "WARN_SVRJS_MULTIPLE_WRITEHEAD" + }); + return response; + } else { + headWritten = true; + } + if (parseInt(a) >= 400 && parseInt(a) <= 599) { + serverconsole.errmessage("Server responded with " + a.toString() + " code."); + } else { + serverconsole.resmessage("Server responded with " + a.toString() + " code."); + } + lastStatusCode = a; + } + response.writeHeadNative(a, b, c); + }; + if (wwwredirect) { + var hostname = request.headers.host.split[":"]; + var hostport = null; + if (hostname.length > 1 && (hostname[0] != "[" || hostname[hostname.length - 1] != "]")) hostport = hostname.pop(); + hostname = hostname.join(":"); + } + if (wwwredirect && hostname == domain && hostname.indexOf("www.") != 0) { + var lloc = (request.socket.encrypted ? "https" : "http") + "://" + hostname + (hostport ? ":" + hostport : ""); + try { + var rheaders = getCustomHeaders(); + rheaders["Location"] = lloc + (request.url.replace(/\/+/g, "/")); + response.writeHead(301, "Redirect to WWW", rheaders); + response.end(); + } catch (ex) { + var cheaders = getCustomHeaders(); + cheaders["Content-Type"] = "text/html; charset=utf-8"; + res.writeHead(500, "Internal Server Error", cheaders); + res.write("500 Internal Server Error

500 Internal Server Error

The server had an unexpected error. Below, error stack is shown:

" + (stackHidden ? "[error stack hidden]" : generateErrorStack(ex)).replace(/\r\n/g, "
").replace(/\n/g, "
").replace(/\r/g, "
").replace(/ {2}/g, "  ") + "

Please contact with developer/administrator of the website.

" + (exposeServerVersion ? "SVR.JS/" + version + " (" + getOS() + "; " + (process.isBun ? ("Bun/v" + process.versions.bun + "; like Node.JS/" + process.version) : ("Node.JS/" + process.version)) + ")" : "SVR.JS") + (req.headers.host == undefined ? "" : " on " + String(req.headers.host).replace(/&/g, "&").replace(//g, ">")) + "

"); + response.end(); + } + } else { + var finished = false; + response.on("finish", function () { + if (!finished) { + finished = true; + serverconsole.locmessage("Client disconnected."); + } + }); + response.on("close", function () { + if (!finished) { + finished = true; + serverconsole.locmessage("Client disconnected."); + } + }); + var isProxy = false; + if (request.url.indexOf("/") != 0 && request.url != "*") isProxy = true; + serverconsole.locmessage("Somebody connected to port " + (secure && fromMain ? sport : port) + "..."); + + if (request.socket == null) { + serverconsole.errmessage("Client socket is null!!!"); + return; + } + + var reqport = ""; + var reqip = ""; + var oldport = ""; + var oldip = ""; + if (request.headers["x-svr-js-client"] != undefined && enableIPSpoofing) { + var kl = request.headers["x-svr-js-client"].split(":"); + reqport = kl.pop(); + reqip = kl.join(":"); + try { + oldport = request.socket.remotePort; + oldip = request.socket.remoteAddress; + request.socket.realRemotePort = reqport; + request.socket.realRemoteAddress = reqip; + request.socket.originalRemotePort = oldport; + request.socket.originalRemoteAddress = oldip; + response.socket.realRemotePort = reqport; + response.socket.realRemoteAddress = reqip; + response.socket.originalRemotePort = oldport; + response.socket.originalRemoteAddress = oldip; + } catch (ex) { + //Address setting failed + } + } else if (request.headers["x-forwarded-for"] != undefined && enableIPSpoofing) { + reqport = null; + reqip = request.headers["x-forwarded-for"].split(",")[0].replace(/ /g, ""); + if (reqip.indexOf(":") == -1) reqip = "::ffff:" + reqip; + try { + oldport = request.socket.remotePort; + oldip = request.socket.remoteAddress; + request.socket.realRemotePort = reqport; + request.socket.realRemoteAddress = reqip; + request.socket.originalRemotePort = oldport; + request.socket.originalRemoteAddress = oldip; + response.socket.realRemotePort = reqport; + response.socket.realRemoteAddress = reqip; + response.socket.originalRemotePort = oldport; + response.socket.originalRemoteAddress = oldip; + } catch (ex) { + //Address setting failed + } + } else { + reqip = request.socket.remoteAddress; + reqport = request.socket.remotePort; + } + + if (!isProxy) serverconsole.reqmessage("Client " + ((!reqip || reqip == "") ? "[unknown client]" : (reqip + ((reqport && reqport !== 0) && reqport != "" ? ":" + reqport : ""))) + " wants " + (request.method == "GET" ? "content in " : (request.method == "POST" ? "to post content in " : (request.method == "PUT" ? "to add content in " : (request.method == "DELETE" ? "to delete content in " : (request.method == "PATCH" ? "to patch content in " : "to access content using " + request.method + " method in "))))) + (request.headers.host == undefined ? "" : request.headers.host) + request.url); + else serverconsole.reqmessage("Client " + ((!reqip || reqip == "") ? "[unknown client]" : (reqip + ((reqport && reqport !== 0) && reqport != "" ? ":" + reqport : ""))) + " wants " + (request.method == "GET" ? "content in " : (request.method == "POST" ? "to post content in " : (request.method == "PUT" ? "to add content in " : (request.method == "DELETE" ? "to delete content in " : (request.method == "PATCH" ? "to patch content in " : "to access content using " + request.method + " method in "))))) + request.url); + if (request.headers["user-agent"] != undefined) serverconsole.reqmessage("Client uses " + request.headers["user-agent"]); + + var acceptEncoding = request.headers["accept-encoding"]; + if (!acceptEncoding) acceptEncoding = ""; + + var head = fs.existsSync("./.head") ? fs.readFileSync("./.head").toString() : (fs.existsSync("./head.html") ? fs.readFileSync("./head.html").toString() : ""); // header + var foot = fs.existsSync("./.foot") ? fs.readFileSync("./.foot").toString() : (fs.existsSync("./foot.html") ? fs.readFileSync("./foot.html").toString() : ""); // footer + + var fd = ""; + + function responseEnd(d) { + if (d === undefined) d = fd; + res.write(head + d + foot); + res.end(); + } + + // function responseEndGzip(d) { + // if (d === undefined) d = fd; + // zlib.gzip(head + d + foot, function (err, buff) { + // if (err) { + // throw err; + // } else { + // res.write(buff); + // res.end(); + // } + // }); + // } + // + // function responseEndDeflate(d) { + // if (d === undefined) d = fd; + // zlib.deflateRaw(head + d + foot, function (err, buff) { + // if (err) { + // throw err; + // } else { + // res.write(buff); + // res.end(); + // } + // }); + // } + + var req = request; // request var is req = request + var res = response; // response var is res = response + + //Error descriptions + var serverErrorDescs = { + 200: "The request succeeded! :)", + 201: "New resource has been created.", + 202: "The request has been accepted for processing, but the processing has not been completed.", + 400: "The request you made is invalid.", + 401: "You need to authenticate yourself in order to access the requested file.", + 402: "You need to pay in order to access the requested file.", + 403: "You don't have access to the requested file.", + 404: "The requested file doesn't exist. If you have typed URL manually, then please check the spelling.", + 405: "Method used to access the requested file isn't allowed.", + 406: "The request is capable of generating only not acceptable content.", + 407: "You need to authenticate yourself in order to use the proxy.", + 408: "You have timed out.", + 409: "The request you sent conflicts with the current state of the server.", + 410: "The requested file is permanently deleted.", + 411: "Content-Length property is required.", + 412: "The server doesn't meet preconditions you put in the request.", + 413: "The request you sent is too large.", + 414: "URL you sent is too long.", + 415: "The media type of request you sent isn't supported by the server.", + 416: "Content-Range you sent is unsatisfiable.", + 417: "Expectation in Expect property couldn't be satisfied.", + 418: "The server (teapot) can't brew any coffee! ;)", + 421: "The request you made isn't intended for this server.", + 422: "The server couldn't process content sent by you.", + 423: "The requested file is locked.", + 424: "The request depends on another failed request.", + 425: "The server is unwilling to risk processing a request that might be replayed.", + 426: "You need to upgrade protocols you use to request a file.", + 428: "The request you sent needs to be conditional, but it isn't.", + 429: "You sent too much requests to the server.", + 431: "The request you sent contains headers, that are too large.", + 451: "The requested file isn't accessible for legal reasons.", + 500: "The server had an unexpected error. Below, the error stack is shown:

{stack}

Please contact with developer/administrator at {contact}.", + 501: "The request requires use of a function, which isn't implemented (yet) by the server.", + 502: "The server had an error, while it was acting as a gateway.

Please contact with developer/administrator at {contact}.", + 503: "Service provided by the server isn't available (yet).

Please contact with developer/administrator at {contact}.", + 504: "The server couldn't get response in time, while it was acting as a gateway.

Please contact with developer/administrator at {contact}.", + 505: "The server doesn't support HTTP version used in the request.", + 506: "Variant header is configured to be engaged in content negotiation.

Please contact with developer/administrator at {contact}.", + 507: "The server ran out of disk space neccessary to complete the request.", + 508: "The server detected an infinite loop while processing the request.", + 509: "The server has it's bandwidth limit exceeded.

Please contact with developer/administrator at {contact}.", + 510: "The server requires an extended HTTP request. The request you made isn't an extended HTTP request.", + 511: "You need to authenticate yourself in order to get network access.", + 598: "The server couldn't get response in time, while it was acting as a proxy.", + 599: "The server couldn't connect in time, while it was acting as a proxy." + }; + + //Server error calling method + // Server error calling method + function callServerError(errorCode, extName, stack, ch) { + if (typeof errorCode !== "number") { + throw new TypeError("HTTP error code parameter needs to be an integer."); + } + + // Handle optional parameters + if (extName && typeof extName === "object") { + ch = stack; + stack = extName; + extName = undefined; + } else if (typeof extName !== "string" && extName !== null && extName !== undefined) { + throw new TypeError("Extension name parameter needs to be a string."); + } + + if (stack && typeof stack === "object" && Object.prototype.toString.call(stack) !== "[object Error]") { + ch = stack; + stack = undefined; + } else if (typeof stack !== "object" && typeof stack !== "string" && stack) { + throw new TypeError("Error stack parameter needs to be either a string or an instance of Error object."); + } + + var errorFile = errorCode.toString() + ".html"; + var errorFile2 = "." + errorCode.toString(); + if (fs.existsSync(errorFile2)) errorFile = errorFile2; + if (errorCode == 404 && fs.existsSync(page404)) errorFile = page404; + + // Generate error stack if not provided + if (Object.prototype.toString.call(stack) === "[object Error]") stack = generateErrorStack(stack); + if (stack === undefined) stack = generateErrorStack(new Error("Unknown error")); + + if (errorCode == 500 || errorCode == 502) { + serverconsole.errmessage("There was an error while processing the request!"); + serverconsole.errmessage("Stack:"); + serverconsole.errmessage(stack); + } + + // Hide the error stack if specified + if (stackHidden) stack = "[error stack hidden]"; + + // Validate the error code and handle unknown codes + if (serverErrorDescs[errorCode] === undefined) { + callServerError(501, extName, stack); + } else { + var cheaders = getCustomHeaders(); + + // Process custom headers if provided + if (ch) { + var chon = Object.keys(cheaders); + var chn = Object.keys(ch); + for (var i = 0; i < chn.length; i++) { + var nhn = chn[i]; + for (var j = 0; j < chon.length; j++) { + if (chon[j].toLowerCase() == chn[i].toLowerCase()) { + nhn = chon[j]; + break; + } + } + if (ch[chn[i]]) cheaders[nhn] = ch[chn[i]]; + } + } + + cheaders["Content-Type"] = "text/html; charset=utf-8"; + + // Set default Allow header for 405 error if not provided + if (errorCode == 405 && !cheaders["Allow"]) cheaders["Allow"] = "GET, POST, HEAD, OPTIONS"; + + // Read the error file and replace placeholders with error information + fs.readFile(errorFile, function (err, data) { + try { + if (err) throw err; + response.writeHead(errorCode, http.STATUS_CODES[errorCode], cheaders); + fd += data.toString().replace(/{errorMessage}/g, errorCode.toString() + " " + http.STATUS_CODES[errorCode]).replace(/{errorDesc}/g, serverErrorDescs[errorCode]).replace(/{stack}/g, stack.replace(/&/g, "&").replace(//g, ">").replace(/\r\n/g, "
").replace(/\n/g, "
").replace(/\r/g, "
").replace(/ {2}/g, "  ")).replace(/{path}/g, request.url.replace(/&/g, "&").replace(//g, ">")).replace(/{server}/g, "" + (exposeServerVersion ? "SVR.JS/" + version + " (" + getOS() + "; " + (process.isBun ? ("Bun/v" + process.versions.bun + "; like Node.JS/" + process.version) : ("Node.JS/" + process.version)) + ")" : "SVR.JS") + (extName == undefined ? "" : " " + extName) + ((req.headers.host == undefined || isProxy) ? "" : " on " + String(req.headers.host).replace(/&/g, "&").replace(//g, ">"))).replace(/{contact}/g, serverAdmin.replace(/\./g, "[dot]").replace(/@/g, "[at]")); // Replace placeholders in error response + responseEnd(); + } catch (ex) { + var additionalError = 500; + // Handle additional error cases + if (ex.code == "ENOENT") { + additionalError = 404; + } else if (ex.code == "EACCES") { + additionalError = 403; + } else if (ex.code == "EMFILE") { + additionalError = 429; + } else if (ex.code == "ELOOP") { + additionalError = 508; + } + + response.writeHead(errorCode, http.STATUS_CODES[errorCode], cheaders); + response.write(("{errorMessage}

{errorMessage}

{errorDesc}

" + ((additionalError == 404) ? "" : "

Additionally, a {additionalError} error occurred while loading an error page.

") + "

{server}

").replace(/{errorMessage}/g, errorCode.toString() + " " + http.STATUS_CODES[errorCode]).replace(/{errorDesc}/g, serverErrorDescs[errorCode]).replace(/{stack}/g, stack.replace(/&/g, "&").replace(//g, ">").replace(/\r\n/g, "
").replace(/\n/g, "
").replace(/\r/g, "
").replace(/ {2}/g, "  ")).replace(/{path}/g, request.url.replace(/&/g, "&").replace(//g, ">")).replace(/{server}/g, "" + (exposeServerVersion ? "SVR.JS/" + version + " (" + getOS() + "; " + (process.isBun ? ("Bun/v" + process.versions.bun + "; like Node.JS/" + process.version) : ("Node.JS/" + process.version)) + ")" : "SVR.JS") + (extName == undefined ? "" : " " + extName) + ((req.headers.host == undefined || isProxy) ? "" : " on " + String(req.headers.host).replace(/&/g, "&").replace(//g, ">"))).replace(/{contact}/g, serverAdmin.replace(/\./g, "[dot]").replace(/@/g, "[at]")).replace(/{additionalError}/g, additionalError.toString())); // Replace placeholders in error response + response.end(); + } + }); + } + } + + + function redirect(dest, isTemporary, headers) { + if (headers === undefined) headers = getCustomHeaders(); + headers["Location"] = dest; + var scode = isTemporary ? 302 : 301; + res.writeHead(scode, http.STATUS_CODES[scode], headers); + serverconsole.resmessage("Client redirected to " + dest); + res.end(); + return; + } + + function parsePostData(options, callback) { + if (req.method != "POST") { + var h = getCustomHeaders(); + h["Allow"] = "POST"; + callServerError(405, undefined, undefined, h); + return; + } + var formidableOptions = options; + if (!callback) { + callback = options; + formidableOptions = {}; + } + if (formidable._errored) callServerError(500, undefined, generateErrorStack(formidable._errored)); + var form = formidable(formidableOptions); + form.parse(req, function (err, fields, files) { + if (err) { + if(err.httpCode) callServerError(err.httpCode); + else callServerError(400); + return; + } + callback(fields, files); + }); + } + + function urlParse(uri) { + if (typeof URL != "undefined" && url.Url) { + try { + var uobject = new URL(uri, "http" + (req.socket.encrypted ? "s" : "") + "://" + (req.headers.host ? req.headers.host : (domain ? domain : "unknown.invalid"))); + var nuobject = new url.Url(); + if (uri.indexOf("/") != -1) nuobject.slashes = true; + if (uobject.protocol != "") nuobject.protocol = uobject.protocol; + if (uobject.username != "" && uobject.password != "") nuobject.auth = uobject.username + ":" + uobject.password; + if (uobject.host != "") nuobject.host = uobject.host; + if (uobject.hostname != "") nuobject.hostname = uobject.hostname; + if (uobject.port != "") nuobject.port = uobject.port; + if (uobject.pathname != "") nuobject.pathname = uobject.pathname; + if (uobject.search != "") nuobject.search = uobject.search; + if (uobject.hash != "") nuobject.hash = uobject.hash; + if (uobject.href != "") nuobject.href = uobject.href; + if (uri.indexOf("/") != 0) { + if (nuobject.pathname) { + nuobject.pathname = nuobject.pathname.substr(1); + nuobject.href = nuobject.pathname + (nuobject.search ? nuobject.search : ""); + } + } + if (nuobject.pathname) { + nuobject.path = nuobject.pathname + (nuobject.search ? nuobject.search : ""); + } + nuobject.query = {}; + uobject.searchParams.forEach(function (value, key) { + nuobject.query[key] = value; + }); + return nuobject; + } catch (ex) { + return url.parse(uri, true); + } + } else { + return url.parse(uri, true); + } + } + + var uobject = urlParse(req.url); + var search = uobject.search; + var href = uobject.pathname; + var ext = path.extname(href).toLowerCase(); + ext = ext.substr(1, ext.length); + var decodedHref = ""; + try { + decodedHref = decodeURIComponent(href); + } catch (ex) { + //Return 400 error + callServerError(400); + serverconsole.errmessage("Bad request!"); + return; + } + + if (req.headers["expect"] && req.headers["expect"] != "100-continue") { + callServerError(417); + return; + } + + //MOD EXCECUTION FUNCTION + function modExecute(mods, ffinals) { + var modFunction = ffinals; + for (var i = mods.length - 1; i >= 0; i--) { + modFunction = mods[i].callback(req, res, serverconsole, responseEnd, href, ext, uobject, search, "index.html", users, page404, head, foot, fd, modFunction, configJSON, callServerError, getCustomHeaders, origHref, redirect, parsePostData); + } + modFunction(); + } + + var vresCalled = false; + + function vres(req, res, serverconsole, responseEnd, href, ext, uobject, search, defaultpage, users, page404, head, foot, fd, callServerError, getCustomHeaders, origHref, redirect, parsePostData) { + return function () { + if (vresCalled) { + process.emitWarning("elseCallback() invoked multiple times.", { + code: "WARN_SVRJS_MULTIPLE_ELSECALLBACK" + }); + return; + } else { + vresCalled = true; + } + // function responseEndGzip(d) { + // if (d === undefined) d = fd; + // zlib.gzip(head + d + foot, function (err, buff) { + // if (err) { + // throw err; + // } else { + // res.write(buff); + // res.end(); + // } + // }); + // } + // + // function responseEndDeflate(d) { + // if (d === undefined) d = fd; + // zlib.deflateRaw(head + d + foot, function (err, buff) { + // if (err) { + // throw err; + // } else { + // res.write(buff); + // res.end(); + // } + // }); + // } + + function responseEnd(d) { + if (d === undefined) d = fd; + res.write(head + d + foot); + res.end(); + } + + if (req.socket == null) { + serverconsole.errmessage("Client socket is null!!!"); + return; + } + + var reqport = ""; + var reqip = ""; + var oldport = ""; + var oldip = ""; + if (req.headers["x-svr-js-client"] != undefined && enableIPSpoofing) { + var kl = req.headers["x-svr-js-client"].split(":"); + reqport = kl.pop(); + reqip = kl.join(":"); + try { + oldport = req.socket.remotePort; + oldip = req.socket.remoteAddress; + req.socket.realRemotePort = reqport; + req.socket.realRemoteAddress = reqip; + req.socket.originalRemotePort = oldport; + req.socket.originalRemoteAddress = oldip; + res.socket.realRemotePort = reqport; + res.socket.realRemoteAddress = reqip; + res.socket.originalRemotePort = oldport; + res.socket.originalRemoteAddress = oldip; + } catch (ex) { + //Nevermind... + } + } else if (req.headers["x-forwarded-for"] != undefined && enableIPSpoofing) { + reqport = null; + reqip = req.headers["x-forwarded-for"].split(",")[0].replace(/ /g, ""); + if (reqip.indexOf(":") == -1) reqip = "::ffff:" + reqip; + try { + oldport = req.socket.remotePort; + oldip = req.socket.remoteAddress; + req.socket.realRemotePort = reqport; + req.socket.realRemoteAddress = reqip; + req.socket.originalRemotePort = oldport; + req.socket.originalRemoteAddress = oldip; + res.socket.realRemotePort = reqport; + res.socket.realRemoteAddress = reqip; + res.socket.originalRemotePort = oldport; + res.socket.originalRemoteAddress = oldip; + } catch (ex) { + //Nevermind... + } + } else { + reqip = req.socket.remoteAddress; + reqport = req.socket.remotePort; + } + + function checkLevel(e) { + for (var n = e.split("/"), r = 0, t = 0; t < n.length; t += 1) ".." == n[t] ? r -= 1 : "." !== n[t] && "" !== n[t] && (r += 1); + return r; + } + + if (isProxy) { + var eheaders = getCustomHeaders(); + eheaders["Content-Type"] = "text/html; charset=utf-8"; + res.writeHead(501, "Not implemented", eheaders); + res.write("Proxy not implemented

Proxy not implemented

SVR.JS doesn't support proxy without proxy mod. If you're administator of this server, then install this mod in order to use SVR.JS as a proxy.

" + (exposeServerVersion ? "SVR.JS/" + version + " (" + getOS() + "; " + (process.isBun ? ("Bun/v" + process.versions.bun + "; like Node.JS/" + process.version) : ("Node.JS/" + process.version)) + ")" : "SVR.JS") + "

"); + res.end(); + serverconsole.errmessage("SVR.JS doesn't support proxy without proxy mod."); + return; + } + + if (req.method == "OPTIONS") { + var hdss = getCustomHeaders(); + hdss["Allow"] = "GET, POST, HEAD, OPTIONS"; + res.writeHead(204, "No Content", hdss); + res.end(); + return; + } else if (req.method != "GET" && req.method != "POST" && req.method != "HEAD") { + callServerError(405); + serverconsole.errmessage("Invaild method: " + req.method); + return; + } + + if (href == "/invoke500.svr" || (os.platform() == "win32" && href.toLowerCase() == "/invoke500.svr")) { + if (version.indexOf("Nightly-") === 0 && uobject.query.crash !== undefined) throw new Error("Intentionally crashed"); + try { + if (uobject.query.aprilfools === undefined) throw new Error("This page is intended to return 500 code."); + var hdhds = getCustomHeaders(); + hdhds["Content-Type"] = "text/html; charset=utf-8"; + if (uobject.query.activate === undefined) { + res.writeHead(599, "You may be a victim of software counterfeiting.", hdhds); + res.end("Directory traversal prevention is not working.
  X  
Directory traversal prevention is not working.
You may be a victim of software
counterfeiting.


To use all DorianTech SVR.JS features, such as all
directory traversal protections; use server-side JS;
and recieve product support, your copy of DorianTech
SVR.JS must be validated as genuine.

Go online and resolve now

This copy of SVR.JS is not genuine."); + serverconsole.resmessage("You may be a victim of software counterfeiting."); + } else { + res.writeHead(200, "OK", hdhds); + res.end("\n\n\n\nSVR.JS Genuine Advantage\n\n\n

Activate SVR.JS

\nYou will then be able to use all of SVR.JS features through SVR.JS Genuine Advantage!\n\n

Wait...

\n\n\n\nThis copy of SVR.JS is not genuine."); + } + return; + } catch (ex) { + callServerError(500, undefined, generateErrorStack(ex)); + return; + } + } else if (allowStatus && (href == "/svrjsstatus.svr" || (os.platform() == "win32" && href.toLowerCase() == "/svrjsstatus.svr"))) { + function formatRelativeTime(relativeTime) { + var days = Math.floor(relativeTime / 60 / (60 * 24)); + var dateDiff = new Date(relativeTime * 1000); + return days + " days, " + dateDiff.getUTCHours() + " hours, " + dateDiff.getUTCMinutes() + " minutes, " + dateDiff.getUTCSeconds() + " seconds"; + } + var hdhds = getCustomHeaders(); + hdhds["Content-Type"] = "text/html; charset=utf-8"; + res.writeHead(200, "OK", hdhds); + res.end((head == "" ? "SVR.JS status" + (request.headers.host == undefined ? "" : " for " + String(req.headers.host).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">")) + "" : head.replace(//i, "SVR.JS status" + (request.headers.host == undefined ? "" : " for " + String(req.headers.host).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">")) + "")) + "

SVR.JS status" + (request.headers.host == undefined ? "" : " for " + String(req.headers.host).replace(/&/g, "&").replace(//g, ">")) + "

Server version: " + (exposeServerVersion ? "SVR.JS/" + version + " (" + getOS() + "; " + (process.isBun ? ("Bun/v" + process.versions.bun + "; like Node.JS/" + process.version) : ("Node.JS/" + process.version)) + ")" : "SVR.JS") + "

Current time: " + new Date().toString() + "
Thread start time: " + new Date(new Date() - (process.uptime() * 1000)).toString() + "
Thread uptime: " + formatRelativeTime(Math.floor(process.uptime())) + "
OS uptime: " + formatRelativeTime(os.uptime()) + "
Total request count: " + reqcounter + "
Average request rate: " + (Math.round((reqcounter / process.uptime()) * 100) / 100) + " requests/s" + (process.memoryUsage ? ("
Memory usage of thread: " + sizify(process.memoryUsage().rss) + "B") : "") + (process.cpuUsage ? ("
Total CPU usage by thread: u" + (process.cpuUsage().user / 1000) + "ms s" + (process.cpuUsage().system / 1000) + "ms - " + (Math.round((((process.cpuUsage().user + process.cpuUsage().system) / 1000000) / process.uptime()) * 1000) / 1000) + "%") : "") + "
Thread PID: " + process.pid + "
" + (foot == "" ? "" : foot)); + return; + } else if (href == "/zsoiebook.svr" || (os.platform() == "win32" && href.toLowerCase() == "/zsoiebook.svr")) { + var hdhds = getCustomHeaders(); + hdhds["Content-Type"] = "text/html; charset=utf-8"; + res.writeHead(200, "OK", hdhds); + if (typeof uobject.query.summary !== "undefined") { + res.end("\n\n \n The Summary of Book of ZSOiE\n \n \n \n \n \n
\n

And Satan created Mammon. His work won people from all over the school. When people abandoned them through the Piracy Window, so Satan went back in time and created the Server to continue to wreak havoc all over the school.

\n

from The Summary of Book of ZSOiE

\n
\n \n"); + return; + } + var randomValue = Math.random(); + if (randomValue > 0.85714) { + res.end("\n\n\n\n \n The Book of ZSOiE, 7:28\n \n \n \n \n \n
\n

The Server continues to cultivate it's Dafa. The Author found the Robot and asked him for help. Then the Robot started to help the Author to improve his Server. And the Author tries to create yet another server without using the main node...

\n

from The Book of ZSOiE, 7:28

\n
\n \n"); + } else if (randomValue > 0.71429) { + res.end("\n\n\n\n \n The Book of ZSOiE, 7:16\n \n \n \n \n \n
\n

The Server is still going. But the Author commanded to the Server: \"thou shalt you split to two branches.\". And the Server did split it's Dafa. One of two branches stopped serving on old and rusty node. Other one is still serving on that, but it will later vanish... \"Mammon will get confused\" - said the Author.

\n

from The Book of ZSOiE, 7:16

\n
\n \n"); + } else if (randomValue > 0.57143) { + res.end("\n\n\n\n \n The Book of ZSOiE, 7:2\n \n \n \n \n \n
\n

The old Server forces died. The all-powerful new Server rosen from ashes of old Server like phoenix followed the ways of Durability-Ease-Reliability and cultivated his Dafa. Then, the Author and Whyvn appeared on best former Mammon's disciples paper.

\n

from The Book of ZSOiE, 7:2

\n
\n \n"); + } else if (randomValue > 0.42857) { + res.end("\n\n\n\n \n The Book of ZSOiE, 6:24\n \n \n \n \n \n
\n

Mammon had enough karmic retribution. The Author tried it's Server on main node's substitute. Then, the Author saw, that Server's Dafa is good, and that main node, it's substitute, and older Mammon are good. Meanwhile the Author, Whyvn, and Snovbyn rejoiced even more from their success over older Mammon.

\n

from The Book of ZSOiE, 6:24

\n
\n \n"); + } else if (randomValue > 0.32143) { + res.end("\n\n\n\n \n The Book of ZSOiE, 6:6\n \n \n \n \n \n
\n

And the Server is about to come. The Author along with Whyvn and Snovbyn passing the Mammon's test rejoiced their success over older Mammon.

\n

from The Book of ZSOiE, 6:6

\n
\n \n"); + } else if (randomValue > 0.14286) { + res.end("\n\n\n\n \n The Book of ZSOiE, 5:25\n \n \n \n \n \n
\n

The twins of Mammon quarrelled. The Author with it's Server and it's main node plunged the Mammon's servers into darkness. Meanwhile Whyvn and Snovbyn helped him to break Mammon's servers.

\n

from The Book of ZSOiE, 5:25

\n
\n \n"); + } else { + res.end("\n\n\n\n \n The Book of ZSOiE, 3:16\n \n \n \n \n \n
\n

Mammon slept. Meanwhile, the Author, Whyvn and Snovbyn being in very skill-requiring challenge casted tcpdump and mongodb on him.

\n

from The Book of ZSOiE, 3:16

\n
\n \n"); + } + return; + } else if (version.indexOf("Nightly-") === 0 && (href == "/crash.svr" || (os.platform() == "win32" && href.toLowerCase() == "/crash.svr"))) { + throw new Error("Intentionally crashed"); + } + + var pth = decodeURIComponent(href).replace(/\/+/g, "/").substr(1); + fs.stat("./" + pth, function (err, stats) { + if (err) { + if (err.code == "ENOENT") { + callServerError(404); + serverconsole.errmessage("Resource not found."); + } else if (err.code == "EACCES") { + callServerError(403); + serverconsole.errmessage("Access denied."); + } else if (err.code == "EMFILE") { + callServerError(429); + } else if (err.code == "ELOOP") { + callServerError(508); + } else { + callServerError(500, undefined, generateErrorStack(err)); + } + return; + } + //Check if index file exists + if (req.url == "/" || stats.isDirectory()) { + fs.stat("./" + pth + "/.notindex".replace(/\/+/g, "/"), function (e) { + if (e) { + fs.stat(("./" + pth + "/index.html").replace(/\/+/g, "/"), function (e, s) { + if (e || !s.isFile()) { + fs.stat(("./" + pth + "/index.htm").replace(/\/+/g, "/"), function (e, s) { + if (e || !s.isFile()) { + fs.stat(("./" + pth + "/index.xhtml").replace(/\/+/g, "/"), function (e, s) { + if (e || !s.isFile()) { + properServe() + } else { + stats = s; + pth = (pth + "/index.xhtml").replace(/\/+/g, "/"); + ext = "xhtml"; + properServe(); + } + }); + } else { + stats = s; + pth = (pth + "/index.htm").replace(/\/+/g, "/"); + ext = "htm"; + properServe(); + } + }); + } else { + stats = s; + pth = (pth + "/index.html").replace(/\/+/g, "/"); + ext = "html"; + properServe(); + } + }); + } + }); + } else { + properServe(); + } + + function properServe() { + if (stats.isDirectory()) { + if (configJSON.enableDirectoryListing || configJSON.enableDirectoryListing === undefined) { + var dheaders = getCustomHeaders(); + dheaders["Content-Type"] = "text/html; charset=utf-8"; + res.writeHead(200, http.STATUS_CODES[200], dheaders); + var heada = fs.existsSync(("." + decodeURIComponent(href) + "/.dirhead").replace(/\/+/g, "/")) ? fs.readFileSync(("." + decodeURIComponent(href) + "/.dirhead").replace(/\/+/g, "/")).toString() : ((fs.existsSync(("." + decodeURIComponent(href) + "/HEAD.html").replace(/\/+/g, "/")) && (os.platform != "win32" || href != "/")) ? fs.readFileSync(("." + decodeURIComponent(href) + "/HEAD.html").replace(/\/+/g, "/")).toString() : ""); // header + var foota = fs.existsSync(("." + decodeURIComponent(href) + "/.dirfoot").replace(/\/+/g, "/")) ? fs.readFileSync(("." + decodeURIComponent(href) + "/.dirfoot").replace(/\/+/g, "/")).toString() : ((fs.existsSync(("." + decodeURIComponent(href) + "/FOOT.html").replace(/\/+/g, "/")) && (os.platform != "win32" || href != "/")) ? fs.readFileSync(("." + decodeURIComponent(href) + "/FOOT.html").replace(/\/+/g, "/")).toString() : ""); // footer + var htmlHead = (configJSON.enableDirectoryListingWithDefaultHead == undefined || configJSON.enableDirectoryListingWithDefaultHead == false || (fs.existsSync("./head.html") == false && fs.existsSync("./.head") == false) || head == "" || head == " " || head == "\r\n" || head == "\n" ? (heada.indexOf("") == -1 ? "Directory: " + decodeURIComponent(origHref).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">") + "" : heada.replace("", "Directory: " + decodeURIComponent(origHref).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">") + "")) : head.replace("", "Directory: " + decodeURIComponent(origHref).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">") + "")) + (heada.indexOf("") == -1 ? heada : "") + "

Directory: " + decodeURIComponent(origHref).replace(/&/g, "&").replace(//g, ">") + "

" + (checkLevel(decodeURIComponent(origHref)) < 1 ? "" : ""); + var htmlFoot = "
Filename Size Date
\"[RET]\"Return

" + (exposeServerVersion ? "SVR.JS/" + version + " (" + getOS() + "; " + (process.isBun ? ("Bun/v" + process.versions.bun + "; like Node.JS/" + process.version) : ("Node.JS/" + process.version)) + ")" : "SVR.JS") + (req.headers.host == undefined ? "" : " on " + String(req.headers.host).replace(/&/g, "&").replace(//g, ">")) + "

" + foota + (configJSON.enableDirectoryListingWithDefaultHead == undefined || configJSON.enableDirectoryListingWithDefaultHead == false || (fs.existsSync("./foot.html") == false && fs.existsSync("./.foot") == false) || foot == "" || foot == " " || foot == "\r\n" || foot == "\n" ? "" : foot); + if (fs.existsSync(("." + req.url.split("?")[0] + "/.maindesc").replace(/\/+/g, "/"))) { + htmlFoot = "
" + fs.readFileSync(("." + req.url.split("?")[0] + "/.maindesc").replace(/\/+/g, "/")) + htmlFoot; + } + fs.readdir("." + decodeURIComponent(href), function (err, list) { + try { + if (err) throw err; + list = list.sort(); + + function getStatsForAllFilesI(fileList, callback, prefix, pushArray, index) { + if (fileList.length == 0) { + callback(pushArray); + return; + } + fs.stat((prefix + "/" + fileList[index]).replace(/\/+/g, "/"), function (err, stats) { + if (err) { + fs.lstat((prefix + "/" + fileList[index]).replace(/\/+/g, "/"), function (err, stats) { + if (err) { + pushArray.push({ + name: fileList[index], + stats: null, + errored: true + }); + if (index < fileList.length - 1) { + getStatsForAllFilesI(fileList, callback, prefix, pushArray, index + 1); + } else { + callback(pushArray); + } + } else { + pushArray.push({ + name: fileList[index], + stats: stats, + errored: true + }); + if (index < fileList.length - 1) { + getStatsForAllFilesI(fileList, callback, prefix, pushArray, index + 1); + } else { + callback(pushArray); + } + } + }); + } else { + pushArray.push({ + name: fileList[index], + stats: stats, + errored: false + }); + if (index < fileList.length - 1) { + getStatsForAllFilesI(fileList, callback, prefix, pushArray, index + 1); + } else { + callback(pushArray); + } + } + }); + } + + function getStatsForAllFiles(fileList, prefix, callback) { + if (!prefix) prefix = ""; + getStatsForAllFilesI(fileList, callback, prefix, [], 0); + } + + getStatsForAllFiles(list, "." + decodeURIComponent(href), function (filelist) { + function checkEXT(filename, ext) { + if (filename.length < ext.length) return false; + return (filename.toLowerCase().indexOf(ext) == (filename.length - ext.length)); + } + var statsa = []; + for (var i = 0; i < filelist.length; i++) { + if (filelist[i].name[0] !== ".") { + var estats = filelist[i].stats; + var ename = filelist[i].name; + if (filelist[i].errored) { + if (estats) { + statsa.push("[BAD]" + ename.replace(/&/g, "&").replace(//g, ">") + "-" + estats.mtime.toDateString() + "\r\n"); + } else { + statsa.push("[BAD]" + ename.replace(/&/g, "&").replace(//g, ">") + "--\r\n"); + } + + } else { + var entry = "\"[alt]\"" + ename.replace(/&/g, "&").replace(//g, ">") + "" + (estats.isDirectory() ? "-" : sizify(estats.size.toString())) + "" + estats.mtime.toDateString() + "\r\n"; + if (estats.isDirectory()) { + entry = entry.replace("[img]", "/.dirimages/directory.png").replace("[alt]", "[DIR]"); + } else if (!estats.isFile()) { + entry = "\"[alt]\"" + ename.replace(/&/g, "&").replace(//g, ">") + "-" + estats.mtime.toDateString() + "\r\n"; + if (estats.isBlockDevice()) { + entry = entry.replace("[img]", "/.dirimages/hwdevice.png").replace("[alt]", "[BLK]"); + } else if (estats.isCharacterDevice()) { + entry = entry.replace("[img]", "/.dirimages/hwdevice.png").replace("[alt]", "[CHR]"); + } else if (estats.isFIFO()) { + entry = entry.replace("[img]", "/.dirimages/fifo.png").replace("[alt]", "[FIF]"); + } else if (estats.isSocket()) { + entry = entry.replace("[img]", "/.dirimages/socket.png").replace("[alt]", "[SCK]"); + } + } else if ((/README/ig).test(ename) || (/LICEN[SC]E/ig).test(ename)) { + entry = entry.replace("[img]", "/.dirimages/important.png").replace("[alt]", "[IMP]"); + } else if (checkEXT(ename, ".html") || checkEXT(ename, ".htm") || checkEXT(ename, ".xml") || checkEXT(ename, ".xhtml") || checkEXT(ename, ".shtml")) { + entry = entry.replace("[img]", "/.dirimages/html.png").replace("[alt]", (checkEXT(ename, ".xml") ? "[XML]" : "[HTM]")); + } else if (checkEXT(ename, ".js")) { + entry = entry.replace("[img]", "/.dirimages/javascript.png").replace("[alt]", "[JS ]"); + } else if (checkEXT(ename, ".php")) { + entry = entry.replace("[img]", "/.dirimages/php.png").replace("[alt]", "[PHP]"); + } else if (checkEXT(ename, ".css")) { + entry = entry.replace("[img]", "/.dirimages/css.png").replace("[alt]", "[CSS]"); + } else if (checkEXT(ename, ".png") || checkEXT(ename, ".jpg") || checkEXT(ename, ".gif") || checkEXT(ename, ".bmp") || checkEXT(ename, ".webm") || checkEXT(ename, ".jpeg") || checkEXT(ename, ".svg") || checkEXT(ename, ".jfif") || checkEXT(ename, ".webp")) { + entry = entry.replace("[img]", "/.dirimages/image.png").replace("[alt]", "[IMG]"); + } else if (checkEXT(ename, ".ico") || checkEXT(ename, ".icn")) { + entry = entry.replace("[img]", "/.dirimages/image.png").replace("[alt]", "[ICO]"); + } else if (checkEXT(ename, ".ttf") || checkEXT(ename, ".otf") || checkEXT(ename, ".fon")) { + entry = entry.replace("[img]", "/.dirimages/font.png").replace("[alt]", "[FON]"); + } else if (checkEXT(ename, ".mp3") || checkEXT(ename, ".ogg") || checkEXT(ename, ".aac") || checkEXT(ename, ".wav") || checkEXT(ename, ".mid") || checkEXT(ename, ".midi") || checkEXT(ename, ".mka")) { + entry = entry.replace("[img]", "/.dirimages/audio.png").replace("[alt]", "[AUD]"); + } else if (checkEXT(ename, ".txt") || checkEXT(ename, ".log") || checkEXT(ename, ".json")) { + entry = entry.replace("[img]", "/.dirimages/text.png").replace("[alt]", (checkEXT(ename, ".json") ? "[JSO]" : "[TXT]")); + } else if (checkEXT(ename, ".mp5") || checkEXT(ename, ".avi") || checkEXT(ename, ".mkv") || checkEXT(ename, ".mov") || checkEXT(ename, ".mp2") || checkEXT(ename, ".mp4") || checkEXT(ename, ".ogv")) { + entry = entry.replace("[img]", "/.dirimages/video.png").replace("[alt]", "[VID]"); + } else if (checkEXT(ename, ".zip") || checkEXT(ename, ".rar") || checkEXT(ename, ".bz2") || checkEXT(ename, ".gz") || checkEXT(ename, ".bz") || checkEXT(ename, ".7z") || checkEXT(ename, ".xz") || checkEXT(ename, ".lzma") || checkEXT(ename, ".tar")) { + entry = entry.replace("[img]", "/.dirimages/archive.png").replace("[alt]", "[ARC]"); + } else if (checkEXT(ename, ".img") || checkEXT(ename, ".dmg") || checkEXT(ename, ".iso") || checkEXT(ename, ".flp")) { + entry = entry.replace("[img]", "/.dirimages/diskimage.png").replace("[alt]", "[DSK]"); + } else { + entry = entry.replace("[img]", "/.dirimages/other.png").replace("[alt]", "[OTH]"); + } + statsa.push(entry); + } + } + } + if (statsa.length == 0) { + statsa.push("No files found"); + } + res.end(htmlHead + statsa.join("") + htmlFoot); + serverconsole.resmessage("Client successfully recieved content."); + }); + } catch (ex) { + if (ex.code == "ENOENT") { + callServerError(404); + serverconsole.errmessage("Resource not found."); + } else if (ex.code == "EACCES") { + callServerError(403); + serverconsole.errmessage("Access denied."); + } else if (ex.code == "EMFILE") { + callServerError(429); + } else if (ex.code == "ELOOP") { + callServerError(508); + } else { + callServerError(500, undefined, generateErrorStack(ex)); + } + } + }); + } else { + callServerError(403); + serverconsole.errmessage("Directory listing disabled."); + return; + } + } else { + var acceptEncoding = req.headers["accept-encoding"]; + if (!acceptEncoding) acceptEncoding = ""; + + // Check if the requested file exists and handle errors + fs.stat("./" + pth, function (err, stats) { + if (err) { + if (err.code == "ENOENT") { + callServerError(404); + serverconsole.errmessage("Resource not found."); + } else if (err.code == "EACCES") { + callServerError(403); + serverconsole.errmessage("Access denied."); + } else if (err.code == "EMFILE") { + callServerError(429); + } else if (err.code == "ELOOP") { + callServerError(508); + } else { + callServerError(500, undefined, generateErrorStack(err)); + } + return; + } + + // Check if the requested resource is a directory + if (stats.isDirectory()) { + callServerError(501); + serverconsole.errmessage("SVR.JS expected file but got directory instead."); + return; + } else if (!stats.isFile()) { + callServerError(501); + serverconsole.errmessage("SVR.JS doesn't support block devices, character devices, FIFOs nor sockets."); + return; + } + + var filelen = stats.size; + + // Helper function to check if compression is allowed for the file + function canCompress(path, list) { + var canCompress = true; + for (var i = 0; i < list.length; i++) { + if (createRegex(list[i]).test(path)) { + canCompress = false; + break; + } + } + return canCompress; + } + + var isCompressable = canCompress(href, dontCompress); + + // Check for browser quirks and adjust compression accordingly + if (ext != "html" && ext != "htm" && ext != "xhtml" && ext != "xht" && ext != "shtml" && /^Mozilla\/4\.[0-9]+(( *\[[^)]*\] *| *)\([^)\]]*\))? *$/.test(req.headers["user-agent"]) && !(/https?:\/\/|[bB][oO][tT]|[sS][pP][iI][dD][eE][rR]|[sS][uU][rR][vV][eE][yY]|MSI[E]/.test(req.headers["user-agent"]))) { + isCompressable = false; //Netscape 4.x doesn't handle compressed data properly outside of HTML documents. + } else if (/^Mozilla\/4\.0[6-8](( *\[[^)]*\] *| *)\([^)\]]*\))? *$/.test(req.headers["user-agent"]) && !(/https?:\/\/|[bB][oO][tT]|[sS][pP][iI][dD][eE][rR]|[sS][uU][rR][vV][eE][yY]|MSI[E]/.test(req.headers["user-agent"]))) { + isCompressable = false; //Netscape 4.06-4.08 doesn't handle compressed data properly. + } else if (ext != "html" && ext != "htm" && ext != "xhtml" && ext != "xht" && ext != "shtml" && /^w3m\/[^ ]*$/.test(req.headers["user-agent"])) { + isCompressable = false; //w3m doesn't handle compressed data properly outside of HTML documents. + } + + // Handle partial content request + if (ext != "html" && req.headers["range"]) { + try { + if (err) throw err; + var rhd = getCustomHeaders(); + rhd["Accept-Ranges"] = "bytes"; + rhd["Content-Range"] = "bytes */" + filelen; + var regexmatch = req.headers["range"].match(/bytes=([0-9]*)-([0-9]*)/); + if (!regexmatch) { + callServerError(416, undefined, undefined, rhd); + } else { + // Process the partial content request + var beginOrig = regexmatch[1]; + var endOrig = regexmatch[2]; + var begin = 0; + var end = filelen - 1; + if (beginOrig == "" && endOrig == "") { + callServerError(416, undefined, undefined, rhd); + return; + } else if (beginOrig == "") { + begin = end - parseInt(endOrig) + 1; + } else { + begin = parseInt(beginOrig); + if (endOrig != "") end = parseInt(endOrig); + } + if (begin > end || begin < 0 || begin > filelen - 1) { + callServerError(416, undefined, undefined, rhd); + return; + } + if (end > filelen - 1) end = filelen - 1; + rhd["Content-Range"] = "bytes " + begin + "-" + end + "/" + filelen; + rhd["Content-Length"] = end - begin + 1; + if (!(mime.contentType(ext) == false) && ext != "") rhd["Content-Type"] = mime.contentType(ext); + + if (req.method != "HEAD") { + var readStream = fs.createReadStream("./" + pth, { + start: begin, + end: end + }); + readStream.on("error", function (err) { + if (err.code == "ENOENT") { + callServerError(404); + serverconsole.errmessage("Resource not found."); + } else if (err.code == "EACCES") { + callServerError(403); + serverconsole.errmessage("Access denied."); + } else if (err.code == "EMFILE") { + callServerError(429); + } else if (err.code == "ELOOP") { + callServerError(508); + } else { + callServerError(500, undefined, generateErrorStack(err)); + } + }).on("open", function () { + try { + res.writeHead(206, http.STATUS_CODES[206], rhd); + readStream.pipe(res); + serverconsole.resmessage("Client successfully received content."); + } catch (ex) { + callServerError(500, undefined, generateErrorStack(ex)); + } + }); + } else { + res.writeHead(206, http.STATUS_CODES[206], rhd); + res.end(); + } + } + } catch (ex) { + callServerError(500, undefined, generateErrorStack(ex)); + } + } else { + try { + if (err) throw err; + var hdhds = getCustomHeaders(); + if (configJSON.enableCompression === true && ext != "br" && filelen > 256 && isCompressable && zlib.createBrotliCompress && acceptEncoding.match(/\bbr\b/)) { + hdhds["Content-Encoding"] = "br"; + } else if (configJSON.enableCompression === true && ext != "zip" && filelen > 256 && isCompressable && acceptEncoding.match(/\bdeflate\b/)) { + hdhds["Content-Encoding"] = "deflate"; + } else if (configJSON.enableCompression === true && ext != "gz" && filelen > 256 && isCompressable && acceptEncoding.match(/\bgzip\b/)) { + hdhds["Content-Encoding"] = "gzip"; + } else { + if (ext == "html") { + hdhds["Content-Length"] = head.length + filelen + foot.length; + } else { + hdhds["Content-Length"] = filelen; + } + } + if (ext != "html") hdhds["Accept-Ranges"] = "bytes"; + delete hdhds["Content-Type"]; + if (!(mime.contentType(ext) == false) && ext != "") hdhds["Content-Type"] = mime.contentType(ext); + + if (req.method != "HEAD") { + var readStream = fs.createReadStream("./" + pth); + readStream.on("error", function (ex) { + if (ex.code == "ENOENT") { + callServerError(404); + serverconsole.errmessage("Resource not found."); + } else if (ex.code == "EACCES") { + callServerError(403); + serverconsole.errmessage("Access denied."); + } else if (ex.code == "EMFILE") { + callServerError(429); + } else if (ex.code == "ELOOP") { + callServerError(508); + } else { + callServerError(500, undefined, generateErrorStack(ex)); + } + }).on("open", function () { + try { + res.writeHead(200, http.STATUS_CODES[200], hdhds); + var resStream = {}; + if (configJSON.enableCompression === true && ext != "br" && filelen > 256 && isCompressable && zlib.createBrotliCompress && acceptEncoding.match(/\bbr\b/)) { + resStream = zlib.createBrotliCompress(); + resStream.pipe(res); + } else if (configJSON.enableCompression === true && ext != "zip" && filelen > 256 && isCompressable && acceptEncoding.match(/\bdeflate\b/)) { + resStream = zlib.createDeflateRaw(); + resStream.pipe(res); + } else if (configJSON.enableCompression === true && ext != "gz" && filelen > 256 && isCompressable && acceptEncoding.match(/\bgzip\b/)) { + resStream = zlib.createGzip(); + resStream.pipe(res); + } else { + resStream = res; + } + if (ext == "html") { + function afterWriteCallback() { + readStream.on("end", function () { + resStream.end(foot); + }); + readStream.pipe(resStream, { + end: false + }); + } + if (!resStream.write(head)) { + resStream.on("drain", afterWriteCallback); + } else { + process.nextTick(afterWriteCallback); + } + } else { + readStream.pipe(resStream); + } + serverconsole.resmessage("Client successfully received content."); + } catch (ex) { + callServerError(500, undefined, generateErrorStack(ex)); + } + }); + } else { + res.writeHead(200, http.STATUS_CODES[200], hdhds); + res.end(); + serverconsole.resmessage("Client successfully received content."); + } + } catch (ex) { + callServerError(500, undefined, generateErrorStack(ex)); + } + } + }); + } + } + }); + }; + } + // } + + try { + + //scan blacklist + if (blacklist.check(reqip) && href != "/favicon.ico") { + var bheaders = getCustomHeaders(); + bheaders["Content-Type"] = "text/html; charset=utf8"; + response.writeHead(403, "Client blocked", bheaders); + response.write("Access denied - SVR.JS

ACCESS DENIED

Request from " + reqip + " is denied. The client is now in the blacklist.

SVR.JS/" + version + " (" + getOS() + "; " + (process.isBun ? ("Bun/v" + process.versions.bun + "; like Node.JS/" + process.version) : ("Node.JS/" + process.version)) + ")" + (req.headers.host == undefined ? "" : " on " + String(req.headers.host).replace(/&/g, "&").replace(//g, ">")) + "

"); + serverconsole.errmessage("Client blocked"); + response.end(); + return; + } + + if (req.url == "*") { + if (req.method == "OPTIONS") { + var hdss = getCustomHeaders(); + hdss["Allow"] = "GET, POST, HEAD, OPTIONS"; + res.writeHead(204, "No Content", hdss); + res.end(); + return; + } else { + callServerError(400); + return; + } + } + + if (req.method == "CONNECT") { + callServerError(501); + serverconsole.errmessage("CONNECT requests aren't supported. Your JS runtime probably doesn't support 'connect' handler for HTTP library."); + return; + } + + //SANITIZE URL + var sanitizedHref = sanitizeURL(href); + + if (href.toLowerCase() != sanitizedHref.toLowerCase() && !isProxy) { + var sanitizedURL = uobject; + sanitizedURL.path = null; + sanitizedURL.href = null; + sanitizedURL.pathname = sanitizedHref; + sanitizedURL.hostname = null; + sanitizedURL.host = null; + sanitizedURL.port = null; + sanitizedURL.protocol = null; + sanitizedURL.slashes = null; + sanitizedURL = url.format(sanitizedURL); + serverconsole.resmessage("URL sanitized: " + req.url + " => " + sanitizedURL); + redirect(sanitizedURL, false); + return; + } + //URL REWRITING + + function rewriteURL(address, map) { + var rewrittenAddress = address; + for (var i = 0; i < map.length; i++) { + if (createRegex(map[i].definingRegex).test(address)) { + for (var j = 0; j < map[i].replacements.length; j++) { + rewrittenAddress = rewrittenAddress.replace(createRegex(map[i].replacements[j].regex), map[i].replacements[j].replacement); + } + if (map[i].append) rewrittenAddress += map[i].append; + break; + } + } + return rewrittenAddress; + } + var origHref = href; + if (!isProxy) { + var rewrittenURL = rewriteURL(req.url, rewriteMap); + if (rewrittenURL != req.url) { + serverconsole.resmessage("URL rewritten: " + req.url + " => " + rewrittenURL); + req.url = rewrittenURL; + uobject = urlParse(req.url); + search = uobject.search; + href = uobject.pathname; + ext = path.extname(href).toLowerCase(); + ext = ext.substr(1, ext.length); + + try { + decodedHref = decodeURIComponent(href); + } catch (ex) { + //Return 400 error + callServerError(400); + serverconsole.errmessage("Bad request!"); + return; + } + + var sHref = sanitizeURL(href); + if (sHref != href.replace(/\/\.(?=\/|$)/g, "/").replace(/\/+/g, "/")) { + callServerError(403); + serverconsole.errmessage("Content blocked."); + return; + } else if (sHref != href) { + var rewrittenAgainURL = uobject; + rewrittenAgainURL.path = null; + rewrittenAgainURL.href = null; + rewrittenAgainURL.pathname = sHref; + rewrittenAgainURL = url.format(rewrittenAgainURL); + serverconsole.resmessage("URL sanitized: " + req.url + " => " + rewrittenAgainURL); + req.url = rewrittenAgainURL; + uobject = urlParse(req.url); + search = uobject.search; + href = uobject.pathname; + ext = path.extname(href).toLowerCase(); + ext = ext.substr(1, ext.length); + try { + decodedHref = decodeURIComponent(href); + } catch (ex) { + //Return 400 error + callServerError(400); + serverconsole.errmessage("Bad request!"); + return; + } + } + } + } + if (!isProxy) { + reqcounter++; + var hkh = getCustomHeaders(); + var hk = Object.keys(hkh); + for (var i = 0; i < hk.length; i++) { + try { + response.setHeader(hk[i], hkh[hk[i]]); + } catch (ex) { + //Headers will not be set. + } + } + } + if ((checkIfForbiddenPath(decodedHref, "config") || checkIfForbiddenPath(decodedHref, "certificates")) && !isProxy) { + callServerError(403); + serverconsole.errmessage("Access to configuration file/certificates is denied."); + return; + } else if (checkIfIndexOfForbiddenPath(decodedHref, "log") && !isProxy && (configJSON.enableLogging || configJSON.enableLogging == undefined) && !(configJSON.enableRemoteLogBrowsing || configJSON.enableRemoteLogBrowsing == undefined)) { + callServerError(403); + serverconsole.errmessage("Access to log files is denied."); + return; + } else if (checkIfForbiddenPath(decodedHref, "svrjs") && !isProxy && !exposeServerVersion && process.cwd() == __dirname) { + callServerError(403); + serverconsole.errmessage("Access to SVR.JS script is denied."); + return; + } else if ((checkIfForbiddenPath(decodedHref, "svrjs") || checkIfForbiddenPath(decodedHref, "serverSideScripts") || checkIfIndexOfForbiddenPath(decodedHref, "serverSideScriptDirectories")) && !isProxy && (configJSON.disableServerSideScriptExpose && configJSON.disableServerSideScriptExpose != undefined)) { + callServerError(403); + serverconsole.errmessage("Access to sources is denied."); + return; + } else { + var nonscodeIndex = -1; + var authIndex = -1; + var regexI = []; + if (!isProxy && nonStandardCodes != undefined) { + for (var i = 0; i < nonStandardCodes.length; i++) { + var mth = false; + if (nonStandardCodes[i].regex) { + var regexObj = nonStandardCodes[i].regex.split("/"); + if (regexObj.length == 0) throw new Error("Invalid regex!"); + var modifiers = regexObj.pop(); + if (!modifiers.match(/i/i) && os.platform() == "win32") modifiers += "i"; + regexObj.shift(); + var searchString = regexObj.join("/"); + var rx = RegExp(searchString, modifiers); + mth = req.url.match(rx) || href.match(rx); + regexI.push(rx); + } else { + mth = nonStandardCodes[i].url == href || (os.platform() == "win32" && nonStandardCodes[i].url.toLowerCase() == href.toLowerCase()); + } + if (mth) { + if (nonStandardCodes[i].scode == 401) { + if (authIndex == -1) { + authIndex = i; + } + } else { + if (nonscodeIndex == -1) { + if ((nonStandardCodes[i].scode == 403 || nonStandardCodes[i].scode == 451) && nonStandardCodes[i].users !== undefined) { + var lpk = false; + if (nonStandardCodes[i].users.check(reqip)) { + nonscodeIndex = i; + lpk = true; + } + if (lpk) break; + } else { + nonscodeIndex = i; + } + } + } + } + } + } + + if (nonscodeIndex > -1) { + var nonscode = nonStandardCodes[nonscodeIndex]; + if (nonscode.scode == 301 || nonscode.scode == 302) { + var location = ""; + if (regexI[nonscodeIndex]) { + location = req.url.replace(regexI[nonscodeIndex], nonscode.location); + } else if (req.url.split("?")[1] == undefined || req.url.split("?")[1] == null || req.url.split("?")[1] == "" || req.url.split("?")[1] == " ") { + location = nonscode.location; + } else { + location = nonscode.location + "?" + req.url.split("?")[1]; + } + redirect(location, nonscode.scode == 302); + return; + } else if (nonscode.scode == 403) { + callServerError(403); + serverconsole.errmessage("Content blocked."); + return; + } else if (nonscode.scode == 410) { + callServerError(410); + serverconsole.errmessage("Content is gone."); + return; + } else if (nonscode.scode == 418) { + callServerError(418); + serverconsole.errmessage("SVR.JS is always a teapot ;)"); + return; + } else { + callServerError(nonscode.scode); + serverconsole.errmessage("Client fails recieving content."); + return; + } + } + if (authIndex > -1) { + var authcode = nonStandardCodes[authIndex]; + + function authorizedCallback(bruteProtection) { + var ha = getCustomHeaders(); + ha["WWW-Authenticate"] = "Basic realm=\"" + (authcode.realm ? authcode.realm.replace(/(\\|")/g, "\\$1") : "SVR.JS HTTP Basic Authorization") + "\", charset=\"UTF-8\""; + var credentials = req.headers["authorization"]; + if (!credentials) { + callServerError(401, undefined, undefined, ha); + serverconsole.errmessage("Content needs authorization."); + return; + } + var cmatch = credentials.match(/^Basic (.+)$/); + if (!cmatch) { + callServerError(401, undefined, undefined, ha); + serverconsole.errmessage("Malformed credentials."); + return; + } + var c2 = Buffer.from(cmatch[1], "base64").toString("utf8"); + var c2match = c2.match(/^([^:]*):(.*)$/); + if (!c2match) { + callServerError(401, undefined, undefined, ha); + serverconsole.errmessage("Malformed credentials."); + return; + } + var username = c2match[1]; + var password = c2match[2]; + var authorized = false; + for (var i = 0; i < users.length; i++) { + var hash = sha256(password + users[i].salt); + if (users[i].name == username && users[i].pass == hash) { + authorized = true; + break; + } + } + if (!authorized) { + if (bruteProtection) { + if (process.send) { + process.send("\x12AUTHW" + reqip); + } else { + if (!bruteForceDb[reqip]) bruteForceDb[reqip] = { + invalidAttempts: 0 + }; + bruteForceDb[reqip].invalidAttempts++; + if (bruteForceDb[reqip].invalidAttempts >= 10) { + bruteForceDb[reqip].lastAttemptDate = new Date(); + } + } + } + callServerError(401, undefined, undefined, ha); + serverconsole.errmessage("User " + username + " failed to log in."); + } else { + if (bruteProtection) { + if (process.send) { + process.send("\x12AUTHR" + reqip); + } else { + if (bruteForceDb[reqip]) bruteForceDb[reqip] = { + invalidAttempts: 0 + }; + } + } + modExecute(mods, vres(req, res, serverconsole, responseEnd, href, ext, uobject, search, "index.html", users, page404, head, foot, fd, callServerError, getCustomHeaders, origHref, redirect, parsePostData)); + } + } + if (authcode.disableBruteProtection) { + authorizedCallback(false); + } else if (!process.send) { + if (!bruteForceDb[reqip] || !bruteForceDb[reqip].lastAttemptDate || (new Date() - 300000 >= bruteForceDb[reqip].lastAttemptDate)) { + if (bruteForceDb[reqip] && bruteForceDb[reqip].invalidAttempts >= 10) bruteForceDb[reqip] = { + invalidAttempts: 5 + }; + authorizedCallback(true); + } else { + callServerError(429); + serverconsole.errmessage("Brute force limit reached!"); + } + } else { + var listenerEmitted = false; + + function authMessageListener(message) { + if (listenerEmitted) return; + if (message == "\x14AUTHA" + reqip || message == "\x14AUTHD" + reqip) { + process.removeListener("message", authMessageListener); + listenerEmitted = true; + } + if (message == "\x14AUTHD" + reqip) { + callServerError(429); + serverconsole.errmessage("Brute force limit reached!"); + } else if (message == "\x14AUTHA" + reqip) { + authorizedCallback(true); + } + } + process.on("message", authMessageListener); + process.send("\x12AUTHQ" + reqip); + } + } else { + modExecute(mods, vres(req, res, serverconsole, responseEnd, href, ext, uobject, search, "index.html", users, page404, head, foot, fd, callServerError, getCustomHeaders, origHref, redirect, parsePostData)); + } + } + } catch (ex) { + //CRASH HANDLER + if (ex.message == "Intentionally crashed") throw ex; //If intentionally crashed, then crash SVR.JS + callServerError(500, undefined, generateErrorStack(ex)); //Else just return 500 error + } + } + + } + //Listen port to server + server.on("error", function (err) { + if (err.code == "EADDRINUSE" || err.code == "EADDRNOTAVAIL" || err.code == "EACCES") { + attmts--; + if (cluster.isMaster === undefined) { + if (err.code == "EADDRINUSE") { + serverconsole.locerrmessage("Address in use by another process."); + } else if (err.code == "EADDRNOTAVAIL") { + serverconsole.locerrmessage("Address not available."); + } else if (err.code == "EACCES") { + serverconsole.locerrmessage("Access denied."); + } + serverconsole.locmessage(attmts + " attempts left."); + } else { + process.send("\x12ERRLIST" + attmts + err.code); + } + if (attmts > 0) { + server2.close(); + setTimeout(start, 900); + } else { + if (cluster.isMaster !== undefined) process.send("\x12" + err.code); + process.exit(errors[err.code]); + } + } else { + serverconsole.locerrmessage("There was a problem starting SVR.JS!!!"); + serverconsole.locerrmessage("Stack:"); + serverconsole.locerrmessage(generateErrorStack(err)); + if (cluster.isMaster !== undefined) process.send("\x12CRASH"); + process.exit(err.code ? errors[err.code] : 1); + } + }); + + server.once("listening", function () { + listeningMessage(); + }); +} + +function listenConnListener(msg) { + if (msg == "\x12LISTEN") { + listeningMessage(); + } +} + +function bruteForceListenerWrapper(worker) { + return function bruteForceListener(message) { + var ip = ""; + if (message.substr(0, 6) == "\x12AUTHQ") { + ip = message.substr(6); + if (!bruteForceDb[ip] || !bruteForceDb[ip].lastAttemptDate || (new Date() - 300000 >= bruteForceDb[ip].lastAttemptDate)) { + if (bruteForceDb[ip] && bruteForceDb[ip].invalidAttempts >= 10) bruteForceDb[ip] = { + invalidAttempts: 5 + }; + worker.send("\x14AUTHA" + ip); + } else { + worker.send("\x14AUTHD" + ip); + } + } else if (message.substr(0, 6) == "\x12AUTHR") { + ip = message.substr(6); + if (bruteForceDb[ip]) bruteForceDb[ip] = { + invalidAttempts: 0 + }; + } else if (message.substr(0, 6) == "\x12AUTHW") { + ip = message.substr(6); + if (!bruteForceDb[ip]) bruteForceDb[ip] = { + invalidAttempts: 0 + }; + bruteForceDb[ip].invalidAttempts++; + if (bruteForceDb[ip].invalidAttempts >= 10) { + bruteForceDb[ip].lastAttemptDate = new Date(); + } + } + }; +} + +function msgListener(msg) { + for (var i = 0; i < Object.keys(cluster.workers).length; i++) { + if (msg == "\x12END") { + cluster.workers[Object.keys(cluster.workers)[i]].removeAllListeners("message"); + cluster.workers[Object.keys(cluster.workers)[i]].on("message", bruteForceListenerWrapper(cluster.workers[Object.keys(cluster.workers)[i]])); + cluster.workers[Object.keys(cluster.workers)[i]].on("message", listenConnListener); + } + } + if (msg == "\x12CLOSE") { + closedMaster = true; + } else if (msg == "\x12LISTEN" || msg.substr(0, 4) == "\x12AUTH") { + //Do nothing! + } else if (msg == "\x12SAVEGOOD") { + serverconsole.locmessage("Configuration saved."); + } else if (msg.indexOf("\x12SAVEERR") == 0) { + serverconsole.locwarnmessage("There was a problem, while saving configuration file. Reason: " + msg.substr(8)); + } else if (msg == "\x12OPEN") { + closedMaster = false; + } else if (msg == "\x12END") { + cluster.workers[Object.keys(cluster.workers)[0]].on("message", function (msg) { + if (msg.length > 9 && msg.indexOf("\x12ERRLIST") == 0) { + var tries = parseInt(msg.substr(8, 1)); + var errCode = msg.substr(9); + if (errCode == "EADDRINUSE") { + serverconsole.locerrmessage("Address in use by another process."); + } else if (errCode == "EADDRNOTAVAIL") { + serverconsole.locerrmessage("Address not available."); + } else if (errCode == "EACCES") { + serverconsole.locerrmessage("Access denied."); + } + serverconsole.locmessage(tries + " attempts left."); + } + if (msg == "\x12CRASH") process.exit(1); + if (msg == "\x12EADDRINUSE" || msg == "\x12EADDRNOTAVAIL" || msg == "\x12EACCES") process.exit(errors[msg.substr(1)]); + }); + } else { + serverconsole.climessage(msg); + } +} + +var messageTransmitted = false; + +function listeningMessage() { + if (messageTransmitted) return; + messageTransmitted = true; + if (!cluster.isMaster && cluster.isMaster !== undefined) { + process.send("\x12LISTEN"); + return; + } + serverconsole.locmessage("Started server at: "); + if (secure) serverconsole.locmessage("* https://localhost" + (sport == 443 ? "" : (":" + sport))); + if (!(secure && disableNonEncryptedServer)) serverconsole.locmessage("* http://localhost" + (port == 80 ? "" : (":" + port))); + if (host != "" && host != "[offline]") { + if (secure) serverconsole.locmessage("* https://" + (host.indexOf(":") > -1 ? "[" + host + "]" : host) + (sport == 443 ? "" : (":" + sport))); + if (!(secure && disableNonEncryptedServer)) serverconsole.locmessage("* http://" + (host.indexOf(":") > -1 ? "[" + host + "]" : host) + (port == 80 ? "" : (":" + port))); + } + ipStatusCallback(function () { + if (pubip != "") { + if (secure) serverconsole.locmessage("* https://" + (pubip.indexOf(":") > -1 ? "[" + pubip + "]" : pubip) + (spubport == 443 ? "" : (":" + spubport))); + if (!(secure && disableNonEncryptedServer)) serverconsole.locmessage("* http://" + (pubip.indexOf(":") > -1 ? "[" + pubip + "]" : pubip) + (pubport == 80 ? "" : (":" + pubport))); + } + if (domain != "") { + if (secure) serverconsole.locmessage("* https://" + domain + (spubport == 443 ? "" : (":" + spubport))); + if (!(secure && disableNonEncryptedServer)) serverconsole.locmessage("* http://" + domain + (pubport == 80 ? "" : (":" + pubport))); + } + serverconsole.locmessage("For CLI help type \"help\""); + }); +} + +var closedMaster = false; + +function start(init) { + init = Boolean(init); + if (cluster.isMaster || cluster.isMaster === undefined) { + if (init) { + for (i = 0; i < logo.length; i++) console.log(logo[i]); //Print logo + console.log(); + console.log("Welcome to DorianTech SVR.JS server."); + if (version.indexOf("Nightly-") === 0) serverconsole.locwarnmessage("This version is only for test purposes and may be unstable."); + if (vnum <= 57 && JSON.stringify(rewriteMap) != "[]") serverconsole.locwarnmessage("Some URL rewriting regexes will not work in Node.JS 8.x and earlier."); + if (http2.__disabled__ !== undefined) serverconsole.locwarnmessage("HTTP/2 isn't supported by your Node.JS version!"); + if (process.isBun) serverconsole.locwarnmessage("Bun support is experimental."); + if (cluster.isMaster === undefined) serverconsole.locwarnmessage("You're running SVR.JS on single thread. Reliability may suffer."); + if (crypto.__disabled__ !== undefined) serverconsole.locwarnmessage("Your Node.JS version doesn't have crypto support!"); + if (!process.isBun && process.version == "v8.5.0") serverconsole.locwarnmessage("Your Node.JS version is vulnerable to path validation vulnerability (CVE-2017-14849)."); + if (process.getuid && process.getuid() == 0) serverconsole.locwarnmessage("You're running SVR.JS as root. It's recommended to run SVR.JS as an non-root user."); + if (secure && process.versions && process.versions.openssl && process.versions.openssl.substr(0, 2) == "1.") { + if (new Date() > new Date("11 September 2023")) { + serverconsole.locwarnmessage("OpenSSL 1.x is no longer recieving security updates after 11th September 2023. Your HTTPS communication might be vulnerable."); + } else { + serverconsole.locwarnmessage("OpenSSL 1.x will no longer recieve security updates after 11th September 2023. Your HTTPS communication might be vulnerable in future."); + } + } + if (secure && configJSON.enableOCSPStapling && ocsp._errored) serverconsole.locwarnmessage("Can't load OCSP module. OCSP stapling will be disabled"); + if (vnum < 64) serverconsole.locwarnmessage("SVR.JS 3.5.0 and newer are no longer supported on Node.JS 8.x and 9.x"); + if (disableMods) serverconsole.locwarnmessage("SVR.JS is running without mods and server-side JavaScript enabled."); + console.log(); + serverconsole.locmessage("Server version: " + version); + if (process.isBun) serverconsole.locmessage("Bun version: v" + process.versions.bun); + else serverconsole.locmessage("Node.JS version: " + process.version); + var CPUs = os.cpus(); + if (CPUs.length > 0) serverconsole.locmessage("CPU: " + (CPUs.length > 1 ? CPUs.length + "x " : "") + CPUs[0].model); + if (vnum < 57) { + throw new Error("SVR.JS requires Node.JS 8.4.0 and newer, but your Node.JS version isn't supported by SVR.JS."); + } + } + if (!(secure && disableNonEncryptedServer)) serverconsole.locmessage("Starting HTTP server at localhost:" + port.toString() + "..."); + if (secure) serverconsole.locmessage("Starting HTTPS server at localhost:" + sport.toString() + "..."); + } + + + if (!cluster.isMaster) { + if (secure) { + server.listen(sport); + if (!disableNonEncryptedServer) server2.listen(port); + } else { + server.listen(port); + } + } + + + var commands = { + close: function () { + try { + server.close(); + if (secure && !disableNonEncryptedServer) { + server2.close(); + } + if (cluster.isMaster === undefined) serverconsole.climessage("Server closed."); + else { + process.send("Server closed."); + process.send("\x12CLOSE"); + } + } catch (ex) { + if (cluster.isMaster === undefined) serverconsole.climessage("Cannot close server! Reason: " + ex.message); + else process.send("Cannot close server! Reason: " + ex.message); + } + }, + open: function () { + try { + if (secure) { + server.listen(sport); + if (!disableNonEncryptedServer) server2.listen(port); + } else { + server.listen(port); // ReOpen Server + } + if (cluster.isMaster === undefined) serverconsole.climessage("Server opened."); + else { + process.send("Server opened."); + process.send("\x12OPEN"); + } + } catch (ex) { + if (cluster.isMaster === undefined) serverconsole.climessage("Cannot open server! Reason: " + ex.message); + else process.send("Cannot open server! Reason: " + ex.message); + } + }, + help: function () { + if (cluster.isMaster === undefined) serverconsole.climessage("Server commands:\n" + Object.keys(commands).join(" ")); + else process.send("Server commands:\n" + Object.keys(commands).join(" ")); + }, + mods: function () { + if (cluster.isMaster === undefined) serverconsole.climessage("Mods:"); + else process.send("Mods:"); + for (var i = 0; i < modInfos.length; i++) { + if (cluster.isMaster === undefined) serverconsole.climessage((i + 1).toString() + ". " + modInfos[i].name + " " + modInfos[i].version); + else process.send((i + 1).toString() + ". " + modInfos[i].name + " " + modInfos[i].version); + } + if (modInfos.length == 0) { + if (cluster.isMaster === undefined) serverconsole.climessage("No mods installed."); + else process.send("No mods installed."); + } + }, + stop: function (retcode) { + if (typeof retcode == "number") { + process.exit(retcode); + } else { + process.exit(0); + } + }, + clear: function () { + console.clear(); + }, + block: function (ip) { + if (ip == undefined || JSON.stringify(ip) == "[]") { + if (cluster.isMaster === undefined) serverconsole.climessage("Cannot block non-existent IP."); + else if (!cluster.isMaster) process.send("Cannot block non-existent IP."); + } else { + for (var i = 0; i < ip.length; i++) { + if (ip[i].indexOf(":") == -1) { + ip[i] = "::ffff:" + ip[i]; + } + if (!blacklist.check(ip[i])) { + blacklist.add(ip[i]); + } + } + if (cluster.isMaster === undefined) serverconsole.climessage("IPs successfully blocked."); + else if (!cluster.isMaster) process.send("IPs successfully blocked."); + } + }, + unblock: function (ip) { + if (ip == undefined || JSON.stringify(ip) == "[]") { + if (cluster.isMaster === undefined) serverconsole.climessage("Cannot unblock non-existent IP."); + else if (!cluster.isMaster) process.send("Cannot unblock non-existent IP."); + } else { + for (var i = 0; i < ip.length; i++) { + if (ip[i].indexOf(":") == -1) { + ip[i] = "::ffff:" + ip[i]; + } + blacklist.remove(ip[i]); + } + if (cluster.isMaster === undefined) serverconsole.climessage("IPs successfully unblocked."); + else if (!cluster.isMaster) process.send("IPs successfully unblocked."); + } + }, + restart: function () { + if (cluster.isMaster === undefined) serverconsole.climessage("This command is not supported on single-threaded SVR.JS."); + else process.send("This command need to be run in SVR.JS master."); + } + }; + + + if (init) { + if (cluster.isMaster === undefined) { + setInterval(function () { + try { + saveConfig(); + serverconsole.locmessage("Configuration saved."); + } catch (ex) { + throw new Error(ex); + } + }, 300000); + } else if (cluster.isMaster) { + setInterval(function () { + var allClusters = Object.keys(cluster.workers); + for (var i = 0; i < allClusters.length; i++) { + try { + if (cluster.workers[allClusters[i]]) { + cluster.workers[allClusters[i]].on("message", msgListener); + cluster.workers[allClusters[i]].send("\x14SAVECONF"); + } + } catch (ex) { + if (cluster.workers[allClusters[i]]) { + cluster.workers[allClusters[i]].removeAllListeners("message"); + cluster.workers[allClusters[i]].on("message", bruteForceListenerWrapper(cluster.workers[allClusters[i]])); + cluster.workers[allClusters[i]].on("message", listenConnListener); + } + serverconsole.locwarnmessage("There was a problem, while saving configuration file. Reason: " + ex.message); + } + } + }, 300000); + } + if (!cluster.isMaster && cluster.isMaster !== undefined) { + process.on("message", function (line) { + try { + if (line == "") { + //Does Nothing + process.send("\x12END"); + } else if (line == "\x14SAVECONF") { + //Save configuration file + try { + saveConfig(); + process.send("\x12SAVEGOOD"); + } catch (ex) { + process.send("\x12SAVEERR" + ex.message); + } + process.send("\x12END"); + } else if (commands[line.split(" ")[0]] !== undefined && commands[line.split(" ")[0]] !== null) { + var argss = line.split(" "); + var command = argss.shift(); + commands[command](argss); + process.send("\x12END"); + } else { + process.send("Unrecognized command \"" + line.split(" ")[0] + "\"."); + process.send("\x12END"); + } + } catch (ex) { + if (line != "") { + process.send("Can't execute command \"" + line.split(" ")[0] + "\"."); + process.send("\x12END"); + } + } + }); + } else { + var rla = readline.createInterface({ + input: process.stdin, + output: process.stdout, + prompt: "" + }); + rla.prompt(); + rla.on("line", function (line) { + line = line.trim(); + var argss = line.split(" "); + var command = argss.shift(); + if (line != "") { + if (cluster.isMaster !== undefined) { + var allClusters = Object.keys(cluster.workers); + if (command == "block") commands.block(argss); + if (command == "unblock") commands.unblock(argss); + if (command == "restart") { + var stopError = false; + exiting = true; + for (var i = 0; i < allClusters.length; i++) { + try { + if (cluster.workers[allClusters[i]]) { + cluster.workers[allClusters[i]].kill(); + } + } catch (ex) { + stopError = true; + } + } + if (stopError) serverconsole.climessage("Some SVR.JS workers might not be stopped."); + SVRJSInitialized = false; + var cpus = os.cpus().length; + if (cpus > 16) cpus = 16; + try { + var useAvailableCores = Math.round((os.freemem()) / 50000000) - 1; //1 core deleted for safety... + if (cpus > useAvailableCores) cpus = useAvailableCores; + } catch (ex) { + //Nevermind... Don't want SVR.JS to fail starting, because os.freemem function is not working. + } + if (cpus < 1) cpus = 1; //If SVR.JS is run on Haiku or if useAvailableCores = 0 + for (var i = 0; i < cpus; i++) { + if (i == 0) { + SVRJSFork(); + } else { + setTimeout((function (i) { + return function () { + SVRJSFork(); + if (i >= cpus - 1) { + SVRJSInitialized = true; + exiting = false; + serverconsole.climessage("SVR.JS workers restarted."); + } + }; + })(i), i * 6.6); + } + } + return; + } + if (command == "stop") { + exiting = true; + allClusters = Object.keys(cluster.workers); + } + for (var i = 0; i < allClusters.length; i++) { + try { + if (cluster.workers[allClusters[i]]) { + cluster.workers[allClusters[i]].on("message", msgListener); + cluster.workers[allClusters[i]].send(line); + } + } catch (ex) { + if (cluster.workers[allClusters[i]]) { + cluster.workers[allClusters[i]].removeAllListeners("message"); + cluster.workers[allClusters[i]].on("message", bruteForceListenerWrapper(cluster.workers[allClusters[i]])); + cluster.workers[allClusters[i]].on("message", listenConnListener); + } + serverconsole.climessage("Can't run command \"" + command + "\"."); + } + } + if (command == "stop") { + setTimeout(function () { + process.exit(0); + }, 50); + } + } else { + if (command == "stop") process.exit(0); + try { + commands[command](argss); + } catch (ex) { + serverconsole.climessage("Unrecognized command \"" + command + "\"."); + } + } + } + rla.prompt(); + }); + } + + if (cluster.isMaster || cluster.isMaster === undefined) { + //Cluster forking code + if (cluster.isMaster !== undefined && init) { + var cpus = os.cpus().length; + if (cpus > 16) cpus = 16; + try { + var useAvailableCores = Math.round((os.freemem()) / 50000000) - 1; //1 core deleted for safety... + if (cpus > useAvailableCores) cpus = useAvailableCores; + } catch (ex) { + //Nevermind... Don't want SVR.JS to fail starting, because os.freemem function is not working. + } + if (cpus < 1) cpus = 1; //If SVR.JS is run on Haiku or if useAvailableCores = 0 + for (var i = 0; i < cpus; i++) { + if (i == 0) { + SVRJSFork(); + } else { + setTimeout((function (i) { + return function () { + SVRJSFork(); + if (i >= cpus - 1) SVRJSInitialized = true; + }; + })(i), i * 6.6); + } + } + cluster.workers[Object.keys(cluster.workers)[0]].on("message", function (msg) { + if (msg.length > 9 && msg.indexOf("\x12ERRLIST") == 0) { + var tries = parseInt(msg.substr(8, 1)); + var errCode = msg.substr(9); + if (errCode == "EADDRINUSE") { + serverconsole.locerrmessage("Address in use by another process."); + } else if (errCode == "EADDRNOTAVAIL") { + serverconsole.locerrmessage("Address not available."); + } else if (errCode == "EACCES") { + serverconsole.locerrmessage("Access denied."); + } + serverconsole.locmessage(tries + " attempts left."); + } + if (msg == "\x12CRASH") process.exit(1); + if (msg == "\x12EADDRINUSE" || msg == "\x12EADDRNOTAVAIL" || msg == "\x12EACCES") process.exit(errors[msg.substr(1)]); + }); + + setInterval(function () { + if (!closedMaster && !exiting) { + var chksocket = {}; + if (secure && disableNonEncryptedServer) { + chksocket = https.get({ + port: sport, + headers: { + "X-SVR-JS-From-Main-Thread": "true", + "User-Agent": (exposeServerVersion ? "SVR.JS/" + version + " (" + getOS() + "; " + (process.isBun ? ("Bun/v" + process.versions.bun + "; like Node.JS/" + process.version) : ("Node.JS/" + process.version)) + ")" : "SVR.JS") + }, + timeout: 2000, + rejectUnauthorized: false + }, function (res) { + chksocket.removeAllListeners("timeout"); + res.destroy(); + res.on("data", function () {}); + res.on("end", function () {}); + crashed = false; + }).on("error", function () { + if (!exiting) { + if (!crashed) SVRJSFork(); + else crashed = false; + } + }).on("timeout", function () { + if (!exiting) SVRJSFork(); + crashed = true; + }); + } else if ((configJSON.enableHTTP2 == undefined ? false : configJSON.enableHTTP2) && !secure) { + var connection = http2.connect("http://localhost:" + port.toString()); + connection.on("error", function () { + if (!exiting) { + if (!crashed) SVRJSFork(); + else crashed = false; + } + }); + connection.setTimeout(2000, function () { + if (!exiting) SVRJSFork(); + crashed = true; + }); + chksocket = connection.request({ + ":path": "/", + "x-svr-js-from-main-thread": "true", + "user-agent": (exposeServerVersion ? "SVR.JS/" + version + " (" + getOS() + "; " + (process.isBun ? ("Bun/v" + process.versions.bun + "; like Node.JS/" + process.version) : ("Node.JS/" + process.version)) + ")" : "SVR.JS") + }); + chksocket.on("response", function () { + connection.close(); + crashed = false; + }); + chksocket.on("error", function () { + if (!exiting) { + if (!crashed) SVRJSFork(); + else crashed = false; + } + }); + } else { + chksocket = http.get({ + port: port, + headers: { + "X-SVR-JS-From-Main-Thread": "true", + "User-Agent": (exposeServerVersion ? "SVR.JS/" + version + " (" + getOS() + "; " + (process.isBun ? ("Bun/v" + process.versions.bun + "; like Node.JS/" + process.version) : ("Node.JS/" + process.version)) + ")" : "SVR.JS") + }, + timeout: 2000 + }, function (res) { + chksocket.removeAllListeners("timeout"); + res.destroy(); + res.on("data", function () {}); + res.on("end", function () {}); + crashed = false; + }).on("error", function () { + if (!exiting) { + if (!crashed) SVRJSFork(); + else crashed = false; + } + }).on("timeout", function () { + if (!exiting) SVRJSFork(); + crashed = true; + }); + } + } + }, 5000); + } + } + } +} + +//Save configuration file +function saveConfig() { + for (var i = 0; i < 3; i++) { + try { + var configJSONobj = {}; + if (fs.existsSync(__dirname + "/config.json")) configJSONobj = JSON.parse(fs.readFileSync(__dirname + "/config.json").toString()); + if (configJSONobj.users === undefined) configJSONobj.users = []; + if (secure) { + if (configJSONobj.key === undefined) configJSONobj.key = "cert/key.key"; + if (configJSONobj.cert === undefined) configJSONobj.cert = "cert/cert.crt"; + if (configJSONobj.sport === undefined) configJSONobj.sport = 443; + if (configJSONobj.spubport === undefined) configJSONobj.spubport = 443; + if (configJSONobj.sni === undefined) configJSONobj.sni = {}; + if (configJSONobj.enableOCSPStapling === undefined) configJSONobj.enableOCSPStapling = false; + } + if (configJSONobj.port === undefined) configJSONobj.port = 80; + if (configJSONobj.pubport === undefined) configJSONobj.pubport = 80; + if (configJSONobj.domain === undefined && configJSONobj.domian !== undefined) configJSONobj.domain = configJSONobj.domian; + delete configJSONobj.domian; + if (configJSONobj.page404 === undefined) configJSONobj.page404 = "404.html"; + configJSONobj.timestamp = timestamp; + configJSONobj.blacklist = blacklist.raw; + if (configJSONobj.nonStandardCodes === undefined) configJSONobj.nonStandardCodes = []; + if (configJSONobj.enableCompression === undefined) configJSONobj.enableCompression = true; + if (configJSONobj.customHeaders === undefined) configJSONobj.customHeaders = {}; + if (configJSONobj.enableHTTP2 === undefined) configJSONobj.enableHTTP2 = false; + if (configJSONobj.enableLogging === undefined) configJSONobj.enableLogging = true; + if (configJSONobj.enableDirectoryListing === undefined) configJSONobj.enableDirectoryListing = true; + if (configJSONobj.enableDirectoryListingWithDefaultHead === undefined) configJSONobj.enableDirectoryListingWithDefaultHead = false; + if (configJSONobj.serverAdministratorEmail === undefined) configJSONobj.serverAdministratorEmail = "[no contact information]"; + if (configJSONobj.stackHidden === undefined) configJSONobj.stackHidden = false; + if (configJSONobj.enableRemoteLogBrowsing === undefined) configJSONobj.enableRemoteLogBrowsing = true; + if (configJSONobj.exposeServerVersion === undefined) configJSONobj.exposeServerVersion = true; + if (configJSONobj.disableServerSideScriptExpose === undefined) configJSONobj.disableServerSideScriptExpose = false; + if (configJSONobj.allowStatus === undefined) configJSONobj.allowStatus = true; + if (configJSONobj.rewriteMap === undefined) configJSONobj.rewriteMap = []; + if (configJSONobj.dontCompress === undefined) configJSONobj.dontCompress = []; + if (configJSONobj.enableIPSpoofing === undefined) configJSONobj.enableIPSpoofing = false; + if (configJSONobj.secure === undefined) configJSONobj.secure = false; + if (configJSONobj.disableNonEncryptedServer === undefined) configJSONobj.disableNonEncryptedServer = false; + if (configJSONobj.disableToHTTPSRedirect === undefined) configJSONobj.disableToHTTPSRedirect = false; + //configJSONobj.wwwroot = configJSON.wwwroot; + + var configString = JSON.stringify(configJSONobj, null, 2); + fs.writeFileSync(__dirname + "/config.json", configString); + break; + } catch (ex) { + if (i >= 2) throw ex; + var now = Date.now(); + while (Date.now() - now < 2); + } + } +} + +//Process event listeners +if (cluster.isMaster || cluster.isMaster === undefined) { + process.on("uncaughtException", function (ex) { + //CRASH HANDLER + serverconsole.locerrmessage("SVR.JS master process just crashed!!!"); + serverconsole.locerrmessage("Stack:"); + serverconsole.locerrmessage(generateErrorStack(ex)); + process.exit(ex.errno); + }); + process.on("unhandledRejection", function (ex) { + //CRASH HANDLER + serverconsole.locerrmessage("SVR.JS master process just crashed!!!"); + serverconsole.locerrmessage("Stack:"); + serverconsole.locerrmessage(ex.stack ? generateErrorStack(ex) : String(ex)); + process.exit(ex.errno); + }); + process.on("exit", function (code) { + try { + saveConfig(); + } catch (ex) { + serverconsole.locwarnmessage("There was a problem, while saving configuration file. Reason: " + ex.message); + } + try { + deleteFolderRecursive(__dirname + "/temp"); + } catch (ex) { + //Error! + } + try { + fs.mkdirSync(__dirname + "/temp"); + } catch (ex) { + //Error! + } + serverconsole.locmessage("Server closed with exit code: " + code); + }); + process.on("warning", function (warning) { + serverconsole.locwarnmessage(warning.message); + if (generateErrorStack(warning)) {} + if (process.isBun) { + try { + fs.writeFileSync(__dirname + "/temp/serverSideScript.js", "//Placeholder server-side JavaScript to workaround Bun bug.\r\n"); + } catch (ex) { + //Error! + } + serverconsole.locwarnmessage("Stack:"); + serverconsole.locwarnmessage(generateErrorStack(warning)); + } + }); + process.on("SIGINT", function () { + if (cluster.isMaster !== undefined) { + exiting = true; + var allClusters = Object.keys(cluster.workers); + for (var i = 0; i < allClusters.length; i++) { + try { + if (cluster.workers[allClusters[i]]) { + cluster.workers[allClusters[i]].send("stop"); + } + } catch (ex) { + //Worker will crash with EPIPE anyway. + } + } + } + serverconsole.locmessage("Server terminated using SIGINT"); + process.exit(); + }); +} else { + process.on("uncaughtException", function (ex) { + //CRASH HANDLER + serverconsole.locerrmessage("SVR.JS worker just crashed!!!"); + serverconsole.locerrmessage("Stack:"); + serverconsole.locerrmessage(generateErrorStack(ex)); + process.exit(ex.errno); + }); + process.on("unhandledRejection", function (ex) { + //CRASH HANDLER + serverconsole.locerrmessage("SVR.JS worker just crashed!!!"); + serverconsole.locerrmessage("Stack:"); + serverconsole.locerrmessage(ex.stack ? generateErrorStack(ex) : String(ex)); + process.exit(ex.errno); + }); + process.on("warning", function (warning) { + serverconsole.locwarnmessage(warning.message); + if (warning.stack) { + serverconsole.locwarnmessage("Stack:"); + serverconsole.locwarnmessage(generateErrorStack(warning)); + } + }); +} +//Call start +try { + start(true); +} catch (ex) { + serverconsole.locerrmessage("There was a problem starting SVR.JS!!!"); + serverconsole.locerrmessage("Stack:"); + serverconsole.locerrmessage(generateErrorStack(ex)); + process.exit(ex.errno); +} diff --git a/svr_new.js b/svr_new.js new file mode 100644 index 0000000..89a5ae2 --- /dev/null +++ b/svr_new.js @@ -0,0 +1 @@ +require("./svr.js"); diff --git a/svrpasswd.js b/svrpasswd.js new file mode 100644 index 0000000..0942229 --- /dev/null +++ b/svrpasswd.js @@ -0,0 +1,346 @@ +//SVR.JS USER TOOL +var readline = require("readline"); +var process = require("process"); +var fs = require("fs"); +try { + var crypto = require('crypto'); +} catch (ex) { + var crypto = {}; + crypto.__disabled__ = null; + crypto.createHash = function(type) { + if (type != "SHA256") throw new Error("Hash type not supported!"); + return { + msg: "", + update: function(a) { + this.msg = a; + return this; + }, + digest: function(ty) { + var chrsz = 8; + var hexcase = 0; + + function safe_add(x, y) { + var lsw = (x & 0xFFFF) + (y & 0xFFFF); + var msw = (x >> 16) + (y >> 16) + (lsw >> 16); + return (msw << 16) | (lsw & 0xFFFF); + } + + function S(X, n) { + return (X >>> n) | (X << (32 - n)); + } + + function R(X, n) { + return (X >>> n); + } + + function Ch(x, y, z) { + return ((x & y) ^ ((~x) & z)); + } + + function Maj(x, y, z) { + return ((x & y) ^ (x & z) ^ (y & z)); + } + + function Sigma0256(x) { + return (S(x, 2) ^ S(x, 13) ^ S(x, 22)); + } + + function Sigma1256(x) { + return (S(x, 6) ^ S(x, 11) ^ S(x, 25)); + } + + function Gamma0256(x) { + return (S(x, 7) ^ S(x, 18) ^ R(x, 3)); + } + + function Gamma1256(x) { + return (S(x, 17) ^ S(x, 19) ^ R(x, 10)); + } + + function core_sha256(m, l) { + var K = new Array(0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5, 0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5, 0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3, 0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174, 0xE49B69C1, 0xEFBE4786, 0xFC19DC6, 0x240CA1CC, 0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA, 0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7, 0xC6E00BF3, 0xD5A79147, 0x6CA6351, 0x14292967, 0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13, 0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85, 0xA2BFE8A1, 0xA81A664B, 0xC24B8B70, 0xC76C51A3, 0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070, 0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5, 0x391C0CB3, 0x4ED8AA4A, 0x5B9CCA4F, 0x682E6FF3, 0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208, 0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2); + var HASH = new Array(0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19); + var W = new Array(64); + var a, b, c, d, e, f, g, h, i, j; + var T1, T2; + + m[l >> 5] |= 0x80 << (24 - l % 32); + m[((l + 64 >> 9) << 4) + 15] = l; + + for (var i = 0; i < m.length; i += 16) { + a = HASH[0]; + b = HASH[1]; + c = HASH[2]; + d = HASH[3]; + e = HASH[4]; + f = HASH[5]; + g = HASH[6]; + h = HASH[7]; + + for (var j = 0; j < 64; j++) { + if (j < 16) W[j] = m[j + i]; + else W[j] = safe_add(safe_add(safe_add(Gamma1256(W[j - 2]), W[j - 7]), Gamma0256(W[j - 15])), W[j - 16]); + + T1 = safe_add(safe_add(safe_add(safe_add(h, Sigma1256(e)), Ch(e, f, g)), K[j]), W[j]); + T2 = safe_add(Sigma0256(a), Maj(a, b, c)); + + h = g; + g = f; + f = e; + e = safe_add(d, T1); + d = c; + c = b; + b = a; + a = safe_add(T1, T2); + } + + HASH[0] = safe_add(a, HASH[0]); + HASH[1] = safe_add(b, HASH[1]); + HASH[2] = safe_add(c, HASH[2]); + HASH[3] = safe_add(d, HASH[3]); + HASH[4] = safe_add(e, HASH[4]); + HASH[5] = safe_add(f, HASH[5]); + HASH[6] = safe_add(g, HASH[6]); + HASH[7] = safe_add(h, HASH[7]); + } + return HASH; + } + + function str2binb(str) { + var bin = Array(); + var mask = (1 << chrsz) - 1; + for (var i = 0; i < str.length * chrsz; i += chrsz) { + bin[i >> 5] |= (str.charCodeAt(i / chrsz) & mask) << (24 - i % 32); + } + return bin; + } + + function Utf8Encode(string) { + string = string.replace(/\r\n/g, '\n'); + var utftext = ''; + + for (var n = 0; n < string.length; n++) { + + var c = string.charCodeAt(n); + + if (c < 128) { + utftext += String.fromCharCode(c); + } else if ((c > 127) && (c < 2048)) { + utftext += String.fromCharCode((c >> 6) | 192); + utftext += String.fromCharCode((c & 63) | 128); + } else { + utftext += String.fromCharCode((c >> 12) | 224); + utftext += String.fromCharCode(((c >> 6) & 63) | 128); + utftext += String.fromCharCode((c & 63) | 128); + } + + } + + return utftext; + } + + function binb2hex(binarray) { + var hex_tab = hexcase ? '0123456789ABCDEF' : '0123456789abcdef'; + var str = ''; + for (var i = 0; i < binarray.length * 4; i++) { + str += hex_tab.charAt((binarray[i >> 2] >> ((3 - i % 4) * 8 + 4)) & 0xF) + + hex_tab.charAt((binarray[i >> 2] >> ((3 - i % 4) * 8)) & 0xF); + } + return str; + } + + s = Utf8Encode(this.msg); + var str = binb2hex(core_sha256(str2binb(s), s.length * chrsz)); + if (ty == "hex") return str; + var hx = []; + for (var i = 0; i < str.length; i += 2) { + hx.push(parseInt(str[i] + str[i + 1], 16)); + } + return new Buffer(hx); + } + }; + } +} + +if (!crypto.randomInt) { + crypto.randomInt = function(min, max) { + return Math.round(Math.random() * (max - min)) + min; + } +} +var configJSON = {}; +if (fs.existsSync("config.json")) { + var configJSONf = ""; + try { + configJSONf = fs.readFileSync("config.json"); //Read JSON File + } catch (ex) { + throw new Error("Cannot read JSON file."); + } + try { + configJSON = JSON.parse(configJSONf); //Parse JSON + } catch (ex) { + throw new Error("JSON Parse error."); + } +} + +var users = []; +if (configJSON.users != undefined) users = configJSON.users; + +function saveConfig() { + var configJSONobj = {}; + if (fs.existsSync("./config.json")) configJSONobj = JSON.parse(fs.readFileSync("./config.json").toString()); + configJSONobj.users = users; + var configString = JSON.stringify(configJSONobj, null, 2); + fs.writeFileSync("config.json", configString); +} + +var args = process.argv; +var user = ""; +var action = "change"; +if (process.argv.length <= (process.argv[0].indexOf("node") > -1 || process.argv[0].indexOf("bun") > -1 ? 2 : 1)) args.push("-h"); +for (var i = (process.argv[0].indexOf("node") > -1 || process.argv[0].indexOf("bun") > -1 ? 2 : 1); i < args.length; i++) { + if (args[i] == "-h" || args[i] == "--help" || args[i] == "-?" || args[i] == "/h" || args[i] == "/?") { + console.log("SVR.JS user tool usage:"); + console.log("node svrpasswd.js [-h] [--help] [-?] [/h] [/?] [-a|--add|-d|--delete] "); + console.log("-h -? /h /? --help -- Displays help"); + console.log("-a --add -- Add an user"); + console.log("-d --delete -- Deletes an user"); + process.exit(0); + } else if (args[i] == "-a" || args[i] == "--add") { + if (action != "change") { + console.log("Multiple actions specified."); + console.log("node svrpasswd.js [-h] [--help] [-?] [/h] [/?] [-a|--add|-d|--delete] "); + console.log("-h -? /h /? --help -- Displays help"); + console.log("-a --add -- Add an user"); + console.log("-d --delete -- Deletes an user"); + process.exit(1); + } + action = "add"; + } else if (args[i] == "-d" || args[i] == "--delete") { + if (action != "change") { + console.log("Multiple actions specified."); + console.log("node svrpasswd.js [-h] [--help] [-?] [/h] [/?] [-a|--add|-d|--delete] "); + console.log("-h -? /h /? --help -- Displays help"); + console.log("-a --add -- Add an user"); + console.log("-d --delete -- Deletes an user"); + process.exit(1); + } + action = "delete"; + } else { + if (user != "") { + console.log("Multiple users specified."); + console.log("node svrpasswd.js [-h] [--help] [-?] [/h] [/?] [-a|--add|-d|--delete] "); + console.log("-h -? /h /? --help -- Displays help"); + console.log("-a --add -- Add an user"); + console.log("-d --delete -- Deletes an user"); + process.exit(1); + } + user = args[i]; + } +} + +if (user == "") { + console.log("No user specified."); + console.log("node svrpasswd.js [-h] [--help] [-?] [/h] [/?] [-a|--add|-d|--delete] "); + console.log("-h -? /h /? --help -- Displays help"); + console.log("-a --add -- Add an user"); + console.log("-d --delete -- Deletes an user"); + process.exit(1); +} + +function getUserIndex(username) { + var ind = -1 + for (var i = 0; i < users.length; i++) { + if (users[i].name == username) { + ind = i; + break; + } + } + return ind; +} + +function sha256(msg) { + var hash = crypto.createHash("SHA256"); + hash.update(msg); + return hash.digest('hex'); +} + +function generateSalt() { + var token = ""; + var strlist = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + for (var i = 0; i < 63; i++) { + token += strlist[crypto.randomInt(0, strlist.length)]; + } + return token; +} + +function password(callback) { + var rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, + prompt: 'Password: ', + terminal: false + }); + rl.prompt(); + rl.once('line', (line) => { + //rl.close(); + var rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, + prompt: 'Confirm password: ', + terminal: false + }); + rl.prompt(); + rl.on('line', (line2) => { + rl.close(); + if (line != line2) callback(false); + else callback(line); + + }); + }); +} + +var userindex = getUserIndex(user); +if (action == "add" && userindex != -1) { + console.log("User alerady exists."); + process.exit(1); +} else if (action != "add" && userindex == -1) { + console.log("User doesn't exist."); + process.exit(1); +} +if (action == "delete") { + users.splice(userindex, 1); + saveConfig(); + console.log("User deleted successfully"); +} else if (action == "add") { + password(function(password) { + if (!password) { + console.log("Passwords don't match!"); + process.exit(1); + } else { + var salt = generateSalt() + users.push({ + name: user, + pass: sha256(password + salt), + salt: salt + }); + saveConfig(); + console.log("User added successfully"); + } + }); +} else { + password(function(password) { + if (!password) { + console.log("Passwords don't match!"); + process.exit(1); + } else { + var salt = generateSalt() + users[userindex] = { + name: user, + pass: sha256(password + salt), + salt: salt + }; + saveConfig(); + console.log("Password changed successfully"); + } + }); +} diff --git a/testdir/.personalized/FOOT.html b/testdir/.personalized/FOOT.html new file mode 100644 index 0000000..dd34732 --- /dev/null +++ b/testdir/.personalized/FOOT.html @@ -0,0 +1,6 @@ + + + + diff --git a/testdir/.personalized/HEAD.html b/testdir/.personalized/HEAD.html new file mode 100644 index 0000000..68f7b83 --- /dev/null +++ b/testdir/.personalized/HEAD.html @@ -0,0 +1,39 @@ + + + + + + +

My Website

+ +
+

Personalized directory listing

+

This is a test of personalized directory listing.

diff --git a/testdir/.personalized/folder/FOOT.html b/testdir/.personalized/folder/FOOT.html new file mode 100644 index 0000000..dd34732 --- /dev/null +++ b/testdir/.personalized/folder/FOOT.html @@ -0,0 +1,6 @@ +
+ + + diff --git a/testdir/.personalized/folder/HEAD.html b/testdir/.personalized/folder/HEAD.html new file mode 100644 index 0000000..68f7b83 --- /dev/null +++ b/testdir/.personalized/folder/HEAD.html @@ -0,0 +1,39 @@ + + + + + + +

My Website

+ +
+

Personalized directory listing

+

This is a test of personalized directory listing.

diff --git a/testdir/.personalized/html.html b/testdir/.personalized/html.html new file mode 100644 index 0000000..e69de29 diff --git a/testdir/.personalized/js.js b/testdir/.personalized/js.js new file mode 100644 index 0000000..e69de29 diff --git a/testdir/.personalized/text.txt b/testdir/.personalized/text.txt new file mode 100644 index 0000000..e69de29 diff --git a/testdir/html.html b/testdir/html.html new file mode 100644 index 0000000..e69de29 diff --git a/testdir/js.js b/testdir/js.js new file mode 100644 index 0000000..e69de29 diff --git a/testdir/text.txt b/testdir/text.txt new file mode 100644 index 0000000..e69de29 diff --git a/tests.html b/tests.html new file mode 100644 index 0000000..6bd8485 --- /dev/null +++ b/tests.html @@ -0,0 +1,41 @@ + + + + SVR.JS 3.4.17 Tests + + + + + +

SVR.JS 3.4.17 Tests

+

Directory

+ +

Directory (with query)

+ +

Directory (personalized)

+ +

404 Error

+ + +

500 Error

+ +

Server Side Javascript

+ +

Proxy test

+ +

URL rewriting test (/invoke500/aprilfools => /invoke500.svr?aprilfools)

+ +
+
+ + + diff --git a/views.txt b/views.txt new file mode 100644 index 0000000..573541a --- /dev/null +++ b/views.txt @@ -0,0 +1 @@ +0