From 2909b798914d12eb2a8ff2bce5443adaa9cb9578 Mon Sep 17 00:00:00 2001 From: bunkerity Date: Wed, 14 Oct 2020 22:46:20 +0200 Subject: [PATCH] basic antibot feature through captcha --- Dockerfile | 2 +- Dockerfile-amd64 | 2 +- Dockerfile-arm32v7 | 2 +- Dockerfile-arm64v8 | 2 +- Dockerfile-i386 | 2 +- compile.sh | 7 +- confs/antibot-captcha.conf | 40 +++++++ confs/antibot-javascript.conf | 6 +- confs/main-lua.conf | 21 +++- entrypoint.sh | 16 ++- lua/captcha.lua | 32 ++++++ lua/cookie.lua | 29 +++-- lua/misc/Vera.ttf | Bin 0 -> 65932 bytes lua/misc/base64.lua | 202 ++++++++++++++++++++++++++++++++++ lua/misc/captcha.lua | 193 ++++++++++++++++++++++++++++++++ 15 files changed, 529 insertions(+), 27 deletions(-) create mode 100644 confs/antibot-captcha.conf create mode 100644 lua/captcha.lua create mode 100644 lua/misc/Vera.ttf create mode 100644 lua/misc/base64.lua create mode 100644 lua/misc/captcha.lua diff --git a/Dockerfile b/Dockerfile index cc3030f..c74b71a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,7 +12,7 @@ COPY fail2ban/ /opt/fail2ban COPY logs/ /opt/logs COPY lua/ /opt/lua -RUN apk --no-cache add php7-fpm certbot libstdc++ libmaxminddb geoip pcre yajl fail2ban clamav apache2-utils rsyslog openssl lua && \ +RUN apk --no-cache add php7-fpm certbot libstdc++ libmaxminddb geoip pcre yajl fail2ban clamav apache2-utils rsyslog openssl lua libgd && \ chmod +x /opt/entrypoint.sh /opt/scripts/* && \ mkdir /opt/entrypoint.d && \ adduser -h /dev/null -g '' -s /sbin/nologin -D -H nginx diff --git a/Dockerfile-amd64 b/Dockerfile-amd64 index 452a0c0..ca306ab 100644 --- a/Dockerfile-amd64 +++ b/Dockerfile-amd64 @@ -12,7 +12,7 @@ COPY fail2ban/ /opt/fail2ban COPY logs/ /opt/logs COPY lua/ /opt/lua -RUN apk --no-cache add php7-fpm certbot libstdc++ libmaxminddb geoip pcre yajl fail2ban clamav apache2-utils rsyslog openssl lua && \ +RUN apk --no-cache add php7-fpm certbot libstdc++ libmaxminddb geoip pcre yajl fail2ban clamav apache2-utils rsyslog openssl lua libgd && \ chmod +x /opt/entrypoint.sh /opt/scripts/* && \ mkdir /opt/entrypoint.d && \ adduser -h /dev/null -g '' -s /sbin/nologin -D -H nginx diff --git a/Dockerfile-arm32v7 b/Dockerfile-arm32v7 index e5e5bbe..84a7bcd 100644 --- a/Dockerfile-arm32v7 +++ b/Dockerfile-arm32v7 @@ -19,7 +19,7 @@ COPY fail2ban/ /opt/fail2ban COPY logs/ /opt/logs COPY lua/ /opt/lua -RUN apk --no-cache add php7-fpm certbot libstdc++ libmaxminddb geoip pcre yajl fail2ban clamav apache2-utils rsyslog openssl lua && \ +RUN apk --no-cache add php7-fpm certbot libstdc++ libmaxminddb geoip pcre yajl fail2ban clamav apache2-utils rsyslog openssl lua libgd && \ chmod +x /opt/entrypoint.sh /opt/scripts/* && \ mkdir /opt/entrypoint.d && \ adduser -h /dev/null -g '' -s /sbin/nologin -D -H nginx diff --git a/Dockerfile-arm64v8 b/Dockerfile-arm64v8 index f4091cf..1aaaaaf 100644 --- a/Dockerfile-arm64v8 +++ b/Dockerfile-arm64v8 @@ -19,7 +19,7 @@ COPY fail2ban/ /opt/fail2ban COPY logs/ /opt/logs COPY lua/ /opt/lua -RUN apk --no-cache add php7-fpm certbot libstdc++ libmaxminddb geoip pcre yajl fail2ban clamav apache2-utils rsyslog openssl lua && \ +RUN apk --no-cache add php7-fpm certbot libstdc++ libmaxminddb geoip pcre yajl fail2ban clamav apache2-utils rsyslog openssl lua libgd && \ chmod +x /opt/entrypoint.sh /opt/scripts/* && \ mkdir /opt/entrypoint.d && \ adduser -h /dev/null -g '' -s /sbin/nologin -D -H nginx diff --git a/Dockerfile-i386 b/Dockerfile-i386 index bf86ae0..f9e817d 100644 --- a/Dockerfile-i386 +++ b/Dockerfile-i386 @@ -12,7 +12,7 @@ COPY fail2ban/ /opt/fail2ban COPY logs/ /opt/logs COPY lua/ /opt/lua -RUN apk --no-cache add php7-fpm certbot libstdc++ libmaxminddb geoip pcre yajl fail2ban clamav apache2-utils rsyslog openssl lua && \ +RUN apk --no-cache add php7-fpm certbot libstdc++ libmaxminddb geoip pcre yajl fail2ban clamav apache2-utils rsyslog openssl lua libgd && \ chmod +x /opt/entrypoint.sh /opt/scripts/* && \ mkdir /opt/entrypoint.d && \ adduser -h /dev/null -g '' -s /sbin/nologin -D -H nginx diff --git a/compile.sh b/compile.sh index 102b4e2..2a99b4e 100644 --- a/compile.sh +++ b/compile.sh @@ -3,7 +3,7 @@ NTASK=$(nproc) # install build dependencies -apk add --no-cache --virtual build autoconf libtool automake git geoip-dev yajl-dev g++ curl-dev libxml2-dev pcre-dev make linux-headers libmaxminddb-dev musl-dev lua-dev +apk add --no-cache --virtual build autoconf libtool automake git geoip-dev yajl-dev g++ curl-dev libxml2-dev pcre-dev make linux-headers libmaxminddb-dev musl-dev lua-dev gd-dev # compile and install ModSecurity library cd /tmp @@ -63,6 +63,11 @@ make -j $NTASK make install make install-extra cd /tmp +git clone https://github.com/ittner/lua-gd.git +cd lua-gd +make -j $NTASK +make INSTALL_PATH=/usr/local/lib/lua/5.1 install +cd /tmp git clone https://github.com/openresty/lua-nginx-module.git export LUAJIT_LIB=/usr/local/lib export LUAJIT_INC=/usr/local/include/luajit-2.1 diff --git a/confs/antibot-captcha.conf b/confs/antibot-captcha.conf new file mode 100644 index 0000000..7722e2f --- /dev/null +++ b/confs/antibot-captcha.conf @@ -0,0 +1,40 @@ +location = %ANTIBOT_URI% { + + default_type 'text/html'; + + if ($request_method = GET) { + content_by_lua_block { + local cookie = require "cookie" + local captcha = require "captcha" + if not cookie.is_set("uri") then + return ngx.exit(ngx.HTTP_FORBIDDEN) + end + local img, res = captcha.get_challenge() + cookie.set({captchares = res}) + local code = captcha.get_code(img, "%ANTIBOT_URI%") + ngx.say(code) + } + } + + if ($request_method = POST) { + access_by_lua_block { + local cookie = require "cookie" + local captcha = require "captcha" + if not cookie.is_set("captchares") then + return ngx.exit(ngx.HTTP_FORBIDDEN) + end + ngx.req.read_body() + local args, err = ngx.req.get_post_args(1) + if err == "truncated" or not args or not args["captcha"] then + return ngx.exit(ngx.HTTP_FORBIDDEN) + end + local captcha_user = args["captcha"] + local check = captcha.check(captcha_user, cookie.get("captchares")) + if not check then + return ngx.redirect("%ANTIBOT_URI%") + end + cookie.set({captcha = "ok"}) + return ngx.redirect(cookie.get("uri")) + } + } +} diff --git a/confs/antibot-javascript.conf b/confs/antibot-javascript.conf index 7ceafc9..2f69019 100644 --- a/confs/antibot-javascript.conf +++ b/confs/antibot-javascript.conf @@ -10,7 +10,7 @@ location = %ANTIBOT_URI% { return ngx.exit(ngx.HTTP_FORBIDDEN) end local challenge = cookie.get("challenge") - local code = javascript.get_code(challenge, "%ANTIBOT_URI%", cookie.get("uri")) + local code = javascript.get_code(challenge, "%ANTIBOT_URI%", cookie.get("uri")) ngx.say(code) } } @@ -32,8 +32,8 @@ location = %ANTIBOT_URI% { if not check then return ngx.exit(ngx.HTTP_FORBIDDEN) end - cookie.set("javascript", "ok") - cookie.save() + cookie.set({javascript = "ok"}) + return ngx.exit(ngx.OK) } } } diff --git a/confs/main-lua.conf b/confs/main-lua.conf index 2f69736..0e232be 100644 --- a/confs/main-lua.conf +++ b/confs/main-lua.conf @@ -7,6 +7,7 @@ local use_blacklist_reverse = %USE_BLACKLIST_REVERSE% local use_dnsbl = %USE_DNSBL% local use_antibot_cookie = %USE_ANTIBOT_COOKIE% local use_antibot_javascript = %USE_ANTIBOT_JAVASCRIPT% +local use_antibot_captcha = %USE_ANTIBOT_CAPTCHA% -- include LUA code local whitelist = require "whitelist" @@ -14,6 +15,7 @@ local blacklist = require "blacklist" local dnsbl = require "dnsbl" local cookie = require "cookie" local javascript = require "javascript" +local captcha = require "captcha" -- antibot local antibot_uri = "%ANTIBOT_URI%" @@ -78,8 +80,7 @@ end if use_antibot_cookie then if not cookie.is_set("uri") then if ngx.var.request_uri ~= antibot_uri then - cookie.set("uri", ngx.var.request_uri) - cookie.save() + cookie.set({uri = ngx.var.request_uri}) return ngx.redirect(antibot_uri) end return ngx.exit(ngx.HTTP_FORBIDDEN) @@ -94,9 +95,17 @@ end if use_antibot_javascript then if not cookie.is_set("javascript") then if ngx.var.request_uri ~= antibot_uri then - cookie.set("uri", ngx.var.request_uri) - cookie.set("challenge", javascript.get_challenge()) - cookie.save() + cookie.set({uri = ngx.var.request_uri, challenge = javascript.get_challenge()}) + return ngx.redirect(antibot_uri) + end + end +end + +-- captcha check +if use_antibot_captcha then + if not cookie.is_set("captcha") then + if ngx.var.request_uri ~= antibot_uri and ngx.var.request_uri ~= "/favicon.ico" then + cookie.set({uri = ngx.var.request_uri}) return ngx.redirect(antibot_uri) end end @@ -107,3 +116,5 @@ ngx.exit(ngx.OK) } %INCLUDE_ANTIBOT_JAVASCRIPT% + +%INCLUDE_ANTIBOT_CAPTCHA% diff --git a/entrypoint.sh b/entrypoint.sh index f68382f..1674875 100644 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -54,7 +54,7 @@ cp -r /opt/confs/owasp-crs /etc/nginx cp /opt/confs/php.ini /etc/php7/php.ini cp /opt/logs/rsyslog.conf /etc/rsyslog.conf cp /opt/logs/logrotate.conf /etc/logrotate.conf -cp /opt/lua/*.lua /usr/local/lib/lua +cp -r /opt/lua/* /usr/local/lib/lua # remove cron jobs echo "" > /etc/crontabs/root @@ -502,17 +502,31 @@ replace_in_file "/etc/nginx/main-lua.conf" "%ANTIBOT_URI%" "$ANTIBOT_URI" if [ "$USE_ANTIBOT" = "cookie" ] ; then replace_in_file "/etc/nginx/main-lua.conf" "%USE_ANTIBOT_COOKIE%" "true" replace_in_file "/etc/nginx/main-lua.conf" "%USE_ANTIBOT_JAVASCRIPT%" "false" + replace_in_file "/etc/nginx/main-lua.conf" "%USE_ANTIBOT_CAPTCHA%" "false" replace_in_file "/etc/nginx/main-lua.conf" "%INCLUDE_ANTIBOT_JAVASCRIPT%" "" + replace_in_file "/etc/nginx/main-lua.conf" "%INCLUDE_ANTIBOT_CAPTCHA%" "" # antibot via javascript elif [ "$USE_ANTIBOT" = "javascript" ] ; then replace_in_file "/etc/nginx/main-lua.conf" "%USE_ANTIBOT_COOKIE%" "false" replace_in_file "/etc/nginx/main-lua.conf" "%USE_ANTIBOT_JAVASCRIPT%" "true" + replace_in_file "/etc/nginx/main-lua.conf" "%USE_ANTIBOT_CAPTCHA%" "false" replace_in_file "/etc/nginx/main-lua.conf" "%INCLUDE_ANTIBOT_JAVASCRIPT%" "include /etc/nginx/antibot-javascript.conf;" + replace_in_file "/etc/nginx/main-lua.conf" "%INCLUDE_ANTIBOT_CAPTCHA%" "" replace_in_file "/etc/nginx/antibot-javascript.conf" "%ANTIBOT_URI%" "$ANTIBOT_URI" +# antibot via captcha +elif [ "$USE_ANTIBOT" = "captcha" ] ; then + replace_in_file "/etc/nginx/main-lua.conf" "%USE_ANTIBOT_COOKIE%" "false" + replace_in_file "/etc/nginx/main-lua.conf" "%USE_ANTIBOT_JAVASCRIPT%" "false" + replace_in_file "/etc/nginx/main-lua.conf" "%USE_ANTIBOT_CAPTCHA%" "true" + replace_in_file "/etc/nginx/main-lua.conf" "%INCLUDE_ANTIBOT_JAVASCRIPT%" "" + replace_in_file "/etc/nginx/main-lua.conf" "%INCLUDE_ANTIBOT_CAPTCHA%" "include /etc/nginx/antibot-captcha.conf;" + replace_in_file "/etc/nginx/antibot-captcha.conf" "%ANTIBOT_URI%" "$ANTIBOT_URI" else replace_in_file "/etc/nginx/main-lua.conf" "%USE_ANTIBOT_COOKIE%" "false" replace_in_file "/etc/nginx/main-lua.conf" "%USE_ANTIBOT_JAVASCRIPT%" "false" + replace_in_file "/etc/nginx/main-lua.conf" "%USE_ANTIBOT_CAPTCHA%" "false" replace_in_file "/etc/nginx/main-lua.conf" "%INCLUDE_ANTIBOT_JAVASCRIPT%" "" + replace_in_file "/etc/nginx/main-lua.conf" "%INCLUDE_ANTIBOT_CAPTCHA%" "" fi if [ "$USE_LIMIT_REQ" = "yes" ] ; then diff --git a/lua/captcha.lua b/lua/captcha.lua new file mode 100644 index 0000000..7244791 --- /dev/null +++ b/lua/captcha.lua @@ -0,0 +1,32 @@ +local M = {} +local captcha = require "misc.captcha" +local base64 = require "misc.base64" + +function M.get_challenge () + local cap = captcha.new() + cap:font("/usr/local/lib/lua/misc/Vera.ttf") + cap:generate() + return cap:jpegStr(70), cap:getStr() +end + +function M.get_code (img, antibot_uri) + return string.format([[ + + + + +
+ Img =
+ Enter captcha :
+ +
+ + + ]], antibot_uri, base64.encode(img)) +end + +function M.check (captcha_user, captcha_valid) + return captcha_user == captcha_valid +end + +return M diff --git a/lua/cookie.lua b/lua/cookie.lua index 4b783b5..5d69f5d 100644 --- a/lua/cookie.lua +++ b/lua/cookie.lua @@ -2,28 +2,33 @@ local M = {} local session = require "resty.session" -local s = session.open() -if not s then - s = session.start() +function M.session () + local s = session:open() + if not s.started then + s:start() + end + return s end function M.is_set (key) + local s = M.session() if s.data[key] then return true end return false end -function M.set (key, value) - s.data[key] = value -end - -function M.get (key) - return s.data[key] -end - -function M.save () +function M.set (values) + local s = M.session() + for k, v in pairs(values) do + s.data[k] = v + end s:save() end +function M.get (key) + local s = M.session () + return s.data[key] +end + return M diff --git a/lua/misc/Vera.ttf b/lua/misc/Vera.ttf new file mode 100644 index 0000000000000000000000000000000000000000..58cd6b5e61eff273e920942e28041f8ddcf1e1b5 GIT binary patch literal 65932 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�+D!e7ssCn!YTBEO@~pR+;}}>O#2vU; z<}fj^F^e-YunF7&4@zA*@&?+-xyj13;`cJ-g^7WiixYeY0s{vl2RC#>xxke(M?l%Z05sCXsLaT&uFA*~v$y!~I;VdJYb6<_ z85!7W6MpVuF4hf5`fJU=$RPhOjFpK^gh831%fW$ zn2TMGQHpCNhj5qBGqIKYj9m=RSXL@d=a=D7k&~5{lH}p$}5c2S3#E~vx81SU}gfflA)6<=EBORio&2$ zPK=dFEkGxsNYaK;{@>wxt3A7{QuIv{^%HZII)5;V+p8R#ZkodSVTr6vRm;DV{eN5k zJ-#d|woFm6a~adxenzkDhlLoJ7;Z8Ou-<0=!XV3F&0y~!uP1M*#h|Ms3|heB+@urXLT@G~$oGDGf5VP$7!V;8t`2C}{V$QeOP0|$|0#$2WX#tQI) z)G{{qJcfD((7CRhjEaoLjEYQa{>3r7{C)p`$@$kTmUq98u=4-12c3(_(95FAqRha? zz{}9);K}>~bY|WQ$PVup%uJwF$1fNd*tlMB^Kf&%;Nau|QCyt7?7SQ=*x7lVIGMOv zp0hJ7K?$T64>>de#0FtdCmDo6 zqYaFrjG%KQg&`D+D(H@hf7;3ribZ+}OmQ#JC8~1oaCKGW4=|Abi5}f|rMr ziD3Oe_Khn0&N>=Pai zMpg!nd@gpzd}f9MK7l_rf3DcP0hg(u#we0cAT9=NaDw;-l^>}j4Dtsfqbh_t$awXi zHlyyp%Z!^~EEeT|aY6s$_A~M@^6W=28JHQ~Ffg!-fX_W=hn(5Y!NSnY#m2-U$;u$f z$SDcx1pWafZcuy9=8ch&0cfxqhMA{bxx%#a>eXLsuU`EJ2A~x%;PXGgD_~%&j@Uq} zj^OK&Kr3KiGOSP;$O;&c3~U7qD`*7_=!_r+1`Zz9s|*4Rk_^@k@~l&t1sSI@G)jO* znK>nd1h_=mm}CSQSU5x_dBKPJT>%}84cg!c8gtZt13D<&NZ=i4SG6E$n}DLIBIx)Z z21Rk`VL-<0zhI}@{l5Os|DPk{e#VXIjsN!lJHY6`=-7xDKdxacV9aH#S@rMXzX$*B z|GUq?3|h;D1l=vt;$X3q}krWZ^;Z1* zWt~V$xsbKsQ=t3B9`Ht{k3E;2{9g1wV&wR>0lLqLY08Yv>*krlcBtm%=H?4C)TTYz*@_nHzaHx!5GxnOWEwBpE@s z4V<}hM&RF-bI|-`z^H60Y|P9GCK($+hwU=X0+Gxw*D~7wJGd51GGLg^$vlsZ0c1A2 zBpW06?g1W98|aL{op(rPgU|3)1(S>nAX6b`vt9+C<+}zP(0Op-uyZ%qHf4jMrg0nddQpbTSr#nhS5>Iw97xfUIY| zI^*9#Mte~2=qAH7*4r%k;5jgE22BSMhF45%uUMEk=CE+_aI!KMu`qxxX8>QyV8|eF z<;WlKMM2;u186HJqoSxH)BSJXzJ2@q3p5-_(i z=`6VCtnDDozz;h8jURk7Ll4757Di?k0dN>Sc>-&5fLa`pO5piI-35PQ7O;N!`59Ec z$1w!4>|xo&06L?)*};>UQ(BZoSB^nlhDlMDM@UzMn~PbPpHWtKr|@h3H|jfOUn?5O z@h}LnXsL)8$cRd7$qLIza5AfKb1^bAsc0}2vz2IR$P`N$2;6z{=F1by<)Y;X|L5nQR%+<}s!GjROpy5GK zH^SJ+jJf)J(K%HWHI0i!=dDX@ttxEK7oJyDRZ}}(eBQjo!KO53c2U8+xrK#um>*s& zK4)ESXJ2f2zVN(;nyTvg;&XOoRt_Z==Zep(tE&8~DVQ~@plHrq2GA`W482Un;Cj#9 zK^1%b&E~|!@|{kkmuVW$K3WZlTrYy+ULSNT1)B(iD1#)!YzGZ)5eatYl?(#yd=mS_ zHu6p9WZ1*XD9FdpBF4nQz%RnW!6DAT#pWXg+F@@5+G7t|UIE!=Z}ZIP-9OMhM5tTs zrNCVUDF!L%Xd!HAvJ`kUSc*XkIvVUCB_JjsE+8Qw$zmWKAl<;C98dua~P zNFZqG9B6JGy!RfqQW@01XYtzkkA3Hc4Qn^=1ntNFxpOCI%Qy2y$WHw2>p|P{88?A; zVI%z7;-JVO%)-yTl9Q#KVY(pa9$qF^1}+X}4n{5>4h{iEAwC~5kQX671T}0yje%!I zApe2tS@>9}7}$4W3}R@$69fBBj6n?MJ3#~S0PzN{2JQy&1>zUP--~m^f|m~|Dxn=K zz~sfmu=C$`#K{7nebKDdsAmfxRv?1r$`ypzb}|TX?Brr$VB+KAX=FMi!p{&X$jcbc z$r{Pc&MYEu#m451(H{^2?zSE=5;$Tc02;n;9rAwr z@?~Z>&>b@C{tGc)2H$5R;lPc!&xQ>&x(%x8{=5M-vY3^HA=L=$j?2>G#wGwzugDSW}fRIe!y6#^ZsB(Z93aykF*g@?WE+6n(OV~YW z_6*((>l|E=?n-lDn8c$uNv>C8l7t)EBom%qD>b1W6^V&Hs-Dj3hT6`&ww%2De9XL} zhP=|Ye4f0b=Gwf{e2UyY0)L*oxpL$U_zaD2ppi0IBNx=vv3X+j#t7OiL@S#?d!Rsv zkVix3P{8Z*U@I^p_A>Z@)0Gc{4>VmNUxxr$k^~tCWkebiRWxQ)7Dj6MAj=_-mvVdw zPf0<(T?ktGdf1>coBls$)dbrETPe>1T`3Qd0l69` z!;T^YG7Tof29klD*}#&(z|5eqonb%PnGM{CGaDf1UML>~A94ZSqk53#9kV@K8Uq`H zxdRUiD`@=^I}1}O8@m8xx#Snn5r+(*WbM5Hvc{AhTx=q3*)4m=KO-LlyJ(nL zIeQpKnT!BvRseL8D5z3|bT1);^AHlW;E6K?G~NOl#X?`&%Lo~pWCu+v8Hs@wK!WC- zjKT9xjDIY}6nWKDm};5QqSYK!QFyFT<|4wLLG6=e6k!ZdeSVcSiCLTV8v`?g{&uGK z4DVP#*KvW{WPb>pVFjAjxXKjvcNOcmDIk-MvuZM1vUxD@GK4$uaerW8XV}cg9>&2K z2D(!14S4A6i_I0Np;&HkV*qWxgpS}rc3<*=3lhZLZ^FjlWgX@a-ySdHP7DuA;4R~t zH*w-THV3lowM0NwKh>cW6F#z9jKWG|cq zIORw%NFXX)t`J0F8L^iELj_Vx(Od*Hj0TzoFcM>$5#it%9`5K6Q8mA~cs>ZTezA`T zcW?}ka424|03-%lo2UG*l<6P?1A{PwyaOL68yD|qHU?n^0d;0h5zwe5C_eSC{5c|I zX`l#Ofv;`~UQKFhqNdIiVs2JZrWdZQCdJg?TxGMcY-n#=$vGbUwq(xQE=er=Hp`j%=ZPf0hmQyPy}@E8Q3#_Af7P>uZsoc z0Pvt4Xd;ktN4c)HxtW5Uyqb$#Rk5jUguI>}KYuG1S9q*JCa4TSE(w?!wy-d6W@lzC zWrUXmHg7CZosUrxFdK`T8#6O*0~Z8eS>7>LeEs^bgMsn?Ck6(VQt;|*dj~llZZ6Pp z9|PM|hDHu%7DjG91{PLE86I{kPKHW8fiE_1KutGDrUxw|c_S!pX|TnBKY*VJeBg|# zIAk>tySn-1(~On@|H_%J|B0L8`;hhO-x;^_|GoSBcoma%R^B{N?qgzLVDbQ;h!*D{ z#l;C)fyl-%9dsohCl>=t6)PhLR~2N>$CE!cpc8w+aitHshz53OJr6j$A#G|F7Jwcn zU(8a)Rlxgz_X95{==fQ4anR-nc6IaPJ8FGS?x^-U$|mx6SyWcix^(FX> zVQ^hx2Cc0)7`j2V73ACw@cx%Dhz!F6260d?k^y{HBUq0uM23-rVINozc(*E8Pb@@+ z;Q@m*1899L%fCg8>sV)i`_M8Dyo_HNSiZ7yurRQ)F>wgou>l>w1Zu#&F#sJCDGb>U zxoQ>TjK5q=-&hzxBq*0-bsM&d9()LqWP;dV@6ce9rk?^SS5q%;%fWKVM+J z;C{~iT>H8A^X%u_&%a+_zn}nk{RMcyOA|DUZ7d8LMG-Uxizr@ZT)?=IaoWGke*ymz zPM$o?81QeyXGZA760BGMLGO=X+B1Ri@juxKe_z3_mVlo1R>~&Az{jA$sO}J_TCJuk zr!1#Zt*k7^U#%>sQLU~ar!1$YuA#1~qN<^xuB5CauOP3epdjz0p{}N?q^Kazp)AMD zugtttnscXUkF@eeO*M5P9z_KS4OL|&c~&`oHXb3NKsF%>S543$*&9L7#1eE?&yYa? zw0KnD9cY>!)J+Djh2lHOF2DiBVD?*fzPF%`K4{!DQfZUD7HAh?nV71!yoQp#vc8I@ zs;-)@x|W8Ss+($rLWCmdq`@?$QiW2*QYB$kWmOe5H8pj04Ko!pRW}t^)hLB1#UzC! z#d?N%#(JhI=4$XcLG?)I1l0@G3)hQOi&jZjf#(WA`w}aZ7HECYQU~?hz;m*Y87@;3 zHDS=AdeF4EsR?-IPn}g=Roz%zRh*UC%_u^1`ObBdCayE%tSkEWq-3Ylol z|94Q4QF7~(dPZNxe>a&Y3kuvgyyxD%Jym-c)%yQESo{CqNk;SkzZlI~S+`^-$DEm% zpP0n(|37r(1k~1njhwK6Mo!?PEon^ZpnL({Nf-x8^PnAz;Pc^m7(^Lt9i;iDa`H}P z`o_@6Ey~Zy%)%%n$!5>X#VEtTA_h8i$_Rc&JZN^%z=4xrTwdH>JYF1fQvvw8L>6sE zL1kg+wgKgnr~bhXhd=eNch<~4)~o-1JiK$~(Z6*p>VGdzYHOPaJ0}Txs-T^NH0VS@ zshtdbJsdk2_cBk%eooSxC*X6E(9a?QpOYjA8vZ2doFw*re-~jrC<(OBmr0lPHq&*` z&7tsHMnQY11VG(BMNviY%0Z^wmgM*!A4)`z% z);VnKY%FsiCsND-ok%eUR3Xg)pGe`v#lg-7y6KC9m6@9pbb2&9Xr&eh69f1#3Y#;C z<=@B)8X>ExK^Xv+5nyzr5+@ToBcnPaqd6lZ>zse)j3>+gnf@~^V?6QCqKxr0<7pP3 zf2RM;D;ZD!Gp%Gi$#}Z*pD82gme)?EIZQ`c?=x^Q=r~BQGu-85zRSXNm-9R8L~f26 zwpivG#u^50fjgip0m1DS@V#0_2ExXI?55_RTNV}PoVow3^X%X2%v?;#nkDLgwevuA zOB~Z2rY+$6Br(@-y6J`p$iqUudEv10P2nqbMJq{_p$ z)pFvxe_Jh>`HJ%7Ef@{soC1}Vt7T+ba=QOLF<@Y1NMh;*k1%R5csQtoZtJ|uDSuZ% z_^v4LUCtkD-(~J9{7{~#DJIMyB3Y*6vewO{+&Q-*KPN0ME-@uuBQ4Og!FiM{~0>}KW487pTz$GT(7A!>|r(q z<#k3*mhQhPP!;yrR4{ymsz`#H@d<|+pdMW}xL(x)t6*p3Vw48gtA+o~)EXbs=K)9$wD$YL zCIY_s(91!Eo%u2+<4>iX3}P}nc?EjJcCb$8W|HS&VP@dq;9_%^6cTm?-Ie*p=Exs$ z8}J=V;Nt1XyT3N?j10CVs5Pi9Py?N22^x4ZH5LWk2?#k*3VMJg=+p+tm03z`%!~f* zVszUvYZmsCP#OJdCr_^Z^#{jksEiCG-HS~0s1^hCK4U{h83!u`q034F+U%D(B^OGq z6PqdvxqeH3qHK>Qr;)ajqNcK%kN_7mFQ*`fj)IynyC8=kuZ$#vnwFxz&6OiYe~y4o zD}_|`f}q_3puuoM27x0+PriVzcY?Hn?t;P&dSo_YXox=~8oW8jdxHdc6OIwMk7UGP z1nna^RPizJF>*6;8-|Ev^5pR4@#hQV3+4;u3ulUC%jYZPE9NWZE9a@?sAj5X>dt4M z&jD{^@xoeJ^M&RMPm`aoFkf-L(tPFlD)Uw6tIb!RuQ6Y9zSey0`8soTjTkr>I0ZNa zI2AY)I73B)<+4Q6<+(sj0LZ3Mc13nY@M2DJMRw$QItFHCb7K=w_rg@!+*kGNuCJi1FJ8^a$f)Gbz{GH!m5*7I^(MGYyVQY~ zh2b0<6MSpbl|SGUra=qNLA?~d5b$|dpkuPZ&3DAMCh#CO_!?etz=KqP!yIK-lwgSW z2JE|{7(g+FMUw;OZYjoCP}dnW`wZJI#d`Df>C+61jKwVPm_C3<7wR1ZnV1=v85tOu zofw#Yu`n_-vj`j!H~^}M&OoaM=>5SAps^1u{c{G;$P7pv>`|m!=M~tPM43R>s+cjk zF@-T@F{U$d@-y%=$}`9_>NDswvVnGlfV!LJJD<#C{r2w%c+`fE*^Bih_^x0@F#&#N zVIFoy24)U%p2IA{how1w$q6v>Gq8(=Nw9};%7MEOupyf-HgB%HF%p8@)eedS@M-IE z;J}rGUj__wtsL03h|7bw2yn|wF~vg1aby{l!TVXE139*gkg*NMKbDg6JSrNs>0v6i z2A1MV{0bViOz9y?)~uF_eB4I5Y^h}e(sH~!#+qy?UBE}%ZsKTJaXv*l!7|IaJIGe9fsLWufeUn2%`bKqCeUJ*Bj9S-<_Ngw4GvHA zPB$z((GSZ5Cvz+(>^TU7k^m12C=tlAfRceO3nxP?BXksR(VolQ%a(Ps{`mK8(xc~+1vLi-Nmw`qIqd|xIK}Nr^SWLP@85o$E8Q26EgqQ``6d07471%%y zg$^<-0=x4v%R5G{O`HCG1NBzzndUHkVZ9H&t^10D80eDjyR3|y-3-n_vK(Y&b)EeuN;wlc_pS7JhM{T5X=RWxP1`|mWPso1ji_6h%zSkwN+GRgdX zlDlhHC8%wBoRy8)lJy4zFGH3CA3FofVeVh>QAfxE8G$cW4#UOg@8&u&@d!uGU>}1 z@GL%PxRLetS#aBn33S^60|Nu-ENMmt21Y3c21YiJ7s0X&jLhJ^AA|!VLB%NxD;qlp zCl@ylFCV{vppdYLsF=8fq?ELbtem`pqLQ+Ts+zinrk1vjuAaVup^>qPshPQjrIodf zt)0Dtqm#3XtDC!rrnub+QFU{G*KXjpheWK?uaY+QUoB12MgN@`kqMrKxaPHtX) zL19sGNoiSmMP*fWO>JF$Lt|5OOKV$uM`u@ePj6rUgo%?TPnkMx`iz;gX3v>BZ~lUX zixw|gx@`H1m8({-S-WoihK-vxZ`rzS`;MKvcJJA{Z~uXVhYlY(dhGa#lc!FfIeYH> zg^LWAE?>EN?fQ+Iw{G9Ld++`O1{Nke1_lNz1_cHd1}TO*hX0K0jDn2vjE0O(jKPeF zj1`P^jFXs-Gu>rg$$UUTO~F9HN@0znk`kMeoRYSZhf=EQsozZh|1&T!C@?GlcQ51^ z4Hz96gBar&D;R4TCovskx&hWJtYDyE3D(P`#1Gb+pnB>zj&VG+YxhLa4(8P+i*F(flg zU`Sy&&5+7)g5eayd4@9#XBo~hykJOUxWsUQ;UdF&hIEGa3=W5Le1<}X0)`@nVun(N5{5E{a)y@-6%17jl?>GkwG1^3I~W!+ z)HBpEG%z$Wyk>aIu#4d@!#}7`{xkewxW@34;TOYWh9?Zy86Gg)Vz|w4hv7cM4TgIR zn;14j)oo+g&Txc*fnf{7R)(Vt3=B;ShZx$Sa;rh-_kh_e7-lgrFwA6l2j=Yolc3|? zmO@wzlNkD;tSJmrSa3=AO5Y|3cLypG{0+ZKj-tl|u-*k>^mve_~;G8r=* zWtL~yz%0+OgoS~@mTe2eO{Q*!c}$NPQkc3KZZmCSU|=`C=Ffg>URx${%)-s%AoyJhi zrpa)Um5<>h%PfYIY{m@5Y%B~XnPNcr|1?&6hLbF%3@6#1Gn{1o!*G(#hoP9|GK6OB zXE@0!!qCq0`2R1~+YFmPX0pFzaAB=xuw{At{|{?5Lk!3-AU8A1GaLuGfra7!8x{tJ zRV>dLs#!%CcCn-}6tapi6tcWwC}g_GpbuiR3Nh?rvSc{OWcmL#%OVJy{WQZakT}az zu=-sfb&ZAUDD=lO@9hCd>cFnJoX`W3v4J1?D!8`;alnUC5Y) zfx(i6f#EQ#IKvuH9I-Gk#IWpOGzP~X7sDX&+W!wLUEZPkE%tslDnU69gu*ov`vEOE> zW_ils!D7$Q4@!F={j8DyP-3;rPx*0Zs%wYb)zy=QY$N&E@hW+bg6=#TJHfET| zY|LE`uI}K7#>+A%hWv zF@p(%DT5hUGSA z3>^%e3|$P}3_T3J41Em!3=z3Wg6jlOEdZ(!b};N@*u}7$ zVGqMzhJ6hC84fTUWH`idnBfS+QHEm-#~DsAoMbq~a2i^xod?%wpnB{o!!?HM3^y2V zLTj754EGrBGdy5;$nXeU4?Sgg#_*is1;a~*R}8Ni-Y~pnc*pRb;RCo%{mk%{;S0kz zh9BTs>o>z6aBcRVkpX;|7wFy*Rz@~Pc18|HPDU<9ZblwPUe4mQl8nlNjMO~7g4CkS z{FLO>ypq(S)D#xi(xQCUWHS>}OAu*h045E=q!E}j29suB(i}`$fJsYmvQuJ!zzBxP z4F4IoG5Iq$GjC%4#-hlQ$1;Ou3(F~%d#uc?uB=ULtZa2`57~X#x3mA@NaZ-s@s3lB zGl;X8a~+pF*M05??hV|(d8~Lcc_#Bb;}zkx<*nl5< zf#3t7y~16>uSEhxx787SE* zc`1b}r6?6D)hTr=%}`pZv`J~d(ix?jO3#!&D>EqbDoZJ=E1M`gD*Gu%D`zNAQ9iF? zqSCKwtQxG^s3xMeOWj+2g8Ft1VT}ll!AS7~3?anb45*{B?!}CTSMpZ`ljopk3j0=owjk}De8$UCtGU+#2Y;w`$zp0F= zrD?k9MALbut4+_FMVn1FJ8SN2zS8`)1%ri*g_kA2rHrMfrJ1F(Wq@U@WtL^RWsBuR z%XyZoEq7QRwY+5c-b%)*+G>H-Q)>?EIO{&^Bi3JRlx*T{mfLKxIcRgv=C;j0+hjXy zI}f{1yBT)R?f%*)+V|SuajNR~;TXIyiZDU|$pCZA)z$IG2Ai$vV|1yIBgX;h73FxdR(We@x~jlrHJGdcleJ*76Rc(em|P1sVI7!U4>n;J z*yP<{_Z$a{p9GT^z_J&?6s{xw+d8?eYXu&;lE*?$=r7*rSp{$FNL zVG#U(he3rw7EG!zSTd+EsDas9VA2Ront(~rn79gq`Ts-)6$VhtUxmT)e=UOwgVp~D z3@V`W&KXn~Y{0T^VD;``(gRF-{@>1^!Vm;zhk(g&FbO)@QH3G$e+PpKL)8DZ3@QvU z|4SKE7?Qv)Ndc=#2a_3KH|K-d1z@reOcsI3VlY_=Cab_?HJGdcleJ*74s1d_m}~%( zjbL&m*lnx8?6qKf*MZ6PU|n1O{a{dG*!rKDL4{!#*loMPemu&c&!EC^9IWyrn7ja1 ze-TVx0+W})W?li4SHa|CF!>5>!fUW;-@qz=g2jJ>*?+*~UoiRa{~ZQZ(Al}58Wqe2 z#fvHfC|*<<%)la+{|_>#GFbiJ&Y;R*^M3+^Dg!9oR2e|wrpmAqEV~M<667OQhAm(< zTfyXRFnRRl>A9Zjzs57Yizr&!;U;IDs>I@)Xt1}$^@4}$Ya2zaq5=?@8stLA5lR@y`b_Pub+5d?QnhYxcT^KaM zp{>ba{{IexCW8xD#N+=222BQ^|Je+h3_<@v>O;Zo2(WkzSWOa`%mkBJU@{v_=77mu zFqsD?3&6Sx!DJDbEC!R6V3VuBWHp$q0h6_0vJR}K9!xfX$wn}_l7WFilL3^fH5t}| z&0hy5LE)guuoW!33oLT`ozgZcmM3|b7J zkk?|c`9F(63mn&444{zL0*Aa7IIguAKyj_b0J2ewVe9|P3|b7k!Rn9xKgpoQ0E%lZ z(2PHW7Q?Imvlz6&wrGP(By9#+1_lOg29Phb!M@OD*z$ingEqs~{|6bg8IJzn&Y;U6 z0471{K^GkRx?nfxg597Cj(uGQQ0(i1_3DE4>M|VtKY>9P9Phdepm^6~5csdjpa)J@ zdJJm+Cot$S7=cMpS*OQf@&6=)9yovLG1&ay&Y;KO^8XHl9)rjKSqyp%L0~chOve1r zX3zt>M2{f_Y(hGi%mAB^3D%VbCbPk04w%dZlX+mW0BlYnm@ER5#bB}$Ojd!(YA{&? zCTqcD9hj^KlMP_95lpW9U(28e4m~|^Xy}1MLyuuS*i~EpA7s!2hlw7;F0jjYgMA0e ziF)Ah(SwH11+e;yVDb`}ybSiy6)<@fOuho^eGN9}H(1Tz{}UMW83g{{0hg5e4EhX4 z|F<*fGnoH-20CBz|11W52Fw5Q4EhXK|6LgL8EpQ2V9*DjEv?T0sx|b%^@Tn|1lR;n zZqjE+VgQ+w4mKwPY(gdj1A{&TXmm=SAsfuj0h764G7n6G>I;1aP<^4#0IDza89?=g zJ_D$}&}RVE7y1mK`a+)pRA1;bfa(iShpIlqTCjW8fywn? zS8e&1$e_=#^)JYep!}uJup8_zP^juN90#jB2_`Rq)n5dYm%!v@u#c{Q$*W-U6<9AQ zCmMm;DGWvog8$bt7=i0+BXEi}0+)nF4A%c^8H~Usp%FL*8iDhI5!jtZ;Mg|;$G#Cb zKN>M?{eOqS2wV~xflERoaEu#)W88=VR1zA4TL{Jsg8vRO7=!bpF@wtgS_Wfq?QaY& z0gM@J{$FM=W^nnxgu$4>6=%nk#Spx879ulq4(NCKM% zs@aVhGQlQffyrzznFA(s!DJqoEC8Ed2qufbWHFem0GkXdCyg0E<)kqKsGKxr0F{%* z44`t-7#y3%44~LFW&p*eF#{+zjlrR03=Sn@hP7b#tOJu<7}OYy89+6ZG3d-~24jZZ zV1FI`2lC@_u$q%#5>(3@Gk|J&V+K$yZ_Mxn?4DO(^{*Ki7>pTy{lCLt0**lwaG7QT zw$TI}7bf7iFkv|Qzm~z2LE!&d22%#X|JxZ%8D#$-WH4n=0gI^ppT%IxVE#Xw!IZ)B z{{#k8aC_C1!3M0$<^N>{QwER!OBhTUg1}@1n2Z6dOahyb0yZHNtTGEsW`oHbFqsP` z^T1>QSZ^ViECQ3oV6qZSR)NWCFj)g8Yr$k4n5+kr4PdenOs)jG0aPQKGOPu=WF45? z0`|*Ru={s`)$9hdkN&^IV9IbDtmY(`d<7PN4Oa6T?2EtuXEB(8+Num@;J$zv18Dt+ z83Snbz8QlF0|SE@xHV$Nki-CLmw?*jW(?_I@wH%)bzpKmSmZdEeG*Jw0E=7%lb66G zs2yU)@D0ocwL{DpK&@yq22d;7oI&8f3xhd>;D25Qa|YS}@(kt-D*qE1%o)`FcQBZP zTMOn4F8|gtm@~NjTf$(@-~lFm{vTv8X9xnbL;qc7FlPt@tBLq`lEIuI3e1lA2U3#+ zHZ28gS|(UL3ruE%$s9153nufxWIou;0x($!CX2viF_^3blT~1{8cf!J$yzX32e!Q) zOg4bYMliV&?8jCACoq^ZtOdJg9hd~Q;>^J{yE(%yuxpS0`@vw&a2zaq5==e;yXrMq z_7|A_8|;@qU>pB}+5i46VXy$VE-V;y85kHW7;OLFVXy$VD=ffm2@7yr!UEi8umHCi zEEt~tzXR&`{O@3}WDxxKgTazP_J1U(CH}vb!4lj;wq$VmpU7az-~lE<<&q^s5SShD zzm&m}A?81*;k*? z=)Y$SmJFcs$C3e5{#b(B?UvwnyCt~YZprZa|49Zba7kjtAoyRD!HPlle+PpVgUbI2 z3|0&-|7#hn7(BotpfIsw2m-T1|1V*%Vu<*^oxzF$)StCtNCKh<)k$@by+i{fMwIcvY>L$ngLYqSu=pjJ!=L~xo6D)a*s8*#I^>fMr&|tv}RZj z*0tq-B7-%<*8gi6tQn5}zsz6_PNml1RBFv|0j%aCn7jlge}irO`#+n(hC$#zFM|z( z;J-u$8wS~b9~f-FrH2i;6>0-+h1xK<{Cmb=!{7my_4&V@!G<9S%#Qdk%3#9)Dj#gX ztxX#SP;1kMArq`V3ruE%$s9153nufxWC7TOLNHkbCX2yjC77%Nlht6d229q1$vUu& z^Q4LtN1a1+A4kwptg!11E`ha2X3YKfml!7ad02GD3v zC_^Mz7iioplp*^6Nrq5xYcP}nwErQLAs?)(08AEw$s#aW3?@M>y-kV z`@j$dj;Sbc7)F8BM}gHxfz?NW)klF-U=+jA|C$WZ44`qdXa%7)(}z$to~e4JK>AWG$EkjUz=ffJTsF!1*YKLGa%lh8PCf|Je*NV3)*z zT@nL!NetK}F$_olU1mrExBQd9X(kCA`$-I-vB4zp*kBR^XbdU|T*D?YfW`)sz-_H0 z@YrAyxOb2Q9ve&oj}0b)#|D!aKx2bRU_T~-Q&AGQmzTr<8XHUk_w|w(K&d4OoO6@F zsU-zFrcz zua^Yw>m`BvdP(5EUJ|&km&5={GfChylLSsPN#Hb-1Wq$a;53uO02&)iVgQW|CV^|` zBnHsfU=nz2FbO<1n8W}Y8%$yVjSVJ&#|D!az%2}L9i0TOqm#gObP@xo*N_D6H6($1 z4M_~3c4iU-XlyWv0W>z4!~hy|OJV@EIg=PbV{SShx#!|pFT?#lB zQo!SHDd5^I1suyM;8;!p*IX&!nkxky<0;_YO$vDYEd{(PAq8A>rGRU$6tH_zz~gTz zU^k?L`!DI>9%wo^RiuM^py}WqXgatDnhx%Prh|K+>0sN_!J(H9?t!L*d!XsyUQaqW zr=){>py}WqXgatDnhx%Prh|K+8DM{9fZK!_;5K0f*k2i7zhr>@k^y#e2H4FR;5K0a zxJD=hlSN>%7)*lZ5EvLLz$Iq|xFoCqmw^>v7gm7FzzT55SphCNE5RjaB{&yUg4I`o zOU_ELu1c`3O0cd#70kssZb&0qd#(>#7Co zss-z+1?#E>>#7Coss-z+1?#E>>#7Co>ICcR1gq%;tLX&S?495c>tq0pAx{9O#tC3G z6ToUFFl_n1mSF%O(%x^FGG?pq74`__VU@mg@*w-#LYtp(S8Yr%EjT5#RB7F_qO1=oFR!FAtS zaNV~ST=%U7*L`cjF|rmMBWuBR-&zLHh}l|j-M1E8*Q^ECeQUvW-&$}wUkk2x)`DYa zEx7Jm3ymXCt7aWIrq+RDY8^PH)`4Sc9XO`efn#bNIHuNtV`?2Zrq+RDY8^PH)`4Sc z9XO`efn#bNIHuNtV`?2Zrq+RDY8^PH)`4Sc9XO`efn#bNIHuNtV`?2Zrq+RD3bZ!S zl3^V%lFD_28DndT>mw2e%y7gIf;k!7YdN;FiOBaLZvmxaF`O?4$MImcx2*%V9mZ<***y za##;;Ijje_9M*$d4(q`!hxOo=!xjdWe?J(uFsS}N$gqV$?f-U$Eez^lwkDX=0*h#a z*}7m-4@~NVNzgpS76xN5+XSrA6wEdUuiyl&UD*O2{ocZ0{l9}@3j=6>>lSePWD7W* zY+(reKY?KjL(uP2p0NCLYs1uO!Z(b&RJ0ajTF)>Q>2tHERq zn5+eponV(t_*cuYg<&n&gmqvNH2biH0n|_2!mu0c&f{S5lVB1w*RX{FG}o|&0W{aJ z1>92F!tfGo`zx?|Q15aJ!yB;q-@xL(!Q|in+ZlF&dl0+8J&0Z49>gwi9PI+PEq8(2 zle@q@h+W_w#BT7&#%}P)#%}P)#%}P)#%}P)#%}P)#%}P)#%}P)#%>1C$i{B)$i{94 z(8$Ja@W{q)@W{q)@W{q)@W{q)2GGdHZU)fE#%>1C$i{94(8$Ja2GGdHZU)fE#%>1C z$i{94(8$Ja2GGdHZU)fE#%>1C$i{94(8$Ja2GGdHZU)fE#%>1C$i{94(8$Ja2GGdH zZU)fE#%>1C$i{B)$i{B)$meeG$i{B)$i{AP`)4;eHSPwFZ0rV)Z0rV)Z0rV)Z0rV) zZ0rV)Z0rV)Z0rV)Y#arTT^|M4+DE}*a+E>+|8|C>44PmPls=C#XoK0h4EhX58T7zx zeK2YCe*(i%24gVW1k5%Cvq3X!M;R>tbub)du=>x;aFoFsEN%l9v1MRjILhGp{|>`Z zhCr}5XwLd5LnxRX`CpXbC_~hL8-}9{pjm^X;F0sA;P5{R4*#PJ6=0R15sahY5sahY z5sahY5sahY5sae@onSQ+7}OY!GJr?i|hoc}1pZm@emqwYr;K%?$Q z!C`e299BocVRaN-Yaa!t&!Y^l!0JKc|3?|#fXxS`&!gb*JPHoaw@Fpy5KmtE;tUZ3yy>9g5%)2;5fK0I1a80j)Uuh0UT2oz_s=T zaIJj-Tx(wd*V-4rwe|&Yjc@_%qYL0#`vSPuz5uSZFM!L*3*cJ&0=U+`0Isz!fNSjw z;9C14*v5-s8!v)wya=}OBG|@@U>h%jZM+D!@gms9i(nfsf^EDAw(%m^#*1JZFM@5n z2)6Me*v5-s8!v)wyacxK64=H|U>h%iZM+1w@efo;45w(%0!#!FxuFM(~m z1h(-K*v3m>8!v%vyacxK64=H|U>iYe?id&@gU2*3gWC+C@iveMXnoXWaG&uqcueCm zxQBQdTvJ~G_nNPObzK4Lx&rQ{UIF(suYh%30qeQ~)^!D}>nd2+Rj{tBU|mctqkQc#Y62a69A`xE=Be z+zxpKZil=A*SW92?T}aCcE~GmJLDC(9r6m?4tWJ`hr9x}LtcUF!B^mV@D;cn@(SDz zc?E8VyaKmFUV+;oufXk)SKxNYD{wpH6}TPp3fvBP1#XAD0=Gk6f!iUk!0nJ%;C9F> za69A`xE=Be+zxpKZil=Aw?kfm+aa&O?T}aCcE~Gmt@;Yw4tWI*nOER;$SZI=^+ufTQdD{#H}3S4i#28Z)&aJ%j`xLx-eoWfs&+jXzO?Yh_CcHL`myY3A* z%-?|LS>Ax>S>Axd{|z|&-+<%j4OsmfaQgoSR{ss04!?m{j(r2SoWFrrj(r2C#&2L7 zzkzN122PpZz$x<^I8S{8&o6%iuN?cv09rZr4V+@Xfm7@^@XE2D;1v54oML~1Q|wQ0 ziv0;ru|L5n_9r;S{sgDkpWqbx6P#jyf>Z2IaEkp2PO(41DfTBg#r_1R*q`7O`xBgE ze}Yr&PjHI;2~M#;!727P*r&h2KK%{0@i%xR_BYs%zrlX|4ff-2upfVe{rDRk!oR^I zvA@A1vA@A1vA@B7{0;WwZ?L_;!6UK1!S?5x(Y}a3~U4OxL z{RP|g7i`yGuw8$_cKrq0^%rc{U$9+&!FK%x+w~W07kHZ{BM*Zw13QBg0}BHag8+jA z13Lo)izsUb12Y37XeS_h?mt-u2ChB-I{$TY`G7>gYq+5VBM$>33m@YI237_ZR(F;& z3=IFAAT(1jLqAg*2NM%F8#6Np3lkGd0|Uc)0R{!gnH>xa3{IIP#S9J%3cs1yME;2| z&SKAH1dWq3!i;8MaAJ_%$kfe}z`&rK%zBl9AuP2hF*q@=7^(tv648IN|NsA)8Ce7w z7%(t6KqN#M7#IaOc~}{knVDHQ*f=?uSXfv&n3GP zjg^C)osFH1ofSl~va&F zD-#++21rU}i-CW{^Xe+1WsLaB#4&uyAs6va>Nmyub$X6Id+^D=Py7 z7cUzFGcyY-2OAeBGdMh0FvEkJot=Y&osENonVFr9lY@hugPom~gPjv}k`OdJSlQTE z*w~nuSedv$E(3)J$P27&%xoO&>>MCJg2X}IWCN*VVuEl%B*>khAm!rXVrAvv0tE*L zD8M<`SV4R?Ru)iru(Pp&;)R2oiv9M!U8rMOn}3Z zjR)i%P)I<+gOi;TBGsSC|X%SsSgw)tZZxy47`FI;PBw);NfOwWo74P zVP#C}xOh3( zS-CklxH&mEcsV#Zx%hb5dD+=nSh(5P!QlZq3XFwa5FAOM^Z^P@E-s|x$Ii^j&c)6G z3Q!IXZf;gqR$g9SE)Hf;GUsIH0H+*K>SASQXJFtHhJ*(XCoc~uJa|}GSy&-K3?f-U zVad+N$;HLZ2}(iCoa{VY+??E8oSa;oyxg2ztgNg&?Cczz9Gsjip!CDR3<^9>&_%tV zo-6J0~Y7 z39|9=@o{r9b8>(qmX(DQ6dIrm%Fe*RFT%+HD$jVi__&$b*f@AWMGz#2K_oaVIrzCi z;latn&CJZj!OP9V$-@Oo9sFFJ+-$6%+yu%(oUD-W;0L*k4OCz8f|5Ba7cUn`H4iA6 zfN~8;8su&e4Z$27TwENST%7#;{2Uy-{5-s%_~YaNMFBfIH$Nu_8!s0ZFBc~#$Q?Wa z{2cro9IULo>>QlTTx@KdoE)sI93ottTp;_InR$44xVgDO^&%woad0qmad2~h!jyxP zlb4r`jg6n5pNEqf6dGI{oS?D-tdfm`gMmRnlnWXj{5+8GU}I$kg#-kH!-G?Ro12@5 zi<5_km6eN=mxq^&mz#@|hg*=FlLs0eAlI;QaWOMpb#R>8P7b_PpD8_hr zc_5w#lc4Z`SO6lS;lU*!Ai%-FD*y@(UM?;UZXQs<$RohT2@Vf#E-nEsZf;&dehvX} zcyMrn!-I>4g`cnAmx@NltmadLBVb8>-8 zCs2TZ!$VLU6dvpxeB1)OtnBQZe5`D&Y@iGb!EB(k#VN?m!^6uBHj9UopO=@5mxr5+ zms^OJi-(KD6-QjH8=HWD0GBW?FE2k2H$NymxCQt? z7ryfF@bQZ9fx|-p6w934+-%%DtgPIuf}rfn!N~#2Pu$!*Y&@V0z{AJS2lYIN0)+=i z88^7>Jed3J*x=gGg>x9!_3vHhykUkb$x!hmepEKQ|i> zCp0{GK&rq=oPj|^ng?`GFqa^&Fh3gy2bUnI6$eRjAQBvw+#-B@eEhsT0{m=jyxfBP z0z3kIygdATB78i29PAu|TwFZ7+`K&OpuEY$Cc?|Z%gX^OcfrYln&;NuqL<>lsO=M@44 zrT|C_$V_nU0SXV0GG1s|!OP3d!^K~Q*rk|6~1u<>&9akGPxDKD>( z5C;c`h=>R%JU~Y;@$ho6g9?059_Qv}U=Wi7g$E~>5TB?Z8z(0uJU|5+1cSqaM~t7J zUyzSiP!JRz!r<`W;}zf+Y=lQE+(h@p1DD@`B1xQC=QSAwE7KeqLTN zK7M{d2{CRlaCmU@@v`uNYEV#k$no>>gY0Kx6BHB_5D-8r19{l^xcPb51$lY-c=eqzM;O$E1r=xz3{G1-;sOE!Lj1f! zg6!=4ydr`^d_n^Je1Zbv0(=6T9Gt>DJba)Vq&fIO`H4-8pO24^lZTU^PneI7ho6IA zSU^C4Ur-2?Z9q8(6wsjb0aC^XE<1U6`1wHvjJUWs509{zkg%|@Fh4(!fFK{J3>D?$ z;S}cQ7v|^V6XzEY5R#PO5$EAyXBXz?<746HQ z?$+kw;pFER;pgY&=inC+5D?@S5{9@3lz6~(9y>;O@JmQY@bHL;3yX+|h=9UFh>w?> zTS%ObmrI17UqpbPUxHsiKv+tGM}mikgF}RepPz-Fi;JJ1hl7Jh0UjR0!oosALhK+P zLNGWycsYc@VGk;zxWvW9g+bvV$ScUp&&dHT*|eRP6X53*5MXEL zV+Vzj02eQp0607ZI0QsNDOgwplx;vc2NcYp2C@K%0j^7Uc?AS``2_?dB_(-zMI}T; zMMXsg1b797`9XDxBtI{gsDOZ|AV0sPfS{m=v=px-FE0m&C@4GxxVQuaK!u2spnxFA zes*>d5fM;$fXXTe=3^J&73Ad*2H7VdD$2#hB_SapEC3Sa69lDjXinzhkP;LU5|NSSl>&u_7%#s7 ziy$mKl!XL@z;!98uoM;+W`~3Yh~#G%9^yhm!h#~AA|P?F zLXbKR4hR=Sg7otV2ntF|OY`xGONok!i;D{i@(GCufa(-!K|XGAK|ygLK|yIjAt6y& z89r$~K2AvDsECLN2e`BV6Z{;4e8PO3qM&kCP#n~d zm6DPY1%-z&zc9Zb7bmo2_a`LMQ3kiel=im?%6B88`MG6lA4k3PFeoirv z#X{ilkd~Gf6XFo!7vUG-7vcidUZ8*jhX?rFJ`M)Z8FZkLV9*JU3_Jl`Z{qoFzB0&j hFo3|Woga39=|kxg|8@S8+%?ac+9nF$hR005F652OG9 literal 0 HcmV?d00001 diff --git a/lua/misc/base64.lua b/lua/misc/base64.lua new file mode 100644 index 0000000..9f9c716 --- /dev/null +++ b/lua/misc/base64.lua @@ -0,0 +1,202 @@ +--[[ + + base64 -- v1.5.2 public domain Lua base64 encoder/decoder + no warranty implied; use at your own risk + + Needs bit32.extract function. If not present it's implemented using BitOp + or Lua 5.3 native bit operators. For Lua 5.1 fallbacks to pure Lua + implementation inspired by Rici Lake's post: + http://ricilake.blogspot.co.uk/2007/10/iterating-bits-in-lua.html + + author: Ilya Kolbin (iskolbin@gmail.com) + url: github.com/iskolbin/lbase64 + + COMPATIBILITY + + Lua 5.1, 5.2, 5.3, LuaJIT + + LICENSE + + See end of file for license information. + +--]] + + +local base64 = {} + +local extract = _G.bit32 and _G.bit32.extract +if not extract then + if _G.bit then + local shl, shr, band = _G.bit.lshift, _G.bit.rshift, _G.bit.band + extract = function( v, from, width ) + return band( shr( v, from ), shl( 1, width ) - 1 ) + end + elseif _G._VERSION >= "Lua 5.3" then + extract = load[[return function( v, from, width ) + return ( v >> from ) & ((1 << width) - 1) + end]]() + else + extract = function( v, from, width ) + local w = 0 + local flag = 2^from + for i = 0, width-1 do + local flag2 = flag + flag + if v % flag2 >= flag then + w = w + 2^i + end + flag = flag2 + end + return w + end + end +end + + +function base64.makeencoder( s62, s63, spad ) + local encoder = {} + for b64code, char in pairs{[0]='A','B','C','D','E','F','G','H','I','J', + 'K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y', + 'Z','a','b','c','d','e','f','g','h','i','j','k','l','m','n', + 'o','p','q','r','s','t','u','v','w','x','y','z','0','1','2', + '3','4','5','6','7','8','9',s62 or '+',s63 or'/',spad or'='} do + encoder[b64code] = char:byte() + end + return encoder +end + +function base64.makedecoder( s62, s63, spad ) + local decoder = {} + for b64code, charcode in pairs( base64.makeencoder( s62, s63, spad )) do + decoder[charcode] = b64code + end + return decoder +end + +local DEFAULT_ENCODER = base64.makeencoder() +local DEFAULT_DECODER = base64.makedecoder() + +local char, concat = string.char, table.concat + +function base64.encode( str, encoder, usecaching ) + encoder = encoder or DEFAULT_ENCODER + local t, k, n = {}, 1, #str + local lastn = n % 3 + local cache = {} + for i = 1, n-lastn, 3 do + local a, b, c = str:byte( i, i+2 ) + local v = a*0x10000 + b*0x100 + c + local s + if usecaching then + s = cache[v] + if not s then + s = char(encoder[extract(v,18,6)], encoder[extract(v,12,6)], encoder[extract(v,6,6)], encoder[extract(v,0,6)]) + cache[v] = s + end + else + s = char(encoder[extract(v,18,6)], encoder[extract(v,12,6)], encoder[extract(v,6,6)], encoder[extract(v,0,6)]) + end + t[k] = s + k = k + 1 + end + if lastn == 2 then + local a, b = str:byte( n-1, n ) + local v = a*0x10000 + b*0x100 + t[k] = char(encoder[extract(v,18,6)], encoder[extract(v,12,6)], encoder[extract(v,6,6)], encoder[64]) + elseif lastn == 1 then + local v = str:byte( n )*0x10000 + t[k] = char(encoder[extract(v,18,6)], encoder[extract(v,12,6)], encoder[64], encoder[64]) + end + return concat( t ) +end + +function base64.decode( b64, decoder, usecaching ) + decoder = decoder or DEFAULT_DECODER + local pattern = '[^%w%+%/%=]' + if decoder then + local s62, s63 + for charcode, b64code in pairs( decoder ) do + if b64code == 62 then s62 = charcode + elseif b64code == 63 then s63 = charcode + end + end + pattern = ('[^%%w%%%s%%%s%%=]'):format( char(s62), char(s63) ) + end + b64 = b64:gsub( pattern, '' ) + local cache = usecaching and {} + local t, k = {}, 1 + local n = #b64 + local padding = b64:sub(-2) == '==' and 2 or b64:sub(-1) == '=' and 1 or 0 + for i = 1, padding > 0 and n-4 or n, 4 do + local a, b, c, d = b64:byte( i, i+3 ) + local s + if usecaching then + local v0 = a*0x1000000 + b*0x10000 + c*0x100 + d + s = cache[v0] + if not s then + local v = decoder[a]*0x40000 + decoder[b]*0x1000 + decoder[c]*0x40 + decoder[d] + s = char( extract(v,16,8), extract(v,8,8), extract(v,0,8)) + cache[v0] = s + end + else + local v = decoder[a]*0x40000 + decoder[b]*0x1000 + decoder[c]*0x40 + decoder[d] + s = char( extract(v,16,8), extract(v,8,8), extract(v,0,8)) + end + t[k] = s + k = k + 1 + end + if padding == 1 then + local a, b, c = b64:byte( n-3, n-1 ) + local v = decoder[a]*0x40000 + decoder[b]*0x1000 + decoder[c]*0x40 + t[k] = char( extract(v,16,8), extract(v,8,8)) + elseif padding == 2 then + local a, b = b64:byte( n-3, n-2 ) + local v = decoder[a]*0x40000 + decoder[b]*0x1000 + t[k] = char( extract(v,16,8)) + end + return concat( t ) +end + +return base64 + +--[[ +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2018 Ilya Kolbin +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. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +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 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/lua/misc/captcha.lua b/lua/misc/captcha.lua new file mode 100644 index 0000000..b2700c3 --- /dev/null +++ b/lua/misc/captcha.lua @@ -0,0 +1,193 @@ +-- Copyright startx +-- Modifications copyright mrDoctorWho +-- Published under the MIT license + +-- module("captcha", package.seeall) + +local M = {} + +local gd = require 'gd' + +local mt = { __index = {} } + + +function M.new() + local cap = {} + local f = setmetatable({ cap = cap}, mt) + return f +end + + +local function urandom() + local seed = 1 + local devurandom = io.open("/dev/urandom", "r") + local urandom = devurandom:read(32) + devurandom:close() + + for i=1,string.len(urandom) do + local s = string.byte(urandom,i) + seed = seed + s + end + return seed +end + + +local function random_char(length) + local set, char, uid + local set = [[1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ]] + local captcha_t = {} + + math.randomseed(urandom()) + + for c=1,length do + local i = math.random(1, string.len(set)) + table.insert(captcha_t, string.sub(set,i,i)) + end + + return captcha_t +end + + +local function random_angle() + math.randomseed(urandom()) + return math.random(-20, 40) +end + + +local function scribble(w,h) + math.randomseed(urandom()) + local x1 = math.random(5, w - 5) + local x2 = math.random(5, w - 5) + return x1, x2 +end + + +function mt.__index:string(s) + self.cap.string = s +end + +function mt.__index:scribble(n) + self.cap.scribble = n or 20 +end + +function mt.__index:length(l) + self.cap.length = l +end + + +function mt.__index:bgcolor(r,g,b) + self.cap.bgcolor = { r = r , g = g , b = b} +end + +function mt.__index:fgcolor(r,g,b) + self.cap.fgcolor = { r = r , g = g , b = b} +end + +function mt.__index:line(line) + self.cap.line = line +end + + +function mt.__index:font(font) + self.cap.font = font +end + + +function mt.__index:generate() + --local self.captcha = {} + local captcha_t = {} + + if not self.cap.string then + if not self.cap.length then + self.cap.length = 6 + end + captcha_t = random_char(self.cap.length) + self:string(table.concat(captcha_t)) + else + for i=1, #self.cap.string do + table.insert(captcha_t, string.sub(self.cap.string, i, i)) + end + end + + + self.im = gd.createTrueColor(#captcha_t * 40, 45) + local black = self.im:colorAllocate(0, 0, 0) + local white = self.im:colorAllocate(255, 255, 255) + local bgcolor + if not self.cap.bgcolor then + bgcolor = white + else + bgcolor = self.im:colorAllocate(self.cap.bgcolor.r , self.cap.bgcolor.g, self.cap.bgcolor.b ) + end + + local fgcolor + if not self.cap.fgcolor then + fgcolor = black + else + fgcolor = self.im:colorAllocate(self.cap.fgcolor.r , self.cap.fgcolor.g, self.cap.fgcolor.b ) + end + + self.im:filledRectangle(0, 0, #captcha_t * 40, 45, bgcolor) + + local offset_left = 10 + + for i=1, #captcha_t do + local angle = random_angle() + local llx, lly, lrx, lry, urx, ury, ulx, uly = self.im:stringFT(fgcolor, self.cap.font, 25, math.rad(angle), offset_left, 35, captcha_t[i]) + self.im:polygon({ {llx, lly}, {lrx, lry}, {urx, ury}, {ulx, uly} }, bgcolor) + offset_left = offset_left + 40 + end + + if self.cap.line then + self.im:line(10, 10, ( #captcha_t * 40 ) - 10 , 40, fgcolor) + self.im:line(11, 11, ( #captcha_t * 40 ) - 11 , 41, fgcolor) + self.im:line(12, 12, ( #captcha_t * 40 ) - 12 , 42, fgcolor) + end + + + if self.cap.scribble then + for i=1,self.cap.scribble do + local x1,x2 = scribble( #captcha_t * 40 , 45 ) + self.im:line(x1, 5, x2, 40, fgcolor) + end + end +end + + +-- Perhaps it's not the best solution +-- Writes the generated image to a jpeg file +function mt.__index:jpeg(outfile, quality) + self.im:jpeg(outfile, quality) +end + +-- Writes the generated image to a png file +function mt.__index:png(outfile) + self.im:png(outfile) +end + +-- Allows to get the image data in PNG format +function mt.__index:pngStr() + return self.im:pngStr() +end + +-- Allows to get the image data in JPEG format +function mt.__index:jpegStr(quality) + return self.im:jpegStr(quality) +end + +-- Allows to get the image text +function mt.__index:getStr() + return self.cap.string +end + +-- Writes the image to a file +function mt.__index:write(outfile, quality) + if self.cap.string == nil then + self:generate() + end + self:jpeg(outfile, quality) + -- Compatibility + return self:getStr() +end + +return M