From 72a82e53a8e318ec9aa91eff87d93b8e4539df79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joshua=20Dean=20K=C3=BCpper?= Date: Thu, 21 Nov 2024 19:59:55 +0100 Subject: [PATCH] initialize project --- .dockerignore | 6 + .editorconfig | 96 +++ .github/dependabot.yml | 10 + .github/images/logo.png | Bin 0 -> 232426 bytes .github/workflows/docker.yml | 74 ++ .github/workflows/release.yml | 54 ++ .github/workflows/rust.yml | 59 ++ .gitignore | 98 +++ CHANGELOG.md | 6 + CODE_OF_CONDUCT.md | 136 +++ CONTRIBUTING.md | 55 ++ Cargo.lock | 1532 +++++++++++++++++++++++++++++++++ Cargo.toml | 29 + Dockerfile | 31 + LICENSE.md | 21 + README.md | 226 +++++ SECURITY.md | 14 + src/main.rs | 274 ++++++ src/ping.rs | 204 +++++ src/probe.rs | 372 ++++++++ src/protocol.rs | 449 ++++++++++ 21 files changed, 3746 insertions(+) create mode 100644 .dockerignore create mode 100644 .editorconfig create mode 100644 .github/dependabot.yml create mode 100644 .github/images/logo.png create mode 100644 .github/workflows/docker.yml create mode 100644 .github/workflows/release.yml create mode 100644 .github/workflows/rust.yml create mode 100644 .gitignore create mode 100644 CHANGELOG.md create mode 100644 CODE_OF_CONDUCT.md create mode 100644 CONTRIBUTING.md create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 Dockerfile create mode 100644 LICENSE.md create mode 100644 README.md create mode 100644 SECURITY.md create mode 100644 src/main.rs create mode 100644 src/ping.rs create mode 100644 src/probe.rs create mode 100644 src/protocol.rs diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..6f97c1f --- /dev/null +++ b/.dockerignore @@ -0,0 +1,6 @@ +* +!build.rs +!Cargo.toml +!Cargo.lock +!src +!config diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..071fb8d --- /dev/null +++ b/.editorconfig @@ -0,0 +1,96 @@ +# there is only one editorconfig for our project so it is automatically the root config +root = true + +[*] +# the charset helps with guaranteeing that all chars are encoded in the same way +charset = utf-8 + +# we solely use spaces for our formatting, so the indent needs to be fixed as well +indent_size = 4 +tab_width = 4 +ij_continuation_indent_size = 4 +indent_style = space +ij_smart_tabs = false + +# provide the visual guide and hard wrap, so we don't write overly long lines (but don't wrap automatically) +max_line_length = 120 +ij_visual_guides = 100 +ij_wrap_on_typing = false + +# the final newline helps with old/unix tools so that they can properly print files +insert_final_newline = true + +# trailing whitespaces serve absolutely no value, so we can trim them away +trim_trailing_whitespace = true + +# we do not use the formatter tag at all, since all files need to be compliant +ij_formatter_tags_enabled = false +ij_formatter_off_tag = @formatter:off +ij_formatter_on_tag = @formatter:on + +[.editorconfig] +# spaces after the comma are in line with our other codestyle and increase the readability +ij_editorconfig_space_after_comma = true +ij_editorconfig_space_before_comma = false + +# colons are used as regular characters, so we use no spaces at all +ij_editorconfig_space_before_colon = false +ij_editorconfig_space_after_colon = false + +# spaces around the assignment operator increase the readability +ij_editorconfig_spaces_around_assignment_operators = true + +# since there are some very long keys, this is detrimental to the readability +ij_editorconfig_align_group_field_declarations = false + +[{*.yml,*.yaml}] +# yaml structures can get nested very easily, so we reduce the indent to compensate for that +indent_size = 2 +tab_width = 2 + +# some keys can get very long, so we don't want to align all of them together +ij_yaml_align_values_properties = do_not_align + +# the indents for empty lines serve absolutely no value, so we remove them +ij_yaml_block_mapping_on_new_line = false +ij_yaml_keep_indents_on_empty_lines = false + +# sequence values are already kind of indented because of the hyphen, so we don't indent additionally +ij_yaml_autoinsert_sequence_marker = true +ij_yaml_indent_sequence_value = false + +# yaml files are used as configuration so line breaks are crucial for the readability +ij_yaml_keep_line_breaks = true +ij_yaml_sequence_on_new_line = false + +# we don't need spaces before colons +ij_yaml_space_before_colon = false + +# we don't need any spaces within brackets or braces as this is the compressed representation +ij_yaml_spaces_within_braces = false +ij_yaml_spaces_within_brackets = false + +[*.md] +# we want spaces after syntactical elements so we enforce them +ij_markdown_force_one_space_after_blockquote_symbol = true +ij_markdown_force_one_space_after_header_symbol = true +ij_markdown_force_one_space_after_list_bullet = true +ij_markdown_force_one_space_between_words = true + +# indents on empty lines serve no real purpose and can therefore be trimmed away +ij_markdown_keep_indents_on_empty_lines = false + +# paragraphs have exactly one +ij_markdown_max_lines_between_paragraphs = 1 +ij_markdown_min_lines_between_paragraphs = 1 + +# block elements have exactly one newline around them to increase the readability +ij_markdown_min_lines_around_block_elements = 1 +ij_markdown_max_lines_around_block_elements = 1 + +# headers have exactly one newline around them to increase the readability +ij_markdown_min_lines_around_header = 1 +ij_markdown_max_lines_around_header = 1 + +[{*.toml,Cargo.lock,Cargo.toml.orig,Gopkg.lock,Pipfile,poetry.lock}] +ij_toml_keep_indents_on_empty_lines = false diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..3fcd087 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,10 @@ +version: 2 +updates: +- package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" +- package-ecosystem: "cargo" + directory: "/" + schedule: + interval: "weekly" diff --git a/.github/images/logo.png b/.github/images/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..0ae06d692257920c61e5946b9292b02a55d0040f GIT binary patch literal 232426 zcmeFaWn7fqxBm|#D4|jcNF&{Xq!ObbNOz~w-JK(f(p}ObAT8a@sC0vLD&0uK5c9hR z^_=hPKKFU_e{^2|c`yjqz;*4t)_Q-|+G}qxSV=+Z77iH>8XDRy>E{wEXlQtuXlR(z zSl59o{5y_Ez&{w~PvoDVp_N7Ap1;5ZzQ%TVuI-G5#z}{Kp?l zZ<0PRf(4+XK0z4)6$vN`P!ymjKv96A07U_c0u%)(3Q!cFC_qtwq5wq!iUJe`C<;&% zpeR65fT93J0g3_?1t>P!ymjKv96A07U_c0u%-R zI|bpMEg$}#1wj1;0MwsBK>Y~_6a^>>P!ymj`2RNrNlo796@^83ZxGGiiZR&e;%1Bi z0W!1})`hL8QF))dd=6Tp6E3~z;d*-E+@qP42xJ|?Q%tz`hwR+{T$S00h z*uiUKq3;rhO7E%-6W=K=W_~TMOwe-+E2_K4VCS?HErhIxexkzM(C^ICt(?ocX$OJO zJ>&5%KJ+&PVPN6YfCJFbQJ>P!ymjKv96A z07U_c0u%)(3Q!cFDER+C!HHq|;@`6XsIEI|>jAa@f}#LL0g3_?1tIL2Nwpvq{5GV`Tw7wr+$jq7yj{-=hW3Um*cBN#6zeG9(8XH) zu06@l?!r`s9u}^U72;N&P|$X8N!41{>kjQV%&5JE6=3PX#&~#AV#h1P z9yCkqm+J%EuLMPv;%_)nJoMP~cw+g{+x)E=^b*!kvsFUwM*B~v9=r+w9T<$uyrlo< zJNPsYGC_HUZiCooM;WyR$_47m?0#N%JTv@O5pn4hOm=Bh?1!!&+dbBadbT5kw<8cx zD$kZJC-+qR3|gg=2R4c> z4LCw1DM*-?I$2sBU%^Sg(2X%{t$j8wrg)d$eA&SlfxCEqNUC-my?~((ml?5{sb1Nn zKDU~zEnC-;cy#}t&k=?I6J!e5eeiEUrx9o!!f_KfXMN{|>LvsQG*73Kvd*twaWZ<} z?>SOdUL5uqE9>B zXW_$U>cw3-1c3v~&c=ow7l;BuQ3nP3QR~*Ftx00Xn>6;3_h#%Plc(&hKl*rgj%*cA zWKC^*!@om62sY{WO#aht5%do%nCz%JzwQ6q*}+zD~oD=^z~i} zdj2#ey`G!Z8Sm4>P3*#B`$dV=*E4Kes;c~t5=5q|XXX^8yWaf!DMYx`kArT`M?YcB zru|()86?j}R$4z-th5#__eb1RT+LvA9Mk#j#qsIq1_pL}78ZZE&R?@!(@5XDe{@g# zXQ$>GLnfMUCV!0y_CMdjz;aReF`S=oT5$@)eOVo|@~pH!m?-PRs&Lv&9oF@{JZ4EK z9S&TKd^97Ns9#iMSnD9)yR?2i>kB~?%fC4oxM3hHd0|2J7!OZG;7C`jbkWIxQ`shc z=;1t>aY21gAUNzarCH7CPEMkaEXCW?gI|{u{^#q}wr9&(T=InfIG1^i9vFE5f3@1b zjJzigw&%K`!A@q?^eOIOfn_C8g7zR%(qDXkBCk3_$%xVHNu5xi(#f7L?zUfd;^c~8TsOWJ8j;0PSgB~l_`W_r?tE8L zf%>{7m%P!&xLn5fH~`a}@*zH|ui?-0jB%=C$QBWC$43$g$4kU!c+N&|@@-EhW-Pc)S0AHLDPzJPJVY;SJ63BH~-Ls`E14{{)Hh(CWb z&#DQy{1*!!M8UdTH~MV(y1R|r=et!|xq=5sj+IWHUL?T22vYwFQYpaWRdwBpIJRrJ z&{eE^_P=OuGfPxEr~WaWdcFa1`*ni^8816w;|G)Wdk2f*tU)(V+Q$>dmQ_S+_nUOZ z$hC)`t#kg9GKf76GZ?}6hWwvzAdT#k2s2IOAe*T?H+8_u8>xi~ft5myD5%)qN`s zJwOJGsz*rc{P&kd(l(S8n>c@c91DwNe>6Tlvf=p-T7%)HU)9>2G&^Z>jbv+l@jbPW znoIirq}tHhwHq`)zhV1uDDX}>eImU8Tu1oAU>d#X*1fmJD|0Gk`c}!gwI({qqT^1BWl2iXIu#VMmwX_J%oT7K>ITo**F(Y+P3Wfz*amB?KGqb zXg;HXnjCI=qieYcNug&G^$~Qv{ExVCseyekb5)S=P<5ALskbB4s)RZ!Sh=W5yaW;x z>?>Ak)x91xZ$CmGQn?sb;cj3=x(&V-L3tR31g3quBYZe;?t{57KmiL#+l`KYf4d9O1P&0LyZ_AO#SKnmsP-{M2 zWk|$vdOlt2U{(vhq3qx!#TLKi-FNw=G&h`lPp8KES_5s-!=1v zhZfr2WTP_FIK$Tn&!18-oxHhrmLNjfj(y3vV%6Q}JMwm@+BVWB?eN8r3J}y_a?JHP z!cgiaKh27_ehk7#sy`k3mrs=O=e|dZ65n3)4LjJGLLWHoH!5KucZ~j9rSN$O6 ztS{99W-rJKtnc^Kl@*9GF92mG1ab#iQaPO^`sd1waJ5gAaA!;N)3<}0@gw-x_QC`^ zH+y?Eyi`*=etnU&I*+7W5x)4Q5Fn}})lGzS2jtCNi(dXxJO0ACP-d#ffsQY(4V^h+ zx1@*5en7`uZO^e-`i9-&S26O|d)io^R;fU8W4+{^1pXCRoba%PyOtwa&^{^=8)Zb6 zhRmjKFhZ(k&g+4^{PexZ83IN7(o$tLCn@!zBN5p|e-?c|PZ7EXTtGM@n2~al9B#Hz zr5vYW*e^JbLrn8)3&hk`^BvCoPEYL7^tn!Yx`*b7RkQVRuDs#JQR_4va>TcBhxwl5 zJ&T509>O$tDG{qnO8H(^YP+)cgDOJ$w>8d7Y9A-MXpTI0Us7o%%-J;L3|}9e;PKg* zWhd>_neJ<)5_F z6AAN85kO3h54SMQGwp`1 zeDAb?S8UFv&&RI)?$mZZi8DYg_$JMymSa1Ke|_{GSI&@^r;h<|KE6dD?B1@9m#ST* zpSO$G{3L6<*o^ZLo=;0am2K1r24IzC#mr0cA1vI-aMgLAe3B6;`}!QQP{ANcUf`kJ zdc*uBi814$F7T>N&?WD6+7OtnIE>Khxu>yTqhBfaCy%UJ0fD67xM4+x`{$|q8)jzTJ$#Nh)_$tJo;3yS%ENtk-@^l2 z#n;>=nV70uwQ>B*Hwrj1w#y&95&wN(pBh-M$8&VJd&9vZ-H6ggJi*D%vB|Gj+cFTz zY}2Ftl4+hujFFDI%bV^cE$k)%=*AMPN}z`;xHT z5&OI$RzNP*b?&b2ws46?ksJ+%LbBfeH|5|H7t$G4B~d;9Aa`q*`TO9^4a~j}to{&f z+%a|6!48N0FT5hVYqbcxy20~beaAMJa>Dl13S;(?=!Aleu&!Gheb!Z7r8NaSRIa&hZ=g@}La zmAc-@x(1KwIt(pXKD#UY~==CL)Z;$7mXiv-1uJ&r5vY7 zUOqUl*_s}kw9+WC}lcAoxSkY}B8lB~p{HtIC1c0Vd zz8J;+8+HKVY4{zoWGfP4l6~74Zjr~)g+%=<`RlvvuN8s2+hd2x72k`zRtF`?4l9L^ zX4?_0l0m3h8Eiwet7i3bnf#oO<8?1TSl=hYy1FN+5u{a?+7AKo?OunRD^M5(HA^=w zI!V)Kqcz!AuYwp=hSo7R^~dIPe|-7*^KHrLY^X;9QfL@H^Mv$|N#Snjiswjt>EKX~ zx{NLugu~L08+Y0~rZ-pHTdhq&TX#uQbk2ylzpYLcur|?zGyzq36}WTls|p`y*iHgD zdHA}(-4E`E%Y+oba3&cXBzY@2{UnOID_Fwi<$Xk(@kEshT(-8==Medoj@d*tlMKa_ zf6^#S7>9Sc%vS!Y!1!aJ%xq9`>N?1U1SECGqL;Wdo{m);FpB}c7I)K8t7Caz-iMQO zUxL#4eS>j}K+1)Jq%D)F)q@r$O}%>fvp{)T@2i`C{snS3^y~em{PbW9mpT}0^1jm0 zzN7))Zd`R`PBi4MmXOG9>hoFq?rmSp^O3Cn?d@4dA`D@BPmxEXrm?Ff@4%m&K3&_UWkGuHT za!1ecZzD%RN&#V(@ORsc^fT^TBg&q#zi3-sY*F78=zSk6U?b@1aK}XEPoS)dpg+t! zp5-Uv{*ORWWFByES|pnEnBN5n-7Sr853H3B0QX~u$w!D~FShOEsrZ$wydrr3iAkSh zcAVc^Y%yGtJPl9Nw7cNB^V`NoFE9!uC#IP_onCm2S*Kt+y=Q^q+g{f2*A&*F6MmA3 znCO?cn<`FejBBjZH6|iK2VJ9ekd!{v{Ghd{`N86swPUKOu8+OV)Ws}EM%bpW=gk&b za11Vo!(P3sqBVbkK;HEG5cjmsJYuiFRfGqo!Qk^#)7>;D(|BF`Nn|+^QS)R2>0huS zc39hIjaY7vQm6NjUojgGWu{+AMv?%*$AEF#_XF8Hu!p{&0%=iATCvR#&Vx!Dy~pRL)s$N{2D-oX9u|#xH0`?Mnyn~Qe0kfzb1{cwa0?Wk$`wp{ zH`DzrXnp?VC1TymyQkjm+Sd5RNGU>pOp@sV2;;i^kKNW)!|<=s^5F%CC6qfeZfBWK zIugN57z*#xsr1!upPMO+_=jC4xO`Cb#b1pAi$r}5lC&%dzvqDPa8ur}y=uzbJ}f3j zx@eUO@{DCE^rc#1=9b*Cb{Upy`bx=Fy?oGbu*^& zfAs~N$%X0Eg)?yG)Lku>&+N9ZV<}chjE$dU2odJz+YPN)`;n;9hcI`^Iq~sps+tvo zz^l&dlP8uFB^k$>`Rxkb*HW9(uQEJXhWcXF=0PLgGk>fW>Gxa|DXBhA{w8#x3b-xy zebnJ%ZT;IWP$r#3h1*KD*2`Thj0uSK%5l3r@re@ z<9ZYy?6$>|LHvkFwZ$%b$~uE1pK*6CIrjB-UL<7rUq!iOv_&V`S&PDSs2k};Uc%RhXklcVmVaom~^hXH9Oe)sarUMxdm zulKg@WmQ=|pT7mQqM^NgXKw|pYb|&s%no`On-eGIs%Mm5#y2MqVp|{cxkEl%>-4RD zznlyvuR7vk zraO%W-TLO6lo&V3{A<;Ev4@{Op9L^XPbm1p^KQlUCI*4`HF2p1yj*D(ao|Mk7BDVX z#Mrs{0{yGGLwEf|TZ*5^=Y!{PHk6(y?%5bIMIH#o{-IPX=)Elp8DNb#*2MK~;T zW6=4Pqa!7DbqCc zQ4t9cS0564rqaKa(nA0AhXsa=m)}T%UBMjgq4&InM%5MN2gR_+;lO0-ap>l@V8rXL&zZ4u|M4&|?(woIQ*sHx-?U!MpCS=FJ2iRi+dlFt{>8P$1pUIa7jm`k zQVLr=SjRo8K0-M6kR9j}EPdDxS2Mv_erDuY37{{+@g$9#Cc-|CEd;{I661c|zQg zUuG4xpLiCHNmjSU=VZbt3p9(sAoGYwu5f||1buv)E5WP&Ld?nA^;={R^+WG0KgHpl z@RDE|nky3mI#D3+n>=d7IAW8NoI&0*Z0cJC%Bj?#-l!klcnww#n8h~p1$KA(K}+`- za=MSFYXG+}G0oQc%+%lApUzswJ_v)cSdXQXoqw!pTc|FTKH#y%rNK}4z`DSpfDt7IB|E(8;ljDCi^`wC=AD1mh+20F*202JT zSnsxaNvV--Vip9Abr?ZGCW@Kfm>PCk?z7E5mwO3*Zqy6NL=$M?L&mnqsp{9_I{CyJ zX__|Tt^(v{0Ia02+OI6msT=L%BYc*G7y}{vV3~4#kPyzJ9_De*-lLlR3HZbMi(V^a za=~SEuiI13HT_M{4C}8Dc#{g$ zyq*v2h%8TGPqE|7b5%;nyt^z7sZtEis5P1{zcS$74fgw69bYhKqBT+16r}!MKOvoY z^D|gsEfrnCaNQZdBYv!dB8mNJ0N5OJujAmoVX@9gUiK9As?C@e-2fYCDY-ZmQRY`F z(Xz{w@u%}JUQ0v@jGJos{5&S4Bz5_L?2qGlMski)qJ2zwJ>%-%jgZP zs56x@8o*EpyElkXy+dRdscq};-g7^MFwQ<6C# zRr@!KJb#DqZ$;%Wa@$Y{JvQ+7RNrZ3e+j~Xs~g$8oIe-@S|2I0e%_0l#MLR#o_-J*ifJpR916ES!Vk6p-+UuX($kmfG32_#Iy&ctUZlBW3K9=0reBLEDI5CPhnWl!Y+@0ufBT5AL7>(ai%fEj7h%% zF}rE7XDIpht*9QfeL2hbYI6?kk9Dqe-gcryq1kM=6y&;cC?!)4P@`#+_bz7_VAG3`Pev6z+y>zCYQdL2Uad`lng7?TtmtBY)`=5jfu_Jbf zu@C=UV;!V;3uB8k)-ODQf+M~Ra;_nG zy?MXIlr-&xux)P)uD$`vR7f>8#blyRP_rOU)3WCG(-pj`;up6FR-<4Y2Q7uNGSrdV z>62NUuHt|p(lrX!7wBTG4fsBx?J*+P+Ctd8Fa5`J9#-^nMJSXcr++6S{<~dtvliIF zNB2j6o^j>XD~iQ%7S&jIw%cz_1*JCfPX>Gfwj?8@ja zA_U%acc;vsOkeB;an=sYlJphX4knNjgBhR29QHvvkeCRvld&GH^=9G)BtDjiD?(dP zamS&i9w>n=PuSDx;#+|=9u~w&h4XX^SVwGC>|Pu$T#GKJ_ck>0@!4G+Zb5V~G(C>d z9y-JB1Xe&;H&;>-MSeRu9(YF*Ew7M_KmZWrg`nm@htD;yMA!Z3DQ4E+Km7yd!eR#R zyeX!a{14TMkfF)@fOhez<+{aB$#2%i0sJ*;*e;eG0WdAl?W{1{JMkpaeK27F1fL%#H0!jWer8=(k!3?m<5tLGbzz{62o}H z`P}CaNQYVs5Hf;~Ak+m;^`b`?dd*Hg=PlW~BSS!~?65vO%#tmeGA0!X9j-F~Sb{BJ z_*uHto(_ID;A`GN%Vv7~-9&#A|75lhb@BO9R{#8| zg?0)s!Bt-f#hIU98JXQI50B99*I7lW>lcilot%6|Y~I&#UygeDL6;b*K_ZO5UIELd z@I1Zxwo`PG>YuNHb}a-GU+cZfX<+n2#f!Y?!-ammfJ@iEIq_fH*=5)!6?)X<(=9}4b23+EEow~CFc>v_ymrvP*7eV7U&PH7( zOhW_kSFb|`53T)*x_Zg!h%rlNK0j#z)8wcZv(5=3_2$7~P%ws_K04}JD=2GUohBc>0KRCSd~pKfGrDyEls=A!12Wafv> zcR*kC;q06N5#<+AxPlX_uFA!|-zUPP=zgUp<^Ng+3g=R>_wnqqnfkyAgQ%26=t92I z{b|GG@NIOE?OiR6oOOH2Tqc8=Q4PMUmZL9wl%ddYk)1ZOS>~Jiarnxoz$~2!N#B20 z`6Hf9oSz+yy{u$Tv+lBRb%l~wR4}ZNdI?H?>&qDy(5X`>`)GC{5kH?CQ^3O;*DWWc zmFD4~eOe>XNSh#9sa0zI4k@|UXa|_Z3-1p$T)(d^-6x%yTnOj;iQH)PB+}v*ZEo!0 zEe*ZxXEXa~5IAE6R9!=|-96a;0>ECL;?`{C8YJ5uMuxOK*a(cNGm<;*ed&*9apW8C zMzQD7s!h&+bI!B%?%5eDcPB+GY<%cEMi9$wox9j3=Qh@&Jmsnc2{IU@AU_4W7a}F| z7}KF9YvNFkb)@0u2tMl-AFD5KETLCq*j{Nfj@)6h{KTCSq*KQp zu|krg*2=d&=K@*%4}oW7H_SCJe@>YVP0i`FE)-id zgjgMD6itOR7uHtakC?DH1{XoasOG3~sotHP$y#nwTjgF9x8AtD=`kbuka&0isTQjN zV4vI!qM38w8}&oaBC%QwF;@f$2k6LUlYkz1X1ab|55E`%^wyn>;~z&vdtfYK0RbKA zh~ss|o%4$09m;oMf9m=ORim-oVg&Ca5l7wgaPMtLe$&G@B!Z6iRXcffVO|=NiRdKn z7BmWPq-J`5?_aA18jp4P-q-Hj1E}(PYBU46U=rSllN~!h2lo41y%i}SfE1JDd9h}DlOo%g(Y6A7UZA* z?%iaLAy>$7;yYr<2k99t`Onn~iZxpBhE!?tI*H~lma{9EJ?0*I>Y4sA@5z2YrzU?B5nKN{!R*&T4*qrNO)xD9WK|Q_Z;=RqO zZJ(9cIlZF+6^7g7l&qqP6@y1My!_<0BbZASgHFEwjF+WSgMJT)eV9Y~Hz;r6E$N92 z7t9}BKEo^VIn$*MF{KJSoUc1K;ypFG^ITMGF5>XG?O>z9eN&|Nv8~!CDmSFJ1UVrm zeOj)1x3ubZFbZNcmrhx{lnJ~gU5J9n!o76IDptiN9II)A;fAaeFl z;kHF#VH*-4VAb&wdboVTaFHvj;OL-IxW^D(1UmRKG5@yT#ivo!?WpUU*xKM5VrKQp z$^q_GB)z`0N_!PLIn%uSLfm!0iKddR&{X;N$ zipMHGwpUTSqnzCJpEaC|WZGz_((xSn(6mO0NNIWui7aF#7xU|DiY<1L<%z9SlF3ph zRtDocJi);MmCNKwPtxBRz7GfCY?DXxbI8U%{gT71G?Z&eZc60nt*&R_i`a(4Sy}Iw z$?_huLoc7+fMAU2^#|d9nwy5%OU#!J# z2v@z>qa`2SoJbdL3qk{7(L5*!FayCqNe)gvv4elzk*EwxwIW~+fnDzR->Yywi)wne zYx`_1N&z2C!-Rf1b!Pj`Y8ArLxu~spbY$0`&wX1G4IKk`lMrN9GB1v+0O;i#*n59 zK+XdAppA9%g2#~9(isc+b-)fr0+%~s${15>QgXuHw77}uU|`kY4e}+*#_u*6^A_q1 zLW$iN)(tYJ+XOlOp_>>-B-S--bvwQf%iq`QEBn${atD_E99EhShT1DrZbf!3AS!8@ zA^yeC^#XaGFNRcR;z$F02JMA(m>B!rHQ@Zwf#Gu69(xP8+K~vW^xS8y z*&KI-R=PViPkyAtzz2-mvlU-1sU#H0D~f4xPE9k}4WZ2|$Tg&~(|aM^JZ?PGR%c^Pl5ah=_L z=-HJQfe+<`?}k1?ivk3(kYfVp*QZQYO;`2qR>pWPwn^ux$#qLOx33zvt_UV~xE4)L z9zXJQYh39RnKfB^O>${&Pt$A4u6=pCNl@8W@h$b~eBh)?_dV4dH;>@HXZ`OZj^?`2 z&LzFq$I`R?nEJ(-=;k}A(swI0*VH@|4O~Y;-|pW9o{Lo#(0C+|=L)%#SQjw(*2E8A z3h82Rf+R7h&BjshaYx0@csG@B7#gUV_`%Nf`$@;h_d@#=hXdl@nib*~uMC3ETo#~Q zuPb_N4)O|ar=_0pp6rQnOA1KG>!GRUb~cmin`~lwmoG(YE;Khr`Q}kR0q|tz@BmSN z@0_!Yg}D@A^Zl)+YdM_|gKspj#r+3i_xWgkIROy~oes>ndc+Xnr>LfSD;@H@TQ0@g zQKJN#um40N7KT;;_9{A+u!E&o@T9Osti$2Z_?o*YYwnwpnY&;s^)K{U?A%_U@c5i;xlw)nw% zrykAhwD82I7iY6USK%27-iqMpUXnk!=V=)xi4V@C2OU9wPUgqjZ)mQ^#L$EDuF%55 zkbrS->tw%uOs{_?#H$;H875@do8g~dzg3+@llWwN0(+Q3&A(tz&DKrISq*}zV%MXq z`$N=%3b5$AdYEq4^%=x_LG!+Qon@(4PA+=0~4#b=qhIY0Nil0x@~NW+1VSz<0imnIY1q|P);w6!KBRw&;ejRZS-PcXH~)8s0pI?H<^OQtxjCTt<=S z!`*D2-$cjw%HWW+;RXFgO$nPzh?okVgmH+5oPa}gLbbyyp71Tvfh6c_qgu5+3Erg#$f?;z7eMiwy7EaOO1SKwY;C=YpoQk@p9((;UfB@C z3I9QxZgG{L`E7ZJPlg!6BC;wkK=p=g*;<}G8HQ%n_RX1EGlPn4pu&fuudZETT7EYv zbqLN>JeXeQw#svRY$3cR3HN0u;p^sgpTlHk`roG4;vcTzRr^;n>9NhFTg<8)j zevk%_Tt(XiR)-i(;^^f-)bvCrjb54U1yf37{15tI8VL`!#K|qzHU3$8w(Kc9Fh6UyDZ!mm zvE7zAYE{ZDXgr>@U-tCyjvRZHQKT22?5bP_yHk?Qw-ldZF6j6sQ zD5IYl)7hkO8ANHp<( z1qLMRdjI%XHjsW5e@7!NIwRi6<`Qt?J>S7IMZX6ePZn; zpIo_!&xLx52~pB*ZRz+du|{9(Xg+VL!KtL>O)UeI%L12x-ON|_vSK`iDu=dxwC!Syr=natTNvC*mzjP3v*=nLVU2| zy5&#DI@d=l;2K9h52BipZ*y*UrDG2^q5N3rBHkt!@eK_i=S>pTAd!gT? zxaDn_{yRngFa@pHj`O}q1M=Ndji>&}xMIlEh;$ST5Jak9==8pT=$3#l!f`7H734VY z6v*AHFzgv*>UuMjS6J+8PZBR~T5lePEB)c@EPAC&-?G$-p0Q)>`=Z(Pya|@pNBoT} zvxApxr}}5ng*`PQ#niEH-RmPJ>oOj59(LT-Tok(d9ybXbx#(Tr@`P}jK-egdB*A6t z?WWTVc9Yabq;4Ab9^SRaKIZu`E{wbzKGUNVz zn$lt$V76$W971M+E+3KENrxAPW0N;z;xR7ZikW8H)4MXaraD(t&l4%{0lH0qaeKJU zSIGcZVctyn*+4EX5+*s2WEvY=)*LOHfIhux=aSX$TOzIa3p4dcA{Z0-sS8GKRh5uy zT4y^lMNP(ALm?FCjE)T&0|SRd6082aY`9f+6Bd zlLIdONqyd;f$rg~?xk(B3YqBywU<2xDmEpssG-oftLhQd^VB)vjTc=dga4A&u!*d1&B39?oqXEKO=ZRe&&9=+nVjSMqy)ig%8d> zuj~;jd@VFk1;wazQ)OEsD;hgv<0yZE26>6oQTIx~f@y@o*Qq2k>DR#dG9H9BSa+1ciWFm$UC`KPcaDuU7v8_0cT$|d_Uj2x z1!@Tdh>zn|#zUR7HH2IzK03x8LuS0FEGN#1T=Qf$P=O-@?(6+4DPln=_|dT+N;F1D z2TM`;S-0G}ui9+hI1$alZ}g_?RywrA%lpQ)1pYN-l6-N8`8dwq-PQV9#wElYJDKe)GZX*qSV1kD+cD#KbQmBSqd8*=9zv3iu@#e-@NAZ)3*+wt%ZU69 zy%Xy+9;o+`amEyWfX;Jn0m#bIuM8PshTYFC-t-Ysq;y|`z{(h1BfnhMK0%q&27|dU zyNuf|Z+}MCg!gcV^@bhlj}G|mQFowY(coW)a1HYcTtFe}B6E1++{1swWEP`xtT=3c zwy{b|oPp?9<$H#-seZeOuk2BB3EkxwE8U)w{9-sB^;40S(L{j!;44FL+E=};R`{D6 zpx(WB_pR_x%eUmQDwS=c-=YBqcOx1Y>#BP-2SnD+<`K=@@2Ue>eoprs;#T$Ex^_`G zI{Rq7Z}ay60CS!oOK4Fl3KelQhy}LVoR5Bwe9rB$w$x@|1;XmBl>imI!2^Dj&Ijxp+PI$c$RKnAkFbwIbRY|mIH0K;y8b~j2RyXmL9UyA2v zbm_YwGJ0C|*e#oMzn0+JFm3Ui>fw(+r86=|gvTZ^?lLBOo%?0?SjFyL-tnK`scC;q z(j;G1wR!?zTI{u$nxr|&{=39B!UHW(8aJy?yglardsy|pM>j5#uqj69x9RK9teq|GZI8^VjBHzD;^E+%;` zk=dA2I*je??Yf0wcI9R6bkxs`wudQaqVP3J%gR;^hhP@jyJ};V6 z5(N5FL>b^agz0b)F`d?M--O~vyk{^ycPEv}3ev>wsXgxrxkWxwSLzoebxuRW)UI(x zi|2G@-|=kN9oy*}*2{e&mYEIS`&7q2z9?bal!p1Ln_Z{8TkwGysBrgKGpj_syx(}I z@J_~z1z7^C5g>o!fQP85;#agxrl86&peSJ3XGnhIoX=*--N2aXrF>s6(p$?#}W zF+M4MRjVh>MD`Ky>6#v6mN%iIj5QlQ@$+f6-69E@SGOQfvBt;lRx{&TwC;f8+A}5z zI+TFbt>AZNU(=ws$fWsN3udwZ(?j-qCoO06&>vBLo@qZaIoE}bXYPXk_)Gb8`qszhlI4CbT<5gmTs3*; zZ>{APU-j65+d`BvE3HawZR;(DFUEhDwGd6LjyaMWi78)Pt(N78@qORi68p2t3P1o= z+BSmwzBnKrpTNDhLfh#dO(_;VD3h&H9_#$tT+>Y-#L2Qk7;H&cbijrM6tUv4+0>s~ zP9IA3n@i$r)me5_c?d4f>&d6^3ZP|ah@c|~$?H55LO6ftK5eiygNaXk(|DI?TM)Pz zQ|sc<+=nmLk#mqA;rH&*DG{P;51KU=E!IALw9;fIEg7Id@T)k0{lH42g-ggi)&3zp z_iy+@xCZpkIoHb2KsQ=-$s*an4ZLeN(6i0YXm2@<#|Hf5b5Ud$M&`T|#UGWETPa&s zH8!+&lLlghwX9c`qY|XFv1pXA(2W!1Q^y30@#ViWBB=teVk#7i0A~|c3qOMYP>z)b z)c@iFAMXGacFvuoGMi-Gv9e$9fI5KfWr=%Yk;|oNa?R)GPUW$Ekug8|Omiq`;J3@c zdNDO@u0AUBW(%JlGdx;I3+*duK7FLIgY~PUi~it+G$Mnrn_=^<^<4C7BG6LI0J*w7 zeIW{L0sg=W3-i4%crTU(gcYX(ZkUxHLOjXofdKtTSPkg=u!DM@eFfcLGqC@7l$+O~ zKco-ASY*_zz!~Q4O;9qLZ1jwGir7mf%mt5~;lIn#GY~)i`Arp=VL(5|@(90x-&b>? zXa!M>$;ning_5{u1Z_*%LX$wObYO+Ks}#s~T0b{o%~e900OEtAr~G}|!4Vi7#~+)3 zGY3HD=z10}CZ*6I&#{I%^_6G~(=y5}9l9yYyH1-VHTL6cl|saHUVgwRTzNJx0J$GZ zqr~Vd7ccnilDt8Ve1BxzVSTEc6|NpkPC`jTy}duDLvWpTPQQcxAg z(L?fU>{Y|HB;|d2Vvq&B11N&Z>~J3zPKHgVL-d%tm%`n|mti|Z48oES2XiVsmNpOz!C_+`f9-8XOD*bKihj^7~--gYu#%0zJHNeT)^fzma;*FBb|c^mPO zfVluxo10rZxLp0gO~xFkg`T4)zrGxCEH0L^TU##|7<_rvBL>sVgS@JIZhF6LA^56v zc~HL`l%1|yb%ghVIf%>vKkB%+qN<#_q2g2lM(Vj`a`iJ!r9p` zU=8rlmC1n7u>ff;a@awY9cW##;zJ!BwLcgS($^U**ba?`2b#5KJFT@*?ltMJr2<8d z3~MG6bM~CQ-pUKtjw3t?YBHY%oQ6RrIwwTyK~K69GSFrjZfbd;77Se!*+GO8zi;ksUj z_NP9fF$8fF>9W~&`Xa_(aYhYI&fbjUpa%hIob^=rAT=tEsG29pxZX#9JmdovFi)EM zAXnDh(frf>I!`ayjp=4h9-Yz{9@6fBNQN~!rI!#VF4OwF-V{ykdEr%LI}%Kj1hOy6 zf5&mtY~3XMPaG)|8G*cpB~!f!z%*=-$-Xy4gp^zD*6j@v8v9zDL<2jCK_v;F3%P)< zY5=fU;$EhV85$Ed-Ht{Wh^=_^HEcYZ%#&Z+bh(IUo!FEHBhwZ6qR?Z4%}2qAI3w35 zKw>ulllRguZk~et=mn^;PS9dRL6{_;EbShNTeg~nzLNcp4*E$Dl|sOeEYH0>SU(W{ z)`D^(54WYRmBrNHi_Hj*6VSQ*opWxV{8zSj%PJP%UKn@<*!$4YxXWJllNdbqjU^1e zs$EQIXxPl4e9gY!Z{4H#S|WO9TzRcpR_E>613s0fXb>275*@$yV`2bJY!n*s5qGG*jhzootpP@m1;dI=Ju&XMi z{aGB<^*fC}*91@XZ;ij=a?t91CV#Z=QjL!#7?Di*isz~JtD@>IYcpo4gb#qPWk8Z^ zcw?3P{1W-g4@TZw*a}PbksA`^{y(<!J>X{1rQyIYWymPSyzLAv?&4EOWpbDKl{ML1NI?tCxHvt2REXwA3Y$(-OP4MfW_?4RQR7m zQ~MCiEWWaR7SxRR2${%EL0D>3jlwd=T&!lV$+lY4<_iVoG%2>L2JbRR_4C;hxKjmVQ37)?T zf7v3}Z;pUiuVpf#PU>h%^ekobuO}kfh~VuU&hRScw%BzNXGc!C)LfK^EM-aGq^OrY z=2_-VSMU_O%e1Xn!9s>2Y2~$L(ICXuHq6{b&*=|g9SbVgV79Ahvuo@Hk3_e)i}m@5igf#f?FZ$3mQCV(YW#)6BgN*{4~%PKPnb$jAO!d{0?I zg$qG&sV#CiE=|5M@EDoj4ge1W;1sXe-%& z_?$F!?D)to#!Pz~M@BG$P26tY)hGk)xynVDtqJJ%JlQ_lEJF=?lHKJkyKzuBG>wd7a*P3%oVAzk&p7wpUl$DF;XFkpCw3xy&p;0g1wfMs^ z(2Z48D=+T)G{_6p$!#&7H#XeSjjVBSXecsyR?xIRg&Rw@840SolJ;_yA9^r3+9nz! z-y_-yTxlt|Jt@A4<4YkGswwmHBTko}?qWpWI5QtY_n0^~bIpb+_^Epsm~E+WMG!q- zznB}yP2AYbry9TQFafwg5C7TWzvacRe;MiP)GW68!t4&Oie`o=ies)QsLPOfhl@a< zHY@XATRcVkRVJSFPq`jv!`I4|9@!cITYTpyPob+j{_EA_kJ>2G3{H6s+1u$VJY;1B ztfm=?kwN`OM1A1_^ncm7ixr>}gz%sxdhup+zLsdK9{DjKg# zxkXXFf@suRO>h>+6g{X{WPw7f`EBb7+4Ryb1fz@gtEy2RY`cix`_+}=tDvYt|b3M-uz%a%NUh;pJK|BtGqYpQq|#k|6hW^ zSX2X8hWk~?RIyfjwSHeSxlvZ1<#sGL3je|gVSC9mPBfGGx<4eeiN!No zHZ7}mjYp7S@KxT`8W9?lnvSEhX;~uAIOqn$Nc4O1?Twl7Y*PCE80G!{E8E;*z${WI zEkS#E!Acn=t@bVb(yf;AgnRj8caR+If)g$CMY7-|s1E|6529iQK`O~TDN8?$JgF(m zn##nsvW~5cVI;5f&h#sI$Hu<0&V*TyRp+g6o!hPslF<>fVMTn?IDu@dbq$mzLpq#(T9O*hi z)Fm6d4DoM+he?uAx%h(u<#J}jQkfy4Q^R+@vR^kg0nmCkC)f>IPyE90)RAGRc9k~$ zFu80+uME2%KelJ{2R^VamDgT_HjS6Wm1KQ!Y?A7I=dZG`<6Aso*@%Eli^Qu z{BGJ*{w@4K22|?%{-orp$XP7ri~`3gR5_P*Nsve~bBtrOc4J4cu>YQ_0#)A9zr*~o zizgC?FR_rv)xrWbb>13KtLAI`OMtVVjsu-nWoXtI7WjELHyi>SL+Ve7Tc)*v=YQd+ zOY+ajl+^VZ!sfvu@lACDDCd^||7MSVvPF&LO~(pH0yBdRQYWSW_*kpKGD3|xiH~(2 z!^a8OOvEj$n#g_Az^KY$@~|N>rMFcEuUu8nzuX#w)kT}Z>KID3u+ED1=(v1D=A?LkP0B+}zi6=S8+L^Nly;`!xiRYw1!I_YDI?=>}s8F9h9h zdCUSJH+NJ0IGIaLmCb-Rgttfe?z~JnHjyb){wDcdk*bg}Umy&NG!m)`9XSJuNem&abx~(=F{-|Q* zHa$ACWhAKMN-*RphQ&2LW)iQX3QWd2`1gCEr*1=@nE(s9sW}aS^e1)-`yLLBAG`!Q zpN!o}7pw0YG9z~q#U*}_%_Vq1qw>tTB#%-DR7K%_jk!N`mfjWBn`xyAo9J);lvw$8 zdw$$tz%zdsXFiqoBfD%txSOuACV-2G&}Z6#b?ILqivJRR(hzhhaMycq%%MsSA;G+t z-#XBPX2LD>DH$0iL!duvzP{SzCL#ace{)SAqz%P21|DZ7UEf!hiD3QV(0D9Sb#jYP zpWAiYP3Ao0Ru;v_=!%j%5ZMQ&HG zPg}MZdff6CZTeTtoJTI@5*{XlgNlYSXH4ubwl8T5#?O2;X}j+%H_foq+|XAP+pi8m z(?b7UW?};*qJjrRtgUO#g zgo0|2v985u*hJ-9o7;Nq*FrM|RUvy~)q^LiZ{k`2;$glRP){2xdr{p^2-1V8piO{G zH{PkpZgi)nlxYDIKH@F3C)!$rmQBgr;0PHW9Un4&cK25sbdp$ngId=gtPB;8N>lb@ ztk>8zEr9=xx(jXaU(K+W7k?Xc|5YYE@8%Jd*#ww5SaX+NOHMDWFtl@V7MInO1=rrz zt?=T--W8}FkQxR`dx)f7f`y{v@8`2{OQg)|Q>@?EStGI{8_G+t`fuYcpTWkD7jnWu ziLt4gR%G&*3NVpEl?w+wVb@d+6%tFLIP)^#@1^ZRj=V=r1NZ5KT=TwqZU_;j6awuV z_8hNBgNmUk%%l0~m2+Ssoew1^WhTY%)SdCEj_7zKCc5`4BN_*+RZ zq8A(DIDT+lRvhStifs~#Er?w8!O_2%EL|876>LL$ZzGA%jy3x%Ny%FL7OmGsL$ z-9y#s*W$doZiCClMffB87#hS)0j3Vnn{d#V(&w89q9k`H3yMN&BA_zdpur+WoRQl9 z3Z%ovcb1zXHG>V|m_0YIKQPrGUOvp{SgZhr+$nwuem@JXqDIPbV>`ZYGu_aTkHl8w zizCh|YAjGV*LF9Ym9}#Q*3+d#m3%IJ@ez>gM*d^a|^Ti=qVTB_(sw&$6$$e-6D$zG@b_JNS_idOUteHf=Z9Pk3|f z^En;K9?I1*Qv5D#jpi20{;y7?1F(#F_v#u@mmD!s`m&BXvjutf55~)GEO8+$GB>Z! zyeZnEKsfUAMemTadK_6ZJ+xdVVSfG>Kd$P>MWWpaSCq`^(Yx_E*|)L7G@brN{VQu z+Po>!fua0=+J~+HAngdwgUJ!48i&-XGdTrRShCw0eS0nX#uC_NAR*{ynU;OV(pbF2sP)L=8TUD6)$@32JYHy@%Nh!5C9(QaueTWW)3;@tH?o@6t2+wr*8H19mD}%AxtdCMRvL``i?0ur z&nf>{Blb9TcA}uE$Ig+o{B?fH+?~9KD8hV&cyIx%RKwi@FA?#QcmEF!KXL&@vi!8`PU`8_u(Hm{Qx{ne%f@{oxrZjCOPA{gmc+#l7 z6$p0jHC}Fb6HB<6*U`+cMT1W~OR~xQJfTtfxM}wh@$eZVHp;4L&`b^(@}s&jNyyv# zNwDu_&fxSuGvN{qM+)>1^Ozw_pW|7A$H`F^f$>2;?YnC3=MM$+JvUx)3*m zHA_>Mz+bG*7Kb^j-eH1>NK>jWv>J1g(4ItX7x&(Z?prw7Jw6K9BBc`GVPT%>{hVJGwe;~p0sbqPOWeQfbn z=7E-{XG$ls3xHuH>4EY-8g?KThjLAfX!So|1N`X_&Y24DX7?#y5(dXR?GHXsSd=l; zRaMp9ZgI0pNl1_P!m-aW;y{#I373YkP7lv%-^qJD! z`#k6{#ZWU08SH75O+1&4llZ7?1AcUUMpgj}(kuXh@`qn!p)UX<9^jK?j}jixXAfx5 ze>dtqQer{`3dkfsJ%wfC%)>Zl`BU?Sge8Ypt^Fr@WPRgT*2Dp5$Z*5*sg1leveMrT^;6gA2Z95vxkda)iFsB9 z1{Sk03>m>_Q0olg3UCe0xT)~SX3CK1=;Y5g~ zUgrjre_w@`p2Vto3up9k(`pX!v)Awd0WlSkTVc|Z%P)LZ?%zy|%9hVI@}KM5clVS> z4br8=L7Q0r5d=P#wTU*w&W5WslsY&H$ff5^jV)e%YuTV{#sdA9Ay5=DU3x3Crq%)0 zw;3Xtc$^#09Z$PBM@7wjvpURs1={;gwW>2>*uS3k5i0#TNT_w{Bq|p6n2Pfo$}=(; z*w?j$Zd(6F4x-482sfgP6$&@pYw5u3L+j$UVDe$Kz>H!CVU*H$NW!!Z-o zaop~bj-SuV#CY<4)Y5B;oQKkLUEJS~>FkT#Y>)P0+TVXtD@O9rZ@okK7!@J(#6 zP36ilpwH4?pJ(mU4ahv}iXj8wg)u~O%fQp{vKB8t!SSZPo1mj4Qa^^{xgi`o6_UwM zn4iCF0ayU0*{`3t(Xcw0=H=73;yO>2^G|v4jo36_p!!YUQv~QaJ6W!e^>@_l+ zf5YrjGuV;&Dd#9!ZZZEBiOhdc{PP{qp_O0rgWlcTHj-=y{S8W8(@L0QP}fpuP?gp{ zy3C>-uV0Hhe_sVJKjp5cW;e0{W-y#9|3N33;qUK*YcS54l?a(t zv2a-PlYimRs6lip8=W2jd6fs#X<@$>nA#@%-`X%?gXCT@JhLW9&Ir0Y1PoIkE+)sM z?k1w9WkBd-*4Zo}Z&QfS7IL-REk8**!iXtc*rV^vBt823=N$&Iy_@s1M_I}!!{ERq zBeb`B${>#(Q%0zCe<1G45jxIBj@7H-7ixgi1Ev z;gwm;)}%83uE}M`m$~V2A+cpiMN|~Lf^leje>WgO=Shs`Hknd(fe0ZZ1)#iZUoWY| z$U8cf!ow&~V_BI*98R%kc@PA4RY$oWgj$vlKu+NM*oadQhc$2xu@}*8URLwA{uBr zumkv4=;tuz?|{#}D*(tqdvhD=m1tu`(FVySxq>SJz|e%_CdB?HkNHD*Xh`}d+?$-(h& z4z042D1e2h@a&QXCek)h{R@HWU_kEyrqJ-=vFsgl~M{p#qGjwLQ^!Ue645wVym^;J-_v+&r0kbF5Im10X|9u&_0KTSw(?82U&Jv2G4?IUR|4rSwZ!rm&g$N~vg4c0 z8Me9>EUd~#G%R1Q73-SC=P3fs<)X2EFKhV8^qd|ZC|f6#9u8TAV?lnva=>hxuC@2u z(zEo($a}eq;2-O^7J3q}Ao%cRVQ{Q_Z(YTh0@?D;ID!%^-0nj zwrh$LGagj`j*-z~GdkAcRRVuN{@?T=?h~}xH&E+_EMCh$!wGO{$i=>2NHNBvbq-UK1P~WK7)D*ftRD z-0hOlhpiq*)A~y(c&&AE`i>2??q#$ND&6>02o21x%PDtT&loHmR9_K7U6Q(xu^du{ zqqOq^u2EOMm>bmiw66`^C_Eg>97EB>A&nV@Yy|UR^{?rM}J8!d8@~@dw>v9Tm#k5HRd$xoi6fjm2{ysvuIRb7-wO z>$i6I?jK1Dk$-;O$m5#WeoV)=l53g36w&f0jk{4oZ^RY>^*`V+(WcZ%cmZa^9BESM zn^&ZLz*<^@0ja~r;e+?S7-f4AFk)FjgSdBhK|ewq%-f@QV#HGBr!4NG0vUIGI4Kgk zeMdFrO7hWpDK>H|w;=5JyCJm)W;nazQxHcq38yLHa)2#T{&7MpT z{2|F68aTa~>OrP3aXIH>l`r#(EtBk89=9jxt`qRTxiAc~E7_XZ$oSuw=+}`r(HlX|xQs8|*LhtsZ6zqMW zAo2wC8VB_C*w18PI^x_&I@CSSK^L{-wXCkZmK{%Q4N>?lh+pBDAby$NmKDT!D4$lS zFwPpGHc;srDsOTTdEg}9diKo-wR0~F`DjVDxWtc7!~Df#dG;bpaXFDn8r z85FTkGna`<>tDXj4EROHBr*G_6cv5&-jKO|aZ3J5qKJiT>Vnu_MolywR(v~_4M5oj za8f3OW=3A7CMdsU(?Ds-@_S;pe(75pj_mCtwlX{Q`wqCslY*C`Z*Cms_T-EwM0=<< zm<+|87hN4R2PI1op99RPt<3nZubk}?j499+V2;-|XfO`{?jE#9MHMPB$NBPbCApwv zT#V!v_pkY%-UU(p=~fH6j!fL_J~z3nQCW64J#|nL{4CtFaZJ`IvUJ~`#dY-P*UQr? z#O2X+(bW;Zs#6P^$xK-*=X)l#C81KzF!Q{{FWQ2y^D21NrXo31GYj7@uDs8{wY#Mq zGIY=QamU1X5zbvsXUG4hwTTd%!VPz;G&-ufJhL2XE80pk3Br%-%4y%*$MOZQ9&G=R zSFvkg^nG1&DsQ;&mc55SOQm}uggPVey)?cviSuzup3ZaR$Gzt!RTa<*D@AKDm*;;* zA(wlD&47B+A%6Nb#OFEb;RhF0rTFMstBNG-H-|@i$sXq=3pR6?N${Whd3RXkV-mU4 z9OYwN`ZP_Fm>e5hA7=I;92NQz!$tQ!71PuKaT5YvwBIBY{L>Ngz!)_{g$9*`K3$?) zn!bznA36ZKb2`uzqlAD|csU!IjE{pJTnBh{9qKc!-NIx`deA+gatGy5L9aY(3y!?r z{*HWs-ILs7@YH=&%~9OBN*4+|^Z`)q;aj#TK4*^-P%|!o>b!(j#8X7S>gKjZ>)^3v zc9EbYgN!<@H0f`no3O7Mi5v1D7)&Ea*q6SQ8CZ6nX9jJZIP(#?^t8Y08;%H7u12;D z!rOzT9Zq3!!Gp83Q6rz@-fzCJu))woSdOyZm$K8WJyLpe1M}e0As9S8tH=5HXIY6Z z3?63IwQ1zL7A1%^%l~$netBZGZ}38UWAdsuC`97VcBgv51Oj>!(F8FbaNrk- z!_<(kcsV*{C%4j73}W8R(zUkl4~9JX2pmKLy+itUHmU6_hlPvRx1xnQ-T}Q!8MX&M z|HT=Bv&P9J(fD38b?`b+7PIOWM%V9dEmq-M3(-bFA(sAzn_=)lbh2b2hE?8h-&Ux9 z=2B`GHR9OA>ZHkrovoLy9G9eAxy+5G z_0O?#K!qALB?JiPuDss+k$m`rBgDeVTq|1%>QNm{n=nDN`de++QKYT2_A0ZNnMv)! zj%?o*U7F8-D3{;|S8gP+@OO!5PzXPJx4~i1{&zzTQtu-bZ45b+qxM2k9N`GK_r*6s z`^|dmLwha(f4!Ud4q|NudQA%_%G9WVIE$IH$mU%Vhu9B+S=PPQY(3ta*)L#*M{EI6 zOOgSu#Jpa{pcfpE|1~(`{rFU#nQSR03kx*<3hK|VmDYY2ebA?Ks6e(xtviV`d)%T( zNS{=rU}E)Hx7+?Lns6+0WxC6O$RxmypVR%xajFS$%8}$5H(sRX-e)~cJ~?~&@wjDq z+S5!lq`4|0c)%{Az>eg4)>L6qamA+A!ZCIx{Nnc>F;ViVbq!W(=+F`Uqj7>J8V^(g zg0~nzZcjHn{-s_wAgg|O)e~aFh4-c+;Gq$04sK=01X*IVs|df&C?%~2 zZt)4auA{xaxHGmO{tA|9%91upZ~NA~M{NYmii=eHj!qR(>XukKRn$%;1*lp1|76r+ zhPsTtty;xM5?Grzr50LJ0m5};#GrvRHXiwZvHpiq<>%G!)s!F@MLJ95MqH_0jy;3k zXHJuL_~`JvwHBY06KFS)zX52sA1)oU<>$A(uB{O54U4qy(feq{S%LF3q^3s6q$0^e zrfJrFlehCdnwD3(SBl9f6Z88fms4Z~M{HTk2K+U)I|uL{a;!r=-@n69SL{apRd?KQ z>-+yx03gFd8YL31$6yJi#(j#Y3;RgWL+%k^I?MF$8D{;v|0WP?(8HqkOutvuQa8Q* zht|_Y_ui%!V?t)-SjJ0vz8WS>I{~0R*K5<^vVl&<%Y%C+@uGyD)VNp(D(di8)o7r2XP|DklIqw_GN8`aB z>Bk)%nGsbBx83?x7a#w)18)uM6$p$yq2r3uuD(Ys-{3t9 zyak_T-l8TT_29x;oSVGM+Nm>N)+ z0H+U%Wi!b&!raNCibKV~AjVOpLU7#h59phiQ@81ghEv8V45qFU0_CO!EEAX5KNP&P zGvA$#8Qh{h08o&kaW*vZGkQg~Cfe`Lg(w-Ty~mU$@tDNt)VL6|!ivT}^pF$c|7ih2 zSR)}G!MdLg0}EfQ2Du)8Fr;#C%V~J3WFe2 zBACdB`;M<0^hAB$biSJc#smHx6u@a84btl2%gL^Lz20g5=b6#$xmA1-M~LeFFrex! zIq?M^^*cjR`+h6io;WG@OIw;lIpYqBHwDXz^UY63s=b|bCafxXC#P&etJHIMCQC-8 zMbCjCla>sLa{@4_E)D(( zf(Q{*#YPAjb(D|d^Khp9YP2wm>l%Fs^p0)-qI(ur*%VEx_Kv zWy19UPT2+NHm%it=c^qqM|oTT-MsIsF0j38z4W|*^p*XNp?V?oFJ*4un1yvEuiV7c zf2B3KEamrosbpq&vP?ry;Y4ahNDx$nFows zl6E$Q9Zm+;Aywl}{>Nm}6+Uqz#bY@mlKT6@V*VmGRz-}*p`D5jil3Vz1I*+o-XVG( z!-t3A2mc-EqUXEt?zU@!EskuRhOGQO_*4dpg?lN0aI`!VFiA@7jC+z4vufkI^u6`_ zA>n+i6#Z8m=|ESxw=x%U)O4-O;%K$+1;C)T0NZ2+t=u!t*vS@vZlMuKzp7!72H)+M zAZlsOuuHTQkH6SB_G3fi;7Yij{+(-d;pkXFh0{0F>o>d1lnX=XKWo;LNk2dK7DKVG zpuPM6nKFTy?*>$|U43p&d3eEfq0!pz9y&tHGRtsgZ$V4b*2nE7$VQ8_2%tr=+Z@5& z#KMZZ?6);hiW_6W9>2|cuGHc+&Y3%YSgd;<*d)9?H{X1Ll{f@`p@g;6;W%g}uZ-6Y z)g0vpM?x05+66?oANLKTju>tM&0Aco(yOg!6|J&y1T9d5!B1lgaA?Q9UmIaZ&$WeR zn6X?JW4Yc}a#{*%E>srVEQxW@r|n>WTEWGLhs!3aK39h=ZOXOesBx+tWnFDoHcyS) z8Tk}7TOYFj_#<#i79gWdMP|{tuwRS&fQ1f4%4ATX-VG|7wjZ8|(eru&^p8sm2arVV zpWFrS)(3{p54)`6#L8({kb6M@_{P1cI>g#4=x&wYbNZq_!H+g6Ro-;rDpOvv^eL&e zK59EOO+5wB?gE3xvmG#(n?$vOF3S45eDxV-M;fi3P{VRed$J-hFvJ^-Uq2F~0{qh- z=G`RxTWDgK?HAGaYC)_rLbr;oWbz*QX0^0NpE*k4xc5$s3rkLM0~yF(qIYz?TKjBK z_<)b8vI59k9;eiwmA5@(&3fhDOZ&DMjG7CsUAqJZ!klPfFqU|<!<{+zr`552<_U z*zo+he-x_$WUqQ~)2s4@@F_mW%)=+XUn(AYAO)D&K?4PFY_S{l5x;4yQ>MCw3+BCsR@P&OOmQBG3i-HyN0JH;LS zp;=lAQA1LKl4&!qc;%P;mz}BP-$$CUI*RbF;FeXeEE!$!Ysz|`Nw$a~;rRj0a7@TY zBH|~hn-=?+wO8pfLJK&f--AbPgMfl-inye8_e4x19G|I~1J&Zyp@y%&*cCUK@@bne z-Q$F(Gy#(`t0r{)F@~@@$`eb{g9dTh)a!q$ii?r)Eol@f+q+mXB#%64a19Xv%g`z{ zN_ThilcPL5fKu#Nh}yUHq=;ozd%itd*Qs63cka92EN|(Z`pTAF+7KJi@Ak?dHm`4y z-}7KX4Qc|T%7XNVYg_mHf(iC_A7~yOhqf%P2fuDMYt!4f>g6Xve;RQn==6X@ebxbu zohD?>Gt6P!zm&rYjKuIjJv>zhn9b~8CtckST^V4{pKV%uIi8dLbcrl8Mo@P4UlL`t zO?BlX(1r`~?P75!n^B`dk%6 zYZ|Bgs$8f9y&D0KUo_tNk$>*1`Ji6hIHa8fHVU&5 z#d|aJ>z>R?V z<9H^o(l^IWp-GQk>;kS`x??u+j`d|s&Ko7o;$KSd_3ljm$2HW#!Tg%icopB3G`(?Y zp3$;bli>G5vU)5bz;W_Wx0U{~F*_+>QA%>Rk-RRkA7%Dz(?a5hlp=^}JJPANU#X_% zr0nv{u`#&$*@~h=%FFWZ*Cqob6f~{DP2f!!n1HyQ%4&}Sma1C=CCB0y)l(eWndbZ2 zYqL!ZDe>v1-}Gx(`Wov%zmTHFZJwepY^L)0qL-89n9#Dd7!sDP7&EiUD+!tpTq;Di z{rNH<(|8Jt*P546-%PD)^bwdGbZRO{@dt;?Mt|5nMv~s4IV1r zAAPvBsOmSK=Vb*3E+YuSF=I*VJbcFHDwnw(uXZ)H{<)Too!y@QSKpQJ@?;yr%d1wp z;Oa}cKG%yCcBIW;6^_*<66bN|a*v<$^yz!FSYN5L)C<=rT<43%=v+$m0oLvCPoI_=>is2-p{OCx>*sH$ne<)&#dJ z5lMVY%7|l}$ffzL8LLf~8rsZvH~VT@ADVUB)j^4TPOQIlA(nzh;H&u_8^Ia{B;VpMrd;BBC=iy*a{90#27xMN%dN9WLl05r+c%a|- z;KgHfe1VcG=l(>jF^>=atzoxyhGwssI|? zhX~94Wi{C%hC6fydq1WBM6~YxV!CcBclDN(DlIR2J*LP=fuQMvEcbDXNjnk4}lH??q4Z`V-;tfu=a&3QAW*2eeh<^`<)aCo08SQ`Z zN@*+hlb_j1b(nbJXuGN>Sn>U<4*`eOwVTu4%jPU8eL$*vAoQY!z(Z|JiV0iw2g$U;Y~-GnVntVi@nu8l|yeB3oS+5F71RYli`;JUD>Orhkek4r_YA( z0a#*UaqFp~qv^Y>xx35@7YBq3nf=pVo;&c40LK{mxE~8n29p@4BFoXs2^K-9FJt%b9RN$X{PEQ$3aZ%y>^W-?cicf#qkz9wfBGwUa zV8GEfXu+E?O;7)}x4@H(4}Li4^1kC!=cd$Wn0p!^(>EKOa7d{zSNV-SS$}EcbasOh zQZ6D1qSITd`UgO?H}yHLd!<@>X(y_Lr40qfgQLelq=)<~n&YWhqFE50&chmdra2vr z6$Q}_XLw`|H3^U!J2!*nwX$yEc@I~>g6+G9!QWyCl6d4(7y z+^zGxPP(GntYtvx`A=C#thm|NGNi$uc3A6D`UldYx*;s;mm&`P)zRu@Fd?0n=!*)g zt^bz&`S0-hiY<~OW!mx-f;~93;ceTH*CvM_6mYh$Fy@d6><8|uPUU)hQ&Hv4@zZ0A zwVi+;t5KpC)EsZ~47ivqDLUG|X54=1*I(%|<*ZshYFmAuAnnkCk12LafiCmJ{2M0j zavpCn!iYlON<8frWTf=XXp?q;DiZ{~ys? z2=m}r7I+?MXIOi(d&OL~gnBE-;znTzSo~LlSKI_yHp<`uraUJb7L|Zr@B#wUiA-P& z>{#|GORKsNn)!l%>4@yOh{y_ciBWI+`z5!lsy_4x6FiJaKXq_T!)}>z8!PAPg|%Xh zdXv^XXr2L{>GIPS=l>eS&?{WrLibk&!wFC!1rJJ5yEHAl#3wYz_*~MS9o#Imve6pi&bWgYi#pr_j!dR5@3qC+;r=njOUN70&{XQw% zrw!Dh+x(d4xc@&LKoB*Dvd5{PLvsIF*vF`#VH0T~5-rS2dzJnz*wO8|P%0+YG_x9R zNP^qq$XH6}#EYxyTYij~7GT0IJoNbXUP_LLDQ`?e5+cT$h`6vxZBIg)5*tj{gKyyy zM|JC)=O)f9PxWhBctpQ;t!2W~16KmNL+(^^&k8S*ievWFaw3#}Wb@BVf>Uf^q0 zwHl$}g$FlLi>NVgo%@Lvb8rS*0OHh^ezc5N{|{}V3P#MdQq6dur#@J|h6~mlXTKA964B0wSp~yz>){Bj zg>Ij&4s$;+iNleef>YjtAT@)rJg4@ew@hMQ2PMWRdc%(@eN#)9G+J_^S*oV4yn6|s zAjV%YMB%@S;hMaW0)~qruGRuXY0Ya>`VE3BUcE(2*H;yB-QEbEwZ{-DNFIOth3k=U zb7*D*{UNzy`{&D8!c&k0-=c3q+rIAo8Y*L6e?O%5Nn$Brw7NpVW3O@{E9`T-v{a^a z`t};=F<6wDs$V%D6>FcH2`)L>=bmNHK``!F>SJTK$VLHKKU`hBS&r1i-tIs?dOb_M zR;7j0zWx8tONP;K%Gn|PbP-$jv8ETsj+iQGa^QVLLx!m7dcW&u^9-=L=##vkjy0_o zPK93AGMP(kB~xCSP1hCb9tbZzY9odZK2qOj5yu0t4Z1JUY{*Aop0&huzV7vv;_sE1 zMNT((;GFLc&Z7IUDHVpFRJNxU`B z6#osmz511Qu%aCOQeT*2Wozy);*s>lN~W7PR3x4mBA>~c!n&^3dYw*K9F4KT8E4 zNKuABNHcQ+=&tA=CtZwMT$U8%AG1qkr?|T~t!ve4(#9wbfsp{USkKZ+k#w9vk8!JD zsV;{ASu%P~y?2SV7!XoOJ!XU4RiYeiiiauU%y|0thS;wT1yYx`$TJ33Z zvY(Ga|NR#QXz>!@J?Y0`wfU6|^r*Zk`p|9l>)$5RvfQj5@>nNb1<{}3(T)$pWda!F z@aMWpcY`OWmtJEx`85pKZdCKjo3RA7v?N=ne5&U2Y9;m-R~E{Mf?&Vl4ujkt zc?yZZn>gHQqg!cGzzNl2*eU7%B>#7a?chFh{9849d+qTasl=f5|H6hL4;p=6D1G}r z#tC?Dsdkl7tMXJxW{V5e=Y&Y-nnI3nmYRDXRSuD;f|sJT(?>6iebKcQtI4H$ zSmIU_Xv}saTS#Nfg+{|BGXG_60ZmU2zbC<}L2XBh$Q-SafvN}Xrd3*!GSLTPTgjce zT(nEw=a(R@AUk28}i@Dp_3J#o4qwJ9TD;#gH(Y1Gd<``E{Z zU?3$bbo*5l{t4OlSTIlq0)Ssc*-kWvsB*fuEgFM3Q%RxwrR-yW+MX_F!R%-sYI9A7 zv_2Dsf#%)&8}qX6aD@R|T}E*)>ZP`yJ-f5abJ-5bzUr?N9jI~3;3GJ8bF&qlmo)|d z96Y=9h8oBrjdu?F&#?>tE7m|(CBWVl%OXHx?08teMspKRx##ji#BJ zs<1EABKyR4h@I=8Lcf)lr4*vFuIIA4M;?uLzc_w|-t6!_S|pKP(LlpYduP1kylOE( zpVrp*`LIMY`}H zIZ21G0OZom1 z`4+dH({I|1b$w+u!;izh^d350eV2UV5q>eb&AmFAUDK+ww*tOl-0-vCOnctA-LbCB zbKk*~v`@58&^ixiAF5AR1f!Za&?3Po(L{9i`z)fKP+(XwI|uisXY7FF8w zf_WN(k-HW1CF2TggQ?#~uTM`;^oz+0m24AuY&SY&MsCXq?J!Qh(Te_ zFr-|2-HR{#H~-GLG5@b7;h+F-3{6JLxxoCdDp^+iu9M-XC(d{U(>zK^-;pa{R^Ef?Fi zdMlw@9$%6KHprT^m1GjDLrh48gtr5*q+xZOwb)B73F0_q46P8w)_=3{e@=QI=m~F6 z_lU;h7vc?S^V(=t%rq|}SnnV@=vaJM#!D_4{fpMPEYQXmL^v%}r^G22ARDBQT&gS9 zN9ZuUZ9AA$#JtjZS<YFpW*u5f2+rSJBm(r+EN1hOJsw)M@*$73FAg zcuLE2(w6F6OA=mLDP38i>%;DbSA40=m!xN1F6hj!H0m$AqBgvk#N$|A+`PwENn`6g zO~xh=Z0XV1aKz4I9h~b~$Ew`ow!I-^g4eeN`p7xTGhB8CFP&*6Gms zR`p|>1BRRbahkx)8fe*=VH3!R+st`7)ZO(JT%c^zy z|KsbeqoVA$_hA_aNtNzSk?w{;=~TKwy1R2m5majEE@??AX)x&S?k;JNes93%Jm+`T zcfJ3x2A9mt{fQmdzV@}(IC__(A0Q^$>79Jgt#ea?Dh#TUfC8-pmke{2z-1JrjuU16 z?Whyxotia`{FT^t(h#McX%4pgP~kH!U=pVVU0n?}|8!5VK5p zCj;)E;H*T04MOu5OIi6%Q$C(4mH2(U3aQsda>0#a>Mw61yIYJHyj29~(a~T=h+fhy zjFtP1JSMY+nZd#gOUIX|k3~^@vvJf>L+4x4V}4pqV)7wPyypqzN5hJ)!YI{jW={mW z1h3_`_v6{?Rc)t9jAm;$gLLe*zA7f_VmXv^;m1OMdh2Ue+?z2t2~4*@n|8oy-`FW< zyI(q#%y(V!#099Yc_BSx-*q~y?eu?oN14Z>@2{|U@9(@tPQ&5=`mITkBolOvibL;; z{$utRvPDmig>3@U4hMc!x5R|wp0C)1OEOTboI$nGuef~bR32F~qn`DG3ejZY=Ms&> z&93!-;MsI41gw6>Md1Gaz$Os*R^0y+r#F)95;ywHP*})HTQLxtX5p7ay>fNlo!N&g!W6pV?4!efXy!9d40(4#(fUXuwkZRMui%G$o|#7u zKGhBVUily=wcj(u_^h+4o1VkBc#sXIh2@ZjEYtPrCz}p@KCAUV`!6F!Ll&jj47W9J z%uakiiM~t3Llv5?_c$wI4q=R<Pd)lR~sS3 z8en3emt8O)N!_4~Tt{@1jhpM2RCv|uxwp~9diwckMj07kgEN;Bl1yVpgR`=m)JKWT zso@^5RF1cY6{BNneFfyHe+B}C9>~0~k6;;<-q$FI77Y6`qdfiti!E`a;%C|8BE3~6 zE^Q*oW)6g!hiHuHIz05*0mdQMwo7HE)^hwowcpXWn6+ypu^7mZ8%?#*wIPh^Tqu*# zCx2ru!|0~B^g3@54~j(uzb^!l)o$G~Q1xn#o-A`B&w9(6FQJ1mN~Q1Y8CEZP@C&MF zRIs*77Xxl`YPAX)|MB@RM1^J~WP&11$BPFMf703&W)h+LCxq>t2Q4E zp|F@AeGIH2OlaF|vNzu>yZmj3mMyn{oBVvthsJs8&j_J6k3yzJ3 zn(rH7NBJ1T1}F)FV2P93{*_T!-`~o>WFpMMx#apfnaf;1RYBlbb`jkqP?FtExsynK~4KZ8L`4q8p4Vjdj1!lb@=e$V^I*i^damIIIz2D zm!ZSMoT%tZO|f7pI3N|X^o3!8Px{wazgK+}hGI0%3(|9qtdqHRP9wei^3hlQ7-%wb zjnoOn5kYJ1oqoK}mq_)*h!gnKBKw62^ZW; zX147fsDk7mCi*#CxSh>zns!7}Hu9a)t>qa7xybbF zcz-E%E5U>S?RCscWD_yNf^Co_}gymEG@>>9ba-`d}tokv72}< zqx%gBn6eV^+)ya|wDHw@OOo4Qhk_lHs+}0GiBdt0P^8_XMM~O!F46~F=WhoR>7IRG zo%E4W5x||87WJ+pSCOS|Qh@ymlK=_mFs26!$;9F{T*^Lfajaj-PJMZ00VdgD;~jVv zG!)EU&l^ZBSAa~>ub-ZgAyM2@opZ%PMsEO!0Hs~CXLj`A~cwSlTCKCf%L?ngz%FXhIs}BW#Hk71c0eBr7stf%t@-E}31-qigI zw&=mJ(^BvpogWSZDP*V}`?BUtLpCVx{hKr1!WFMCOx;|@;uL1E9&h2Bx+`w>AhA;f z)b_|%C1|Dm_YF<}cpHKWho%mEG#@`fMo$qe2rvBO6(P|Q7N2({p>yOj4;%p@jJqROi%)^j;7x5sfsxn@f+VlBi}YvI zCuN48=*_S2vo}3yAQ#A!Z;oGx;clMz8!$^ztO2|g&P4gJU+iJtW^_`s9qGcET37Yh zNqp{W(W?z~y(qUrwa5z1oU4KTM7awVU?Em)lyyZ#Q={a8dW+AyEsjoInY%G$a#;O= z{LfLki#sQAs$)Bg87PUs%8dOL9xJkTMloMDc6cqOtNrqcFyH-c2Y|KT8n5K!Czu`* zhU?Q?PLg%xG3IF5=)VwQA!yRQHD2;^2cApImVMkxsfmPPtBg6`o$O5!2shuNcM=Hs z9!jJe!;-hJq0uE{-6dW5LvSQJVv(Edqk-FtLXQ+H}G;=-=?QpQ=Da z%zMO*sxs@1f4F4ryh0r1ovGc9c;%hG{|PY>uf#)>=uD&K@c@Jg6vjhsv=I-`v?zi> ztwUDocu1*}>}$fsEebo!|4An>fpmh%+)Hf!0c?}s8C7y?h*oVIDU!8SFZ)UVf{Y|$ zReQDlY zHfz#6{0gEkvi=l&E=QzD=a%U~V3QmSP%l&`N8w7L!1xBSwN-pEZ~wM>Eu;Io(s7sJ zP*o943nEC99we{pczE!wcS}`|G;L0UN#^0*kz&AvdzY6g>y)hxCJVS+{-Nw3d&xHN zw9#;TDk~fFHvfg8Sgg>SJ7tbF->E9uV0oM;x&I!KoikC9|E2sHI+`F2i6eebuJRdu z#1@mys**VaL&_bLShE?{KDgAncM}n-`1lPaTo%?-r+1@mb~7nn#f4mb(7^e(6&@&9 z!XDt4szKZGk)x>{{)Co)ahVmCi&SW$UMoNF&acH*fb`dt6jc zsEw?WzWQVGXZDrIn-j4qWD3Ke=4Sm(>5nw_Qe5J%KQ}vv$`Pfq?2~+vq5+bo6`~C* zU-6j%1y9g>F>63bc2~9c9yh1(1@Rv`Ia!=PicP^>(H%MO*hc?$7(uiYwsHAPY7uKM z`UJaw0x&ADVN?(Osm|diENBloCo~cC>~KV=(1sFP*@+ZDUdpR#jLrtiMzp;T5tr?> zsGm&#yzP^x5QhZ@;+>@|7(}(l0+T3+vHDfm!qNYV;Pc&HLzT2fPI@-OH+r+S>`spF zYa|~j8L6!5BcNAf!|-FA%hqs3TR_zVqn|IOXoM@PP%~MWhCd~?iy3HV4Nlqz*q(Kr;73y*&*&Az{Un8N= z(Ni~-cR%;hN?-V1I`^A`>DEHm%Ror?z*9TCIfs_5DQST^4!xRN1`zG=2DPX#r)CV;O`s^ zWcG8(98_|MR*h73wg+;$lY^Ny6>+{b!sLk3sTMaHl=lT)RvOJ!M=Hnnk`PtfV_uBr^j`NR!S zW2f%dN83jq0IQH%AoXjIJw=@zQHqHBc# z!!ll+qS?7!&dPUXyn9#FWL&?Kzjc*op6)J0;DRRiW{q0JF@UmB0BgEGjvx6EMkK_} zL5UuX*Ia~-Ml|uU3Z&ri?NNPFjyCy3F&%(5DhDUgzE?`iMW7J0c$=7v-|7Q2HpjLw zlE=vhAq_}3LI-Hl>HYD5IlOmYWFH6`$Vx;T%*74G^-|nUa1O@0%a zeFI&SI@rIC)BW*j?jnyzF|o1Bd0Ni&@saJ!=4%9)4f0Xs8~u4g^8+|yIg*)idv>U@ zy4dO5%Q2Z6h0fjV?U*0xFlgZmaVKX)zldZjU@d5RUEV?wK?f4!$Y?((0yqExlxxja z%Vew!X!pQNOgAYFQ{+7|%|}GJcZgP&c5_d{3J0W7N%}X_+M#!f5)JQVC$&3?aL$3c z1*IXvl7N>)vn_Bwfoq%%NY;iIp@*b@!tOu+wfLN_e6)Xm>1kBEbtIv0YbMV*FCo74 z^xlf|+Q>(=M5r$;LkgJcJof4igpWXJH`1O9F60zIRcg+Av%sy`b-z~>a72Vi!*aH+ z5O#f-TETGGwXo7rMXeTyU+YsCFNK#>GZ75$%(8l5sCFdr0}~33xf5~A>J(O(xr}rG z3^ENutSLTHHm>gilEzJZcQ&4rCRUUgC5i)9lKowl-uKnG^3iM001Ra>cv1DI0_d}0 z&Qow920ZC4$r}j@@ar$l#jUSPZlv<*UyEwPa*UM@ZtSK>ZA*^S6CdotvOQlD-v#MA z_b5?PoPBfrNk)#lk9Mfy?tCq2LcNcg?YGK2jC`n%IYN9i5_G z7qjK;u1eo<(c-b6+x=L4q@7ZQi5Dbgp@(c?`g?1UEr1m~MWBD2usx{|P!*PGAKQiAtZ#KiE3Mt>>e~?qFlWh3ojMmH4lQP9EAtaFz0E z5z)S(#E5J5?Timfq?4zXIhZG0pV(x$I*?`YIrA=k+*i&-A+v7vNf*a)q91YWq=!Qs z-}qERq)De!DVs}f;Ytmob7EPxKVl`hjeh{8{XKg3YnSPx0AQhCE_$WreB@sb)p zYk!pD|66c*{dLONyfg`@m0(t75eguO7#~!cZ^$B;BS7ct;(>Z5{bAOUI9f4Qu_$~_ zJ8`Czn2NjrPV2v&gBrw)%Vuj8kez`E5cINC4G#yiSv&gu;szH?Ej359h?tE3stv*B z0TcdoHX0ApLm}O4;rJ#a^i-ka#%s!2&=UxD8=Ku2MAauJz-<{IAi3#KY*4%|mz7Pz z0c@s+M0(JrV#-3ow1y_iRe#*CMz#K=yOXa!d zU@BaAQXkxhFy8}`VtM5OVIGhl2T8JPydl=;Z+sni+KhV1cRL&wLXiSuF7mNU-AgFy ztnpS<`a{r(YGCcEs73Fj91pT(2GF(TyLntDlFb(C&lNXKx>DLyQlNHRGBE(|@6~YF zkSc4jo$$D>tykT@x4s)h<1w%8*+#?X^8!$r{e`+Nh)pa2YkCd=oflwT{&QeVQQ%O0 zJYssv{su_T zy%(~HczSM#^XEc8pN;++*K6adhltt$_eD8YZa?bd<(f@48v3@V+jjG<>v;w~_swyG zAN{-(hbfRFYXb5y*ik-~--V)y-WF1vYUZ!K5+wtA)tzyl`hIPA!dyjle4mH5e`-Ei z=}KCrV{Yo~^iUD#EmsjJE)Jv(ZYmhiQ=8X30!6N$$lVCjo~Lfy&bs}eL}CBs8pB}s zNcSVuM>KU4C>rZnYSM}%6DKu`j?OrBrH*^$E|q~tdAYV@cjrc9Tsi1lo>SZydx{q# z^z#GcPUU8mSym4IJ(tJlrI}XN-r3gDA-CnpTZ-~$9N>b<+MaiY0Lzsi)nQlyKR{rp z=|)e6QN2or`Xh}<2`QB@6npVlcRXL0v|OSO7l~9rkPL;yfv#58P3?-=e&->g{SlrD zvzmLpUS~SipzKFw`VQypH@rv{R_~_urcSlu!zR8qTDOAusLMMjDX8$;x=kn=@BJv5 zOiy-1i~5vve8Ko$@K$`11J0WGy&6*p3ZFJ>&p3RDXZop{fs5o^#p5pp`@_lpzw1_1 zv?IUNzWB5dzqGH9^-xd`&`s$E-GZz%ltQG=1TF?)(1D*_{LBn%(r<;+-qD(tO}6Ea z%uR}}I68qVjDAOQ>ReDCdz4Ive+Se6KFAw(Eyi5CcK1mE<1XG)l@@%la(MQ5=-Itz zLpFH<1s}=FbG{3aO*i{)!)>xq*eJWjDxGGWttv6Xdqn}KfQ33m^pGbu3AmcSH4390 z&iOcG0Iw6eaHp6flY8Lu3EBWU(2FO^39Yp%nY_CGX{NU&YCc6j4;1hA=XfvG`gbFK zc(;orDPIpvQg+nflp=3gt+4ZAdI8niuc*VLoi{352wz(vYo;j6+RouIu2l3AEc4Zf z0M2k6DL|6P(pdd!kWlq++xSBezses2Lb^G28uw!FFQfY8a&~{RrGE2U$#0lDO16Up z3%qvs=I-?7Vd$vqbLI~HmIO{sVoR;Zaz7ygMDN(GETk?b%z>%SH0e4b5-CCE@pZk9 zg~Fe?cVk8spTH>LP6%38tG&@r*^hsA;mW&iB6U2t585CUTma1SGBbOD33b^yr<1_B zk!H+KqP6c+p_UI&%`pO}a%zie%1dmvm$kGCQ9Riy1;W}Vh>{(<)7D<6?F}_%e!?>A zNSHEM=Ux+)(w+9%6(lb4xiiQFwvui@Er%V&LzbPFePW0i^coHQced5%-%-~f%<&q_ zvx87rg*bHvfG2AmymcuPJPKP+*G154u*S!K7+nCgq1{FT`MB^H!K-CwEGcZEpH2gt z>);>*?R8hzFrToLl>OKoY-8K2p~t*8HX0!a{oKGAz(|79lte(ulKf0biF0?>JGD#C zY=nQUu74=vM2L(RjQZ-=)&%Gk{jg?1lR0k5jRjH?<2bJ~P$W6ZFBN4Ea)uS$H#h}0af`Tb^lZ7D zu`_t}C^2%OnNGS6cx&M3xo>Ix-LZf?Ds=>Zxkn?(NT+v3%i&9l```_QuTzWY*22_4 zuiI}Uoq~GO3!OZ_)%;WG=%6Wb%POj)iZ*ZQs+{Bj;IauKjJZlf?AIc)f;?m7Y(cft zwyAy1kw-?;_yum#MvC(l4OnGc5zy4`4&ub_(vJWZ8p$%x_nSOYB(SiJRmOPwNN9@{ z)cTkGj0miU#%;P*PiGi1Ia_<^a%SGRO?)}&M+OLqd{va|#IZ9kK$XDH*OjO3gpRWQS}SfyDQU5xl!UkU4xx~4f-D^p z`_ZP&?RjcA<<`0v*j-5j>4b=raXov>SdJME=-Rt2c8zD2RKnosRws7+S?NmkQYyUK zZGwQ9-IdueI&Km$*18G1=XI%mjNz;G(^2Y=Bwx*-@-ffO)fI^&q9Fo}pFdsiuuXOuEoB&>l~xNcn=hBUOyX>+C;Rni$4}o^5is4m&)-g zMaO`3f-k{GNQ~B0!REv_PnPS-`J7rqAQ2#^hIj!T?=ODyyv4f)%lukjR!b~A&laE_G*pE>`WQ6;$gPU!x~G-@M8Ay8edLM z1!vy<-_?fjedmAyDqQw^h=KBPH#856BV*K*w z6O@Z=aA4{_Tw(6kUzw7?762}>59n#z7`LznOZrt)&22V(=fYrRK!U~^92$i&&iab8 z>3S|YchjV=b60{X>9(213vdMoIl1G)dtq!W zJFPeBr-Qkj9}8EtV)Z@f2{S!*k;CI+0RV^+Gzmbj@O%L_`IO!!#*;i@pHugFdHUr9 zKXytOmf0Gp`^_59DA~m8XM3rZJKGoppbb3u7B7&X9^hEFxWZ*pMUDQF&dmTTY@GU_ zR;jpo0j*f=<=PlIEp`0!6=CH+p0dAPusmHreT`fzR_X^V0~hbRa=95>@Of#=UJECI zu7ga{dT^(M&;5<{DU-lKVYQz7sGgQoSIuk?4xI=WZBw|K_B1bHQ2&K&O$&Z5dB{=0Wl7G=!ydE66^q}SI&^JOfS z^#W54N$kdlvf-6TyxPQsFkC$nM|xYC4w!4WE=^&cEG016*Jy&= zZf;ZvO%2JXx>Geo02ks=yczAZq|Bp1uBPK1SJ~{s+L};a-gN@@Ze73mvf)BtcwMtu zXkg#(gbLa$1{yh#^E6W1N)1=*E1^g6B@d&?=t^T|_4llPaeXDqV0-aoUkM*%+etXm z^5#hk1)yB(jP(wPOfb?O$i(eWXbf_qtjDakII}q8g?mRdSgAu;l|NbCS>Fj9wu8r{ zjD+-_zlDVbIu_gsy#d%Q>|&0(T#JUno~YhjlZ!k6kR5bH1@7$9GHcVWDqXr@SX8HD zjA%~>pNe&4Tok)Xid6n%=kc1b2B``sErglb4N4BMGO%(ajOp|qfh&uz)gelImzaOC zb4N5FUq40f_oqS(oKcO7OFrzA*-ML4j%gRe-~u@!698~h!f&UsiMB4G7=SkSx%pZ4 zv5RTcMI;Ol1;eGqmZas5^D(zQ3N26~_{czUloiK!#v@ZK=f8@kt!(_jN?!hmUx$aYV%d?Y`m!-ox>6>aUDY zVXUw;=(RP2&JJo%qXj^Pi#U;p+4)dmq*!)^YIu}@%|@d~=hPcoE*#-#&S$YyvD3b{ zw?kgL+R%S3L-pb|_J;8x^32sA*u{pc|HB2~!#aG$n9>P$S@HD6=0pM48bC+NPjzUH zF`eW46Nc)|>0DNkiD9b97RRd3TO+=UwR5`%nL3Q417+_!BMQ3ZuTe-Rc7#EC-cFEf zNa`_ClSQYz=2Qm11*Di>mKoPXOWHG>s`kF5_zBuo)}B#qC`}d2+4aV3f4=S{Tw$l1l79yQ!63z|M zo(}t|*)^87_1KVIWD}q-AH2!x^D|Z+=!#HUt;f6#dx49h4)E;kTDUL*c0kmdo5Kr- zW>2_NHR!*lnt0d$3W64ainMLNaq~bB`=HafcQNH{u{ZCN-S>KEj`#Y32$g3+jH$emH+Y|Zp`w#ixKCYR5&I&+1XIa| zzT3IAFgo05-R7eqE1%hbo0;63vAgjl`KJ% zjx~5iTpV&%-u33%9M}NThKCr!1GtOnk86T!%e|x#_Z$$6I>2arRYE|Z5i@j8^AXk9 zVtrO5!n9xZ)(VR*l%NRb*>XtJCW{UssI{l^wy*>QuZ*B~%V}~e5BpEYm;r zUHm2TDwX}{R%94bajIS-9?2C6h6}s!dM{#(j|lGey${Q@KNo)h^8hc$VHaGtzBG7U zQY^*cECFRt-X>aQdUW!+Za5J){Zs2_sylz0gJ^Qxke%=dASM9*W8ay^UPYD#r08K`=8NdgN65)$)9jD~avHG=ozEyX8D~w(8nqHqR<7eO| z>2r?%sXS1a*N3{UF8@%B0{$9dClZ=EpKp$t3RWV^_`4S0fVFV!dvK-0`bxN@ao*Cm z7VH&)!{YbNuNAr93?UQU{S~A0TlwCF-`S>swvFT6ER>i zugcT!)?zx4aPRbfGoY$^r_bA71}WI$YX75CW&>aT+9iQz{$+AfbsFr`U%W#Oui3Nz z)M*wD39gr#HdEpg2V5z#Ce=J}wmambaRM)ARP_c?Wb(EhAGXrS4}E9%;*X4b$|Ey* zn9T0O3$KtlTpH2+nf%L=+6+8M)xXH!mt)FANFw!I#Jgv{p55f$h!z5~CuTlbQXtnM z9udZ{QjBMx0f8}uIiG>bKela+% z*Xj(-~oLXZ(z1vv9=MxrMsPMVxNOpZf-R6X5`Wm*TuHc^aZWt ziCQDd*r(49yu~{owK&|mAz8$V(}Txf7;OU&Gx7Qvv z73AeIdIXe1NYO0p_1ciet!o|ttTqv`03huL9v|8Rq_a~(g1_)en!l(i2D4ngdh1b# zQ}a6oXl8zn3s*BKV6>$!U$a(=n)u4;0DgmD>o}TgQBh+|;oiTTD`UOD)XardOnNqn@fL(h94Mt!}#(8m9PAye2^nLl$?&CJS zo1~Pg1Tu;K;ZfA?^UtZ3YC=n)UwNtvADMNBpEmHCs6icXzjO*Jfo-CIa65uGV)7BK zb(cF-?c^=CmN|%-84;^r@TW}sbu5{8!q0$iwF|b&!S^S9p0_jU#aRra{ZlA)4sH*> zr?cd*zlWb1IQ-lyViZ-#s&fu{Vj|-rdW&bDaYW{;OpQKJ(}K5>ebV?#xmgTmdBlBZJnS8@znasrH3F;aQKtgC4C)oWU-kl%RhLciJ8$$^%5 z{CTv}_Kpo;#2pjPy!~r5`uFg=NNyXKO_sHsSAc3uv3bbK#oe_~SYJmco8(x=da298 zkj~sgseOH#(*_vVBS~?i%wCyi90%pukb~gA5xFVbGF4aQ3Ob%p1R^fw6ZOrJ!6^O; zcQ1i(dE!}4!LbEo;E^c|qb4pM=EDrqjM zQrhQ`)Mq0>xQ?i5K-daKILmf^t6lkNBv6j+ut2>pVTpc*RAbRn6@c=K5igtG&E;2| zIUU!Z|Ew!c49x^op9?~c^chZN_BDcyyd@|!%B|5p_OUk+S-EW!P*N@%J$d&V$e6tl z8XpADCX)gB@H_}b9;_9%iXU!8?jg-q0Le*vH{t=WHYv?_S6YWE68Ys*=@H>Wa%IWm zT`LEYU|qwdRlU@k1l~L100ard-$%P&`VfE1J)09DrLU>be}R-}K9HG)mHbO&p)m33 z_Cyqr3yk8!i6iz>JLG`+xe;!^b~(takn@$@THx02&g>pDp`AnUkw7Kkik?G1s+fHC-#o|Aw*rTsB6Bci|#ub1_pM&!u%kc{>{ZDY}87= z=dISYF;8|XUO-)TA);{;wBR7~M0Dvj3CPwnj{n2MR1wS%22dcVPJ2iN*zjt^>UgH# zgz5fwRxj(8l!SHAne|?wz66DY-(s5I-hW5yEg6AvE6uZMn}9FJsK=e&FL+wJo&l&W z3=2`WNLv=*(eof%47CNafx=+BAkd@GEdJ}>PXYYHit#^RdH%H0EpA#C=48^H1jfXD z2DI7>q59F$t}%bE-poW}-07`80CiweeCOeJmdK(6baG2dlsiM>?0Vr;;Q(Ai0fx=C z9?z1zsLQt-JUp5*feNb#OFs$%aGjCCW3X}kH*`(Sxb_%VP|o45!xa+4soVsa!_-Gx zMQcuuuitZN9NJX6#bj3_-%z~z{J;PjgDmL^#*of_u=_qgS^d$S<`_m8h(DR}RW!3aQ3pYQ-ym&NnvIoj#PiVQ__G{(s+ z?`ir}m3ZC-YeD=PX%Z%H5{wyT({HvX{2JV)H`InvQ~`(S1eUbOG{>8MG3TVHIK78=}9nE2%F zJu>+g>eG5G_3`a=yA$atu>;O(E143-GWY_>dpQ36;U9ai}JKiQU?8xnv*g#9mydp3t zq|~5UMe*?3T zorgb)amBvpA!+lD3OQ_N3W;H-GK4jYv#urQZ*E?me(AO=dl4P+eHVV9!VoTP854ON zy1W)|fH3KR4UNx*0ygU&wY;@ax38q*p>?XeqQVVIHF8=?Shj{ z#qj^Av_L3c0`Z8sMH%5{2I!q#VHa^P}+!W-iqegg@gOxiAqZ4FYWT; zo`xLPnTSheo2!O^fe-bPUOgMLI$w^etX1U8D(1R~kFLuqe3~09Ug=eMIlFk1vY1#F zx6{M!=&ICo@Bi*{*ZUuEAy^LWV*sA>@xnn7z%`85<@n9|DbDw;5SyHK6j^TOpqyZ~Pc1_kRHYj)F7 z$R++|5(2lC3W6n}Z1#!oC1(3K^#%>1M)A%!87>fDT2p^SGi2hdD&xxeoq#}lqZ_FB z1iNpJCELBOK?umQ91z1)v<+T!+OIAwxEZ@|T|Zw}c9V;Cm-T$rsaQw-M7Z_~?8f75 z5-gy1+7apDXrX5cN&e=aXWsw2k$@QuyGr%n{hm{MY!RDS%nc)V>_nq&hmb$Zc^mC z?aq@wMP?b&-T$7={~T2;hWUzhT&!xJ;oewr1dXkMsy1I$TU*wm?v1Lpgs*>u&X~q- zChq3yVyr6yocEn#)HY_E{(O1J8j(7hfmHwyE{~$#V%Ea|W<7xcU+I&9^tGyT{)J;P zAoJV^*HTB^sAMLR^PP+%>BMHI(fg^?E9gKeIwS=~Wf}cx4!T zM5Y@jQlt!xx0xsbZR)r8=ieVRTIrT8pB`h~Y|?~NE`8NWgEkgOU1xGg1)2J!ZWA@C zH(6?zEhE;wr2^pP+tmT#umR8b6yrnA{cXPew;wp|f=_^^c+k*oWPSGmh8aM%jHif7 z`JVBW-i*Q8WWBppCQDqZSHKi;5n!+Y&}<1wr69Q~)8zev;ujP5Q@H1`tflK)e`0FWE-9tXd{@h;ghMu49B%H0Qf654w_R&QTwURwrZ{t}LIf}HR1rF_h zw?7GdQd`XQw)dV2ce_?_THpYdb4`UDVOUJg4{d9qX z@?V={h|pN4xVQWd(hYoP7Z?agS$t7H0)we0zeb z#ovB^&e%;Rg2c2c8WqPx*PwfMvR5KN))qT>M42o)vSTja>09(`4!0^kFv&Y#$KO4G z>$hma(MZw$ucLSO-Lnh~akST2>N3+^UAypG zE$8=CGMr5j|xJFJnIlTq)Jq1z)#g+2eUqdji}(7IaMzN%23 zG*F9AVjX9rjA@&{QQF2<9+mYxkMTclqQM_l5^*=pzTjDrG0Ict_e|B$KJD*R?Xm*< zVGTK!-FRrhF6OdCQ2IewLnm}fYjo0c8g0W(t}{>Suf1wC^LHMh*p#|{Hw7lQ_!_57 zmo778hSYx}@C+Z=ie9pLojUd$y|}*o&n~;)+q9c+s^1A_q^w;-hK2Q0FIG$#q~sP( zLn&x{h?$xM?Fh;oMzNFVZ!Z zdulU))*ha2hIq6%o%Xz#sPf<2c>k(B7V6_yCvWYnUL~0lIY4w3xOr%;WT8E1II~8k z?QP6)f|-BC`n)J^lAH$f{{FoB`e4jkU0(X0*L=5AE2xFU?4M2dIIm3ufKvQFH%to? zSlz;It|irxL-Ph1ER+(QPx=lt+J>3B-CrjycAxC5{(c>H8X3|WBmP<`R@mK{Zr8Sc zu^^~e(&uc&YA;Dq_5F?x=@>Wv4nv!?QSHV^UC?a9mR=;6S8c`N8wM#E$~USQRFh6l z#!yDuuZL2R<7aUn&MRt@bZ4*1>RadRT;Ut&y#^DPm<;ZGVdw9s^A^bus(adTO~Ur| z75VJ>PfW^>*F-fo{jb*`KrxoP)Lbd0k64Pu=*Zq9B8oR7S$EhYIr*C1lSQIQ;<`v+ zPdt@Y+OT@R`|(e^FM`fLXtbNaEc;asWJe9+mmyu4F!FoHy?6n<@DiS%Oz-D z`TnkvYWIH5D1*CHROyOugpbYR`oG(NtYg^UTqFuasy93}UoCD*FFs$*BZhRI6{Po} zHm)4#6y_K0EHPXS9O%qg(x;5(3KK|7;4m^Z`+O0HkMn;wt*27>*{VANLU<*BV}D$~Q%`zmC~~dINJyuH#jV zU!-r2shaV~oOJ&VTh8yQUBCFIckqfscBHRht$QQG$u_q$vNbJ+TkmSQ)=&9zS-?NW zxM~mlC1g!c)Z%wHds0cf{`qtuzD5@FQ%%j>g8ECH<;rWsjrcL$Kj+u7tAYXNo-#xR zIWcuUOTP-z7z=b{AahjiU*XO(zAGbUWHCtLVYU0MT2ngT!jGZ}S+CCUJ0C|aHZ?hv zqlroPpA=V-s-=+%*`Fu?9j||!Q)(1|b{bRMBmd6k-H63G)rCKWl+|HdCBJ}zT>y5; zp;gw+qb~Ij=(n7B?cx6fNPl;N!WyLHf7sgqbT5XROyir{Zs!w}sArmzsMDq=rS`Vb z<`mAP4{gRp@oe(kn9pod{L@8f2(Ip>Adu4j-!}w|3-H!3s@oDg=;QMaJa|kDTI95V zw(`R{dg=x4wB(Ixx(t!*nCtgjHKKN@LRXGj41&X#nK_IlN362dQ>;KUB~gA1VNqq1 z(^s!|IR0wrk#7+xlNFj6*Yf0onBskxaz3hDCV@<>qgJX8(acDhS@#Gz9Q@VZ0K0Jc z>(Eu13~0Q}tL&+BpNbi}8tHpEmD+gebd!T`ovkZuaovp=Hb@?Q8t1&syn-{~o`vJb zi}C_gbuF2o)V2E_NlkAiEt`g7`PB;mBD$K3A%}D5%06XCm>@Bh>@Xy6?}joH;D;qp+iez@i3{UoNV4kODLztEz;+gPq6OUI~_um)K2q>Z2a>U#Ge(UcZI_zDOzqCEP4>r z7DB2r)sOSSPRdw?5Oxgp3yU3Raz7Qx3I9Lq1)Q2Px_+POkJs)>tR}Y8mV_l+{N_^} zv75oY>x><1sY7BLWtc(}ra-f-jn=T`rFzyF_C7==K1_1$YQ(G)b|i{_`irO9Cb zjfv~WhNfY|_K60xvD-13n3!I{yUcMB$3J^Sv#JPtvmVgeNVRj-Zpsd=(H)&leg?Y~ zjx{VB*kF%*cVW6zkxIjehUU&wMR~aC{cOqag^Y4RNVNfvBB2SS&N8D@lsU>f9wP%C7#>wC4aA3w-3?;z+)VazFj^K~-8XsudnyplX1_=E z5vo%}s3I6EfNHI_d~2Vbr{R`sQKbtv{J0_fMnm%BzmDbo<@~R|2R4C%85@pf<>1lGSE*r^2QuQP*gK;njSeRQ zV}}5X>B{8wP3xl|nKx*P6!Z!WI@Wsh1!(>mGWiPMxosWg!bB9X;3W~r;iY_?S%Ir1 z!f_4!b~~;EJhfUzOjYxM*YRo5M}N*)OR69IewVU-hXOUD3VitPh){I*GIEX&I3ZUdnN}{ED(gUu}VRSv$`R&DA^Gv%BWs&Fc8a z8v}<0V3?4Y=O5IgB#`=-AzfD?7`Hu{tF@UBT_Owg(dVZ7O|X7rh+x0*-P_}KHu*n3 zzQCa zqA5tP8=NW0zU2O<`|KYsfLLcLa$VRrMgT!=SL&O0;Y?FYeig{~iho zWmt|*oG?`y{^3o3#22Kogs8(_)Z)qmZ6Yo^T~YqSpDQUnS_{bQxrwobm(F~)L;vMq z|Ifvmz%xWVvgz};k`nd!-tA-e<_br~N`$uZo&0t4?1UgyM%EBdnDWjWSp4%mt+VCHEAy6hzQ7<`1lV3Tz>S4fr1zM|H?zPiwN_&&cAjK>{k z*=k9Pd?dVEZ)GZfgQeJEqqelpoj@w|@Sa}v?<%H)5i;vd4`_dJ(VyS8_)^?AXc!~gKz~<*uZWyVt+{~1Dz2+ zq*+Pi#-K*5xdkvba}m+YhYuwwloIKbbc#rQi-mOQOxwPLX|KABhV zmr3306-5N_7b zduZYd&ZV|(+O}xFz8}HPd|FSaftz|=ed+8 z|NP_0|9Xc1cC5fMzrAe$0V0pD(&=W`@;5);F({vF;^1X$H; zI(#ZDe+;{(@)*RfH!-{)dG#(u^2UAmY?7PP`W$cem9V+uqQ5zySg}iQtrZ^SsK?6a zLQc-4v8r<@K95ji`CXj7Q331n>eu)$I|YM;{YV{)tKW?={W8xhTs*?D z)V|H8l*Cleba6_H?ygs?WT%mk+V6}Maq0bDt7*MsvT9zciM4sp+Wglh^+!OKA_H?x zoBUNIs+NHkNPhEH+>_?X$?L&Wq@~R?KS7*7OF23kB0~$9ZvUIW|NA!guOKa;k4mcr zn$Snew56;FHudP6Day*_MoypTEOSFl8_kbKdNjH<(_$SfcY16&CHtcGee^nmEeYdj zm)2<=eO`3F(6jJ1IR0^-OzRj!BH8nJ?D43!LKJpMtWz@1Y^JRG_7PYk>{44gw|(>A zlvg1+c76F!L#pQNpmx;nOvI1linc5qjmqHt*DZrH9qn*NkF(PWT=lGhKfJML<2VzktTYm| z5*5pSDqm50?YzV*6kgFxzSK4JDaVluUO>O@og>6n?Jp5(YaVC|%>xQ2WnK5In)fAXJVAvw{G*8~3CeY333_#k z2xA>;JClv}Yb^l<65qFK|C1HEs@9@2zRZnBF}Q$saBXK@gco3^aIB>8uZE0rL6*hJ zMeB#u_y2^%pV~j|ImEq#Vc-r33pS{JPo&CWr`psDQvq&ejseymR=AQ!t# z+@$-LbbEdcA)5@LFanObe2uhYj0dru*8v>@SIOoO*hTVQ=eWzD8>ha;^OJfPqlg7p zyoBnS`ut+Xum>+K^;g?A4z+VtLmyAg+EEbLrB@0OuL>IP7y#{~gF2Q;{72!HuIAVC zszHsPi1ODI<7mZ?=b~)R;o>7=yAA!pGV`ZtQe6}6SRb#pU;C?`wj7jKpK-<4$@cgt zgv3T1<3WXEFht^aFLzxednKY)Gr|P1T=x-$ttI6qq^FmoJXL$PbCe^y{F8YYM7(h(pF z6o2!z1yuDg*{?jP2O#dbEK{M5m5+mVeUZf4~O>l)VVtd6Mu(r&W0tMeS@S5DiW|Wly&A9ctm= zWS#5q`!zFU!LYFvJj%~oAD5E+BOs@}l_4y^^2F|BvMhHkvm=JBxH1leb^qISl~?Eid$PMA5*;NWkUDR2DVbk zNPlhj{?16H(qPrCYybD}R|>18rC9cG9MtCyJ_R1yk<~#Fb3I}*w9F`|(7<-QVz1v` z{XTB~e^Ly@{C18;pNs5VUNDd9y`$|zB>FwF$>QkvGLtrCWC~AmiU(gh;1oICM6@V! z1r+ArDluuN)hGd*CC~4vnVlqZT*0FQot~~&ts5nwj!*5o*-M)BK7Cm~J4&^d-rk6S z*(q`Md=>Sz1Nl_~1$?FWl?FU-OYQpuG#w8>$}xP9=jsK=PPnX2iPw)A=} zN(y89s`2cJQuxV*)E4ns>48w@dZ=eTcG%9m<%!qph7xh4|AS^w@}2>E4HjZs?4fu@iY_f5WKeD9)EW{( zJLxXDEwx5Rr0jL5=<6+ixrz1e3FiL+9a_vTeOsw)<0Pv@Z!Sqv-nQzza>7vk)H#Nw zWxz2|uP`07UldbyTPDQ4LC&gGb9iGteU-)Jwe%H1Uw&W1*22Q8cw&&gdzH=Xet)8a zGl~r`#ifQ>U;C;>wLg_dzTwl%tUb(blJAun zC}jzZ|D5|`veWxKe4TubT&8#$cA>>|tOPgl{rKGgm%z#c4H_@xJOR4IL2^+)L8kqR zCm`4Go|M=ZFo&c?d4DQ+up4A~W;S_$`i4J{K@YUQ)OsJ7%DFL@#(gM_$3b~0vzmqh zVUTv4X^`N7)c+~Z|1GnC`3@;}oYoopgDdD!aO72U9gq9Go;>E6d1(#%6%1APtaExG zV=4i8I=Rf~#fLm;iLJbNu7XD<2imw-4cVovj;Bm_Z=5uA>Q3`Y8Xt&0%w%$`oKOTB zN36K8|*JkJUXxOr>5AM2YBe_@{euN+tUyR21 zvn_rGS91bYe~5%$+e8KrQr9Htuj> zd8VZSYe-k(wCcUSMCH-JJ;coeEARqUdajG|ayV{W=QBU*npf{?YjK_PYfVRfC-i^t zB_sqIKRed3GSvePh1t-4T>iK#ku4k9a-E3N9&T#|KQwg$tuYTs%l`k!4om?eA5_lh ztSf_4Vk+!+<5|*C`chKZy}?}_zgoIbrM@xsu*Ftc(NDCZ_8>x%zcZYCSrsiwyr*V` zgE%lDZ&tyS+{vl-(yhEu)@#4Hb98@NR{dn7bOM-E8o4)2D6?~a3$`?FJI#)g@eONcKt%X}W8cCo9F>TXif(vOsa4*35L{DyZqaRI z1N#5oAAn921$f?4%{&RC#@n$E4$UXkTabJE+3L+52$qXu>i);nC`zlErjI#riiwp& zH)Iz>32#1o*7A!GBLZ`>TEcfqQn?3Z%?+lgN`WwmN}X)m%s#OG0N984neK&e<4&qh zldj?NwTg*$W1AO)W!U^2b6bluS8z$6UOS^8FjyE)0~W+I^^k+!H%IYvR^d=JrGw9N z@SjL~LZ=%v*qgZ(F$+S=v$fssylc7%ig zp0@vqd+-WSJK<#6jw%U59$pYj&UJx}eknww&ojj#%VcWc*=Vv~N#OX(K$Kl%&216J zEtOyp1qRfO7N#s>xu(j!IidfQK)Uh+OMiDsd#bpVM#fsWb^yjI$&nzX-GQaDYHUY~ zs$=&lQN(^6MxFfQbzJ zRFaCTQ*s9N4H8yAZCkI#hg*HBF8{k|<-7R}`2@`gjAF&E3tyb$<@@9r2vd13CT(Gn z%1j!CCm9NtsWnNj@x`AZFy^Levx4W3sQlk1yu@wkmOiV@8%5nNpuWyD16zWJL+VH{+>^TBtBr&x)Q`Z7`|8uD*#$K*))LG45@@a!?LOt+QoJGnQ*xTQ>*c7|Kz8*29l{{M9S zsD+6G{h1AzZ2}lT5nnDTyBE)c81_&)p+I<*spj?z5s+pYuXz83s!Lb$gu&NurbP#z9WpgN-Is;E{ak;K4?!Fl#PHSAD zetjXuqI??xUe~iYjO4awx|J?f3LCTAW(s8YAT>R6%<&BnnS*)EeWS!lx(*$fnH!$t z)Li4?!XbhobJP1%^Ttga9iy4RjEz4Otvyw#g-v^f!ph0a=t8Xr$TqVplu%h?if*7Q z2p8Shja?<&n{)l-cBF1`h6wl1ZmPdv@{XDPOcqlydVl9UU++|cKXoDP8;!a zsAle9oTP0DY#es4oT0PoGF!uC6(j~Nx*WD3wHarVWNgJ9|Bd4gaaETT)(!GqBXn$j zC^D3yR(0`R_jHpXr8B=G97s;d$8kD3NFiemqr-F5Q+*(20?Zz?3rgPBfVrwcYa>$_ zvKRK(bYL=i5>)wiv%cPsSLEJ@n_MiGhPtGNoHwRQe`N?Eq%hi-dcv9R&F2NA1&V#N zP#)*E9dDv2B)CPL(6^%{$#g5vmu_c^yfg_``JZI?6MGm?jGIhP1MACRy8;iz)cTI_ z*Dgz+fzpPzq4jN^UnOqS+FXJmH~q88t=-C_{)9p+Dh^J^*`HB>&D+EU@Bwd_A41pI zcnL?AlFxGoG`sLxvNhWnrWA!ihmi-I3%2ohicN_NZ`7u7Cz34$^#=J6!$*L_D16Ol zNJz|_Mg?}f9=}v`ty6Zenz7ry(h`i_&gVTzy18+)bjUB*@Cr>DPFza4AKDnJ0ZVKh z)hevM!5})**;fX0?XpQ(DJHI)FtqMLKvx{PQKctv;?T0RTiQn;gd+*&BGC20GyCvat zHJVvL0v&cWSC3ggjhx|{vT-?8#96ncvtk^Hvdz{R^gVQYME0}a{5QY8kxxs*Yhjq# z@-LID&yVzOTyRw@5`Oh`n8st5YfYVGfzQ+F9lJYK^Zyye09sa1)9y5(F-(qW5hK3j zQOG(n#I4QV_F>aig5*AaaY`(sROGXjFyd@hEq{E4Nho0ly-!-IiZ)Yu=HvCrE~^bU z){CpAm!x0G8K-Z9inzpLWNJ##tG)6ZY3-w!tR#8$9SYg>I~FhkvP~J z{4g`TFhl~Afthc4x+Dip)!Qk#{(n=E2b z(@>HSEtpS~W+E13f6!2Sk=^nPW)r^O$}Dw+n`2CyV5hL9p@yEMIQGhOA~jO$n&!}L zIN9e9S=gxUH#tuCQPm^* zJWe>HAsmKtV_;XZyGxKv5Y5;?)wvqR8K~E?E^IB9yU!QWl<4*WEi6F$g3V>&*_JcE zGStPx%XPW5_k+q~7tA6h zy{;jP-`l-@`wn1J(8;tStNKwkr{G;x24XAjX5qe6ox+^EMWaij_?;}>usAQamG(8I za-3ZMBBgzg2y}%t zu2RK>2f~N1s?sJ#lh0li!}C4pe$^T_lz_ml#4-w@d8LjEKTuFA`8DkAe*6d7{F}y=qN#tya~K<(cIhmdfIb*U-{*I_7{emnb3!jl@yN$<$oqGe~6PUkUL?6 zqp<7ucM{itFt5IH)JTwPt3nYZ2!%t*7H^~>sswt=lN_y#-no}R^lr4)M#>W!ltQX3 z?N~>=v_e0k5UnL~ze(?N)@w<{hkVg`$i*+OOA)WVH~X{Eo&@>4?ve8*jLcQRI1tSo zG0UvV1lIcep|nRxiFYpKytNT@T|295ighmT%loA|_OnIh!gXTGQS|y#M0&n5dw54SOJN>Qp0<$%r7aR1tBfh0l6b>Bg zA67+piqiq1bq|6M+hw=VYDa$m?T5tYI9Qt-4SRdX5>{~V?A-zUL_&HU0h(8C#7|cn zIu4D>g<{eywCcws`igqNJUH)-eGH;pIKS3Y_Zk0Lt#Suc1!um~yuh_T8`|`SG|>pO zFF0%G&jh~Guslije*>=ClJGz3TVOLuls%*I9~QwmG@5EH8nKN zR38By2#^;r52JGKTs?~^+o5?_mx2JZ`a-yFG($~r@l5_tESLfXbYOAjByO43)U2Kgw=F80KK=3vUWmgYtq-}{D3^-3o zUJ!{!C~<-x%{c5?zbb^MKQqqkK~+nGs*LIp+XPYA%nI^eh?UwjPlaJvlGA|usB$O5 zyV@kSClW1%ug9L6iUFhV{y1lzn(oDxhip#=*-g_jFgtA<^oqH(AT6{$I>10hh)6ka z8~NJ#OF@(DsE5+M|McO3`5+$vO8mM;!SVg=XxIQH+C0DahI9ah(_XAW(!eTkD24L@ z91e%1#D`6ZS#wN45^bFc7PYk-hk&)Ab0h>zCAA}hd1L|o?D)}RNNXSDn4K)i8>5q2 z10=2@8oekkLKgia@kj;|q8rQ>kzS&-8^6AA`Y3SVZ!B%{tqt&Z2o!j-frW;s6Nm~O z%IiSY%`X|SW)M0kWzv9&F!P#`d-V4-3z&E^|4ZgAg2A&+KtcomBOA?kb1@9q2(&8R zJ!EAM(Xbj7*xZDl{3XnQK2 zKftNZ2%j-3ers#3!H-9$>R{(jW3uiep|FUnM)^ z_PlXnbt*A$S*yR-xe&zAc)hLdf}2jrT^sviqxKU)_d&?~+QYu8Asi7lP3$7vqQ4GF z%m_)=S;Ld-xfIsC8kD_)mNMR?U2V5ar!74{D`lmbc{_@n;#Hi@F4+E`1v9|l3WEGC zT-KhBWLn} zT+m8OOPq#Hn=LD*r~5ic6l`XpO(3g! zw)PZnm}DX)J~gNt1CkFd!zNLq?4Tcl9I=$e)NOOFCU&K);Ops$h4z-%FQT#vW)-=| zQuWD+Wh4S^nX6N9#cRDM;367usMyon3F7|}XY&|wr8SpYYgVdBKmo-*Xy0%_yhxRa z`(!m-9h-?;nQ{3%B6DZOQ5|$%2X`Uql+D4r@ZT6R0O~3F_ctSc7=_&kfaqvsAqyg9 ze$ipH37_i4FM&?DFZ|`XP@6qj&$32+rTZ6yoF^|-X$%2L9}Xj ze5Hs{+7|X)Mlt zAA}^2O-w$#8;5l!W)|*|lihx1BmB1aU4OD&nINVqL~bfx0~o4*M^o z=~pvN3W^1eU7jVIvfY+e7uC}7d)uEl=UzHe%!WJ-oHUrEYCsF^wTXHtQRuzB4~_cE z`u<_k<-i2NyRs(hI!12rQ*u~PgN5!`zOCriTAlz`2|eC4WmzHvIAjQ4sS0 zc5h@w_6iI1q-DKHg35aT2jdUzp0HAMoUcnx$VLt`5u@UXSFS10{N42ctjR;`!x$f| zm&4b!c2JA?3F+HUcp4Y4?OvSDNp%&tu_?a~(1bt;F1VuD&oqyawlh^Xy?96=PP+EscBCwk3RW zz`+4wxa-zNl*7q;_rGm<8z=%`3t0ate&ml@2yu9TGP4*xLa7J#Ci-cW@f#IlYLZx< zlM(JpwEC`l6miY2Dt|PA!85f>O1g;6;SgLP{Sxi1;+0qRTzMKclGC%>VZUC+i}`Ulj^!k5|+o|`?Nkb9fCLIgKv{5W5euG0=ywK0+s6r_s!X5juw zp#HFjN8PW``w09GrE~q^F<$PL3*U{i?OI>)W1c>pEO@|QGY$Ki`VX%C{$p2yHxPd) zl%@d|F5kK>a;gCOHhTJW{h+0oM+$?R6)dqI%yg=@z*KT#7d&<@pHmy5IhSLK34gf3 z{;-|+OO3ZYn?qimuCT9K5`VMvTUJvQ-yHTm;WX-}bvqVnHXfGOO<|<=4e;k4}uqFx4p(o0#(O+2pDeaCh!C3 zw#DTdDT>N%HaLI6;hSe54{gr^6dhB`{R}!l^kAsLL@}@UBC(iz8s)DCtW^C+#6zFn z0MVHNUd^=n`gP8<(7RQDSEdC&f^~|+$3hks<2|^nWuXIjS{%*D3qAcY03km1k49)X zW!4*fURg5C6ozoz$gh6zv9ex=^(=XFUtzGM5641hs$7|bxCKuayPH81`z>zppSJ>5 z9zh@}e4dQSC9>vscxA*|lLpI;)5{tVtqmXh3|K6IHkR75Zq&~7@NXxG{^9EohXu4= zp1H?xVV+LjBY3DZWwrsy{K1vP{kPVm%duillSRi64eMccc}*4usGX8b*DIy5%0poR zuG>B}TwB;cRVXzw++6G~#1&4{_!yh1@UyO9NM~x| zhlz6jCXv822r;v~H>tq=6pPAV((nQ+;Lr6@D{ZDg*SP#lY2pGAB&Y5%J4%=mV zC~(#lMgOhuJcE@+2al#bPNU{^jAjtn*wQXjb|G@&d3IFxjT8{)9Pl+HqKV zLBD9VcZr1Y4r@^8ij#EMX}^^@?22=WD8<*E$PYlw;|axmo}n1h^>r#x1FU0?W;M3a zxABty!WkqAMoM1MJ7piA*_{(w>QPa*2&Mjipk`56*J?o!e&8x`-8wCIh3BS?^7#)% zY5mk2WUp#SN@XL}^7VBF25Jh`T1rf0)IrR$z4)m#0bL27pq0O950}zTjAI39RW>Tn z!_TmrKPHY;B5*EaNtYcce4N2JW{6K1kKtT4biNe-(L65eyumGa`bzRPF6Fs1JjS|} zV3u=+QHoGT$ZqCHg4Ou~HZjqu$cbO1?U#k{<7ULzpsRv0etQ4OlTyGpNppp`Aq&Rl z??A}@#uF6uI_*GhL+gst1}$N)ZodRmvj-Mi%=Kee1Wb$?XO zfx*hUDrz6UnW5^V7Ggw%*h+h^SE)>XlqCg{4(^U?Z*A=$o56nD%XA=EC35CRW=BGy zI*v1^0UO^voYF7(B68c79az7%M0>Y*(%-3b*se?_zzIuJ$q7t6@q2fQqprUxGp!}Xvt`_>V2MZb^P{MR?Z zlrXO!_KbL+a#i%7VzZ+D&;#C*AdENl?0C4L*$y2?)!b(o6HFhS^Ic?1l;M|(;j7IP zxGW4*XBi#<`RF=5TO8k=SiIxQTeou^debZh;q^lw7y`Oj%=`^f4bduw7C~6)Q`+uz z+6k8Ku|vY-zDs6is;XJ%g~1b}{p!M(gc8qBdYi{;pqF^WSsXed4VeRVv2boukWq&+ z{<2H1;Su9G&>B660&nL?VZ-`7n$h@QVj~AE4+9LaTZUF*sm9plSMJcmza4t^O}sP` zgQ3n5Fx(?6y$hk4WlWBEaYf;yCRiDFA9n9-b(E5vsPgb_FK;01`y4;dKAd{*t5eVB zIUlJ#585gRZAR;fL2{#GMjz$$_QxQ1)l{Iz8hKa3$VAq8zM_r78+woxvT&pRp}Zh1 zc7xlnPuN75#UcBR2q4PI(&E7-fdfJRfio(IMN<>rl0bu3!LT)=4|oiE+vS=ks;aW< zKjH&j)nGa5#b(ad8r+zIJO%IVsVr2c@0SaKlk?sl_todYw>V@s7`f@P4Ou*X>3y}d zf2H7iG>^UAy};!(`&Q!BJUvaEM`)Ip0wG)%E+h_S@v-}Qbh%UYtozks+{JMzpGPqC z6(#ars>L3d0NZXzZzU%>t9bS6WL0V&t!v~P zqu;bT*TwY{Q4LijCQC5Xl*80GBF`Z1jpr@GAQ!k0^|G!Dunxur(;i;>P$G^!H5Nht z_pNS?^vu?Z0u=!|ulw~4A;PQ}AT^ZJ>*MBT0yQyT{C|Pq8$=+DaJ~{1#t~MLmcRh- z8X7X~?{CyZr>@6B+0$1{d(q*r@>)bItZ%c+6a%X6i?{Z8v-?DU-~L8R{m~0A0d|S0 zkNhLobrcZVcx=h^FLUAwnrjSfY?7ohaK;YDo{HzsFZQ!{Cicr@w;gG#eP6DfZW@is zm6mAvnPwxh(536Gj_Eh0Abh^s=_9&Kgz{?-qNoJ??BR0buZlwx!sS9#7bDMj zWtt-wc(E7~YU>*Ku!Ukxm&`XdlgL%Ew=Fv&2akjPP81xIJJ-03J~aFJc?YzXa+J>EaI?mCN5EqD7iKYytv3Qk&}6 z`yEbJMksygFlvvFqpUm2P$O2Pxhs@eovXDB{Q=M7T7mBBIAlqZ)oZlEgsuUEp0A2i zwB<*&7;6p?mzV@NpA1WE?sL^#V>CdDV!-(zLL zr*CX1Cd=WAYqIY1DHoz`&_bOFw2+Hkc16v*`)EDqP66zJZctf4s@nnnn=rJ$!hBsp ztx?a1Poh-{my_agDZ-z?EY`8sF<);#=}fcxi)?^M%6sU_2hYsOS!VQkEgwFd43p|V z{8>xrf|}+`!^D>SJcZ9MB{4JDRWX2s+5Q|_E<=9(7?v5P5H@l@2bpyh*ZQVkiC6*t>OS-;nB3gG1XE#qiWt&8A*AH#dd& z3gi5sg)eXKz^taoB+2UaDtPQf2{gQd2uh2%mi2JJjszN<785IBJJ7cX+6lSkFlt~S zZksySv7Qe{tk4-SWp(y-!7fU)V!iMJW4*otcLr>*)uEm5ZG4s)puDVIJky@YT{3kP zcjSh+7%0aHP2W{(D7j5W@(7S!w3kmQXDu%IUL>j*rd-I^qv?4oXT1ds6*Tm}OmZ>l zv_{^lbX**$ROrVYb{)BAq$+mcLU-@9H*ZF$7S9v|DQ+-4bmYGD~H zxculNtTX)pWtkT}`+2(sU5sHqScB1$#iW#_R(bzIaiT6Ru#?IpC_OZ`px*vd!%|G6 z=Bj?nxB&Tu$V3x0I5zbuhO#LCSEP>f!7Qm-Ms;EJze1PbhoCE})V&hcV!#yxT>c>X zU8x(I+$-ZnKbN0o+&^Vns{_5FEvvm_=rVU>MSWN3c$IL19s1>A$R zYJjKZwL` zSD3{EZHzQz*@fk%*C2Z?s@HknL9i>CwyIkDWFr!wxPFYsndVTuEykzpHb?$d-Ntrq zfDq>LRll)F*Ib{wT=7~Mz5igcc_e$Z`Sseqo_{Yz>{1(Nx}bd!U|wayjswivh2>T4 zv3(v&n@Kcv9^UfsVRn-n@{DVN=|q!-CQ1(sgTz;~x7AJHmy4u~+kHxtsC`Nhm*`84 z!`3mOt^Ew@hI`Bx=Rn_jXs;mkh4X~g+-67YI4K-XAu0DOJqYFi8Ta6yI0SqamEKb# zah`jxMO(EXFRS}ee=6iWE#eI?qGGK#guf9Ufj*#s-9|UG=)SN$5$CtC9dZ5Rm&9Hm zz-nyaDbe`w0h-OA)K32-T`td(5%KV4tA6H3swz0u*N?2VHv%YT3-M>`=g&o|T29fr z7exa4b>d{3AGP8_xmax=E9bSmee9q%wZD;Q@D$1DIUYk;bz-q%QA2f=WYw8l*5W0n z+UD6~lYX34sN9 zYWWI1e$4*6LSgqwS^-^HKSQ+}8iQVd_k)i<;9r(p6};StQUP&iD79{spfn-B-TQm| z-rfr6FnW;9)Zq`BQ}ym91sl|Ty(MPh%F%Na7ue)u9px4KbsLeuXcr9Tbwu$?Lzjz1 zD|ITa=0YID_%xP8=2=-r9_UJ)MzBR5?+DlbNGW!KqIv{o)9kb&3Qk5`Ac7a#x)k<` z1b2On7ZJM?<=yd0PBMn5sM_|6iwZ@Vn9EZS`%Q6eK1X!*UkClEioFp)AE;HF9rZ}x zyyJnYqjm|kdxZL9GUGLT)Fo1K*Ke249V>3{y$=Ig%W!JP(FB+OI6{s;=#X4@?fWz# zND|yGu^;m@jw9I=yVx^s$F6TU^!} zJq&jgvX5fP1>N)l4Y3!EwYpVGe7 z2%<0!)_Nm~PMyj$Q(f2K7AQ)B{zx|BgbW)MaNMp6Sm~Z1NS_-8KIDkxfNUZK^`=MjX0ZZH9iC?b zc-{PxpaqI}JAv;EJr~5Nh326!AxY1&1r~Vwx#o>B=-kf}TdaWz{mR!8cu>^^ECIYlX z30@OS=(lkDIaK4q&suHa)1^mpi# zlZI`t>#-l2~Qm^0D2##OUuEd4~Sm~yH~!R!1!8Cv>!+<)8!2=bbFUt?cTUAJg;UjEdm4YV zRHTim@n{nqjhvYjYIx!Yfs;U|vT zVV7G!Mj9V0k?&Vua45R`?M}^G&`#V2MfmK*f4R2aIw1ArHPu$0&@mjKF2oZ)Oz`_R zZ2%|{#*$xMYPaUSoURo+uAZCgc7j!+XO;;V!0uYJkH?_CdKPm1Aw?YNhNJ-7Y78=q ziZRkGO?0l`k^I2T8>6a5%G8+NnB!^j@hC0P176tx1*TXV9l9+lL^nhyQg81P5CqyY zhX1qMVx6UG^i?%uRLfMg``SH&kz?DsQnxSYsjv$Ymiw_SS&5EYN%X2!#xcs{TeK@y zCgT+wwr8eHBW@gcg0}MuP`(!KTKRI1gzg3lsVdj52uR zr%l>Ty8+61agF;8=*sFPa1*7p_=_gJt-YgGdUJ`;MJ$r1)Mi z@cRfG9SUiO4>L#ai7yyYvY$jhzIua^X%!Yd$ki?CT)pvnXuHU360^uyDgP+PYj4>G zNjQ6LNVQ?;EB%^NO3(q?nQM5|ZgOE&`O|#Btqs$Hx)2->)W7i^S{B|vGTOKL?1deW z?Kc^t1N_&!;YVsnn{;c*<>iNRO0UPFB?D-c6tl2uIpofrD~vkL*KX)}uHDye@QV)L z=Xnf10JIL|((Op4uJzqX_Li>iaWhw0$4Y2-v+S`ABNbT*4FEp7m+T@h9=;tK3&z`W zxt-30MuTwecDlD165sa^&!_Mx9ZYES$?_Myvxa^F>fwHgp-h&AdQ9$${a{(&)=$yu=A((YQh6BH zMvR!yNYDU{X~z(*t+^kDUsd;3{Lry}-3%k2mnJ$iC$ud!v$n10);o`qODSgw+yiHv zJci(4!^Eoz(i4mDRGJ3lOLLa5f<<>}jyY^0h-VWSk7<0777NzG=It&HvZW^{Qd#V> zQh96}^FG5sX*!{U2g3dX>P1qzVTV!YvScj=ikcL@}$2&pYc^+ z+9dU{L7m})yu1$0Kfz*u^Z$X5u9x5qq85vCT;5%tYx=CnK>XuaYH)wYlSuOK$a4T{ zaGD-;)!{uZ=p{E&aqgRW|5@OxA@1Pz3L*Z)HN>Ds^Rg8ybaKa_Mn^iYl2L5W#b;VI zsziFh6z?}24RHYXZR>_b$OF4c<7ziX_GfJUW zEh#N4C!iP*Y{{90$k!g$)kx8( zphOOQIO_ZU-47~9kp{!(j-$MhZ^b$5Xrm7)6Rm$tobS!>JOS|EJx-w~R=Nab9W}&w zRgcjPQfb@*H>+WjbzZ_ifJo>EjM9>5bNRYL)8Y^yV>oW+l{22oaBTDue`2dY>A}QS z2ZOT(1+2LPW@$;&tk!4LVS-J0+*vFKzqQva$E(6f1{Z8EEpQ+T&sq&}Vl#JG)qx$~Usv)O3A{tcEuq5xX^>EoioDy3Hv3!eqW1ZbU27kgiy8V74SOW_HJ zAHkxhV{(!W;M+K*%V|5kCz$>hZGF?RR?z1svD-1aHE!t45K`cP$Jb3?k>`OHzHesR zJQg*Uq_oY6xYrTNz$cx&2159t-w7ExBd8bJS}Uly#rL2x+fzrZx&3~fOB7bl+Y|ae zmno`o%TkV^!AN%1;k&ZmkWz-lxe5W~_x>&kDo`cR;NylVcf|MJn4`us^Nb@xe*8&1 zi@>vIc-aJhWh=q0k08y>k6e%!@g9hXh{2LG60dlsMy!D6@3MT8E?{ZUk{qxqLi(i% z0X|SyrhXXob3-lcqLS|ZdR&Jj@`sx~5v%X+Pdu%9bUfGHEHp_%Q^lHqIUt(VY4{{zPN>xG zRxNmWMeh7??0(uXz+8MTNNe6`bB9zT?0XUv>b^Rn`{W+CCJY<`1|rNV96Hr-(;!9~ z6`5qoZ$Od3vik}jERhJbwNG!Qw4N6goO9M;W1;?tJOCC4KpnhxmLELAeK`jE2HyOt zueUa(i2-2qFijM4i0-4V}lh3josa%bA4Mcpr2h` z;-GE+Nh7UH5DT51E5Jq3mI_CL@WYvpk2NiKx=6%n-s&(Nn4U-J-Ji$p2xNWwYWv69sAGUSp$GZ6?L^PkN_Gs94Fci>46yNk zYhSsh)+aO_&CFCV{SD3i>BPe9V^}+IcnCdQRE|DPV&fud36mO> z08^xN%Fh=7hC0wEu-&8iDcDu7fu9W<*PLThs7A6{NW^Cfv(;BOVn$1_WC4GLU}4 z#>=$MBOHHZU`ts5?P5|WdQ>w&OhB1f=yxZ4 zCH;0KLTQrgCfW2tK6t_xs5)6+M8W?4_7tFqVteg952mAPEUQQ|1q;E7lIFM3-`~8G zMM>S9MruC3`!rLa!(8NTM-F?V8fN;B4xF`7py3`}w0GJn0qWTQRhrpq11Ty{nPVck z9@}GvLx#jb9|~(Udv=yQUk|eU#sX+q%)G{u&am~eVB!IQQ_!umC4U+C5LDq1fU=D+a4xQ|6R~81S3JKxn;EYBp7S} zqR`S-oItJo_AN2RR_J_=zCGLq35eWQ;l9{|-;@bXBZZiJ)?|~$!gDcudVi2x8A#E} zDj<2k`*APgNZ`~)w(1)xBlNg~SZTlxjc?)<_{gHOu7?lyu!uQgh=L}H^oZR2q+K>l z9Z(c1h+3t9kwz43ySasgXVqZVU*=?o{3k`jo>G@)HuP(Y4HomazXr#^c)IRt_5=CB zTXqK~Un{xP<~dX^OO=NUMOgM~DvIp-1u1?=nri+!=)dQ%rY#GYkkxl)v}e40HW^DW z1m0c+(D6>AOHpN1oADZTb;S#Ey$I*{TL0`k{qNkGwMbg}vu4GYG(6}i5WoNdzfG50 z53G@N{YDpJ+F)RV%6?Z_D9USLpl6~OScvmY9^8J2B2>1Rz0=GbeI+{AZ{Y`2lIM!H z?D||i5Ec8rxfp=LkCX$O-iB_#`{xJ)|7h}@Msdz&G0ewHC&%u1M|#+)58VJ?Uoj>3 z!X>5R-6&6~!n60J0d=C zCX`(gT{w!UJ!DPMX(G`6AEZhgmIubUSLm6NXMLHR2=-vQ*8aB2{Tn{z0zmekUM2v8 z0@3WUd=$;reSLm2^*jbpQpL+uU|E7)D{V91F{7#rkD*2M1S#kpX&aoOQYq(SwuE2c z`3Wd*Cs0mjLl_*+t*befG=-YTOWSJxQ_ym9u& zjE5IrcF*DC=EJT%J^b|9e^60ZAV4K9@oQQ8?1s(uGf_$>hrcWsVxV`NJ$sanR1`mzE5G^yH!lpNK>;U{uWt({T1Nc`^M7aaT{U7k!>ay0bUuKOx-L?ahD9^2@c@wQKK8~OXo|~u;gbb4 zN}zB5Qe}V#238UBYOr^b3DO!;CV+nVc(;icvHfSNH%#%i$%%K*CTj)-YnJ}X`auUo zoaiTpz>T0tD)YI^b>{~A>Y5|8iF-zwiWq4(o|e?$o2HI8b47T2sD!^{+V?{OKkmR( z2Pd9~Mm%Xb@ejKL>PI-VN@QM2ZTR)m3SzP`Ak>j3wNLt9@IU>ld)$ft@g0Lg6kL5(n>F0?6#5NMQuCFl3ogO0kT9yo~@# zaO2b~_T|ehD9Ec!*pc9W|C^3k?dDE=B| z|5>O3}637jYVX=%=Gm*7@ugo0I7)1rAhQoa2T!@O0N{e|ZazDIDs zG0Xp>>#f70dY}JcSU?FClvE^?mIeWlT8l10K^mmHq;mzNySqW9yGx`bmhMuzrDKWb ztgyi6`~E%uc>iX=Lyx3`XTB?LBKdukgBz_Z zQ=MjxZ*RbVI$z1Bfw*sM==gO?rfusK9p%;nTEf4cJj@$#iEwoR^$nfv-SW2o>=KMy zAlDE|d#M+Igq=HhdqDo!d&fcYo>QN03TP}-a_qG6wa{hBzO@E!@&wSnqp%3p$OOzD ze+vjqMQ|X*9{uSP+ywbRNbkc`pn=A~dZ5|OAtu@HnKe<=IH8*74Y#{eL5-xe9AO`j zPgt;PFSBYFPasbPZ8=A%hW3mL(+hyIsNTaS8U8`7fxAUZSDRC4b$5_XO8bSEWXXB; z+CQ1#O*u4r#t|)(tR#c4)OS;IHSfJ_eBOZ+|C1kmpVW%75;2m@XsAT3(;039flH8`Vys>{EU9fbJ16&)pV7|N4ZL!@; zt-T{gFAze3I8*>g$ne)^rmnnLdV#f%sEZ)cR8n)AR?|RpUQj=Blat7guOZu+zw|ES z)i=&RwYt{>KDcvg@u3xU$RqD@483&HXJ&U6m@MzX9n8YV>Hy{j`fZ%MyRWDZ+Iv(yp z*l0S~w>{YT{Hl6`%G!`i(QA~KAiH|Zro9W!XrIiR)w4q-0C{U+`(u8|_!lOokP^p0 z?gg~hah{e?fjiQYOK4l-JHVNY5CDk3x=?cv>mS(ny%U%Ji$uqjN?6A%W>Th2q<>03 z;UCm0h|4kx^tR}Mc@ZIK0lZp3tnK`~_g5U_h~zJ+W-SD`1l^XNh5J^x(^|x?aUf;+ z`@M+(>d~H~7Sq^OJtJ>VD;66jb&iZrWUrPc{peVLE#!{=?CW}>la0OcPLawq zcqpYM2_`;?>7WV^buX4VhQ)?fAQ~Y7v@0p7Q=%bKD{g2&lc8A{49==5%T_4jh9oTA zySO`?XYg0n6fW0%3IULH{*f(G;SV_{qO#_Yl99jJb0{#`h$h*H-a>}zy76Ri4%y8!*=_OM{05=k9|m3TSWlbdyJ{N{1UqEacHJXos@g5-9mkb7lF4^(Beg-i4E4h?#SSF7Ya+AmD5d$F(Tc#2q7Wk@H$}?T^`5p z?$bvf1#@c5u4QW`P<6htY?Dz3L)*)1K$UkalwD0enfPv`^0&{SIv@8iAFI3c?_Kpk zA#?`-FXy#^t(|N^pf0U~oLa$l$T`mW@J5knXecUhZV*3Acfqr_g0k&Gip{)nfv?*x zZy!uf2bph@S56AARXK`$pu-u6Rykl?X8u+-kS3`6{LKHt!I)Dz44t-BmBq)stU@mr zgW&sIFaCZ#He}{bwvM`yzcptWfWWH)9TfqPr0$z49lf*V=t^C@fG9kpLrMKm^6?f0 z>BUi8ew{oEw51OnQ0VlpE~Fyg*f!7AF%)gPfsATF1AwjeZMxb|U_W+Gzevp@t{7NV zh*hjwV^%9SfAd~Td9kL(aSWEoKJEk99mg^Qz0%`!{E^!t9 zbyfXWDha4Yt*YMctXpb*_tS#)`H=GlG>o~eUp~qFIALRbipM+`Kp#Rpwj0{lL(|?3;3W3HQA#8g~ixRIAM(*(%!{ z+V5mfgmiyKp$+m@+3(`A?u%S{R`p*2)C&I-Y-~~{^!TDKI!-)|bRmkIsI?_PpO`79%p)v-4*=}U3u)z`x@`8a0y%75T4I?i zxB6=;cqN6NKf_;<;+2e}s?fgs7q(KfaCqkdeI3Q9Kf+L*gV* zh7fpU)HuRtZw(693@aE+lPUN8RtvkULeUoO7rNI&i+GV6=Sp>7_RM#nWq)bpKISI@ z^4{e)r!MOV=;Rnt3>ye6z&~*fVnB-JHDtpY{$!+a*h7t8(w?$)RR3f}1HFJ;&d zZ4^^ghFx;Ug={&>*eoVe1lQlK4fT^Fo_v0@; z9DE@Eu8J&|8#`&2c`~3GLlhTecr)a3fc`PwvlpW0Bw3?+utzUtl+~l5SNM2QshCmA&}e9V_&U8D&mo#H#g8qZ>I{NnigrnrIEG=i3jP{Wxv$#*7nH8IQ&0Yc0P6vh=x- zVx#%VknI7R8~ByU(E-oX47>L7@PNsuNmiTqLj>=CBkQxc{1oJ!POXT;5d-PmZFIyP8ji@)&BHapvz=# zn|+ykWKHy~gNo0ZG{bY=zxGGnoyv|Jqr+2=h6Mo_#P9%g?XVr;q#zyzjMgDw(e4YK z*%6u0NA9dx+BwE~2>czfQh{=_u_M!wJ0F=}Z^iyMB|9(e#C8NCf-CK&e>Z^v8 z45rrj3I2rwD>@<1U9up=;dOEJyoaEiAvs(0&MSes_$s*@P}Z|KR?d~)|8PT=f~pQ} zu)Rs(tZxI)3!Xh_&2y#u8aT@Z>(Dx#4H(EO)1@Z9cO}?f@723FhWrdwi~mwoW;^E; z+dE~)_eE2Hj*@|bMNNCKIy%aQ{;;5x?$GE;Q}%a#Tl!%M@36`Jn)#4|$Tlvh^^Ls$ zQB=)G(RRC)zomrIze|m^^!SJ+FukVbIWEwzF6Q;05#_DEuGNu+iWVm;)w5y=>sYPJ zqI+>#|2&t!(n)K}9xmiA#EwOt$+%m;L4p$w2NKs<2wacZ0h^AEDK$;pnq2q=I=MlsUzNlqj$uEyS|-- z{H8f62|4}dFqfh+a%E^1Qfur<(Ex06X!@?cak6MG+asF6L6JKFz$A=^S|UDIoOAIB zN2FeQmAK7YaQ7a|n@(;8W}{~KVgJDcyw0$aqnB|iz4D?U#S^yl;QIs!#SE^0gM7#< zxAKQRcj9!lUnqPK(Dvp`Uqr+UFviDRU@-~9_NkXHWfCSJ5?SE(nMaDT_hnf6B3j1V zE=TvZes;}%XZcyi-k!kXK82_8)vN3dTME|zxhWVKFS`39I!7^X2%?#kI z?wfR>6J5Rt@T$ORLEKS~sx}UCJ~^qqoakc&g<~EUx(vj4((`$p+qNaFVn6z7HU5R& zkR6IgpB2$w8h=2pk$W9*Dcx-3-PUvAnZ*o>wkqwD@qza>YZ>U;bdg7Z_FID!jke$c zXc2$U#RMYP8PJ=S846w@)M8B4kG5;etwuQif@Zj?l&)~C#!`@`<`oM#mY5TAu=?rx z+7Q6m66bA=JSZzIe9{%_MQTO=T`PqSzkM4tGmb|YYIM8i3mj@{_;*J7Qe|EcSxB7} zz8+lHo)B76-P$66oO(Np{*cS~HGF^M*EV0mGFo<961)){Jt9nC)&;Sw2m#`ffvpvt z@+ur}mi}&mqYYn@sD3cN`Ea@fD5H0+mE|2`=Y^8nUi`IIcO(tk^Y`P| zr@08f13v+bM&;z|n<91F3zY68g^^mO!UAy=6x~mXfgpbvgfE<{>}uuLHuPm?+7 z{{ebOe~80U*`Fo;o4VpI_%qlClw2KYC*(83{;xO)4hKo-{1rT0>DYOKy}(k2nlM?Y z)2UPT60!CZe#w+*8BEK(-_(5~Q59<@ziugMWp6qjJrOb2g-yz1_S3}q2h<~Uj66Ko zXHaP! zP^;ZT2KJLGz%y141a(Cqh8s6{mRSoLap=!y8TjwE7L{;Q! zP~it2(`hBhZRy@Ey^A)B*@TxA__gucKE-p+n3V@(w#XUg01?mzU6B3!R_(xYzZ*Hy zKT+~Q&0tnEi>Urmo|EFHw_5DV3V^pWb-kt$gaDSDeQJLY!wp80$dtF)6#{)1kMF)L zo;slLii$%_tbv;tGdbIYdjpx0&9iAXg`?{j1ug#giMPscb9d33p=t8?MNr)jqfb^Y8$?^!0j#-xztwX^@U1n16pV)ht4J*F|tkVV*Q@rc3 zgrLCD-zVG${maj%X@$@_q=}CEdxp_3_nryA24akt3-Xo2bv7wa2-1Eke{aVs{E^s$ zMx_?US(tm+@EMEo{ld+`)g&Z};BCs8_(?97jh#GgULnL>69B#hg8rcz#{;&9FJ!PV zyt=w$O7fxte@-uipHvv>0VXs20!Ww6silpdmGyZ5zQq?%KCg~K|Aoc)elo+&ytsI=DSIFsmb_3)%So|05yAMo&SAmt*#GKAK#}G z(Vd$4b&NX8)etZ1BQXUvJ<@^?vaqEDcuh~?%&d$dr+hf|D+WgjkQ{Eg8{7LoW5f!{ zlR7g?9Zv$(v>)MHT>J@$5j>>NITWWRPL&kN(7ZO>?rWK=-13`UQg)Bc8(QM9-^vOP z>uV%#8ss{>Z6Xeq^XORkP!*+`w_cI?&Ou=3e~$OxX)EB_Iu3ky3K^QxW#zK4_yArp zCFR|Jcqin%8>j#HuxW79W8kql)q@c{7Db?OyYm~>^*ZPm+yniyw#=!g<0#{o1N+h# z0WWftefr=4==pz;SNyyS4jVwoE|@&U^_><}u541#Z+Lu22P!hnqh(WA&h_;9Hhq%k zFD?F-(Zq+pD@S2TH~)Lg#jbZ8*y73BDrlrR<1E)ZG5?`tgwVxnwtg*j(sUh^vMz5? zkWLF`p!&Qznqm*fX}iu=^}?VkEQmrjyr}cQ>;fkXDA^2vC-j#cSV||!;%GH25^P!I z-T%oE{LEhC=`9XV2(W)tVvdEQY{H>1?XowEUB=IEHjMz2!T1NgY3Sv%NwRJSUW*CO zPa9l|cC;Tu=3SrRNFU@xf|jr(F!$5}*l-O0@v|r0j=MkP@v(lNjDF?fdGJ`H>2cDK zi9(T%C4)-O|FkGSV@wJ^T8zk>D41MAtPKSc7>r;I^W03A)!A409U*dYG@7zKAjCoc zeM1h!627;zor=&Fq0ghWf;|7MuI6p2Zdc&TxT+RCBDsDt!0V6)Splf4LgkKpt`+@| zL}JjE#*nIMX_YVR3?5+wae-s zF(22uh*h@0VfOduBLz`$Seeqad6rDHAOvjWiZd2a#lOn6s~HNcDyyT0y@3oUi2KCB z#d8YnpK)_^UfC9n9y&_E+7!YK%JLq=x}ARodjPaVDcTWA-(Q*$$>7xssV2t);SuO+ z5a4z+5H(>gVjT5yAhc7^XE6Cd171=llFM)qu3}#8QF)_@?(*262bQr3E?f9JP}^d| zk5SahYXBI;g|tS2U7#d&(u#Z`1;xTGd=RU`(O#FT3#8W}zFt*^rQmjoN14dx;YkX3 z(=I{MBjogLLNAs46}hNebn263Yb$Cl)QJ_Li1#|61Qy%zcgj5|ct9uFyG3?2i3Ti( zI-7D>K?~l9#IUqP`-CB$)ysPr(#92JCHoBOuDoL8M@Ga0&)M4faq`0b%>Tm`4{?It zcRLizshE)mg}J`Xp!rt2^v{?ju|byN<7Ictr=dRzPq7Z`2Y>zw$gsMBx;{2IR>g{9 z7xX_%5B$i<=eQGteP5iOpkrHdA^y&E7}9%WNeNG-24I}S4W;{<@-4Hx=7t8%KFz{z z(tCxgR3n~u)?u?Na?yP>v)J!YwoXpn_QyC&bNHG`FM?$7J7m~KbFz!gY8{2=DdX>{ z(667DibFl_^=AWfikAFT>)x)dkmzD{xBmi?wc;k)xLTZLL^OT_P|crd*VWm-`!8gY zMt2)ffCeA;;?ss<8TR_S_WbiH6mu1U?yMA#GiDiS#&|hvP?UtEJQfFu(=3GEha<#)ui3&3wYC`_UH|Dg%TyJkjN8~3-_6WvPXC>DP z>%eX;t=NvNjZ1~sya2G<|41-0uV9=nIh$XNaH?8W3L8{PyWx}>mVEQ3bIQmv@Z^)4fp3?An9;jVRS4Y(!c&6>7~ zeJ1O?V#Biq5A5ApRq8q<3nsv@O)b$_Ua~i273;WA|HrzGNX+q3twKc{G@)X$7v9@pU9BO4(Y+6Z%!-plwYlx2n#{~NzM9m$DwBPh`Pr5=#5#`&XhNjE}8 zJQJ;_tx=qmO+h-LB{UgiBKI)KGTiVuZ!dg|(UGVY$6s}FOnz&Uc5Ui|%q!+l=?@wm1=e}m_Blx$z}1m zh@6Z|#c{<7sKT^%l{q3_z!?JsS35x($V#>*zFlVudt~i_CpSwsNbS>hT=?Gy#8{pw zO=`ewm#0q~foWq8Fw?(;7We29X|LAN5dv(ps=O+QBQxBLrZ_faFO|k(t@34cWBs=F zds>)Ij#K=sGIC}0*I0Q%FT5M9)KwKtP5OSSgz{=T{~h^xh(n7fTBA{6R;1OAmb%S4 zxBIJ*fiR;kRn?944*VVIk?A;16P(6U|4r-0zL#wp4-AsQq z1-cj(j})$@y72w^Rb=j7yG!As?R#@5D>t_VXj&Y`@6|PAM00DdmiM^}_0qg`^v~rp zlT?p=z2g#or#ADGU5x>Q@zhbK3J0?e@89IyM@u#n#o;P}c#VJRT0x5w5H`ZLrgxL{ zggo3s+#ahppYlI#t{rSYvi^!RQG>m;C{C!aTUySaOC$c)!*FKV*ZIQ4U2*{!7ze5( zziVuu@Tn;hmncQ1f{F zF|8k5a~!+ut)uV6Kqz`AlN$38GU;dFmsWI-2X5qTuV9QlK4JoxoGXVA+Bkm17H z!e1B~sHR`i(glJ2s&dR8K~R}~d&!c|y?iMQ8YW^<_j|9RrBqnVvvFqUfjnkQWt?a; z&*gYSjE8*##ye$Gthm<2elfuW2#k!74|z6=jOhnY3WiU55!q<+AZ#FUwVg~f$m!<@ zPKgeGiqtqp!J6&e)c@z5C?5$|o70%?f+~8{TUBrOV1u7379F{)Lky0u`SGeG_<3U~ z$>LPV1LMQqDy{yrp*Vm^g6X(rRv!u;N|kj~r-`*ysUE6Ycdo)nbWFc*1s1f=o3hQl zGLgMmDZt3ZS@DuItJB7%I8sjCYp=gRzwC>yTl~p5LGn#gRs*fM**1=N!=cewes>-2 zz3R%*$+QP|W85)Ay}Yr<&OT_wWW4{6DnT!ERbV^>2BELNU^1~*9JBO6gX`id@a~QX zbHB<$&6*5{ZPE(Sd+K=NLp9~51nkDp4Q!pGW1%dlOKr@Vl!90|#|gpPdV!#=)md_e z>tcZO7}>Z#+?akS2ji`18Ui+b7Z-?nKcXbOtyFB!j?xhpY{|FeM_UyyaEZJRB`z^t zA9?Gd@Ise|%xIdCo!AKShrN&~SLOMcvkD8LL^=;&kb@9i%4K^YE;Zg z{>L=?_`Se4!j&7GbQ>*?+B_~2J7$Vt{)cq}MqIhp_F(iY_!|<07o8|4qBu|4#*Q>5 z1q1QSBrQl2YP4 zNzpcW@U9|ydq`_+N~^@f{rt>L(1#s11*lf`0bN#r`4jhCeek?=4PEDr z{}f39g>Zzw_@Tx=usToI6sPkuxC^AX#BQ>-q08=+GHsUOLu#h z7}JtTmT9$uPFFlFBsaK(q8k|(Uf6JT#UG|y2`qW5wtKv#z*t0FiV8{B)@fYdvS2LQ zj~clL-S~_n>!s->QgBA=RF4Jg{9?3er>+~b7k)>jpj7TR{dXvA+jWy`p+@4tb&L3q z8VEYZ;=oc!BCcVM-c^I(J=1$oLBCh$w$#risrK#;Jc9Z9Df9$f-BKaeME z-hGIJ0g2*l4C?_}rqXRerOyof$MsLYL>%4Vi7l7)ja(j?T{&oy@jAuEI2J2RSbhgW zh2zp*zapp7KYc@2qwE7Ujr_5__CgFcg7v`u-Q&%^m|q8dTRfYX-W#|td|6*_-r{H` zranZq{_>ai!`L_|Hc?!FtP=D|)97tYP3Ao@MdZ#~i~7dH*F3TdCmsdUkF;hbH@-@6 zHDupT=yo|f4|$NPx~@Z{i~Lci(M6pFl7gYP3rsPR;PDwGBl(}mr~#FhpP7j9HUjhR zjT$vk`TFmR4LMOkBi4Ipi%rB|`FaOFn%9jt{jqHC=cP$j+^3FB4_bEET2nr>33ZBt z>l`pS3kXV? zgh|jS)+gl3j+X*V!XNCs-^Bi%3nPq*e8Bo^#%fFKq2qt&5Z}i%syNL>QKZ*t?dbNO zz$OfWrn{uY#Ms|)2}3mMB;kRYhgL*;n6E)Wfe6qUjg}vpN0W0wJeUQr=Ge{8P?<1u zj(mcR?mid$jdtV%wGw9*Y0`w^*H(e{f`q?D95vU-h+9?d9YaN>pal6`=L|$tU9V%; zv!BRD(bY66#our7yXj__ax&4un=Bft4ngm28c5(kCz7YW=8sc*=3%tOM{&q1v41<4 z6E?^;!j>oEHaPXKk|hW`okFc%Zm!$^g%^5g|0T9=K00jv+>*D-C2=VPA8#08&p$)x z9r%~fzaTX&G!WfHl#w9ESUg)J0AmV$X;XnlBiHf-v(vtkLs?Q1Tqh>?Z)>(at1;}1ODS9{l!(}IfNB& zV||Zrzm=UeQCV59UpLQl5dB6y^oROl%+oa&1w9Bn_AT$kYIi1Zl=uZ1SyDcF6A8rF zct-7n*yab7%sItB=+(o3Rnf7r&ws=|ioV9eIQGbP3VHKZ*~(=sJJBKJaIFHDJ{ea~ zg76(!y-0hOx{{+L@=^V}iq!%&pQT)8;z^UtG>3Ul!QRI%stl6c!uAS``f$WKRKC-rXdfCYgCxYN|OW@wi~=zYK2 z4QOrph(B3A{)P3KP|KHae{9L85dT@$aNz^URV@Q?-2edoHnN?|3!Q$26~X-1_n#tb z?kG;CVvc^r`PX^@euq}vyOI*aV|Oj=!~sE^el1Fs=A|6)qH2m@URdlPIQ;4<3vc!p zZ)CJZ51zY~=}V>#+DEZtbwP`a%3SHD3MM*#^YO|$!-Z*IGM|{?0GKj&wR!^GDdv^A z<|wSO`KPBDR_of<)St>CF>w7B_9t_~Y8CErPg}D=e4M}bj@Hg&ZGTj=98u^-ahL(A zWpx7$Yjn6V%9sbQ3MyQU`!1WpE|t`4Ve*Xr81!_p?{;`zO&kl7qpPO2*S)K|IB8xBcG|?%3B%na1*MRHUxY zExL7D-Yflk3{%WHGk@wq{)O+IXC2bSY;HJwPBoFdNVpq^jX{vp!F4u_o z?*^P8EG|<_LCV)x7;E5XZUijLgL~`!uvqzAqx(CotCz{uXAD4pPotZBYqx*zt8}NT z!dZFstllFfK2sN{=33GGT-ls!*`VUou=W%CyigVwx~zD#gmbH610nd98y_m86XMYTlJ_)Bc7!42O9r!jJvq&Bp2~~ z(2w1FZ4o?FD`a7ZeEaKm3l2uC?`9(gv1%vR4!^73)17Z=_2BRuS#CAJ)3P0H(@X^HaUB53}XQVC0B>V z(08*Z88Y*}8QT&xn-Q;@8PH5F`r%En+42Omx;vWdq80Dt}!}M>myyBxZ?M) z_XffrwkXwgk56kj?dSGJyix27`r3427M}7`gsYdLI|YVf;J}rJv?4N3S^F zbeKp)&@p|3Y@p#`0P*qN;PE0SXr}~8a7SDkqF9L#F>sO@s8&Ixyjh=H3lvBHh*-M! zpNnKBr=GacGWd*`4{}}EGD4I-jhqgZ_8ZMTpg{R_cGV zkRZQlaLPD(;_s(HM)$?~Qe3GjrQV7+a!Pp3|8xRNzAIz#@#8Cl!H*mf_Z7Z)il>R^nQ`D@>o;>JDJmvk7QYGin`_b7Vms;uBOTe^HfDhy{_=YN#AFke7 zWH`h<2<}Z_tkfw0;ZhT&?%2#`-9-9#jzrSrR1U|&gUm!EO5L`em;kH zS;}@PcbC{!4oMF61Ftsw*}zNkHmjHf6`TOR;J!`X<}tmZuF8YiXs+$Jt@_TUcLrH< z^MWHqHq$jDaHHb&)%7j3)f3vMbt023ZfX>3)kaE35J21fTYsNY!k2eTS0Q?2!7S!h z9ea4~UL%0L1vG9R2goU*)$s9xZ&_o|t1kk)3ajg;U4O{$T)5|#+zHTn1E|5}k($w1 zdzHKXe&~I0AAIyrcp*R+N=MQ`OFa-)T$g4uFV_*Op+!kkwTja|Ab(IwQV3y2usx&| zfJBmxBugch=iOolhAg)^`f_rWbXFTF&ENb2^O`?ux7R+JoDzw!*8cY{wj%We9oB7V zZe=?UcJqvP{a3WshRrozrRnmycrP60S*wjly;J04nBudX<*bDYC&|Rt4msZ^nH}z; z^XBRn5D?5V4~VX1P@b|8@QkDi-@h4!%(s#D^wJbua;p+r1Q;; zo>cTgT46a*?JaovI}D(5wIw@Gqwc%G#tezan%f|P zqb^yM{0C@@dg_54yOE4LhA^K$0HUjaJh-!o zT1|A-jp91Mde=JF9HHtr{Mx6YgZ}aZSD(INnqId+>1N;Q^N^?CqJF{y$=NK0;VeGj;d^Hh&Q)gcm6Mxv}xe6?q5~u zCwnjVUW`d%7MI<=e<&a+h?q3tE~YLt@O2{VNBF&wOU?$esVT1O??#ms?-Oqs_&`-2 z3Vgh%fkHlFxz`M*4JRZoLWwk?SUO;+(^Imb*g^imy^{8{<`}AYu+T|0sd*tGAIl@H z=ai&ENRWKY;m^YYqHq7m`}f`kL~?(~a@0XJlWYsK5 z51&z+B_mB_-CvHk_&NM++gQTGUK_P5HLkgl-HA)(*SFhuE?(yHDuGKl!Y^Nz_(rz2(p6ydk?KG-2M; z8uf4FeTmwwT3yEtMO@jjF)5Gr5V`^LX9&c=>whXU zJ~;It8r{=yyW{ChTlag7=l-WTiT0~&jq1MVW>{IaU1tmp<|wZJrIYXoFRAsJyIYK* zsM8x^L35MY8x<2oC*1)fYPy1hFP@pia+hUUFCFuQ_nMOZTwjH|0_6xge#+cA^&-;a=b{A^Z3>awOQb31$dH#?Al$5sLXf zMdJ0KaEz3}{U;P>BKY>yo(1si`?Qy9&7(Hlt*2$r`G1}b2IMK%LW9du$TE(5|IS5v zo}4s<#DkT)+F1ybl<;{_?5DB1=bl=ZBB?mo^9p#N93vS7f_9L-hW}flw!6!+B{HUH zmpA{Xx>%4R0BfhyxeK-s5cv4_zq@$GoZq)0$z;usb?=(nP)`EsR9w++tnpDQV^TAr z@&9;5b?XbjW<31+!~Uxy$k;dN*N+&ryIX~1e@q9cOH0t5JTiQ@EKby+$bAeLB*Zak z!tkW0Z7oaX8}jvTMaDq7GbtIzE~5!MUiR5YCN8G7&LZ)y&V0h8u|M1Ogh_@JZ^i?d zEKssGm77M-rteRUjh~VJHzAFTx)GRPU6o^3Xe^0q#;2ZRetB_`j z+0(>;d#S%`HP&k~7^>Rc{l3cRlsT-2JtdOwJ`ic{@?d&_vO^iL?4$+|suVpwS zVA;#y)?iNR;u=j}YtrUSV}XrS>d|0!`^(MTAt8#aUH6U0+&TSX~34_NjK`;EEvYT~LwIZT!L?o>6M3&pVe2?5SiuYe4j|BO~S*uvNzorh; z-9*k)R4scq+YRo=W(*}>cnxTE8}+1i!8>nh*5sB&ZhmxP{aK)`O+yn7l^|EevpXbA z7>rdtr478&(3hXk?si)cSGS?*M+=hRvB**`-cYFEPH{^WYW=4ey0CaBIY0gE?9zRK z5PRUg(4Re)=5XqDOX#H!_s-E*Dp}?h`avy@;4|{SKoE4(*-I2BDflsE4!~)dQ+a$f zjIhR3huQqDb4A#tD1QVv+0g? za-V^XHn8B(AxA}%TC&k5)erhE{A-<$Ky^RG9jj57-XYtYDi~DnH9BI>%@hN3`N({V z|0nnGj|vp5{si{l6SaAg4{W9Oyu9i~O8Tg4vaALwrvxe0de6%PK%ROFV|8vhBi0@T zK1>5n=lMT47RVK!*KV@`b*wN+)%SeHy=PCP^88{C0)d3aIN?^m$Ysiw_A7W;mxqB? zRgZY+R{|k0Z$+RUHuEikWQ2@#f})K2-c3qaP7gjg0D3TcS3mhl%U_(FExDw2^SKYy zm&>R5fDKk*?~z^bXZJy6!fMBW##wBAm-9L>6eY9s*-#ftyi8Y<@+Zw1QzrYwPyh9u zgYkq9C;Nq|C42SJWPX`RrVVJIoKt~zuX`CAsC(uxO0PV(=(UQ>?x z{YJp@QmAQ80_*QrPlC~5Rv{-@`Iimstun5JZxw!c9J4s64u7PK=s;U#h>ai9rKw~S z2&1@z`IG{F^3NWt>vEq(XIsi>*DRC`LLIt;%U|JUm;x#EmM_%Fy_EMNxs)1Dxghb# z6F`B}c{99#J&C8Jb!kSq&k-6+Feb*1oBjwQk)s}wSd-Bq& zD?|OEYV>DN50ysIL6rr?es89L3-*dz26Zn9&IYK;_0H1AQQ1>0iYi*gBi-g2z*JYw zJBGHI!XIheIVv8{;YW&*{eTm7G7hABOM1KTEs3EuDTj|wjB1AcH(H{QPu|?F{g3v8 zyu|2Vo!VyAulg>%)|XoZde5N}PiDnw4?1sGsON)hl;$X&5$<2Dz=1#HC9_%eppGQN zynq}#u&vM_bAa0>M|Dxi3*L?ici+f^KpM=P7?hPLFWNdc>A+BnI6pk|x2_3PlMRil zem~%61P($Qq)VlFl3v$m{X;*ntTSfeXUDG>H#(_HV+;v~?<=V?u9%NJgt9VYerV}F zmFTXuh_BIb2LhpcSq`QK#QQ$;YP>Mt?WO=Abx_eDW1hYAFExiy?10XL=waC!7!)J7 z^4GZj9v2R5ym0xq=(h!vdMI;65drB|7yWHhq<_rvyEi*W$-9p?i?caEB3p{&c~*iN0oPe#3#G&Sr{ zUL<;`sKUQ$7t0v==*5Fb%x!bX*|pvFVWWe0)A?aYB{h;g(7RrjOn=8wMu#2aD(S z$5KbGNPLUtKjefSU@6MwoGHaP*Qh$ z%JGu%!TY<(Mk0U^er8t3l1~iX9ZMKA&H4L9#*x&OHLK=Gl#%(PRD|>-fvcG^c0(lU zkJC!%Y3IzZDZW=(y8Sgj!b86sEcee)4L-{KspRu)&eIpPBUAt1{q1*gS=u!D>FBWi zKa8gF*g}#sbQee5rsDY!9EAmejmc-L1UH#q?Yn{N$8`L^er#5BIcJJ|n!QrrGbJ6x z^c#~e)1KrXKpjN)vp3O2pO)f$lb6GUu#?^8(VSzACTqUSjINjSq)`=Z|7Gqmpp+nl z!}NDe!m5ZTr(>}3GZ@ex8Lfgoe|oVB&N&vM zj2glU^a0)e+oQnb^YEH}1nZ*}SEmSw*&SsAs!?u_ayPrH#BfZM_+HJWtDjD2rcGVn zEB1yS1k0tqPy(hmOoxT$v3WLf!xLt$k@Zuv{%LviIm|}!R!eInMRfd7LL=2t z)=64A{=}fjQoYxz+tJ+sc7eEIuTaUMFDHur)f&AfR(Jdd$Mksb60M|B-5Wb0zx)2P z!LkR3h}~}M10DaF`qM(H_EoeU&!evJRpM#k3)+P#$cv~{M+yy2rh1L$H7-vCcrP<4 zb$h#UUPV6RKezT@(jeAsfywLOZ8bye3^_G`I{dIjr$efU`PGxY@wTxgm@K%l;2}JX zHv1%B_CdMaAT<2jpId}5U?X+usJKFcTqu)wdy5g0Y)`}#!CDr4eLKgx_{um%hNGpk5 zypP}!D~kOWJ()KV(TE0iWZ=Qp@L^V2d!eRsl1v)Zt;tw=SRlkrk*jW zQj@9E*&eP|IYD`WXI8z-p;V|HPPNPLtW^RVjsi z*Y^{?0cPI2)drl@*dNV`4IC~+;uq*vULvAFj{w*``Iko@UI_R2GSi#-=?XBsD&aSC z-18LLZ;i>yEA)W1ypeb9xC>4~1qmLlzEfn}bv#Q2giOFN)=EFK!%_9K^r8hhB!N=j#4C5*Cslr>CcA?y=$aIdw3fNi_QM&My zt_U0I^>0r{)Z?Ug z{8(O}z8n(vB^ zces*%ZRndp!IuCC? zflNUukvA0&L_>55w}|KIsF1$#B%U#HA&~5OjPXfKz$I)~bmkCOX=UN0RQRM<;tq(T z<)>8dua0nP!P)Lxp0&#LU9L>~q?o(+LRVLpTR%Q&kxb;LUTg@6=C#ZI`HXkbJc5ls zbvHw1PipG=ssE!5#~Im9(^T}Jh!Sw8^`roAK}ve`ABTB;ub&K)OrS@j(WvXOdDjwI zeNDeEO4WzbGZB9$b(sz5v9h9ojRhMco((2~qbV9`=Xq{MBdf${-Pl=G@oZ<<%53wz z#U@KHrURNkiC%+F0>2M#26bBew9%@mC)(gKW@jFYHphA>{RtA2jwRHtLIIUmtdV`b zuxscnME9m`WCv1ajU%?_8r`=yIODk7Pv>QX^pzReBL zfGkbz8>UaZjBc-n@sYKm(J@P*leZA_J+zqBi;u?F!zHA2ra(fMUI*}f*A)N|#a@$K zaF&(8zsZri@bdWbsV*SAzdM<@J;rQRw16`3ck+11?`)*KcZfEQzyef1Dbq=@+S%k{ z2)qB318uSAfpy`Vq*l<8>XO+PW#y8jlA?X6aS`(xodwdTNRRrctUQLHW=G{ad{S~c z0?;33uCMI19ip(hjgNqg<+nPA**G#&6zr|XF6^lM%`&j2kKa1e10bi@uI0jo`3YhT zjLv?=TkRpyX||3>5Otoh{lSp5TS5ZF1K5^6|Hd?4s8As2#9OR-@pZptSwHm5Fi0r> zvYTK!&Q;cfsB7u@gZ#In1dm355MUK`RXmpFu$hOusxj5>om8M*mX_-M&eb+xkQgRI zH!$GOXJ=EbS$4m;%VbsB`!%F0?a&duU)8CEQ+0KXZTm31ce3D6FO*Z2Iw*{|-n|Bi zk4XWTrlLzw!DBG(TczSv(2tVq@G(}D1m)zv*C~t!gzvwiydQN26k{@w521+5>^($e zav!xHf+%$b|!(@P|J3kM=%KTtZe-h4lroyf%^Rlqmit;jE0Dpxu>wwuj&3!uR zf;Yl4em_Ip>bA+=HL2LGvD%T+c>wd$yqC=k@DlBvMNCTV!O3qT zVnBaLtzK;Ey9Z3a5vQ6lZT@xF)UTz3^*-7C1{8z6wFlaMr~Bv`lD~kuEDfC=as=)#Q;Osgff9M=#w|VZQYp>>;NW^Y& zpGRi)rfk-xa71v^q8~qkry(z~%H_P?>S2#=;Qlp2u8il4pyGnKgwt_J@tCwZI(HqJ z^X^r8%oUDwIjaspO^o15{l>jvdLE;o-4xPzqp&~Qb=aSf#!#s9HQc_dlrFy3?s%9! z;OM;`j_k{l{d-6Bu)=ALaK4EkX>1@p-F`NVF4gV9--cn^wZSPQV67EwM&nXXT-r%3 z=KQ+Hrx+ClmfCxaW|SZ&5XD|v<*f3hggT@7RhBpnRAwDbc={vMDd2$toctS=mIT_63C_e=(hMYSGL2M-!+ugejBMO|8l0mrB1!ou?2C)2AI%n!Tgx&ka*uc|!4hr8OR5C=iW(g1JO8}lEr zFMb0(9|>&{nKVwugPPe|ws#*QvrG2fxg;SYjEnOfHxNFJn$o0^cQ^{iIKCo{O{W7Y z&>^mJD|~BC;QJ>hd%xe0Dd|G7VB%fBi6@9n{f3wno+;TK$%l*!X=~3X{0Z1Xo^#kM zQHvs^0h~Jy8Fk<4l06WylCFt6nFGSx;t%ixhXn*f>z%EHq_76;uiB)~4ncr{7@IF&*QYwP~nwyQ@H%y7*La z*PYiAPOgWhx-OV&6qU2oHLiW@R@qsB>(7^n!GM?SOOEpxvNdTN|2Bj7HC?c%KH9JL zkIwP#X0rN?Nf+TQh~{s$NE37wKu$|dKc@Jf&R{c%X^G9Kv-Ykr8#Q zqg+|hv&=*Wm`Ejw9d9{^qn+UAOvdV`|BtP!4r}Ut`!GRD1tb+Er5i+$94VzBDUBeV zQlnuiAl=fTk|H4~HBh9xdvwR>G4efFU-kETU2^S@aX8O&*XQnpvNILqj+=pi6!%@z z%)aLh;DoZRu_W73TBl~x+Y?$%p}4lzU&t41fTeoIK3ozsxPMaSJ9)9AeGz%gPul`? z?lBRbZRZaGyZ%;_HC; zN~r+}l-Jiz&@7N`F1S5d;5Nfurm1*0V=~70g=6Krb7{0w-ufE4*PLgpfc=wSY&SaS5eF-Pj0<26G4~dQZ3gzQBX4PiJ2Ia|G>zqk6c?U;t%;Fy zaB;SbY(v$Px?$ZfH@s2|Ck_$#-mCgI=YGAT>jo6W{57_xI5qQl?3sR zmyOY#_U${%b}4vrtlUaR^L;eJFSLN2RIgJLG;%psR=I(sJd*xrotOj%0lu4eMw}nG z4e2ht0f0?EJ+Xk~g|)+!;?oDG>qPkvwS695_m(V;VgPhNi^?>5!uE-FW=Em6kVi#p z5~tR^hNtvIpSNWSUF^m^=EKkpBFW*bRyM6hZ|CSR1!J=tL@ymF%T>*`e0zCTds1_e zrCy$HB7Oaw?`#qLP5jr)*gsg61$~m0SKSH~jtM=%HBT9c&TP%w9cTMl)jXYCk8Y4u z0x1}=4|j|t;`&Lb@6`HClRg-bM>SQNu9vGYi5<`M8$%{Y5MyA35i@C;lw$+i%jlju z9DB#~9km-5Xt_$*cD|ZhPNd%zV@XJ+W9=57Ay+8|rz?Z1Gqz4xv>0>MJZ-GJ=o**& zqBsnz%M=!e!$!wlDxIDOe@u&so!tewoAo*^!BW+TFY2|btlB@7IXgGJB;ETG-*lP1 z0NnUK0bN9SPMqtfn)rRxSJL&ostr-NZWsz+)~{- z|MP@W^7XnZd^S7Tw6Q#`KS8~3KZw{zP{h*@=d5zHM73{6R4dwVg(fP0Y!n;*!E-lZ z4(1`PbqCYBj~|}%A+D1uuk0+NQES)XtR@R(W<^c8nqNmN&imR=4gF$=&DX8CjuVY# z^>x@)j9cGss|F7YrdRg@Rl?$kTfMhKckW38^AG+fB@YGRga9DU$z<;are&%r9S1dH zrVfU<75w?q%6F(CrIG%?YB#rBea)Lx3;p`*ZpK&PZsNiYQ6I5cVrfA`06>Elj8nr%}WoCP2*E<8hynf9D z1epy%2bYJLBOk zZJnI&xYwTr0Yyn7WqmA44>nP zmQ1)IhY8eQrSrGlOz19r`AX@B%R*1MtS+Nuu2Xi9O}~=z=(pSgcR;-gD%|mRWyPEk zGg%O+%t^aC|6@H9*cX!tbaiVwM&Pa~m~#THMzi30LY|kM(@Yh%MtfO1={0WoH2TM0 z8bgo21cd?WKK&bLvhQ3*i^9vm0O(2&d_dQ}Y4wKvV@@ucM_o;k?j8OjEta|^pogNb zxFx{3^@*R?Wv63g7(2L-sn|xpinlGKT|v^#`kOh)=lGp?ebGdBMIbRPdh^}Yzi9Ox z=+@&7JWAT*KrKDHOTbiA-}>H6(c27qobLAHt03BRq1c0NYlE_b178fOzbrO^9QtYC zhZQ2u;Ag9Meu4fjU`pO*hcbzXGEg1Jj6XoD%|^F-%I1Sz%GBPC?IY?pI%mIcT>pgh}&<~*X0*i10i||y~9L2 z#ewh*RvC8~w#X{6Uq_wd4M+GUT;#ZkLu)TIUa$yqye>s^%xXCf$ndv}&wCG5e7&=s zgaLJT5lqEUr>3uX>-xx4$bjh_J|JwR&yoYZKHQ5rpT%scUrMn;h-O6M^) znyEEGyu4G2b9^x;y@aiE~{lvB2iOp{|nRy^kHUGG_JZPof= z@31#7G4HEkHXZwrI}YW&tJyCkx7|3*u$yTKuA!sK-X)soy)Q$3&Xt9aNNiU|(^XeP z5ag9|ol-y^;nSThdM!PUb!UewJGY$T9{*_Z=zbj6tvZ>o>TaTZ*<1+$Q>A~ST%*q( zlY8=YTsNoO;`flF-}vhtlVO$&x0M4&im#n;S7qOJKks)$`4(gTDD$meF1}Mi`eBXo zx6(fnE@+@ioEB#@vLVH^y~PRb8S`JPMSTwH@J;pUw=c~RVV2op26J|qYqU{aNsB88 zV^*NAqdynbO$OE+E6~2R^p6Yem3jDPXyf+hFjg?p6@t)3LU`AWxRjB{Cn>svL4 z0`J#Fq@VQR+-Z*jUgcWA|BMU-Q`12cyOY%|+y}L8-2T>uJr0F7acxH}*x=$U$Ki;!d%o+{&RRar{ zaXeu7r;`J}-~g~4n2$G|?0IQeB&>^DCQZBA=}TEYW2#GbBG;GP)l{I2BE!pA-y{T; zIKLyO)lA$QUI08i9#oEPSE+GyFeD<9G=E7iJ9YSloc2kMWgPphsWpX}N)6|}W2+!4 zbP-bJbk>FOq-%ce&jGzo&wDKtzYQDJ7Gid^>;2&U_j3Bv%`Y;)i1c5$!Y~4KkwM&n zef07P#)KN-UfB=$*7fF^CTWT4(T}tRH3Fo)F(jj82UxJMnu>?N2Z?_V0f5ml=ErLI z)za*(y{c6-Nz&aCfA?_~U-T5PmTyrJb1H2p95j8^k036Evc>s|GObyZbqgP49Y&#s zMfpA8o9UPd9H}weVt0s)^qI$}sMjK#jxXj-Nl6%{=@KKS92mDLVzVjD6GnS(2Y!@% z^K3uLPg2CDORY(ldJaV@*9~X$qt^u;S9_>D^{F-E>~Z$E?8JO@t<54k^c$^eaY+V= z;lXa{UF+Xm|4S7@J23YKI>KkLF?MJS*DD{OyNfdY=s!wV1D$W342pqpdJ18 z3cvqqF;J?pX55u<`2LN~uvqHg_N`x;0_=IM`gTW;yJXkV^=7Cd)LhyUugJ-BWIm6= z@UT!>J1@|x*}BbRY@hD)$SRKgrcSlf86@-Yr1?5h$0YM`BC{m)31LfJy{d}qStHNw z|FHTq7yiyu98_3^FI&26FL8=-kx1WZuh zGv%rKPhR|*4i44D4Cz~&{jk14oVLW$y8Y(^lxd-Fncv&7J)Uy9uPW;h%`r>q+Ikz#fZTV{JPW1ATsCTy3SLgZyI@mts6~^@^FY z%5DHcP{eb#pc1mjd7Zfb=^M{y*l5rwoWnlH?4hG;Q#rvpX8hKiT6dh1%j!Q3vk$cD zt1?4GP)N$?8kw7d+-pq&J(%dnHw&e0o#rGyR(@MPxw5=--Ab1O%}qojZ~9x*@VA}8 z_`3TB?7bIbCVpw_r-bGPm0`%x2A=qNpg=~NM7FV0YK2Q<;L*wzpeofUmxoUrR(qBi zaQL$Yt{(4J#9BPZ=999`vHAz{zW+T6_yqO=$#@mHH_bbaX@0CNtD7A8ZFGn)YTb>P zgvJBjfSmg2XMkRbD(;U_+z}wVeP$s%o6cfPr2GQ%&0?Y6SgzA{;W$O5`Qw_86JlCH zT~pREi5&Dxh`15|*suU#RpEq-dEWuo-YuZiALp&JKBt*^(kWswf_Fv9&veYmK^?QtvG= zXlV@bXs9BWUOKnM^I^HyWYb1Ew?uu^E>`wvCk>5$p3LODd|*`U6RFN1ZWibZt~jv` zmq4%bs+~RRbeJ`MMCsuWtP&o#Q}a>e?E$gDbH|{wruqz(q@(e!d;gE}s5@eRP-XqToN2C?n$@mZ z0*TjqMgKG9wN1E2tn7R~`Du*QJ8J~@Ctx^>*ui;-1|rH|DGsI-9sAW$+)$i)7Ia|i z-kxdhmN|4(2qbpSUV8{6z=XS-krt|qUwmXtg?*AvbXK0xVx;7Q<9gj6lw%1wC3{WI zo>;|YP*HHY#gw9nWX{f&32PN)kZk3BI1q(Ly6{r`Ql|eQOz0P!3^nTyp4Wgav8|XY zV@b5)p6}cr zB|ouaZG**D$A|5ysbQY*L)v7cFCCU0QJ97B>_L<3lUOFV=~#z(FFWSo-cYYF3jqYm zJPs&3S?}}+iyYhs+AtOpZfLGTaV4%~-dis8aqFycZr_KDQj`|`RyY4i$bFoa&}^Gt zPCO7jS&zI@EH&UGpk#mCQ+*_8r?LZSJejL?flIr7;9YFb>`_1+voC zx$~IjRIav>R&u25+atc^g#jQN9nzGA`EtY8rt#`~tb;dr$eDk?=TPGVQ zQ^I59K%Kc987f5pz7-Sca%H}WEve7;+0S~(|E>`jvMmd}$P%+5reKy4)g=@lKT=oq z&ikro`5Qxg_{3FoV?R6?0P>tibU%YlR9Q~iVO5G%|Iv&NSc6A+Ez+>KkQC9=ti@PX z6L0a~6=&&(pe{0F;xslv5mBK$ShmZ`S5xMuQfYtU9890(4f4@_zpvYK^J{Y%pHDJd z@WWg%R0cHDsbCd}a-S!Vc1ts=ji`Q)q>k&wqj4{|fDsHQ&#dKE)T|)k zb&&H3DfxpNdk*W)cp}ki%_x{#=1EKn3sx#i<65@7_%!PRW5?OYTCRqIQ~rC){*xoU zaPAW&mBc#bq@B=C$HiyQmSlov!X4J5Fi9Nde20&`EeoD`lz)6&_g3N%tM>fLLhfUc zq#EIprLSj6SLTO$uMg&cW!^kh-U-T|$6N+R^Y4)JJY!wmZ0b)69-~S5`)+{i2FqfG zbO>}d7t`F<;b1FOyH|U+}+X z)-tzKC@43?T?~^LV$ss`IxD3bJCnpk4Yy-eR+M&FSVtnXZV?GQ9pypzuB1Nkxb~|S z;3j896bgSMnt6L2|Ni8>BJ#%)o#wJtw;t1oVp=>xbXr?xl25#$j0l9=(v@zdfw z%$Tl(i`a1nNfBnRQmdsIc`Pj_d)_D;8O)kT#?CHnlG=b2GV0b!e}MXgnm($N|KOST zhT|sD?lXTUV}17UI{hJj%9CMIiK^led|SJ?DfGOtRnF7*9S5886W`ok*T>W2eaJXG zq1)+kdLMf_EcA@mKt`uNZYjr%7Hnt-x*i2`Le)mey3$v-^dFoazAp@^zaQ-tH1-}% z#6u+Pw|nsWrU-LBy!HW$A_YjVV1O9xb@z$zeSk2x2Z)sHM@Z(C0k{Bbn`8Y+*`m+a zG?p8G-_9wnS`z#zSu5r4@nrDt1SQ~@0GbrdGCpx&%tr=Ss4~ed{&K7?P)i3cu;8wH z^J2F;RwMkTars*O@PvDBrpF~sa(2J5+w!9cUv&ojulnSSmS?J%;)}tV9HV0F&y$4D zytSrS)?~F_e!dlv{!M!_ClnQH<_@{sCGgr3JvjGZHBV%LzME^zQ(8hub!hHc=Ap3R zoZ77Y`cj~M*{Rbp}r z69xA#+J=$7zf10kFZMbZSqhEBO~`2k=^mMaAzbbfKoGkEbML)&QmhI+iq~YIq-_@x zcU58E_FQ5XRSFv-Sl?q%RJM_KmX8P@D|16r3e%!Wrfv-SCi=OcwA0CY9-1DK4n4AW#ObW%fcNpvKo%HXNeQnc>U%EGh zhhU8lG+9&d5^YBv0Vsq)Z;7w9EQXFXBZz z-*{0{vxqOJ1rP3&slwAD~)phrRxD8o3d0HV=8>Os+}rc*bS zP;TyS#vv)Ex8ChZ{VJ*d1972a0C{^+8eXq3azp-mbjmDHLXVTpaD^o?7In0}@BSH3 z)o>a$S(ZBzkq2QnfUyoFM13FQ^b?f<)p)_aLk2MoKq-1-gz?Xy9?%EKIY%cztpaL* zWTr;H!k1#Ph>}JFnjuH4CqdMyVu5kg-H}* ze=GQQvGA0Oi<5I1N%fS`fu@L8RBi8&xfW~is2$BQj|zr<%fYeLIDgeMFC>|!a=Dwf z9j-jvx8EH88)(_$wk%<&PKG4sya!5aW^RSH8AdY#vZE{v^^#w+s(oUq3p6lY1-g&$ zQ&WFyBtU@bwV=bHsoV)`uTpM{{wnl;h^`a=&@KN^MX&XYl8pjiE5{22TIbV*&mF^o!LcRJP9a*mZkHk`Ef7m8rJi{;=-YXV_d}}}JV=Kz zuH%T9N}aH)xOx{%)XxxIKf@`=1lQ1$az!$|DeF!t#}>H@xfeN=K%eBNOVAu}8islQ zRPCmZEOX9yp#@{&vSe=kokjy>zu8V|q>^Alrf?qd*0o+7@Apu^>3*D`)bmA06 zCMU5{AfppW;Hqee9w*5?<16qaST%<+08OGaq7ROspIxPT!vVxG4HlLYq|>;Kn#Z4s~McmFU{W0H1>CV*59 z+|`%cM*_^R*~y}~;ASZhvb_vnVO+_pbAu&vYa<1v^@nr_Bwn=rIRvVq#Z^^}jL=B> zJHxGY7`6#9o7mm z_$ClWC4optvXB#xGUwere}lzzpf!!11U`bI6y7*(B%k_GVI|MflNXADE#>*;eje@< z%zImr(Th>DJERrIf|}kR<&IsTh`Cp%F5{)nv04fKN|`9Vtmp92X328SBfP9THNcDX zb*AXE_1Q-Kwl%lB$GNQYg5Mx=!VU7biB79or`=Z3A@8Z2=J|@3TEr0>O<~OgsX7qr zwVkZnjaMp6$4ciy_?3vC_440CljNmfRn-q#xn9L}dL`O2 zj}myQCU%vOB3}NA%Fh0}SnmGY11k<*_9F$)+uH(O@&F4HJ;lSGFt9@Ib%uof5_@Vq z08*EL0R+!*<2X17b+=YoxcWQ1Ki@Ak@$S0i=er)JyOgknzC!u47kAf`v0iVtEpuO85Kj?JEH zC1f7ll@7Ei`1t-MDc!aJW5kY!(PM!3Vf@xq&kp4N5fv%FEP!2wLSiFEtoJg^)W=HBRqu$K>$;wJbC@m zE-Ds@XFxm~{2rIwS$lu46#)$2IDo&mT@wEVoT$Su8Y>8SKRzt$N*TWh*SoYL4vQ9L z^#KzJLDw<2HM?XJg_mt>og#u5tw9oex;(z5d(a#i4BLhW@gS^Z*1F}77+Y@&;Y!L= zddLi=c?g!}RGvAQz7@b#nWl=OAQ-sDnWw?BuMt@G82+%-j-wbDZwh6%Au~wcPkFLS z2#y?!NK4zAygkJGX*+7zOPXhWt%l6qYW;Qs z_W=LbOHqYU?4#!{A+zLpk?=hk4N?q!2I^sw@jzfviBG~6{GV(OjL)A%*+v{lux|0U zhJf~Zu5kv^L})^7EjAvx&CLmA@t^51JC*W2FPRrNL}tbz^7MFoa_{a#wCY&as-NAa zUBgod@T9S0SYDV!xFWlS&Znv?Z8tD)1f1pTRmqxuMoux&v`g!nCY}IOZmxwI^hm}s zVjd@Q3@8B2@(0(7%F(=}mantRaeoFPs@^FXLUB_5pgku6;fpro-?I7r(<$!vsth-~ znf=qvZ!&yo{LjhO=qB3N0Dn#J#R}>|3oUd)%(GN|CT%gZ7Ol$rJh7Brq*DshDwrHmzHZ zEG@1~H}yT0%Ge3mZ8hwPqL{+A8~PUB#IubGB6;LDt5CZv1x?jwB9cQpAc(>)AyLGQ zSjr%9+kxG!W7FfJq6Nzs_RE}`v`ZbX1?2K}o6NQMPc_Q_zMSueTfr1w7)^@Ent}rQ zOAUN1)1@~^(6jYpBMDGAq-~?wG_h!9*U&{`g^0FwrBP+F8v7$NGVO-mUu86Du+UzY z>abZE3IKFL)R96lY!20VA~p*$5osOKGO~C4c3bvWxi>CQ^6L(THuTkSAJH(2;wT8t z131o)@Gx)U;fB1$5t+)J+Cnb9o)07=ysVK?K4<#X&%W@-4EaPXE%Bq8CRnI|D5Qo@ zLJBmsQ5?Nr@5~kza>k=I#WuscR{7F+S*k@_WLYY?64Sy=w8Dz5!t@2CRCp?!)kSTG z4BUIn<`Q;)KO6AgyKy33{tKI4fApO&9#RamKDe`4P!;tVa|@5)nL>LO>9gNMzQe05kVH)=nX#~=n`YTANqCQcVh zbdss}RFTL;n@rfI{n?K`L7a{@+4-E+eIRw|eDaVd`;*GfDClD9u6;aMK9$^i=GZc< zcQx{$R!-DjI3(MC3aHz+rYhM>T8n)&IaJO$N=J1-TEMH#5f2_z*9;A7jNY9W%;%J6 zlr&j@*nBCao3am|P+N|~W*Qmo4rssmHI3d;d%L;2<>MMnMh8*Gqb6VzQ8az$zwALk z53JJJOL0&gjeZ})Tj?)9J>R>ec<3W!H)&{A_hSMPb{x3+@ll+1UUeDOvGn){^=>~_ zk>dZxHh}BBped|FqIa}BD*>Cz8v2MMrm#CQ7O!T4YAl4eN8Tp~XoMuZou`RtJv6-b zJ6(5~8H|2J{q`L!iI{aGN=zX&QphBQ*copJXu4^@IB0{kNAp-YHYdAPUmO3*FHLBk zTlbCh1WBC9E7+O@?u?SwZrroqI7X@db9zSVKJdDGNPPofH}^-X2KrVfWkwF*3FBkM-WGm(yH3ovr<&-qlD^lC6CTa9S-_? z-043j=zj-T#n%N~8*+9nvmVyKhmUj|W+tjjEMLFWC1pa^%qK+E3&urLyZp3M*yU)L z%U>wdsHGv`pl+&YU!XCfFNXjR==OA#5q+&YCRlTHdEbvSL!JV+mTo!>+^MeFSQh8` za%a#f&^__v;HH#PhAm!v^ZfntI?Cp?l@ZC$CQ# zBt7AIp%l>;{3ZWw$NuF-69^>M1Fdek1tQK5#=jY33(y%3U^t(l$a0fxSDhJ5LEjZb z5vaT6$`Y~__}|{yhp-wSIw44Nwm15N&8Ex2;~(~YBCo(z`HcIr%N_?Wtf@|Py!qzb;)A5h~>hkTp z7=YwlkvHs!@?*To=~hmR-W_r2OkG1rhZhedeZ40s_TFQlnc@*x(!&@l|KuG>x|MMg zMI|VZKE=20{v;{o-*qqFY1D@^;N%aVDFj0`ceIy?&9) z_)6D9lPjDN>(;{$N5pP#B7o{+9oIg(UptPzbGCq5x^ZI_RSKXfAud@}b?o6ji#;}; zxAoC0Q_qcgHYLkB4B)rnY^}dm5Xl2#;Lg0Sw>qvVqC$%#H*w&&dh-*lG6A}dwJjos z-+Uh^zC%5%_RvqT+*v%QA^LE$$a~-cg3=Z>>_%fm$KP zq&DW}6x<09>dbhyDF{PAU7Eao=CoIv?nE12msSSfhPOlo|+qF0KS1Ltn~yBB-7 zPW%dxL62^T{=K)=d3o>;kji$1Xm@7bX6;h)`8i~BT&-^Z451}RIJ>(`AyStZ-0gwy zc>e>`LOypER2g#k=E*lN5`4!`GtLfJwZPtKvhm02<8d^}f*(fO$RDYDi&ndEbk(^X zlynhLgMHD^K^WM-K9~~8u9*tW2|xvL=8c-*wI9)HA4M~0fG`qSxKfDLPP=Aivw&8=y|4gKPJHzVqjLhJ9ukO5kASUp33%#kUSM!czBX#97L88 zFIu_5N5)tC`nhYAyKQ*=RsL5CQ%9?T(yK6rI^UuBZ%Q2=h2S9>!vPR-35(kE{W5Va ze)?Sp9hkRGB+(j4YGK=PpA17nblW+VsEu0XLO^GT^~c8xcYB`8V=iCXAGq=7EnpLv zL+ji4tisyx$vq$Asl*!LVeAA|ajs7GkKeNy>wn>tDeH>{eU7*1-3;--UMbgo=B*Rb zsHu8Z60(pF5qn~LE$JTWr0KI+%i(`_%W|jaG;!^mi|nlrjgReq)2$&@7UD!@f9eP^ zilg>Q9n59PqCxr}?~xucBEqEvO)-EcYj~gYLgw2MZO73|qa7v~*mo5S9a-Ar8b?qn z9HMp_ro}W|cc3IwpZ6J!*?h#}LcAfAUnRauIt*>pU~G0&Oiw~;SHHUH>GlV^V=gn! zdzrvJ4EUWC-m~FSEj%epDtwe(UFjl@7ylO{U=xt%fh$XQ74G{sG~ayn1?S46>DN13 zfd(CuwI23*tAW6`^NCyVqpm>~3>c82(axH(D+R<2Zz6((jO7j19{G=NRu&$2>OO

Cha*XoN(q1R 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 + org.opencontainers.image.url=https://github.com/scrayosnet/mcexport + org.opencontainers.image.documentation=https://github.com/scrayosnet/mcexport + org.opencontainers.image.source=https://github.com/scrayosnet/mcexport + org.opencontainers.image.licenses=MIT + + - name: Build and push Docker image + id: build-and-push + uses: docker/build-push-action@v6 + with: + context: . + push: ${{ github.ref_type == 'tag' }} + tags: ${{ steps.meta.outputs.tags }} + annotations: ${{ steps.meta.outputs.annotations }} + labels: ${{ steps.meta.outputs.labels }} + provenance: false + sbom: false diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..257f1e5 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,54 @@ +name: Build and Release Binaries + +on: + release: + types: + - published + +jobs: + build: + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: + - 'ubuntu-latest' + - 'macos-latest' + - 'windows-latest' + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Apply caching + uses: swatinem/rust-cache@v2 + + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + with: + toolchain: stable + + - name: Build binary + run: | + cargo build --release + env: + CARGO_BUILD_TARGET: ${{ matrix.os == 'windows-latest' && 'x86_64-pc-windows-msvc' || 'x86_64-unknown-linux-gnu' || 'x86_64-apple-darwin' }} + + - name: Upload binary artifact + uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.os }}-binary + path: target/release/${{ github.event.repository.name }}${{ matrix.os == 'windows-latest' && '.exe' || '' }} + + release: + needs: build + runs-on: ubuntu-latest + steps: + - name: Download artifacts + uses: actions/download-artifact@v4 + with: + path: binaries + + - name: Upload release binaries + uses: softprops/action-gh-release@v2 + with: + files: binaries/* diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml new file mode 100644 index 0000000..d3e745d --- /dev/null +++ b/.github/workflows/rust.yml @@ -0,0 +1,59 @@ +name: Rust + +on: + push: + branches: + - main + tags: + - v* + pull_request: + branches: + - main + +env: + CARGO_TERM_COLOR: always + +jobs: + build: + permissions: + contents: read + security-events: write + runs-on: ubuntu-latest + steps: + + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Apply caching + uses: swatinem/rust-cache@v2 + + - name: Install required cargo + run: cargo install clippy-sarif sarif-fmt + + - name: Check OpenSSL + run: (! cargo tree -i openssl 2> /dev/null) + + - name: Build with cargo + run: cargo build --verbose + + - name: Run tests with cargo + run: cargo test --verbose --all-features + + - name: Check format + run: cargo fmt --check + + - name: Perform linting + run: + cargo clippy + --all-features + --message-format=json | clippy-sarif | tee rust-clippy-results.sarif | sarif-fmt + continue-on-error: true + + - name: Upload analysis results to GitHub + uses: github/codeql-action/upload-sarif@v3 + with: + sarif_file: rust-clippy-results.sarif + wait-for-processing: true + + - name: Perform audit + run: cargo audit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..90db511 --- /dev/null +++ b/.gitignore @@ -0,0 +1,98 @@ +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +*.iml + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +# Block the entire idea folder alltogether +.idea/ + +### Rust template +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb + +# Compiled resources +build/ diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..704b032 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,6 @@ +# Changelog + +To view the changes for this project, please look into the [releases][releases-overview]. They contain lists of what was +changed between versions and list all the relevant Pull Requests. + +[releases-overview]: https://github.com/scrayosnet/mcexport/releases diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..d16a881 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,136 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, caste, color, religion, or sexual +identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the overall + community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or advances of + any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email address, + without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +[INSERT CONTACT METHOD]. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series of +actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or permanent +ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within the +community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.1, available at +[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. + +Community Impact Guidelines were inspired by +[Mozilla's code of conduct enforcement ladder][Mozilla CoC]. + +For answers to common questions about this code of conduct, see the FAQ at +[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at +[https://www.contributor-covenant.org/translations][translations]. + +[homepage]: https://www.contributor-covenant.org + +[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html + +[Mozilla CoC]: https://github.com/mozilla/diversity + +[FAQ]: https://www.contributor-covenant.org/faq + +[translations]: https://www.contributor-covenant.org/translations diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..54a25c7 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,55 @@ +# Contributing + +We're always open to contributions to this project! Below are some small guidelines to follow when submitting any Pull +Requests for this project. Thanks for contributing! + +## Code of Conduct + +Participation in this project comes under the [Contributor Covenant Code of Conduct][code-of-conduct]. + +## Pull Requests + +In order to contribute to this project, please note: + +* We follow the [GitHub Pull Request Model][github-pull-request-model] for all contributions. +* For new features, documentation **must** be included. +* All submissions require review before being merged. +* Once review has occurred, please squash your Pull Request into a single commit and rebase it. +* Write [meaningful commit message][commit-messages] for your contribution. +* See this [blog post][logging-levels] for choosing the right logging levels. +* Please follow the code formatting instructions below. + +### Formatting + +Please note the following rules for formatting your code: + +* Format all Rust code with [rustfmt][rustfmt-docs]. You may also use [clippy][clippy-docs] which performs linting. +* Remove trailing whitespaces in all files. +* Ensure any new or modified files have a [trailing newline][trailing-newline-stackoverflow]. + +This project comes also with an [.editorconfig][editorconfig-docs] that should already handle most of the cases outlined +above will always be extended to match these criteria as close as possible. + +### Continuous Integration + +Automatic checks are performed through [GitHub Actions][github-actions-docs] and run for every submitted Pull Request. +They are all expected to run without any warnings or errors, until the Pull Request will be merged. Please look into the +output of the Workflows that were executed on your Pull Request to check whether everything complies with our checks. + +[code-of-conduct]: CODE_OF_CONDUCT.md + +[github-pull-request-model]: https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests + +[commit-messages]: https://cbea.ms/git-commit/ + +[logging-levels]: https://medium.com/@tom.hombergs/tip-use-logging-levels-consistently-913b7b8e9782 + +[rustfmt-docs]: https://github.com/rust-lang/rustfmt + +[clippy-docs]: https://github.com/rust-lang/rust-clippy + +[trailing-newline-stackoverflow]: https://stackoverflow.com/questions/5813311/no-newline-at-end-of-file + +[editorconfig-docs]: https://editorconfig.org/ + +[github-actions-docs]: https://github.com/features/actions diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..c807154 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1532 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + +[[package]] +name = "async-trait" +version = "0.1.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "axum" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f" +dependencies = [ + "async-trait", + "axum-core", + "bytes", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "serde_urlencoded", + "sync_wrapper 1.0.2", + "tokio", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-core" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "rustversion", + "sync_wrapper 1.0.2", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "backtrace" +version = "0.3.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets 0.52.6", +] + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "data-encoding" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "dtoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcbb2bf8e87535c23f7a8a321e364ce21462d0ff10cb6407820e8e96dfff6653" + +[[package]] +name = "enum-as-inner" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1e6a265c649f3f5979b601d26f1d05ada116434c87741c9493cb56218f76cbc" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "hickory-proto" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07698b8420e2f0d6447a436ba999ec85d8fbf2a398bbd737b82cac4a2e96e512" +dependencies = [ + "async-trait", + "cfg-if", + "data-encoding", + "enum-as-inner", + "futures-channel", + "futures-io", + "futures-util", + "idna 0.4.0", + "ipnet", + "once_cell", + "rand", + "thiserror 1.0.69", + "tinyvec", + "tokio", + "tracing", + "url", +] + +[[package]] +name = "hickory-resolver" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28757f23aa75c98f254cf0405e6d8c25b831b32921b050a66692427679b1f243" +dependencies = [ + "cfg-if", + "futures-util", + "hickory-proto", + "ipconfig", + "lru-cache", + "once_cell", + "parking_lot", + "rand", + "resolv-conf", + "smallvec", + "thiserror 1.0.69", + "tokio", + "tracing", +] + +[[package]] +name = "hostname" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" +dependencies = [ + "libc", + "match_cfg", + "winapi", +] + +[[package]] +name = "http" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" +dependencies = [ + "bytes", + "futures-util", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97818827ef4f364230e16705d4706e2897df2bb60617d6ca15d598025a3c481f" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", +] + +[[package]] +name = "hyper-util" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" +dependencies = [ + "bytes", + "futures-util", + "http", + "http-body", + "hyper", + "pin-project-lite", + "tokio", + "tower-service", +] + +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "idna" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "idna" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "ipconfig" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" +dependencies = [ + "socket2", + "widestring", + "windows-sys 0.48.0", + "winreg", +] + +[[package]] +name = "ipnet" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" + +[[package]] +name = "itoa" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "540654e97a3f4470a492cd30ff187bc95d89557a903a2bbf112e2fae98104ef2" + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.164" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "433bfe06b8c75da9b2e3fbea6e5329ff87748f0b144ef75306e674c3f6f7c13f" + +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + +[[package]] +name = "litemap" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "lru-cache" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" +dependencies = [ + "linked-hash-map", +] + +[[package]] +name = "match_cfg" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" + +[[package]] +name = "matchit" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" + +[[package]] +name = "mcexport" +version = "0.1.0" +dependencies = [ + "axum", + "hickory-resolver", + "prometheus-client", + "serde", + "serde_json", + "serde_test", + "thiserror 2.0.3", + "tokio", + "tower-http", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "miniz_oxide" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +dependencies = [ + "adler2", +] + +[[package]] +name = "mio" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +dependencies = [ + "hermit-abi", + "libc", + "wasi", + "windows-sys 0.52.0", +] + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + +[[package]] +name = "object" +version = "0.36.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.52.6", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pin-project-lite" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro2" +version = "1.0.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "307e3004becf10f5a6e0d59d20f3cd28231b0e0827a96cd3e0ce6d14bc1e4bb3" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "prometheus-client" +version = "0.22.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "504ee9ff529add891127c4827eb481bd69dc0ebc72e9a682e187db4caa60c3ca" +dependencies = [ + "dtoa", + "itoa", + "parking_lot", + "prometheus-client-derive-encode", +] + +[[package]] +name = "prometheus-client-derive-encode" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "440f724eba9f6996b75d63681b0a92b06947f1457076d503a4d2e2c8f56442b8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "redox_syscall" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" +dependencies = [ + "bitflags", +] + +[[package]] +name = "resolv-conf" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52e44394d2086d010551b14b53b1f24e31647570cd1deb0379e2c21b329aba00" +dependencies = [ + "hostname", + "quick-error", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustversion" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "serde" +version = "1.0.215" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.215" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.133" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_test" +version = "1.0.177" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f901ee573cab6b3060453d2d5f0bae4e6d628c23c0a962ff9b5f1d7c8d4f1ed" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "socket2" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "syn" +version = "2.0.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d46482f1c1c87acd84dea20c1bf5ebff4c757009ed6bf19cfd36fb10e92c4e" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" + +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c006c85c7651b3cf2ada4584faa36773bd07bac24acfb39f3c431b36d7e667aa" +dependencies = [ + "thiserror-impl 2.0.3", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f077553d607adc1caf65430528a576c757a71ed73944b66ebb58ef2bbd243568" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tinyvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.41.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cfb5bee7a6a52939ca9224d6ac897bb669134078daa8735560897f69de4d33" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "pin-project-lite", + "socket2", + "tokio-macros", + "windows-sys 0.52.0", +] + +[[package]] +name = "tokio-macros" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tower" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2873938d487c3cfb9aed7546dc9f2711d867c9f90c46b889989a2cb84eba6b4f" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper 0.1.2", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-http" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "403fa3b783d4b626a8ad51d766ab03cb6d2dbfc46b1c5d4448395e6628dc9697" +dependencies = [ + "bitflags", + "bytes", + "http", + "http-body", + "pin-project-lite", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "nu-ansi-term", + "sharded-slab", + "smallvec", + "thread_local", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" + +[[package]] +name = "unicode-ident" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" + +[[package]] +name = "unicode-normalization" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "url" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d157f1b96d14500ffdc1f10ba712e780825526c03d9a49b4d0324b0d9113ada" +dependencies = [ + "form_urlencoded", + "idna 1.0.3", + "percent-encoding", +] + +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "widestring" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + +[[package]] +name = "yoke" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zerofrom" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..d521ff7 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "mcexport" +description = "Minecraft Server Prometheus Probe Exporter" +version = "0.1.0" +authors = [ + "Joshua Dean Küpper " +] +license = "MIT" +repository = "https://github.com/scrayosnet/mcexport" +readme = "README.md" +documentation = "https://github.com/scrayosnet/mcexport" +homepage = "https://github.com/scrayosnet/mcexport" +keywords = ["minecraft", "prometheus", "monitoring", "exporter", "ping"] +edition = "2021" + +[dependencies] +tokio = { version = "1", features = ["rt-multi-thread", "macros", "net"] } +axum = { version = "0.7", default-features = false, features = ["http1", "tokio", "tower-log", "tracing", "query"] } +tracing = "0.1" +serde = { version = "1.0.214", features = ["derive"] } +serde_json = "1" +hickory-resolver = "0.24.1" +thiserror = "2.0.3" +prometheus-client = "0.22.3" +tracing-subscriber = "0.3.18" +tower-http = { version = "0.6.1", features = ["trace"] } + +[dev-dependencies] +serde_test = "1" diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..c851a84 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,31 @@ +FROM rust:alpine@sha256:466dc9924d265455aa73e72fd9cdac9db69ce6a988e6f0e6baf852db3485d97d AS builder + +# specify our build directory +WORKDIR /usr/src/mcexport + +# copy the source files into the engine +COPY . . + +# install dev dependencies and perform build process +RUN set -eux \ + && apk add --no-cache musl-dev \ + && cargo build --release + + +FROM scratch + +# declare our metrics port +EXPOSE 8080 + +# copy the raw binary into the new image +COPY --from=builder "/usr/src/mcexport/target/release/mcexport" "/mcexport" + +# copy the users and groups for the nobody user and group +COPY --from=builder "/etc/passwd" "/etc/passwd" +COPY --from=builder "/etc/group" "/etc/group" + +# we run with minimum permissions as the nobody user +USER nobody:nobody + +# just execute the raw binary without any wrapper +ENTRYPOINT ["/mcexport"] diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..fb5dde4 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Scrayos UG (haftungsbeschränkt) + +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/README.md b/README.md new file mode 100644 index 0000000..eef8db5 --- /dev/null +++ b/README.md @@ -0,0 +1,226 @@ +![The official Logo of mcexport](.github/images/logo.png "mcexport") + +![A visual badge for the latest release](https://img.shields.io/github/v/release/scrayosnet/mcexport "Latest Release") +![A visual badge for the workflow status](https://img.shields.io/github/actions/workflow/status/scrayosnet/mcexport/docker.yml "Workflow Status") +![A visual badge for the dependency status](https://img.shields.io/librariesio/github/scrayosnet/mcexport "Dependencies") +![A visual badge for the Docker image size](https://ghcr-badge.egpl.dev/scrayosnet/mcexport/size "Image Size") +![A visual badge for the license](https://img.shields.io/github/license/scrayosnet/mcexport "License") + +mcexport is a [Prometheus][prometheus-docs] prober exporter to query the publicly available information on arbitrary +Minecraft servers through the official [ping protocol][ping-protocol-docs]. Unlike many other available exporters, this +exporter is explicitly designed to query Minecraft servers from the outside. + +The idea is similar to the official [Blackbox exporter][blackbox-exporter], but mcexport is specialized in probing +Minecraft servers along with their publicly available Server List information. It supports the Minecraft protocol for +all servers that are using Minecraft version 1.7 and above. + +## Motivation + +While there are many Minecraft-related exporters available at GitHub, there's none that use the +[Multi-Target Exporter Pattern][multi-target-exporter-docs] and that can be used without exposing [RCON][rcon-docs]. +Instead of exposing the internal processes and metrics of a single Minecraft server, mcexport probes any requested +Minecraft server through the publicly available data. + +Additionally, mcexport was developed in [Rust][rust-docs], enabling great performance and therefore allowing to scrape +server metrics on a large scale. Combined with its well-tested reliability and hardened security, this makes large-scale +analysis of the Minecraft server ecosystem possible. Therefore, mcexport can be seen as a specialization of the official +[Blackbox exporter][blackbox-exporter] that offers fast and efficient Minecraft metric probing. + +The difference between mcexport and other existing solutions like +[sladkoff/minecraft-prometheus-exporter][sladkoff-exporter], [cpburnz/minecraft-prometheus-exporter][cpburnz-exporter] +and [dirien/minecraft-prometheus-exporter][dirien-exporter] is that those specialize in aggregating detailed metrics on +a single Minecraft server (either as a plugin, mod or external service) while mcexport does aggregate only public data +about dynamically submitted Minecraft servers. + +The only existing solution that also supports dynamic probing is +[Scientistguy/minecraft-prometheus-exporter][scientist-exporter], which requires [RCON][rcon-docs] to be enabled. That +was not an option, as we want to scrape metrics from servers that we have no control over. Therefore, mcexport is the +only solution for this scenario, offering dynamic probing on public Minecraft servers without any configuration or +necessary adjustments. + +Since metrics are bound to a specific instant in time, mcexport needed to be lightweight, scalable and hardened to be +used as a reliable exporter in aggregating data. We offer robust container images and wanted to minimize any +configuration and flags as much as possible. Any configuration is only done within Prometheus, making mcexport +ready for [Kubernetes][kubernetes-docs] from the get-go through [Probe][probe-docs]. + +## Feature Highlights + +* Scrape multiple Minecraft servers through the public [Server List Ping protocol][ping-protocol-docs]. +* Expose information about the supported versions, current and maximum player count, latency and player samples. +* Configure the targets and scrape intervals directly within Prometheus. +* Start with zero configuration, no external resources or dependencies for mcexport. +* Use it without applying any changes to the queried Minecraft servers. + +## Available Modules + +To select a specific protocol version to ping a target, you can use the `module` field of the [Probe Spec][probe-docs] +and set it to the required [protocol version number][pvn-docs]. Any number (negative and positive) is supported and +will be sent as the desired protocol version to the target. mcexport supports the latest iteration of the +[Server List Ping protocol][ping-protocol-docs] that all servers since Minecraft 1.7 use. + +If no module is specified explicitly, the most recent version at the time of the last release is used instead. +Therefore, this may not be the latest version at any time, but at least a somewhat recent version. You can override +the version with the `module` field until a new release is created. + +Ideally, we could use (and fall back to) `-1` as the protocol version, as that is recommended to use, when determining +the appropriate/maximum supported version of a server. However, our practical tests revealed that only very few servers +would support this convention and would just reply with `-1` on their side, meaning unsupported. And servers that +support `-1` also support specifying a recent version. Therefore, the "somewhat recent" version is the best we can do by +default and without further configuration. + +## Getting Started + +> [!WARNING] +> While mcexport is stable, please view the individual releases for any version-specific details that need to be +> considered while deploying. Changes are performed in adherence to [Semantic Versioning][semver-docs]. + +### Setup mcexport + +Before any Minecraft servers can be probed, we first need to set up mcexport on the corresponding machine. This does not +have to be the machine of the probed targets, but instead, it will probably be the same system that Prometheus runs on. + +#### From Binaries + +To run mcexport from a binary file, download the appropriate binary from our [releases][github-releases], make it +executable and run it within the shell of your choice: + +```shell +chmod +x mcexport +./mcexport +``` + +#### Using Docker + +To run mcexport within Docker, we can use the images that we release within our [Container Registry][github-ghcr]. +Those images are hardened and provide the optimal environment to execute mcexport in a containerized environment. + +```shell +docker run --rm \ + -p 10026/tcp \ + --name mcexport \ + ghcr.io/scrayosnet/mcexport:latest +``` + +#### Using Kubernetes + +There's currently no public [Helm Chart][helm-chart-docs] for mcexport. We're open for contributions! In the meantime, +you can create your own deployment using [Kustomize][kustomize-docs] or any other tooling of your choice. + +### Check Setup + +To verify whether everything works as expected, we can invoke the following command on the same machine and observe the +reported result: + +```shell +curl --request GET -sL --url 'http://localhost:10026/probe?target=mc.justchunks.net' +``` + +If the result shows any metrics, mcexport is now setup successfully and can be used to probe Minecraft servers. + +### Configure Prometheus + +Now that mcexport is working as expected, we need to configure Prometheus to probe any Minecraft server targets. +Depending on the individual setup, this can be done in one of those ways: + +#### Normal Configuration + +In a normal (non-Kubernetes) deployment of Prometheus, we can probe any Minecraft server with a scrape +configuration like this: + +```yaml +scrape_configs: +- job_name: "mcexport" + scrape_interval: 60s + scrape_timeout: 30s + static_configs: + - targets: + - 'mc.justchunks.net' + - 'example.com' +``` + +#### CRD-based Configuration + +Assuming, there's a namespace `mcexport` with a deployment of mcexport, and a corresponding service `mcexport` is in +that namespace, that exposes the mcexport instances internally, we can probe metrics with this CRD configuration +using the [Probe][probe-docs] resource: + +```yaml +apiVersion: monitoring.coreos.com/v1 +kind: Probe +metadata: + name: mcexport + namespace: mcexport +spec: + prober: + url: 'mcexport.mcexport:10026' + interval: 60s + scrapeTimeout: 30s + targets: + staticConfig: + static: + - 'mc.justchunks.net' + - 'example.com' +``` + +## Reporting Security Issues + +To report a security issue for this project, please note our [Security Policy][security-policy]. + +## Code of Conduct + +Participation in this project comes under the [Contributor Covenant Code of Conduct][code-of-conduct]. + +## How to contribute + +Thanks for considering contributing to this project! In order to submit a Pull Request, please read +our [contributing][contributing-guide] guide. This project is in active development, and we're always happy to receive +new contributions! + +## License + +This project is developed and distributed under the MIT License. See [this explanation][mit-license-doc] for a rundown +on what that means. + +[prometheus-docs]: https://prometheus.io/ + +[ping-protocol-docs]: https://wiki.vg/Server_List_Ping + +[blackbox-exporter]: https://github.com/prometheus/blackbox_exporter + +[multi-target-exporter-docs]: https://prometheus.io/docs/guides/multi-target-exporter + +[rcon-docs]: https://wiki.vg/RCON + +[rust-docs]: https://www.rust-lang.org/ + +[sladkoff-exporter]: https://github.com/sladkoff/minecraft-prometheus-exporter + +[cpburnz-exporter]: https://github.com/cpburnz/minecraft-prometheus-exporter + +[dirien-exporter]: https://github.com/dirien/minecraft-prometheus-exporter + +[scientist-exporter]: https://github.com/Sciencentistguy/minecraft-prometheus-exporter + +[kubernetes-docs]: https://kubernetes.io/ + +[pvn-docs]: https://wiki.vg/Protocol_version_numbers + +[probe-docs]: https://github.com/prometheus-operator/prometheus-operator/blob/main/Documentation/api.md#monitoring.coreos.com/v1.Probe + +[semver-docs]: https://semver.org/lang/de/ + +[github-releases]: https://github.com/scrayosnet/mcexport/releases + +[github-ghcr]: https://github.com/scrayosnet/mcexport/pkgs/container/mcexport + +[helm-chart-docs]: https://helm.sh/ + +[kustomize-docs]: https://kustomize.io/ + +[security-policy]: SECURITY.md + +[code-of-conduct]: CODE_OF_CONDUCT.md + +[contributing-guide]: CONTRIBUTING.md + +[mit-license-doc]: https://choosealicense.com/licenses/mit/ diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..a5a04e3 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,14 @@ +# Security Policy + +We take the security of our projects very seriously. Therefore, we try to react to reports as fast as possible. However, +please allow us some time to implement the necessary changes. + +## Reporting a Vulnerability + +Please report (suspected) security vulnerabilities to **[security@scrayos.net](mailto:security@scrayos.net)** and do not +use the regular GitHub issues. You will receive a response from us within 48 hours. If the issue is confirmed, we will +release a patch as soon as possible, depending on the complexity and severity. If you do not hear from us, please follow +up to confirm, we received your original message. + +Please include as much information as possible with your report. If you have steps to reproduce, that will greatly speed +up the fix. diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..0ebe438 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,274 @@ +mod ping; +mod probe; +mod protocol; + +use crate::ping::{get_server_status, PingError, PingStatus}; +use crate::probe::ResolutionResult::{Plain, Srv}; +use crate::probe::{ProbeError, ProbeInfo}; +use crate::protocol::ProtocolError; +use axum::body::Body; +use axum::extract::{Query, State}; +use axum::http::header::CONTENT_TYPE; +use axum::http::StatusCode; +use axum::response::{Html, IntoResponse, Response}; +use axum::routing::get; +use axum::Router; +use hickory_resolver::TokioAsyncResolver; +use prometheus_client::encoding::text::encode; +use prometheus_client::metrics::family::Family; +use prometheus_client::metrics::gauge::Gauge; +use prometheus_client::registry::Registry; +use std::error::Error; +use std::net::SocketAddr; +use std::sync::atomic::{AtomicU32, AtomicU64}; +use std::sync::Arc; +use thiserror::Error; +use tokio::net::TcpListener; +use tower_http::trace::TraceLayer; +use tracing::{info, instrument}; +use tracing_subscriber::filter::LevelFilter; +use tracing_subscriber::layer::SubscriberExt; +use tracing_subscriber::util::SubscriberInitExt; +use tracing_subscriber::Layer; + +/// MetricError is the public response error wrapper for all errors that can be relayed to the caller. +/// +/// This wraps all errors of the child modules into their own error type, so that they can be forwarded to the caller +/// of the probe requests. Those errors occur while trying to assemble the response for a specific probe and are then +/// wrapped into a parent type so that they can be more easily traced. +#[derive(Error, Debug)] +pub enum MetricError { + /// An error occurred while reading or writing to the underlying byte stream. + #[error("failed to resolve the intended target address: {0}")] + ProbeError(#[from] ProbeError), + /// An error occurred while encoding, decoding or interpreting the Minecraft protocol. + #[error("failed to interpret or encode protocol messages: {0}")] + ProtocolError(#[from] ProtocolError), + /// An error occurred while reading or writing to the underlying byte stream. + #[error("failed to communicate with the target server: {0}")] + PingError(#[from] PingError), +} + +/// AppState contains various, shared resources for the state of the application. +/// +/// The state of the application can be shared across all requests to benefit from their caching, resource consumption +/// and configuration. The access is handled through [Arc], allowing for multiple threads to use the same resource +/// without any problems regarding thread safety. +#[derive(Debug)] +pub struct AppState { + pub resolver: TokioAsyncResolver, +} + +impl IntoResponse for MetricError { + fn into_response(self) -> Response { + // notify in the log (as we don't see it otherwise) + info!(cause = &self.to_string(), "failed to resolve the target"); + + // respond with the unsuccessful metrics + generate_metrics_response(0, |_| {}) + } +} + +impl IntoResponse for PingStatus { + fn into_response(self) -> Response { + // return the generated metrics + generate_metrics_response(1, |registry| { + // ping duration (gauge) + let ping_duration = Family::<(), Gauge>::default(); + registry.register( + "ping_duration_seconds", + "The duration that's elapsed since the probe request", + ping_duration.clone(), + ); + ping_duration + .get_or_create(&()) + .set(self.ping.as_secs_f64()); + + // srv record (Gauge) + let address_srv = Family::<(), Gauge>::default(); + registry.register( + "address_srv_info", + "Whether there was an SRV record for the hostname", + address_srv.clone(), + ); + address_srv + .get_or_create(&()) + .set(if self.srv { 1 } else { 0 }); + + let players_online = Family::<(), Gauge>::default(); + registry.register( + "players_online_total", + "The number of players that are currently online", + players_online.clone(), + ); + players_online + .get_or_create(&()) + .set(self.status.players.online); + + let players_max = Family::<(), Gauge>::default(); + registry.register( + "players_max_total", + "The number of players that can join at maximum", + players_max.clone(), + ); + players_max.get_or_create(&()).set(self.status.players.max); + + let players_samples_count = Family::<(), Gauge>::default(); + registry.register( + "players_samples_total", + "The number of sample entries that have been sent", + players_samples_count.clone(), + ); + players_samples_count.get_or_create(&()).set( + self.status + .players + .sample + .map_or_else(|| 0usize, |s| s.len()) as i64, + ); + + let protocol_version = Family::<(), Gauge>::default(); + registry.register( + "protocol_version_info", + "The numeric network protocol version", + protocol_version.clone(), + ); + protocol_version + .get_or_create(&()) + .set(self.status.version.protocol); + + let favicon_bytes = Family::<(), Gauge>::default(); + registry.register( + "favicon_bytes", + "The size of the favicon in bytes", + favicon_bytes.clone(), + ); + let size = match self.status.favicon { + Some(icon) => icon.len(), + None => 0, + }; + favicon_bytes.get_or_create(&()).set(size as i64); + }) + } +} + +/// Initializes the application and creates all necessary resources for the operation. +/// +/// This binds the server socket and starts the HTTP server to serve the probe requests of Prometheus. This also +/// configures the corresponding routes for the status and probe endpoint and makes them publicly available. The +/// prometheus registry and metrics are initialized and made ready for the first probe requests. +#[tokio::main] +async fn main() -> Result<(), Box> { + tracing_subscriber::registry() + .with( + tracing_subscriber::fmt::layer() + .compact() + .with_filter(LevelFilter::INFO), + ) + .init(); + + // initialize the application state + let state = AppState { + resolver: TokioAsyncResolver::tokio_from_system_conf().expect("failed to get DNS resolver"), + }; + + // initialize the axum app with all routes + let state = Arc::new(state); + let app = Router::new() + .route("/", get(handle_root)) + .route("/probe", get(handle_probe)) + .layer(TraceLayer::new_for_http()) + .with_state(state); + + // bind the socket address on all interfaces + let addr = SocketAddr::from(([0, 0, 0, 0], 10026)); + info!(addr = addr.to_string(), "binding socket address"); + let listener = TcpListener::bind(addr).await?; + info!(addr = addr.to_string(), "successfully bound server socket"); + + // serve the axum service on the bound socket address + axum::serve(listener, app) + .await + .expect("failed to serve axum server"); + info!("http server stopped successfully"); + + // exit with success + Ok(()) +} + +/// Handles and answers all axum requests on the root path "/". +/// +/// It statically returns "mcexport" so that it can be used as a startup probe in Kubernetes. That means that this +/// endpoint can be checked for its response: Once there is a response, this means that mcexport is successfully +/// initialized and probe requests may be received and handled. It can also be used as a liveness probe to check whether +/// mcexport is still running as expected. Since mcexport is stateless and has no permanent connections, this endpoint +/// can accurately reflect the readiness and liveness of mcexport. +#[instrument] +async fn handle_root() -> Response { + Html("mcexport – Probe").into_response() +} + +/// Handles and answers all axum requests on the probing path "/probe". +/// +/// This endpoint is invoked by Prometheus' probing requests and issues pings to the requested targets. Prometheus will +/// send [info][ProbeInfo] on the corresponding target, and this endpoint will answer with the status and metrics of +/// this ping operation. The ping is only started once the request comes in and Prometheus is responsible for scheduling +/// the requests to this endpoint regularly. +#[instrument] +async fn handle_probe( + Query(info): Query, + State(state): State>, +) -> Result { + // try to resolve the real probe address + let resolve_result = info.target.to_socket_addrs(&state.resolver).await?; + + // issue the status request + let ping_response = match resolve_result { + Srv(addr) => get_server_status(&info, &addr, true).await?, + Plain(addr) => get_server_status(&info, &addr, false).await?, + }; + + Ok(ping_response) +} + +/// Generates a response for the supplied success state and optionally allows adding more metrics. +/// +/// This initializes the [registry][Registry] for this request and registers the success metric with the supplied state +/// within it. Then the closure is called to add more dynamic metrics (if desired). Finally, the metrics are encoded +/// into a buffer and the response is created with the appropriate content type and status code. +fn generate_metrics_response(success_state: i64, add_metrics: F) -> Response +where + F: FnOnce(&mut Registry), +{ + // create a new registry with the appropriate prefix + let mut registry = Registry::with_prefix("mcexport"); + + // create and register the success metric + let success = Family::<(), Gauge>::default(); + registry.register( + "success", + "Whether the probe operation was successful", + success.clone(), + ); + + // set the success status of this metric + success.get_or_create(&()).set(success_state); + + // add dynamic, desired metrics + add_metrics(&mut registry); + + // create a new buffer to store the metrics in + let mut buffer = String::new(); + + // encode the metrics content into the buffer + encode(&mut buffer, ®istry).expect("failed to encode metrics into the buffer"); + + // return a response of the metrics specification + Response::builder() + .status(StatusCode::OK) + .header( + CONTENT_TYPE, + "application/openmetrics-text; version=1.0.0; charset=utf-8", + ) + .body(Body::from(buffer)) + .expect("failed to build success target response") +} diff --git a/src/ping.rs b/src/ping.rs new file mode 100644 index 0000000..bb4e116 --- /dev/null +++ b/src/ping.rs @@ -0,0 +1,204 @@ +//! This module defines and handles the network communication and data layer of Minecraft communication. +//! +//! While the protocol module handles the details of the communication protocol, the ping module handles the network +//! communication in terms of establishing a TCP connection to the server. This includes the initialization of the data +//! stream to the target without any specifics of the Minecraft protocol. The ping packet takes the result of the +//! protocol communication and wraps it into easily usable data structs. + +use crate::probe::ProbeInfo; +use crate::protocol::{execute_ping, retrieve_status, HandshakeInfo, ProtocolError}; +use serde::Deserialize; +use serde_json::from_str; +use std::net::SocketAddr; +use std::time::Duration; +use thiserror::Error; +use tokio::net::TcpStream; +use tracing::info; + +/// The (at the time of the last release) most recent, supported protocol version. +/// +/// As per the [Server List Ping][https://wiki.vg/Server_List_Ping#Handshake] documentation, it is a convention to use +/// `-1` to determine the version of the server. It would therefore be ideal to use this value. However, it turned out +/// that very few servers supported this convention, so we just go with the most recent version, which showed better +/// results in all cases. +const LATEST_PROTOCOL_VERSION: isize = 768; + +/// PingError is the internal error type for all errors related to the network communication. +/// +/// This includes errors with the IO involved in establishing a TCP connection or transferring bytes from mcexport to +/// a target server. Additionally, this also covers the errors that occur while parsing and interpreting the results +/// returned by the corresponding server, that don't have to do with the protocol itself. +#[derive(Error, Debug)] +pub enum PingError { + /// No connection could be initiated to the target server (it is not reachable). + #[error("failed to connect to the server")] + CannotReach, + /// The supplied JSON responses of the server could not be parsed into valid JSON. + #[error("failed to parse illegal JSON response: {0}: \"{1}\"")] + InvalidJson(#[source] serde_json::error::Error, String), + /// An error occurred while trying to perform the server handshake or ping. + #[error("mismatch in communication protocol: {0}")] + ProtocolMismatch(#[from] ProtocolError), + /// An error occurred while parsing the protocol version (module). + #[error("illegal protocol version: {0}")] + IllegalProtocol(String), +} + +/// The information on the protocol version of a server. +#[derive(Debug, Deserialize)] +pub struct ServerVersion { + /// The textual protocol version to display this version visually. + pub name: String, + /// The numeric protocol version (for compatibility checking). + pub protocol: i64, +} + +/// The information on a single, sampled player entry. +#[derive(Debug, Deserialize, PartialEq)] +pub struct ServerPlayer { + /// The visual name to display this player. + pub name: String, + /// The unique identifier to reference this player. + pub id: String, +} + +/// The information on the current, maximum and sampled players. +#[derive(Debug, Deserialize)] +pub struct ServerPlayers { + /// The current number of players that are online at this moment. + pub online: u32, + /// The maximum number of players that can join (slots). + pub max: u32, + /// An optional list of player information samples (version hover). + pub sample: Option>, +} + +/// The MOTD of a pinged server within the different formats. +#[derive(Debug, Deserialize)] +#[serde(untagged)] +pub enum ServerDescription { + /// The MOTD was configured/reported with legacy formatting (plain text). + Plain(String), + /// The MOTD was configured/reported with text components (rich text). + Component { text: String }, +} + +/// The self-reported status of a pinged server with all public metadata. +#[derive(Debug, Deserialize)] +pub struct ServerStatus { + /// The version and protocol information of the server. + pub version: ServerVersion, + /// The current, maximum and sampled players of the server. + pub players: ServerPlayers, + /// The MOTD (Motto Of The Day) of the server. + pub description: ServerDescription, + /// The optional favicon of the server. + pub favicon: Option, +} + +/// The full status of the ping operation along with the resolution information and ping duration. +#[derive(Debug, Deserialize)] +pub struct PingStatus { + /// Whether an SRV record was used to resolve the real address of the server. + pub srv: bool, + /// The latency to get information from one side to the other (RTT/2). + pub ping: Duration, + /// The self-reported status of the server. + pub status: ServerStatus, +} + +/// Requests the status of the supplied server and records the ping duration. +/// +/// This opens a new [TCP stream][TcpStream] and performs a [Server List Ping][https://wiki.vg/Server_List_Ping] +/// exchange, including the general handshake, status request, and ping protocol. If any error with the underlying +/// connection or the communication protocol is encountered, the request is interrupted immediately. The connection is +/// closed after the ping interaction. If the async interaction is stopped, the connection will also be terminated +/// prematurely, accounting for Prometheus' timeouts. +pub async fn get_server_status( + info: &ProbeInfo, + addr: &SocketAddr, + srv: bool, +) -> Result { + // create a new tcp stream to the target + let mut stream = TcpStream::connect(addr) + .await + .map_err(|_| PingError::CannotReach)?; + + // try to use specific, requested version and fall back to "recent" + let protocol_version = match &info.module { + Some(ver) => ver + .parse() + .map_err(|_| PingError::IllegalProtocol(ver.clone()))?, + None => LATEST_PROTOCOL_VERSION, + }; + + // retrieve the server status (general information) + let handshake_info = HandshakeInfo::new( + protocol_version, + info.target.hostname.clone(), + info.target.port, + ); + let status_string = retrieve_status(&mut stream, &handshake_info).await?; + info!("{}", status_string.clone()); + let status: ServerStatus = + from_str(&status_string).map_err(|err| PingError::InvalidJson(err, status_string))?; + + // perform the server ping to measure duration + let ping = execute_ping(&mut stream).await?; + + // wrap everything into a ping response + Ok(PingStatus { srv, ping, status }) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::ping::ServerDescription::{Component, Plain}; + + #[test] + fn deserialize_component_description() { + let status: ServerStatus = from_str("{\"version\":{\"protocol\":1337,\"name\":\"version 0\"},\"players\":{\"online\":4,\"max\":6,\"sample\":[{\"name\": \"cool\", \"id\": \"test\"}]},\"description\":{\"text\":\"description text\"},\"favicon\":\"favicon content\"}").expect("could not deserialize server status"); + let sample = Some(vec![ServerPlayer { + name: "cool".to_string(), + id: "test".to_string(), + }]); + let favicon = Some("favicon content".to_string()); + + assert_eq!("version 0", status.version.name); + assert_eq!(1337, status.version.protocol); + assert_eq!(4, status.players.online); + assert_eq!(6, status.players.max); + assert_eq!(sample, status.players.sample); + match status.description { + Component { text: des } => assert_eq!("description text", des), + _ => { + panic!("unexpected description type") + } + }; + assert_eq!(favicon, status.favicon); + assert_eq!(favicon, status.favicon); + } + + #[test] + fn deserialize_plain_description() { + let status: ServerStatus = from_str("{\"version\":{\"protocol\":1338,\"name\":\"version 1\"},\"players\":{\"online\":6,\"max\":8,\"sample\":[{\"name\": \"cooler\", \"id\": \"tests\"}]},\"description\":\"description text 2\",\"favicon\":\"favicon content 2\"}").expect("could not deserialize server status"); + let sample = Some(vec![ServerPlayer { + name: "cooler".to_string(), + id: "tests".to_string(), + }]); + let favicon = Some("favicon content 2".to_string()); + + assert_eq!("version 1", status.version.name); + assert_eq!(1338, status.version.protocol); + assert_eq!(6, status.players.online); + assert_eq!(8, status.players.max); + assert_eq!(sample, status.players.sample); + match status.description { + Plain(des) => assert_eq!("description text 2", des), + _ => { + panic!("unexpected description type") + } + }; + assert_eq!(favicon, status.favicon); + } +} diff --git a/src/probe.rs b/src/probe.rs new file mode 100644 index 0000000..c53b588 --- /dev/null +++ b/src/probe.rs @@ -0,0 +1,372 @@ +//! This module defines and handles the Prometheus probe target models and resolution. +//! +//! Therefore, this includes the necessary logic to deserialize, validate and convert the supplied information as well +//! as resolve the real target address. The conversion is based on the defaults of Minecraft, and therefore relevant +//! default values and SRV records are considered while resolving the dynamic target address. It is the responsibility +//! of this module to standardize the desired probing that should be performed for a request. + +use hickory_resolver::error::ResolveError; +use hickory_resolver::proto::rr::RecordType::SRV; +use hickory_resolver::TokioAsyncResolver; +use serde::de::{Unexpected, Visitor}; +use serde::{de, Deserialize, Deserializer}; +use std::fmt; +use std::fmt::{Display, Formatter}; +use std::net::SocketAddr; +use thiserror::Error; +use tracing::log::debug; + +/// ProbeError is the internal error type for all [ProbeInfo] related errors. +/// +/// This includes errors with the resolution of the [ProbeAddress] or any other errors that may occur while trying to +/// make sense of the supplied probe target information. Any [ProbeError] results in mcexport not being able to perform +/// any ping of the desired probe. +#[derive(Error, Debug)] +pub enum ProbeError { + /// The resolution failed because there was a communication error with the responsible DNS name server. + #[error("failed to resolve a DNS record: {0}")] + ResolutionFail(#[from] ResolveError), + /// The resolution failed because no valid A record was specified for the supplied (or configured) hostname. + #[error("could not resolve any \"A\" DNS entries for hostname \"{0}\"")] + CouldNotResolveIp(String), +} + +/// ResolutionResult is the outcome of a DNS resolution for a supplied [ProbeAddress]. +/// +/// The result holds the final resolved [SocketAddr] of a supplied target [ProbeAddress]. It is differentiated between a +/// [plain][Plain] resolution, where no SRV record was found and the supplied hostname was directly resolved to the +/// final IP-Address and an [SRV][Srv] resolution that was performed on the indirect hostname, resolved through the +/// corresponding SRV record. +#[derive(Debug, PartialEq)] +pub enum ResolutionResult { + /// There was an SRV record and the resolved IP address is of the target hostname within this record. + Srv(SocketAddr), + /// There was no SRV record and the resolved IP address is of the original hostname. + Plain(SocketAddr), +} + +/// ProbeInfo is the information supplied by Prometheus for each probe request. +/// +/// This information is supplied for each probe request and needs to be used to ping the right target. We cannot handle +/// requests that come without this query information. While the target address is mandatory, the module is completely +/// optional and not required for the correct operation of mcexport. +#[derive(Debug, Deserialize)] +pub struct ProbeInfo { + /// The target that mcexport should ping for the corresponding probe request. + pub target: ProbeAddress, + /// The module that mcexport should use to ping for the corresponding probe request. + pub module: Option, +} + +/// ProbeAddress is the combination of a hostname or textual IP address with a port. +/// +/// This address should be used to issue a probing ping. The information comes from the request of Prometheus and needs +/// to be validated and parsed before it can be used. To get the real target address, the hostname needs to be resolved +/// and the corresponding SRV records need to be considered. +#[derive(Debug, PartialEq)] +pub struct ProbeAddress { + /// The hostname that should be resolved to the IP address (optionally considering SRV records). + pub hostname: String, + /// The post that should be used to ping the Minecraft server (ignored if SRV exists). + pub port: u16, +} + +impl Display for ProbeInfo { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match &self.module { + Some(module) => write!(f, "{} ({})", self.target, module), + _ => write!(f, "{}", self.target), + } + } +} + +impl ProbeAddress { + /// Converts this hostname and port to the resolved [socket address][SocketAddr]. + /// + /// The [hostname][ProbeAddress::hostname] of this address is resolved through DNS and is used as the IP address + /// of the resulting socket address. To do this, we also check the SRV record for minecraft (`_minecraft._tcp`) and + /// prefer to use this information. If any SRV record was found, the second return of this function will be true. + pub async fn to_socket_addrs( + &self, + resolver: &TokioAsyncResolver, + ) -> Result { + // assemble the SRV record name + let srv_name = format!("_minecraft._tcp.{}", self.hostname); + debug!("trying to resolve SRV record: '{}'", srv_name); + let srv_response = resolver.lookup(&srv_name, SRV).await; + + // check if any SRV record was present (use this data then) + let (hostname, port, srv_used) = match srv_response { + Ok(response) => { + if let Some(record) = response.iter().filter_map(|r| r.as_srv()).next() { + let target = record.target().to_utf8(); + let target_port = record.port(); + debug!( + "found an SRV record for '{}': {}:{}", + srv_name, target, target_port + ); + (target, target_port, true) + } else { + debug!( + "found an SRV record for '{}', but it was of an invalid type", + srv_name + ); + (self.hostname.clone(), self.port, false) + } + } + _ => { + debug!("found no SRV record for '{}'", srv_name); + (self.hostname.clone(), self.port, false) + } + }; + + // resolve the underlying ips for the hostname + let ip_response = resolver.lookup_ip(&hostname).await?; + for ip in ip_response { + debug!("resolved ip address {} for hostname {}", ip, &hostname); + if ip.is_ipv4() { + return match srv_used { + true => Ok(ResolutionResult::Srv(SocketAddr::new(ip, port))), + false => Ok(ResolutionResult::Plain(SocketAddr::new(ip, port))), + }; + } + } + + // no IPv4 could be found, return an error + Err(ProbeError::CouldNotResolveIp(hostname)) + } +} + +impl Display for ProbeAddress { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "{}:{}", self.hostname, self.port) + } +} + +/// The visitor for the deserialization of [ProbeAddress]. +/// +/// This visitor is responsible for the deserialization and validation of a [ProbeAddress] and returns the appropriate +/// expectations of the format. The address is expected in the format of `hostname:port` and the port is optional, +/// falling back to the default port. +struct ProbeAddressVisitor; + +impl<'de> Visitor<'de> for ProbeAddressVisitor { + type Value = ProbeAddress; + + fn expecting(&self, formatter: &mut Formatter) -> fmt::Result { + formatter.write_str("a string in the form 'hostname' or 'hostname:port'") + } + + fn visit_str(self, value: &str) -> Result + where + E: de::Error, + { + // split the supplied hostname and port into their own parts + let mut parts = value.splitn(2, ':'); + + // get the hostname and port part in their raw form + let hostname = parts + .next() + .expect("splitting a string should result in at least one element"); + let port_str = parts.next().unwrap_or("25565"); + + // parse the port into the expected form + let port: u16 = port_str.parse().map_err(|_| { + de::Error::invalid_value(Unexpected::Str(port_str), &"a valid port number") + })?; + + // check if the hostname is present + if hostname.is_empty() { + Err(de::Error::invalid_value( + Unexpected::Str(hostname), + &"a non-empty hostname", + ))? + } + + // check if the port is valid + if port == 0 { + Err(de::Error::invalid_value( + Unexpected::Unsigned(port.into()), + &"a positive port number", + ))? + } + + // wrap the parsed parts into our ProbeAddress + Ok(ProbeAddress { + hostname: hostname.to_string(), + port, + }) + } +} + +impl<'de> Deserialize<'de> for ProbeAddress { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_str(ProbeAddressVisitor) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use serde_test::{assert_de_tokens, assert_de_tokens_error, Token}; + + #[test] + fn deserialize_without_port() { + let address = ProbeAddress { + hostname: "mc.justchunks.net".to_string(), + port: 25565, + }; + + assert_de_tokens(&address, &[Token::Str("mc.justchunks.net")]) + } + + #[test] + fn deserialize_without_port_ip() { + let address = ProbeAddress { + hostname: "123.123.123.123".to_string(), + port: 25565, + }; + + assert_de_tokens(&address, &[Token::Str("123.123.123.123")]) + } + + #[test] + fn deserialize_with_port() { + let address = ProbeAddress { + hostname: "mc.justchunks.net".to_string(), + port: 25566, + }; + + assert_de_tokens(&address, &[Token::Str("mc.justchunks.net:25566")]) + } + + #[test] + fn deserialize_with_port_ip() { + let address = ProbeAddress { + hostname: "123.123.123.123".to_string(), + port: 25566, + }; + + assert_de_tokens(&address, &[Token::Str("123.123.123.123:25566")]) + } + + #[test] + fn fail_deserialize_with_extra_colons() { + assert_de_tokens_error::( + &[Token::Str("mc.justchunks.net:test:25566")], + "invalid value: string \"test:25566\", expected a valid port number", + ) + } + + #[test] + fn fail_deserialize_with_port_negative() { + assert_de_tokens_error::( + &[Token::Str("mc.justchunks.net:-5")], + "invalid value: string \"-5\", expected a valid port number", + ) + } + + #[test] + fn fail_deserialize_with_port_too_low() { + assert_de_tokens_error::( + &[Token::Str("mc.justchunks.net:0")], + "invalid value: integer `0`, expected a positive port number", + ) + } + + #[test] + fn fail_deserialize_with_port_too_high() { + assert_de_tokens_error::( + &[Token::Str("mc.justchunks.net:100000")], + "invalid value: string \"100000\", expected a valid port number", + ) + } + + #[test] + fn fail_deserialize_with_port_non_numeric() { + assert_de_tokens_error::( + &[Token::Str("mc.justchunks.net:text")], + "invalid value: string \"text\", expected a valid port number", + ) + } + + #[test] + fn fail_deserialize_with_empty() { + assert_de_tokens_error::( + &[Token::Str("")], + "invalid value: string \"\", expected a non-empty hostname", + ) + } + + #[test] + fn fail_deserialize_with_empty_hostname() { + assert_de_tokens_error::( + &[Token::Str(":25566")], + "invalid value: string \"\", expected a non-empty hostname", + ) + } + + #[tokio::test] + async fn resolve_real_address_with_srv() { + let resolver = TokioAsyncResolver::tokio_from_system_conf().unwrap(); + let probe_address = ProbeAddress { + hostname: "justchunks.net".to_string(), + port: 1337, + }; + let resolution_result = probe_address.to_socket_addrs(&resolver).await.unwrap(); + let expected_address = SocketAddr::from(([142, 132, 245, 251], 25565)); + + assert_eq!(resolution_result, ResolutionResult::Srv(expected_address)); + } + + #[tokio::test] + async fn resolve_real_address_without_srv() { + let resolver = TokioAsyncResolver::tokio_from_system_conf().unwrap(); + let probe_address = ProbeAddress { + hostname: "mc.justchunks.net".to_string(), + port: 25566, + }; + let resolution_result = probe_address.to_socket_addrs(&resolver).await.unwrap(); + let expected_address = SocketAddr::from(([142, 132, 245, 251], 25566)); + + assert_eq!(resolution_result, ResolutionResult::Plain(expected_address)); + } + + #[tokio::test] + async fn resolve_real_ip_address() { + let resolver = TokioAsyncResolver::tokio_from_system_conf().unwrap(); + let probe_address = ProbeAddress { + hostname: "142.132.245.251".to_string(), + port: 25566, + }; + let resolution_result = probe_address.to_socket_addrs(&resolver).await.unwrap(); + let expected_address = SocketAddr::from(([142, 132, 245, 251], 25566)); + + assert_eq!(resolution_result, ResolutionResult::Plain(expected_address)); + } + + #[tokio::test] + #[should_panic] + async fn fail_resolve_illegal_address() { + let resolver = TokioAsyncResolver::tokio_from_system_conf().unwrap(); + let probe_address = ProbeAddress { + hostname: "illegal_address".to_string(), + port: 25566, + }; + probe_address.to_socket_addrs(&resolver).await.unwrap(); + } + + #[tokio::test] + #[should_panic] + async fn fail_resolve_illegal_ip_address() { + let resolver = TokioAsyncResolver::tokio_from_system_conf().unwrap(); + let probe_address = ProbeAddress { + hostname: "500.132.245.251".to_string(), + port: 25566, + }; + probe_address.to_socket_addrs(&resolver).await.unwrap(); + } +} diff --git a/src/protocol.rs b/src/protocol.rs new file mode 100644 index 0000000..480ca7e --- /dev/null +++ b/src/protocol.rs @@ -0,0 +1,449 @@ +//! This module defines and handles the Minecraft protocol and communication. +//! +//! This is necessary to exchange data with the target servers that should be probed. We only care about the packets +//! related to the [ServerListPing](https://wiki.vg/Server_List_Ping) and therefore only implement that part of the +//! Minecraft protocol. The implementations may differ from the official Minecraft client implementation if the +//! observed outcome is the same and the result is reliable. + +use std::io::Cursor; +use std::time::{Duration, SystemTime, UNIX_EPOCH}; + +use thiserror::Error; +use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; +use tokio::net::TcpStream; +use tokio::time::Instant; + +/// ProtocolError is the internal error type for all errors related to the protocol communication +/// +/// This includes errors with the expected packets, packet contents or encoding of the exchanged fields. Errors of the +/// underlying data layer (for Byte exchange) are wrapped from the underlying IO errors. Additionally, the internal +/// timeout limits also are covered as errors. +#[derive(Error, Debug)] +pub enum ProtocolError { + /// An error occurred while reading or writing to the underlying byte stream. + #[error("error reading or writing data")] + Io(#[from] std::io::Error), + /// The received packet is of an invalid length that we cannot process. + #[error("illegal packet length")] + IllegalPacketLength, + /// The received VarInt cannot be correctly decoded (was formed incorrectly). + #[error("invalid VarInt data")] + InvalidVarInt, + /// The received packet ID is not mapped to an expected packet. + #[error("illegal packet ID: {actual} (expected {expected})")] + IllegalPacketId { expected: usize, actual: usize }, + /// The JSON response of the status packet is incorrectly encoded (not UTF-8). + #[error("invalid ServerListPing response body (invalid encoding)")] + InvalidEncoding, + /// An error occurred with the payload of a ping. + #[error("mismatched payload value: {actual} (expected {expected})")] + PayloadMismatch { expected: u64, actual: u64 }, +} + +/// State is the desired state that the connection should be in after the initial handshake. +#[derive(Clone, Copy)] +pub enum State { + Status, + Login, +} + +impl From for usize { + fn from(state: State) -> Self { + match state { + State::Status => 1, + State::Login => 2, + } + } +} + +/// OutboundPacket are packets that are written and therefore have a fixed, specific packet ID. +pub trait OutboundPacket { + /// Returns the specified + fn get_packet_id(&self) -> usize; + + async fn to_buffer(&self) -> Result, ProtocolError>; +} + +/// InboundPacket are packets that are read and therefore are expected to be of a specific packet ID. +pub trait InboundPacket: Sized { + fn get_packet_id() -> usize; + + async fn new_from_buffer(buffer: Vec) -> Result; +} + +/// This packet initiates the status request attempt and tells the server the details of the client. +/// +/// The data in this packet can differ from the actual data that was used but will be considered by the server when +/// assembling the response. Therefore, this data should mirror what a normal client would send. +pub struct HandshakePacket { + pub packet_id: usize, + pub protocol_version: isize, + pub server_address: String, + pub server_port: u16, + pub next_state: State, +} + +impl HandshakePacket { + pub fn new(protocol_version: isize, server_address: String, server_port: u16) -> Self { + Self { + packet_id: 0, + protocol_version, + server_address, + server_port, + next_state: State::Status, + } + } +} + +impl OutboundPacket for HandshakePacket { + fn get_packet_id(&self) -> usize { + self.packet_id + } + + async fn to_buffer(&self) -> Result, ProtocolError> { + let mut buffer = Cursor::new(Vec::::new()); + + buffer.write_varint(self.protocol_version as usize).await?; + buffer.write_string(&self.server_address).await?; + buffer.write_u16(self.server_port).await?; + buffer.write_varint(self.next_state.into()).await?; + + Ok(buffer.into_inner()) + } +} + +/// This packet will be sent after the [HandshakePacket] and requests the server metadata. +/// +/// The packet can only be sent after the [HandshakePacket] and must be written before any status information can be +/// read, as this is the differentiator between the status and the ping sequence. +pub struct RequestPacket { + pub packet_id: usize, +} + +impl RequestPacket { + pub fn new() -> Self { + Self { packet_id: 0 } + } +} + +impl OutboundPacket for RequestPacket { + fn get_packet_id(&self) -> usize { + 0 + } + + async fn to_buffer(&self) -> Result, ProtocolError> { + Ok(Vec::new()) + } +} + +/// This is the response for a specific [StatusRequestPacket] that contains all self-reported metadata. +/// +/// This packet can be received only after a [StatusRequestPacket] and will not close the connection, allowing for a +/// ping sequence to be exchanged afterward. +pub struct ResponsePacket { + pub packet_id: usize, + pub body: String, +} + +impl InboundPacket for ResponsePacket { + fn get_packet_id() -> usize { + 0 + } + + async fn new_from_buffer(buffer: Vec) -> Result { + let mut reader = Cursor::new(buffer); + + let body = reader.read_string().await?; + + Ok(ResponsePacket { packet_id: 0, body }) + } +} + +/// This is the request for a specific [PongPacket] that can be used to measure the server ping. +/// +/// This packet can be sent after a connection was established or the [StatusResponsePacket] was received. Initiating +/// the ping sequence will consume the connection after the [PongPacket] was received. +pub struct PingPacket { + pub packet_id: usize, + pub payload: u64, +} + +impl PingPacket { + pub fn new(payload: u64) -> Self { + Self { + packet_id: 1, + payload, + } + } +} + +impl OutboundPacket for PingPacket { + fn get_packet_id(&self) -> usize { + self.packet_id + } + + async fn to_buffer(&self) -> Result, ProtocolError> { + let mut buffer = Cursor::new(Vec::::new()); + + buffer.write_u64(self.payload).await?; + + Ok(buffer.into_inner()) + } +} + +/// This is the response to a specific [PingPacket] that can be used to measure the server ping. +/// +/// This packet can be received after a corresponding [PingPacket] and will have the same payload as the request. This +/// also consumes the connection, ending the Server List Ping sequence. +pub struct PongPacket { + pub packet_id: usize, + pub payload: u64, +} + +impl InboundPacket for PongPacket { + fn get_packet_id() -> usize { + 1 + } + + async fn new_from_buffer(buffer: Vec) -> Result { + let mut reader = Cursor::new(buffer); + + let payload = reader.read_u64().await?; + + Ok(PongPacket { + packet_id: 0, + payload, + }) + } +} + +/// AsyncReadPacket allows reading a specific [InboundPacket] from an [AsyncWrite]. +/// +/// Only [InboundPackets][InboundPacket] can be read as only those packets are received. There are additional +/// methods to read the data that is encoded in a Minecraft-specific manner. Their implementation is analogous to the +/// [write implementation][AsyncWritePacket]. +pub trait AsyncReadPacket { + /// Reads the supplied [InboundPacket] type from this object as described in the official + /// [protocol documentation](https://wiki.vg/Protocol#Packet_format). + async fn read_packet(&mut self) -> Result; + + /// Reads a VarInt from this object as described in the official + /// [protocol documentation](https://wiki.vg/Protocol#VarInt_and_VarLong). + async fn read_varint(&mut self) -> Result; + + /// Reads a String from this object as described in the official + /// [protocol documentation](https://wiki.vg/Protocol#Type:String). + async fn read_string(&mut self) -> Result; +} + +impl AsyncReadPacket for R { + async fn read_packet(&mut self) -> Result { + // extract the length of the packet and check for any following content + let length = self.read_varint().await?; + if length == 0 { + return Err(ProtocolError::IllegalPacketLength); + } + + // extract the encoded packet id and validate if it is expected + let packet_id = self.read_varint().await?; + let expected_packet_id = T::get_packet_id(); + if packet_id != expected_packet_id { + return Err(ProtocolError::IllegalPacketId { + expected: expected_packet_id, + actual: packet_id, + }); + } + + // read the remaining content of the packet into a new buffer + let mut buffer = vec![0; length - 1]; + self.read_exact(&mut buffer).await?; + + // convert the received buffer into our expected packet + T::new_from_buffer(buffer).await + } + + async fn read_varint(&mut self) -> Result { + let mut read = 0; + let mut result = 0; + loop { + let read_value = self.read_u8().await?; + let value = read_value & 0b0111_1111; + result |= (value as usize) << (7 * read); + read += 1; + if read > 5 { + return Err(ProtocolError::InvalidVarInt); + } + if (read_value & 0b1000_0000) == 0 { + return Ok(result); + } + } + } + + async fn read_string(&mut self) -> Result { + let length = self.read_varint().await?; + + let mut buffer = vec![0; length]; + self.read_exact(&mut buffer).await?; + + String::from_utf8(buffer).map_err(|_| ProtocolError::InvalidEncoding) + } +} + +/// AsyncWritePacket allows writing a specific [OutboundPacket] to an [AsyncWrite]. +/// +/// Only [OutboundPackets][OutboundPacket] can be written as only those packets are sent. There are additional +/// methods to write the data that is encoded in a Minecraft-specific manner. Their implementation is analogous to the +/// [read implementation][AsyncReadPacket]. +pub trait AsyncWritePacket { + /// Writes the supplied [OutboundPacket] onto this object as described in the official + /// [protocol documentation](https://wiki.vg/Protocol#Packet_format). + async fn write_packet( + &mut self, + packet: T, + ) -> Result<(), ProtocolError>; + + /// Writes a VarInt onto this object as described in the official + /// [protocol documentation](https://wiki.vg/Protocol#VarInt_and_VarLong). + async fn write_varint(&mut self, int: usize) -> Result<(), ProtocolError>; + + /// Writes a String onto this object as described in the official + /// [protocol documentation](https://wiki.vg/Protocol#Type:String). + async fn write_string(&mut self, string: &str) -> Result<(), ProtocolError>; +} + +impl AsyncWritePacket for W { + async fn write_packet( + &mut self, + packet: T, + ) -> Result<(), ProtocolError> { + // write the packet into a buffer and box it as a slice (sized) + let packet_buffer = packet.to_buffer().await?; + let raw_packet = packet_buffer.into_boxed_slice(); + + // create a new buffer and write the packet onto it (to get the size) + let mut buffer: Cursor> = Cursor::new(Vec::new()); + buffer.write_varint(packet.get_packet_id()).await?; + buffer.write_all(&raw_packet).await?; + + // write the length of the content (length frame encoder) and then the packet + let inner = buffer.into_inner(); + self.write_varint(inner.len()).await?; + self.write_all(&inner).await?; + + Ok(()) + } + + async fn write_varint(&mut self, int: usize) -> Result<(), ProtocolError> { + let mut int = (int as u64) & 0xFFFF_FFFF; + let mut written = 0; + let mut buffer = [0; 5]; + loop { + let temp = (int & 0b0111_1111) as u8; + int >>= 7; + if int != 0 { + buffer[written] = temp | 0b1000_0000; + } else { + buffer[written] = temp; + } + written += 1; + if int == 0 { + break; + } + } + self.write_all(&buffer[0..written]).await?; + + Ok(()) + } + + async fn write_string(&mut self, string: &str) -> Result<(), ProtocolError> { + self.write_varint(string.len()).await?; + self.write_all(string.as_bytes()).await?; + + Ok(()) + } +} + +/// The necessary information that will be reported to the server on a handshake of the Server List Ping protocol. +pub struct HandshakeInfo { + /// The intended protocol version. + pub protocol_version: isize, + /// The hostname to connect. + pub hostname: String, + /// The server port to connect. + pub server_port: u16, +} + +impl HandshakeInfo { + pub fn new(protocol_version: isize, server_address: String, server_port: u16) -> Self { + Self { + protocol_version, + hostname: server_address, + server_port, + } + } +} + +/// Performs the status protocol exchange and returns the self-reported server status. +/// +/// This sends the [Handshake][HandshakePacket] and the [StatusRequest][StatusRequestPacket] packet and awaits the +/// [StatusResponse][StatusResponsePacket] from the server. This response is in JSON and will not be interpreted by this +/// function. The connection is not consumed by this operation, and the protocol allows for pings to be exchanged after +/// the status has been returned. +pub async fn retrieve_status( + stream: &mut TcpStream, + info: &HandshakeInfo, +) -> Result { + // create a new handshake packet and send it + let handshake = HandshakePacket::new( + info.protocol_version, + info.hostname.clone(), + info.server_port, + ); + stream.write_packet(handshake).await?; + + // create a new status request packet and send it + let request = RequestPacket::new(); + stream.write_packet(request).await?; + + // await the response for the status request and read it + let response: ResponsePacket = stream.read_packet().await?; + + Ok(response.body) +} + +/// Performs the ping protocol exchange and records the duration it took. +/// +/// This sends the [Ping][PingPacket] and awaits the response of the [Pong][PongPacket], while recording the time it +/// takes to get a response. From this recorded RTT (Round-Trip-Time) the latency is calculated by dividing this value +/// by two. This is the most accurate way to measure the ping we can use. +pub async fn execute_ping(stream: &mut TcpStream) -> Result { + // create a new value for the payload (to distinguish it) + let payload = SystemTime::now() + .duration_since(UNIX_EPOCH) + .expect("Time since epoch could not be retrieved") + .as_secs(); + + // record the current time to get the round trip time + let start = Instant::now(); + + // create and send a new ping packet + let ping = PingPacket::new(payload); + stream.write_packet(ping).await?; + + // await the retrieval of the corresponding pong packet + let pong: PongPacket = stream.read_packet().await?; + + // take the time for the response and divide it to get the latency + let mut duration = start.elapsed(); + duration = duration.div_f32(2.0); + + // if the pong packet did not match, something unexpected happened with the server + if pong.payload != payload { + return Err(ProtocolError::PayloadMismatch { + expected: payload, + actual: pong.payload, + }); + } + + Ok(duration) +}