From 7a389e9cc372a79ecb4cd882da4a6d1ab9b3fceb Mon Sep 17 00:00:00 2001 From: vados-dev Date: Mon, 17 Aug 2026 11:40:03 +0300 Subject: [PATCH] Backup for MikroNUC created successfully at Aug 17 11:40:03. --- MikroGig/tmp/Config.rsc | 0 .../20260817/20260817114001-Binary.backup | Bin 0 -> 444189 bytes MikroNUC/20260817/20260817114001-Config.rsc | 5533 +++++++++++++++++ .../20260817/20260817114001-Diff-Config.rsc | 12 + 4 files changed, 5545 insertions(+) create mode 100644 MikroGig/tmp/Config.rsc create mode 100644 MikroNUC/20260817/20260817114001-Binary.backup create mode 100644 MikroNUC/20260817/20260817114001-Config.rsc create mode 100644 MikroNUC/history/20260817/20260817114001-Diff-Config.rsc diff --git a/MikroGig/tmp/Config.rsc b/MikroGig/tmp/Config.rsc new file mode 100644 index 0000000..e69de29 diff --git a/MikroNUC/20260817/20260817114001-Binary.backup b/MikroNUC/20260817/20260817114001-Binary.backup new file mode 100644 index 0000000000000000000000000000000000000000..3b4017250723c8d6705f1a645fde584f2626018a GIT binary patch literal 444189 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

uwyFzE|zDT9)ahSBDQLHPga+9;833Ke_Z^ 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�w^g&WRh+N!^V!$AX~LG6K=8Zq>>cTR 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∨&nA~e^H<U#Dyym3Lp1Au1IVN;Np{5e7HpIv;|^0 z70Q2APT{h=GLA`S#E|S5^E@chW=%?SD3>NPJjczpgQ4?jN}SYeSDL@ls}wamPuN%F z&<8f`$UJ9vk|89NYe^vKh5f!!avW^g9tXSOo$7#PgZi7=V$n430+ke!6I-Td zB#n@f7$XDeFpm%SvD1{GE*RySnkK#mXAK+?f-=Meb7)|72FVSI`x(j!wcoLOmT*pa z_t)LaawK7nOAS*w=jR;92oJI+WRdh2Hx5pUXohZUQs*P|51BV9Hp?@?c-0 z6jCyqyi!Twe6z+LLBsWzI$Pmv4uysS?41yrf&s%@?=bnueNfiaQNbHbj6>k7H2J$= zLzyynJM92A8Mgn~L89XG+rv1|`XjJzF*;&XcBjtLIy z$s!30gFmz9CTxZAB1a4A?1s?a39BH*v_6t8Ry1?QLN}*T$&N~=QGND9*pWk7oUbUF zu`EfBO^MYOCpX<~Chn+MFab2n9C0f);OfTD$uFxZR`!KXp7>1yC%mjw>tuSUd zZe@v0oi4N~Cs+v)iVk{tkM)f0WrN5_o8NlWpW1ZV$}n{oWZ9=so>@039@$~Rd-x`+JXw17vJdyAVIN# z^~kklxJ!fW36$(kja3UwS?b|}3Akdd1v?-EglBj|=fe1ci6KndPrkvp_624d=7fHE zady^9i#fRVJ}{MeP&HZ0!q2`5%li@<#GLtSYLO5msoq$xY4wM-y%kR89dydVATW2i z*D!wI9=2vHw@7kn;{3eI)R0kZ)1^~B%Q4+{??$5NS#D$MiXKgAM^Tl|2)4r(Bh@j- zfK4^tos*kEP{ZTjEW9rsTe0=nWlnAei75w`u)GG^P|uVh)SL<4V59*{EK{05BAo#p zGON^WB&P}TEF*o_vqWfVv=H`37)y6LXDiB1UU2WkmQB;TVKc4qW;w+5tnxxtU=%KpYPbopBYipS_?n=& zTBo4(%f<&P1N{YCTwb3u0%+XJYa@RFQ(*fT}STf-@sve3m3gr`W^m!s|C}oMX8r6gI1Q$AROdPjz|N(-?~{8UaJP99^;wb zt}GG#I2=qf)%z%WykuVN;8|5`8KHR;F z3;^00Tnwavr)qlTTzpxMGs;M7GJ1L&1^KHDVT?8L-nEJS4l1 zzMst&u(SoFH3@ScewMZk<|^*Rlk|4Ua8!@Vi;Mr~JlQ@K;%sx9xf#}KAN z!&cv}-N83OVn3pL$1#F!n)29(^d_6r@Ppf$kS_A)ga~V#70I>}=_1Orkp-jmc6Rd}UXbYS>^4 zdaD{GRhVXoHE0g(BA6uep<{A+yijd7VikRy6?B(Q`R^=sD;Y zKu9%M8K|1zq~4lC3jb!(>97*~XuC#Mjzy6<6W6_WymmiVAf>S>FgD;`#J`99x)f|A zg6d+C;47pvwhN|F&b=)~RrIHGPN%4H5scYp3Gn92lg)E%RPt$G$~Zk~4kiH~pHsOY zsK|9Yy;p;QfPAJ=v%p?c0cc%S@;2K(oA7?6=e}24n>l?@HsHjY+G-Q$8kGew)G2wZ zIc+cv+Mz#|x{7zkXdW1i)FX|CK0q7YyCG+6>Z@@!Ei;hhEk%tfRF@BHCb*%Ra#1B5 zwnxGH4IH>yThyhD`gQCpUP^bIlLGOiXn}V#krjrq0v%a*TUeal-UbJ>^{r<*-k3Qb~ zjqiQ3_3+W-zwu=I>9gnG|G{tm&3Lyz(0ls_$xjZ4qcj`;R$i2oqvKa6KV4W{TE2ec z=B?Xz*w8l>`j7Q1g59W}1{`;v`?~?{0AL^i2CM~yWs5-$n>-f)zzj~O^%hi_or1XQ zOLwl{xp`~p&JD7QvW3Eo&O7mfz%epf9O%9|1Y_JgMhm8^#odmgy=e4c@$+~=h@AG3 zNl#A1Avn?eUiCd0-IkPI_RX1Nhd7PXbnnMcvR(h!As-*JNjLfaR2mT)aAr3-sfXHh zstjLQoA;e%#{JmA_wL$#aU;R`mtk3;z+aUpFM<$mFIP=;3_5apJR zx38*oz~466R$zLSm$-41HZ){_G$nypb3Dg4^_mgekGvaYL#O%JG0ouKPfFuK`D(>p zZo61ew|&kSBIyYnL0OONp2nq=jwcv8gUfEFPoQ3^&Vu^DV^K@otheVhq24Z>+kJGp zVYsWUPl|Z9(t%FPCz^6G8xZX-_HnLp@rd2%+hu7>Jf~aUy%I}~mrz3qK_HYaAECU`R9kuxV5My$QTc+4GjNPOBu~SqzNb%S8PQ4F zlkVMIiA}dHFd&C0&1O~1^AIWk9_o`wwZErNEGiMJVwDiLO|!L4^e$+cvFcUDESN|a zLUU*%ua3DWVNQRyq12H*)KFM3Sq9Mt6yxNwvus_$JeQqlhwS>!M;SsTDCWXGG0GA& zR3q&~p39HXfJa1@A+$OOY|*f8(su>{?O<^j*<)c4VGzs$QtPJ~C13|K#T={h z=*=*o=0kBbX~LrX65hc$vTz!fPKUOT{K_-1*^fvU)%II)hwrE#W=Fq~?INHa#PO4C zm(TedLu$pLiZD*jzGniYpXMisW~0Px$i$__ligv`SDTL|OW;6~auqtqF*1oc24sjg zwgJNQfcNGnF3y{%!Hp0%YIwKlAiZE0x3*-1UPQiIQuexDM&;g#F1jXQxDRZgV@@{M zn;?r$Ty%+Y6yk>maT&YlVR|ThsCUC$A~G#(6G{#t5j(C2bRWh$<0QgnggS5V$70?q z8T^0t{zL^4j#Hx=&>qfEx?=A@2YV*N+FxoySZX{!5X{M2QNkHmv7}(bfklHFL*f`~ zciSzIm#XWcK1tZSZ^4Zqm8AuC+#S#g)`Q3h&`Kc_iQ$@DP;COmsQgBGazz4e=e6Ufj9A|v&Pk-uX8F| z5n^eq38NK3+Eq((nHj-v8>#AVHfl-0#!Pby(0BvQRszzev{!G)gT-jK()&wR0;Tmc zg{yZ^t4dGXr59RJB_@f`hqMHCh3M$Xb4^V$bNn6`x{_>@7o!~A^ zaF^eQ;Mi{xO;K!mUcNNbXi1yKEx~033|f_K1W3Qq9u-;e3c(s=T&!F+-~}eMwl;If zF;7=`nlmod=_5HCatpHo6x?m;qKNmkzeTk3Cc2eNtsJay_u8Do4?dek&M$eQIMK7u z0h$vxw*mKzf6zaq7V6rL-ttF*w>kNO(eT?1pP^;DI5RsHi>zD-@RVCseacE*@6OFt zIw#n>b8A%{bGGr`o#j`{jkG%$zGFo`kD^YyvC8o znVohN+}>EJ+*1)%k$s4#*|a~&_8C_Vb*KZsj-nbyRJK#t@3cnyRZ$Dyh#UrAFSxlV)CBOy*)Q1G;T4=(15I66g)3sg|N3gsqV57?T z{?@4r`B{Oi0*y9bW5Le+)R^pm8t`SPbmnnh6cK&eU|V)INN$s)rA`ML z$Cn@A!cy$mL3dyuF7_~Qy zivz{O*`09eGwWxJ65MOUmv(608)e5S&lOvs)#+}J82vM=}ALRG8Hd3UZ` z^XA@M%N87#ZBTPAyL;?|lf8TOuZifgylPHG*C=zR zO|NWDFk4%if6J2LN9^wueg}diQu0Ud#_y<1Y-FDk9`07|h^-#E<;!@GnUn`jmu%w@ zB}^6`lK6!6nOK@r+XvZXIKZ6|*`bFe&!aua9-1L5lVKR zuRmnTi!%t{(~T1c}kfHr%}%tv{3_^j26+Bkuk|oTPO8zL~5OWaaN? zqw!FedRC2+g05QN!WKTP05_XwP`#}+h31>7zGfT7cH+FGaSHN1atn#j^YPVn3Cp4k zjXG}7pt*=n!meSCg)Q#I1uhw3%4{lNFS`$lc(6eCwHLXWCj5LzPTOfpy5hdtygcp| z&RRama>>_2dQp`%7&b=v6w-m>8^rOKFPrJ64Db#o)ID4^(wreAK`me)HJ_YoCf!qo zdBQ9_*1Tf~?9aaK_iRfqq6>R;0}UIB4|F`xd3P_-!$Gg}DK>01RH`<@GTwL&f{r-* z!_G&O#hgBwi|k+zd$SSD$guBu^0JDO3S zsfg~&qV1VQvw6@*KK2HtOgp`2 zFZxrLJt0l@lF0eap+1)F6o8|)4Z_oAc~IdZ{K!pxjgwI4GmUdEj;^19&qUYUHmq4$ zAoY>{3sgoHQIr9nvY~qHbQ~AO_#lr9-HW1-xrW&Qnz8ft+$P+O)+Qo=d)hV3pLL=i zR)tCQqZbR^J1;+g2}^8+G7VLR@2L{!+~X7_ZsnY$c8R)qs4q`e;Z0-+I+GAKPd)Gz zWcZNJ9*|YMRIsNqx=@wXQ+ONo?QwcR3j=r#rO!5Q?W?>o*@{bK-mROQjSb2uE3Cn} z-3(NjX=vzpfcDzlMV5yb2HUrEEdu{J184MtXX`X>5*mGtzUoGrNIlHd2t%rh{=ICH z4vbdTwBQ(%4K}=Bkf+T>P!O6ugH7W@7>mJVz%FH5H+2SUfmoIKJJsJ4I%%x001TBp zAl8Cl`i}xnj5s=srx5wgHaS*x5!|1e064x^#0y=4kLGM&7MMBbORNsfviSz;4z4W&4u(AQYK`fmNfzC4_D~MXh7%Aun6Z>3 z<(^rcg4DPM*|7zP?p+CQ>v0KIP;6^tTMD^>k#BAfh}^AJBy;c&7PE2mEwUZlUhXWo zejJyj&b_nW4%ISv$@EhRwQ8G+Cf+N-ArK-PA^I2WME7)N=@JQT(Oej&RB4}1ak)$p z(}1D2YOqX<)z`p6<%ktjqR;{$fGtiw!RQkhD45T%EdF8ud!xv3)3Y4Se1@{gtkq&8 z@D^6{TZK^Pjf?f&YQ%^_ZC1ftar(k7Kr6ugn1&}$_zL%sn%*wJ4 zTQHgn>>POP0we$oc~hS+X(ixD9DjVNy433f( zC<9+CD(SC9i!1sQtRj>qLPMx&`Gyqja@_EFx>=Y~*>om+hguF;k5gDGItMR?2F$Zm zo~|~~O0+{UERe=xy0BD_>2nCMOa zIxOiuECe_O9&D~#Yd=tn3s~5d=4lGbEG=}Ohf!ic9d3IrlwvfR2=|{srF+TD7!Uu6 z@Q=d8d&nuQ@Ctz{@}@2%Po1>Oq&Cn~m37(39K?BnD3Q4rFL&CVnb+U>;Jcr`+1WA4 zi9MD6FJNu5io`I;!V>vJeh)10PPDT_yMLxcG40~>G-@S!;ORA-tAS!SOq%y9%JJdR z_og#1veNtUQSQOD!~;m*m(^` z^qPhtAx0e&t3;({NQaQaAb~I^BygI2JAA=v4eRzq;uNggS(&P`R&WYmFNRqe+qrQ2 zd11EF_dDa&?dZ0uH}Y7-S~)sO4)cuZeK*U7E~wDm_SYZsbrU3}7~w~Baq~~sn+|*}u)M9=P|8V5O!w-!XoTdivt=YxJ*~QuA+3T}6Exfik z_uG*O(dgByIMusxKF;HlNje!$_Vu`5W=Fce_Hg6g+LOmm)_(KZ!)Kp7`)K3w`qM|x zH`b$b4p(&-A!C+|dQ1?|*tYI2E_s}EOLf{?&>WVWW-l^7KyL?|j1N?MYep@qWrHrm zba~=WPEJmy!MET|$6|Fl27~GIc-^abqgwIfkNt{7Sqn-n8Sn(I(Wq8xS*aV9QbkcK zbyF$2+l;WsW3=+ZVU$;iA1!EAK3^CaIwdSEsq2UsH17rj@Mizb#~)YUj7DlXtW^~1 zrn+U%&Y4ii20{3OdZqS3?C7~Y81T=JKjxpK5&tX-(<0CA$|<7KbCcWEA&9b8F{|r4 z_8~=3+v$Kbdo|`9?BW7_u2BKBN**xpfxs42yBn<8?09nd?s22@1y%?P>k{%LEYgV< ztgt)0!h8c1pU1L9(BOQcaS#CsZpj44^JcQ%w;7mG{HnKzJO9F5kfO!d#Ll##cp-gx zQ8#QLw}^n|NeP`i6~fO|`PQ&zX_vHso;2Eo&B55&hrLD@6jP{QB}G|xzStGR`((c_Ywo6~ZZnWT zepA<>OWs{}&p|n6ETr`s*Sy0^9U?dX0#fCQYOHsje{pA;E@(3Rn~@u!t4~Z5n0s`F zQlkGi^4v)O1_EXZ2-%O*4aEcg_6zR3^o4c6q-A(hKwEfOB`#Aipe|GJFOIyx0+_m_ zFH2rHGhnG}ioYcqB*o!bq=$xOI_)4svIDwKr8t7}OoB75zZJ!y??&kA)Ye331lcq? zqZ{+8WJYF6t2NYk`gDx|!?XT6VR+^azB5Am;EC8ed^#HMIai{MDXdBn!}*_%#t(2d zaUPTGOWk6amA#T0gJVm66-F;I$eO+%u+&)$_7_r!TPDXbcQ!~0-2dhdE(Em*vi%^| zmF^s7P?Ew{pH(`IcZW&3@2Cpwi3UQSG@0ce@aa!HG?92Jlj(gUzU2qns`XsQ6cUMs zRklayU~pW=ZI)#bL1{9(G!e4_Fdhu1pqQHIj^bBccu!$2ZahX(W@S9=j`S$YaYl4r zEgMc*i6{zT@_DqNx)xJ1eZURth1Cm(QYS(zzH8X#1qJn1)=5>hWh+1Pcap&L0+;Ie zv@)gLx=7xN>4GxL=|sAq(ST*+MU`bYg2KV&l-Vd`p#gYxK%8z64|5$4PFOYY2x-cg zfCOu+A&?dn5@Ges^adSs>9%18jSAr@Gj;|i2I&$@@I?nJ>3~cqBGgaq2cP~3K~?Ij z&Xurj8Z!x$I?HAfG?;S)cn05I`vGxnCa^_ySGA2%L0CN>nY4f5j!={7Y-{sjQ2E1i zRu;23ti8bnx>fn{mp-h;j?g5nspw(Vi&mV7G&TylJ@`SM$vNxLff5w@e!{-R=9AiD zNHJVGAYse~`g&`3)k5L7c2$))XL+3EhXtMK0XlS?D#}(<%&K8h z>O3B*EHx*#IH})(E_C5fPo*y#9_}MwOer#$0Fw=8m8xr)?_)A)Sl&FO8JQz%~G`$X-)b$&v<3XeNsL~cs>n#DJH{` zvV^iKVgg~!2`&zXb$3fjgF$eOV1<2wwqn#HFRDQ(>7?8#QhwOAYRkiQ+t`Lhiq+^j zV@kaV5I_?YVc3@KF=!cs;N%>=h!!iW8-zvf4{r%~G!(x)$>VwjdhKd}-+`Y1*UWv0@rNXHs^{u(oTej|3fF0=+j>w&z!3R4#bNJEe#KZAH{CZcH z@$0<*dS_>D2Jc_jb09z^4Q)mbGjNtqEePtSohe>%dqjJVi;gC6wlw0)+MiMq`%97lb56vnIi!l|h>jKA)=Sr#jD~RTU8n zz%Sqe_?h>k0IBEet-Yx_=xq+a&7os+0)=_15rNG0la#{Q?B&Wln3uERS!h8_7>&;j z7?I8rgR$CUyjE7T$Wf!Op)t}OoyNwRHp!nR?fC<>@M>?*j~+EX;>oZ~;6kRK^U`}w z?0C&P@dn;QGr-WAMOhJoD8=(5#ijzdOaPwJ49)IYt=u@PmArpu8@DQL2)i*{=j`d2 zHb_w<5NAvnpP8}aCA6F}9ZZ5wtYe`CI;%_EU0u)o#goP!b29=vDYs(DKiurdwXt(v zhIn4dQ#Kj$+JW^bA7}~bt6;EFMu@v1S1KW?S{j+`2@*z8*tR*#jKt*xK=-b6$44_7 zQ?gPwr`4=<$w>T-Y?m+ScABkxkqL16k_mBghA6v3F$PaB$GPVZu4PDPCc-0UFrFs; zLtVZDB&ST6Rj~aV$dzNr+$XWb>STKOc6f0iv4*Ef3t~YCrk-YlJ4T9dWbZ&^xvWO{ zVOItf@W_FYvROPx@}iVp93bbIAZ~zeiuaa~Re7D8p+c9!M6J|#M;sZ<^dKd5|B(5g z8E$>+0V0!qeIrhB9^8Dn(%h)OJ*l9#s`)Z<)&_%j$LtzK4|$8jY$ndcteB^O1ICIG zQv#M)ocP+F%ItV#Y2>ho#OV~=y$ie1jB2k9{te~f2hZKf*4DxJb>3Y~y8&17h^_(8!m z9~TNHP^pWh=C-yX!aVWCo)SSR9|{a~-G)8-n#eq=JCL2Gv_Lz}Fw(^B5#{Vem18la zNLb9G#I%KB2C%q|7#j^}Bla*e;Bb_Q4@gp&`+v_11XD&8j=MdW3+e#+tD*r>Tga$X zlJGB7c@U=t8iBZ|gFK6btGa}T*8`c&Q9QCHwaF?!E{fPwR+`TGSGAU!c-}gZMdBaQ z@DxpD(h7t1N>PLRwy8|53abQtZli2+%51kj)Ox1oQdm@Q^QS z;XDI@kPTAFOdZELs8`thsS}C(2(bM-)+`t%_?_Ymz}%3|)3RIGoYkKewk+iOsxHbn z$BBs%EiWvqEbVI;3B&?caWy!a`Op#-ZizsODeSBU$i%OXqqH z*@b)UrByXd(!*YRc~uq3P^V?DeO=KGjLlf4(+!-q;Ugcbici7F0gyH_Cg*u@K%@?f zQPO3l2IDnu7ZJezH**q3@}3(ZY+fV2a`t{w^y7Sh{mPW9)J*3{DcRV1_io#7a)#`s zQ%decWK;6XlLZhF9YGyK$IgwoeKOk3hWAdMrAa>%{q}OGijhWMgUP z_Jf^!3nm5#UZ?DslC{d`we_9bw<@0}WAEcl`>`JnbvlT1*Y};}o!d9O@A;sM*tYI& z_R1@CYv=Z|D|8U&Nb%p#b&R}!Ub*F++jlDElC-(pZC7rXO$Gw)o!dLN7u}aUD-gMq z8%*4}zjJ%te%5hb9=P6bukYNxZ$IuOxpv>~T;Ex9!`vHYxgXzU~&uzL}ppYddRhOq4h=N%y-tJ<`K$ z>;t*Hv*yr(n6Z4s7YPd;T;I9p02#*lzV2puobKyxKgs(;zo`d1YmQ{NXs!qKa@Tk6 zUAH&`5AqOledms&D=<{L0HvFxhuyKJ(J}qrxxRDzfo%j4WL_i7J9pfqjIv!2*Z3ey zePEY&?l}aFC&j@y@ta@vC%xz=MUmySd@wodJGYl?-$g&qhQqLTM=!-F8S0bnIL=F4 zHPhXT`yLc)H+JqhBA2rG;TJ`Zw|DNXxgNRA<0M5y7&XaQDzkKd;x&Kg_KpwAIL;4; z+8c#8^Tk-}{y}#)9uBk2N5s<3nm3x#Qs~<{Qf?k1xq|``bcy!X`^oRZu zTL(jcD0?%j+CEQX{YRMER=l6k0blaYyX^__Eeg)018*g>4uklF z-d(ufU0C$yZ*Aw^J&WrGimz6e$J{kmNAb*4JF3g8t z1J8j-_gjX&%^9l<8Y=puNwTqI;#0_?XG6d+ZiQ>dA`^eZlv;`OGHMTx35pIGpD7H4 ztp9`#+D=GKFQ5sxgEZ(J@>0DF6FYeo0a-8;&(h@8(*2ChDsI3lrMaNe(^_ae1ktm6 zxRFBLadXClH2y-bmP5NiocQ6~cHZ>+7H8&n(w$sg*@>?1w7>Q3Yn_?zd@#GR`eE$C=kW;nW5aA7HItrsBYR!P={Ac8li~1WJxf#F$1c_OCrmB`hZl?e1S7)sN!s_< zP&=&Qj2NMrn(P|h(JeBEnD)+7NygYPBHK!REauaC+&|FawX~b|;}e*)9+qP_s*8m9MAWMiB3z>75H>T^8pSKvr}poWn519ut-##r2T_DOOu~!a}Tj~ zUVuz%5DTJs`Bt!pmjt;rT0a|;6W8fF!7UsU{yJ^>xV{Z4WhDh(xhde7cg4oQI+Ik1yPml8MnWgH<({<7V*FS#3 zH=+ovWbk@*`=&#Axa5T<%#um4$g;_R*4ShWjl4@V3?>fZ56qyV&WfY+g$k?lZJgbB zFvxX*1xJD)83$t(|A?bNVGZY9Xg-tn=FOdZ5e(8G3XZmQD7+$S7tZSL=W$=}fse!X z$80Oam1Fpnz5V0KzQ$QO=dChI2wmydqnYxqdSy6*6NF+U z2-#=R8^bJerIuhm`1-D4Bqk~@XpD{@25;)e1 z2dggs>75Rj^Ot6xXg!=Z!OT3TjfoW$5=TDEZ3GxshSBk2lMG!C8X&d;_#iu0ld(HE zYPeh=`zkcJjWq8jF$Nu^wUo&j;CcC*-2#Q5n&VFV@_C$7BNCKqTzlp7G zvKPRSR1A3@M;KMMw^!)0XB+DuOwvO>ySlt|>*j63j+dp~5Sn-KGVd!y8W}*&9VULPYO0rHI=QaodTW6*}*(?d!B$QCrBKRVIgg z-wF0~lf19HccUYn7fF_a<8fj!|6`o@VYBR)lbn5i$hZ*%Lm8+ThZreb>Y+YH-+bY(7+#bMe=~Eo znI^afWP)uMEn(3H81Bt*v&KEkeKG4#K6wz#B;#Q{OZOy5J@~qb8-0^9yrUSh8D|XQ z(p}CN)FO>$don7X>pa;DD_8LzR79QoNxqg24BrVpSL<$1_V$wS#v^0m4C>VuN$?fP zx`P*0;uhpKDn<3PYe7@ye1$px6MU?BVZ*Ov6s*`)gA(Sz!}dv9#vvMnh>Fcr=*|#| zh@++{RMC?xE7fmqME3p|sPd->Cv&2jeER+kPu`^sa)x47l{sx&aYXp3ctKAdzJRW2Pj;G-226E`TMNiW!do_a@VdKPzHwX7LjLm$pL1I@r zm#HmfQ@Hkl$%tz`(YPX$i+IX`e7&!alD^*J!#ZZ4J)oCe+2rR)2%S zX2ygq0G7NTNl;^_{8x){lJl-I~im6U}lEVw2HOu(MxvY(S5;n{BDJ&asTfA?;b?59~ywzsNDD;$}(DcnRAFoAOnW>&=oovYMEsl6L- z@JKjpVBQj_&AeFN!AepCo1})AtW9B@$uL|KzS|lx*&Pm5W@#V91XK??rkdbI4uyO(i;tK;QE>aEoYsE@Ynhv zGe4kq0fik~l?r`2^k$1tQA|eXCO^8c2&oN-G^z=mrp%iqJMWiMFze|)G_XnrN$TY^ zEzDp@&>uEuF7|6w%=nqIV&6?YGnj7o27QyX<$>EIw!5LiARm0OG?Yy+XupICh>SX;K{2JaAMA7GW!Ld4o*M!nT83 z5wV=Sp59O*4m`dQ5zmw*AQnGT#Lmg%Mf3CQe|UI|P>qm*hph^nEQ(XTi|_IJ!u6Uw zwW7h9;rOvj%QWA>C!vW|G^f)zWk826C+1`{I+??bUKu<}lKz0g3BPL%*8DitCQrtt zIcy-1c{6eTL0RE-s?ZY@G|MB8aeOY4i|jF!n8*Of?bb0nm9W+Zu*H@K+AQ)>oKE85 z@Z`H{`!E@chfQuhzs71w54yoMm851L=q$B0g79gB9SMO)FQYtj3vO2EYn(rAHD;A~CpkYxnOT zAntcgZKN=m^|mM!97}J!<;r_{Cp0BV%N0N{#eRP z)kksG3h`98hQ;D1lEP|PP>a{){NEYu){QY>wx49#VWEb}q4vy4cB=zs>PdbU{E{@8 zGvsteNr4D@RUGKuINeu0b*(MGUz<^8mlAG{&Rlpj)iulDl#Pc`<)pma5ZI=h4ZXF{ ziFTHjjnz(e(<=y>bnCaumHZZogV)!ChgXj^!2woinEb&uot=&m+qk+}r^<2z8Y+q+ zwwn!3W)0fu(o&qyLmIAT^iG#@mAeGw@i}n_n>9MH{CfQwtI0n%&BWmqyMkk0LZ&|Y zAR;AFkBx~mP0>n(Tw6;nx1RU4lHABXj*>l$Z@en_;&}n9>ya*@; ztD08lbHss_F7h-EAH2YfoV-TU0Gk8FgUrsS6!sK)3CZMP~C@89xgw`u^tA6?n9*qJ_Bym>fxoA{JEhao}+z5%6~6LTv49AaGwY z*9Jm%)QaUKfDCDnB&EN|gOM)bG&sp7FgKFBkyQ1$DO_rr5TFrNXXpMFkknu#J$r6E z7l4@_yQG8$wpfPU-wMq8E=9)!9m8!qYZE+KRNUVR453b|l@K&si(%XnI2yL`B5ME2WpinIG|i3^?pJZ3e9j!A!oPqkZ3S#0duWUWY2vYngD@{at5*vfa%l6bMN0L^<~uwa|nlzmu8qG!~jEm?4;pPq(VpW*t&j@|g7 z;?B<8;PogQ%*@X8c+Ig9|3dl&p3QU+PlQ#~o zo79bdvi62xJ{KD5_QoAKjE5&ACG}}ONtMM80zQ)cRb;JjFM+aEK4)NzYHkQ##N*qH z!7f8A05YUik?8j65`iLa>$}E+>jkIyTLlMNO+JS>6|F(Z$z(rl&XVF(C=hj2W(<7| zQlH;Ob4ucY^gtnT!Em5+)3>p#iKJlasWm49|F8iXf8Zt{#noS@O}~jItAF20_L;s! zdWnSQYM*5LNRw$kv2Vgy#9&O20!?B}O4m4k&@yp-W+|ZvY^G{FHoIrFC13nfYo}2G zK@Sct#@R9hy?ZuYnGUyudz;n@3cK-S)0N0H=ufi=7Id>^XQL6L-U%-g^WZH2ArZRl zyUcoCtFm~iC}wM$>(X!2zU|(1@qwErfS8}a$%@Aih{=0q8xf01QRWR+X)r{IwegsbhK$StZ$_bzyahGvbow&%7ox2kns&tb;-Cwdv8!}8a;C3N zbW*=N&(5w{ZA|L^G2|`IqoO*rYipJYSt_(9p^n7yB{_v2&^Ie3RCJmsh-OuPlIIBC z47(b|Co0vH)@HzglS!6Bu=whFB-xj9eU#{9QUt{O$%;ak+w5jXIv>U-v+y^hD%V|z z@SuoWA8uAG%>?JA1wV!y%+e-rh~rlxT^hL+bf`v*H}wP5?xZ`ZG`Ql~9{yt39(NS* zJn8#1j`t2@s>~%l_F#7at7+@s8cm4)tQhuLsZ zc)O7;Lfq-zm1zo~2wz$-;c{{7@7=npiTYVp946zj7j$G&a$TjYthDn>UYTv7jgWi) zGCADfWL8-|Z;J71amC~`qfG9jbOT|BDhd&-J`Fc~(|3XD4bDbNYsSbNZ5-+RMBR^1 z)V3}S2pj29l4mI$T6iAk2~;$?na*F1?nH(wzBJROyyRAze*+zNtW7@IEVrZCH*<56dTy{#Q(CU=ZL}9>$nT^PBPv%X*@tRe& zBb*_8Z5wGIQVQr$53)Rj;N6LnSYptBjYjJv6H;hX6E#Aulch=|zh-Z7Fl}nyzg%as z2BS{6KEZ8(#P@IzD?d@6GI#eBJ}P43ESPf47ON8p<6&)-Zylx!2BCGTH;|r`qq)bo ze&a^&8)V-Mk_kW|PY?!7GDaqrj#7V;#R#VHbmOYQ5)+N14;1GyNe|QPIE`k=&BUMR zxEhmET3arGdklLOJmC($;&w!TeinK zXEgp{k0ndqj?ZDw`R=`MiO5K7EVg8iQ&e>#I;3nFKkjMH@t!5?_s=s+Xb?B27MRb#k z`k1VUz%lhD7D?-ajEUFuPb8LgRJzhY&$&4vxV)`s^{3{9Q&}d@Pdrz8@4{CITw_uE z*wd@6Zz_H92cIf^Rf*?rk+SKo50fAh$Fs}Gk-{Zi?^MPwq=}92g2KT?5Td^A?a%aal1;q(^g9M%kH^dR~WssvQNcy@9=TeKt+t*wzxv8xb$r$y=p(ESFK#w zDl7IWtk}Q#vF{16V!!aC-yLAZeyh7;f2LE)t=NwW2)|Ui@NrFKT_WOnJzSnYx@hjQjs$N>8vQ}Nh+eW z`R_Hg>!tpocf^0+d?VYzwK(YR`oNFB)o$FSijEGF(j9c__h=5l06)I{TfQ8wio6v zt*Y4H4U#lI!XXtF`&g>_$7a17b@u(Z9z?jtx*Npp#7n&Jqk)3z5RY1=e@C??ZP zeGo@f$RUY4g0^WS1aTU5le9ep^)plVlD!!9!_L&(U~fbQ|i+PdoVGQcM3# z+-|4r!~q`H6?Y+&IPFlp!JrT(ekYW@0ZwqA3P(0uigDD>2&VjTTDW?0lVqpuPd_N+8-H_?ER=av$R@U zS!2oIF1a87#lKBU?*I9UzeAG!+hxf;f`Jy0exh*l0M3^PIt9T;XFUkm^=NPu2fKTzs&=MS ztI@bvYc*Q)eBN8VOcur`wViR5%*sh1F_I-Vtv8L^3-#vZOcN)WXPVEk5FAkwJRDt+ z>aA#-LW9^c1rWrK=)zwawd=Gyd%mX$)VTgyE<}^_^45=%)bE?4rcRooxJVAp*boU4 zb?5GeT3_C{pk4_cs}<}n9I`7Tqcp*SoJH%z4yj6(qrs024ZPy*`-&AD1qLkGkr0!M z?UVD_^$}{uK@Js!iVf-wvZZLs8t9O3rDkMbh2W1LdH3p?eg1{h;I88yaODJF~w)a(>_B=_My)QY1I;C%)Q{0;|}@?d*9epM;~lRSvDtL?<}P zfu-YxT@uqHQgh);BQ<6HNMuDHr#gN7%!E^Naho#)*66rU?sXe73$JNs)8kf04){(ytR8D6&31&rQfb@)C-}BNXTE0AZ$!l{T-;AT-fYXzT z9_I=cxo@s?lZ|+Y1NLKq-=(r(V;|oehA4Yl_+0kCS8f#JcFzmDQU8YD^RhLJG=dw^ zz8@0ukOO*TMYVM@z3U^<*&maRH}GGH;(Pw?a<-TcVjIhkgCxyj`9zC5>f%PP!#PT_ z_^vL!nL%}RCN4;No$K%z8~rt%Xki{w-m%ydV;XKFi3O0MWO zQT#wte1> zqZ(J}Ke>>B)bzN19A1@Mv(G)8qZe`o~s+L@!(ZlNTjZ}F*`;BdZn{LFTgQvQu*JJA8~ zUSM)d9{o2SZ-&Hq)EA(8sZ*9lJ?;%#Pj*kPg1f*FNe(0OwwQvaDWRzk3JfTGvnfIPiL? z+KO6b?YELBL_+e=Z!^m5`^am|n=3bTaaLZVXQ*CpTvgf755jh}0%U4e%Y1frR6Ltb zs}=rQyIN)re;9PlEEqb9{ZYtD(km@L1GcUt3+@4!UsEHEm=@BGQ<_DZ^RP{7s6|Vb znn6lht|Uw05$>)yqVu-q44Ocou@8Dj57X$Ofk2jl)>yJW4Y*Ye%`8Q0hdOzQ)#G}o zUDfCk8Ewus@hdK2(7m)(lROQ*oU(!kS8bwffpEc9b+JE1msT4BUu_o)tyWeN$mqma zcZ5EzL8dure|K1L6b-q}@x)h9hS_Zf-X3j+s1AY$Zp+kvTmW!m4?fXKcO;d-PGOKd zOrt^2ai-9PD--)LB}hziTnZ%Wj>f3tIM!w11>zEZr8}Larfr-hr`Cs{1xlH9QT(N1 zTaTWIQc5>Q;XfFJr+EcaQUpJbjh4ymi|JGZ8n~64BtUtUY5esIqX~gTGcgmQ5OqR( z;&9Oi2gJRp)An$M z;v7M@jhbwlic{vEPDrO#2r3sW+{Y31pwUMcXT15Fpo zQ+#Kt?TxKQ@SP;u=trdc`6H?yjl?2_WXu#H01^$=WO0vfvIm^>D$ra^M-(rp7v=nI zCF}HKeRH{Gp0Ce;lK50l8<>m(DWK)lW@Yq9BR&}u5bp@da^tjTp7es1K_em0?NVOP zFHd7E@#olucqclfC3lK>9-$wN{KW2z6^a! zRt<_--=0wvXTlZX+OE+bjVfQHAvbVjAYz#o`gZzbxk!?(t+Z8DUBB(Pi#F_sCOw-t zXCLA-5|6PTM+V?yvEoxS8KmwFL%LT+WXMCOGYMBn-p;mO= z&N$^FX3j(L6$vJzOJ9JDj3}gAaqjyju8;$uJfo`e3Ga8K(5Kszk+aaQ()|sDMWf*u zW&F`R-~`&B4m2tNRYqAFTNW}S-XvD5S`B@XP+?^b=?jvInfn+`D1I4VdMbKw&o|Sh ztD$gl!fBPHL9d4-d79K9R7m&RjiX`Mt+V!Wyls4PyrKzUW47-2c`8T)j$gJS!cQET za|!b3xa7dYVUX_OFfuJCQNJe)Kuq;0OmTo9djrQYH*OSFn$Gp~K^av~$gENym|>MQ zt;U#abg?u~q{xTGqLt6TjVxkZFi&zmZ#|jw$9hIZ(G);9W);!h&5Yb#sxEGEBRP$S2B^2P=x z!ri<$m+!#F*618P5%=LgaXugK^qfW@7R_JYH*iF{`8vv5A}A$1b%q<_fn#q`vE_;ZZg|*1R1F7(5${xX2(etail^(k%EP9bRa?_~wdK~?sREr&E2_L06%0oj-&XBs+jNm_3mqM1x>OZmB`_fcE~7NIA?0j#-z?C`YogRMD7uAeFT6G z?X3JXXB7QGNyf~i6MdacC&~aoV$rx~NHO}Oxf-fSAkv4e^2wPSH?y#dl8xB=;!L6p zB9K^m_VMDcO={6}Cb3d-VP5GClRb^wr-uFz836j>O+VF-z)h4-qMx|~#Mv+ujzAWO ze!e!0cYXbK9+MD8l^loHZev&vAZ24 zzx9jGrudRB^^$3jzkfUPP$#hPj}kS>zfl~27!g+uRh7(DU3-QujD_yzo4=OJ-urO6jDWFs?RLDLTI%((QByI4i1%*$p^0nh$)*Q- zACjl#)Cn!+hokHVFN9ne+NCnxwv}^A+|_Buga9!J9JT~BcG^mlyPix=G+$;-3jr+P z_XjB*aD~2OMPzq-riGue+mjo%jRxzX>ns=j4gzAbPpGod9KT>E~y z7j@f%C`q+~&!1wEBKuLqR^1UE6c@8NCxV8g7hg6O zuZ=Lvo0PJk8*`v3zgl$oSuX@xrY;MP=8MICKRU=|NmJ(8n1w3j$rx?akzPlZY+4CW z#5QzoCHV^#eUT`l>!wU1seb+7=Xj0crybHLkZEIA@bNnJoasE* zanMjNMNK+gP!8l2EP_cwEtQ&UxD6~e)V}fhSxPK&>St%-e2Ub#A zEGR06OwpjLf`kR4t8FN4gtHdpzB3x8+oT|$V>IGEu5aFhAJ=<9E(p8W@2?QlB)c?7 zR-n6e1Eg{Vo;AyB%6~lYJ87b@BTd<`%8Pf0D7+)WFI#oZI1II`Wo_I$ThwAmxGsjO zuu9Y7P47o%WS;(U`EeSHkSIv`KQ^cT?tvf2fz%w$ziriZrrV6BDtuxRCZGxPQ1!!O zyDriJx?g!#_c_|<^E%mwh35fm0!9War z>F2Zdxs0WdQg%9XWHE5@TB2+&LmAh5l-uv%YE9hh{RrEi9IaBSJm`5rNKYT);fwg^ z0sh&-KUMrQg@3+)e;(nVtJ$pv{*)*2=q3D9!#{2O^LhO94*q!;|J3o%1>3t=YsP;7 z;+!W&L#SP?fP}6cX8cF(JeW&Ah?w-A{Xw*wzBzaB$ZP-B-7p;;fN0tAe>qX0u^qPe#a>ZTkYB`F7-5^9JP3|2~)LjxbiVVe=8Eal_!Jry7OrOh7sYit$GDBf65* zUW`Ktb}Jmd5kFsZ5cxvPJQ7{dF1DtN)hFz2hfxZPP4ldq@8mSCzVj|ubJl_{9)`H_ z-c4=Qk9hCr#fJ|wn;xq-RJHto)>B4hBAG+~fR(Z3?gfo7sO%enLq%-yHtfTPFFN@M zg2`<^Tpw<~<#&u-$m=gNrMG=u_thGYIW~6knB6sS+YfIC;l9Irx%YZOxKGUA)DN#QSTHu7HpFE5heO3nM~s+zq#f5{bWR;aKVXEOc4!S% zoxL>IP&L9s(~bWkJebfZtT-iN&L%x^|Ar?_a-H4^ISIc! zc^5m6nh0eUyW=-WX^LoM#{Xg_g`lD{h^sg7J!4ahlF`g`hAFo){#QAh=7{x>AGUzW z0GSfcz%FWJ5zFEmHR8`hgrpNBNfgt?<4#7DO9Cz#Ez_b7y+ox^qPPN_x> z#h+6Z?d&KL1BSX)VfwzR>GdXya!S3ZnvI5<5eDp2^z7pgkw>a2QhsQd&ef2&Ha6DS z11ClJFzC`zBP}QEs{Dc+lYi(s6x3*B5|W_jhd8?7kfQto?Rq+g8gw5cugmr*$NpXr zirGwgM$BlKOdlG@Lx20>z}xj74!HU?B~QOF@Z!XONOj*3x1YJZO9~09xk=u5)Vwe)_hm#mI^$!&8OJ*O# za=w?;=$z8%F*aJyU$WqBafru;BIE!UBHEdK19w%~h%H90YP1=GeIaNLaXQ8sClYss zA$^Qp!mW%+;@b45m;7GOi~F@sFY4^)6hL0?1xZHmNxV7McYZ#^HW^#P$U3l*p@elEELnHiuKyy6Piw`ca-c) zv@62OG8=OJwd_Njg>SVJspnJ5sGN8)h?zfe3M_C2$%gX{->5gc5}#M%Tp=DBK09 zAK^(hOjT*Ud&NegX0M>`yTm_(bkC3P#+He5u?6YPR*Iko)k;%_>AER8Qc-Cd=TK5C zfPnRKfbr45k9IW1Wl%zgDS!arUsa7{>b&q-Te&22jvX1S3h{g%qEkvw>zsYGkoT!RdU{(oCECk|bJ6SS`kbRV0XIX@Viae(Z zE_B0@JlDZ${XP=*+~+!2Ezd1KGF0-^rPH=5Q(!6+jHO}m!KP|O>b7OpiAiFlhvr-% zvSeK?Ue*GxtmKk-i3!Oa81y#XeChJhdqk3h%oDpghc#ecz4`a4BRf7F%IUt0(U8IZ+KAhCb- zTTW48tq^We_M_B5z`0W%Pk}L$MrwK03+VkPp9FceJzt-jtuFvOQ@e6ZVaiFtx4WpC z7iSuCGp$DJB3^vM-%f(mUnKt#m3?=EbZ@v_??nBXZQnog575fB3zT2Ol&R_}>NO_A z*vA=PwdWn+Fc*pl4`aWpXkJyq^)@&qG>UqOs$JzrQ&z^ac+j~tqX7kNLve{Vlv3xb z6LJr*7|(jA0+n3&^<4UGM8y3Um3vN={(e=iRz_cXEq9DQ`_EOAX)UIgzO&n|e_HMa zsi|k{refdl4_3mRXeI3QhFyPU%{5)=g%!EMMU@x2PH|d#ww`)%iZX?&+?ZXQYc94H z@=b6kkgjy%OPChu8m5sOZce2b>VORas|^XL38?q8zAt1;2XW#a`b} z{WzhQnsu1LH73un=f_;cwmM9PUaxng5?p)cnJZ+FhB&1jytsu9TXj`wshTnruln`f zx+*ujWmWC^JKnIDBCQucOyMnlZMLo|v*;3&p=dVb#YSV1{wq%_=XHJABg}LQWv;F& zbE;;F4p*ZAa(TR-d3OxI&n~V#Es$G3M*4GOucCE$RiGBi-RnI+%|A?I=p8afoO7Fb-0G zdO&TaVTetmZo^>|cc+su*x8v*{4fbn+IKpR`carg2@HUFbh=%rtIC3zgByq`%2?m> z!kI>Mrg?c`%rqbXX-Rh{N`2;}7u6^m1&Ov@y3@2~f&}c^1=|Kxgk$H3(K8c;y0)$)l$Xrw;BC63oO)b-?DYpdpy2KDGAD(BP;y2>s&fCR#-xCkk}3Z{@w1E_ zrmHg0ek#T4P63tHwUwKyym}=5fE8r4a zbpk1=ubQBoAG@4Sy4ZlFw_YI@Usz;N_i<-ogsd7>{E!J=&#U+-YGflYFG~M(2nOPbgE?rn)sU{;Icc&&kqc9-U zMq#)YYT`Y;$<9J$6iN;gy}u@?Xrbw*Bb@>l4aH7embRzGM$fKbBeLg=$Y>yjMb3u{ z^Oa!2(&&tcWI6MESXg!zOpNe3BQh3{vo78bPPCP=g*^egqRxdA0F7Cdyy=(m$n7Gk zE1ki>9^OnJH_#tBLKSk0i$-ML`b5E5maHvecEV*dgG@HPOTa5DZwk7hTkcM;CHa#k zjvOz3tQ9)c3kRZI>uT2o)17Uv(~VdpG`uDqE!RaYT%%ORWHNQ!F;1%6bRyRH(BzBX zUFQfbvZGC?apVa$&>*Tt9=OB9aoaooVJ@RyXWYBWuA-YcYFLDA&!G~PnVvD*qiUuy z+P^;a!$)4(-_VpPI^Ucp4GCA$>XOgTQR=0zYw1EON*hvS@P=!An89LI6}5fg z1xt{&l48Sm!%l892J|m_-P$~csv-i2n3NhM*RIZlclYaRrVtB&PoqV>B_s2QU77l3 z_~~f)iP%@3+Kr;F+K!GcsHv&VaOD$y)$@iSpD2S5DYY?zP+e4s--*JmTRfUB+Eut- zH>(YuV1TnUa62zr>Rm{hCGdyY&N~~2kS|&^Kt4Q;BqB-c342Fs)0v1(yV%AYHqXiw z!(=*GPg{|b_F%-Rvv8!aTMf0IYJ8JxZQ#XefDjElKxaP`UV3r5pCfLnrT`$7k7AH8 zi5TRxiv68o0+$*BxPu~BRB}NlGl};rf`vNF6(*dNC8ZY(?Qq=mLqGO<8wc!%1asr{JueJ>STD8?MPcxBQpr`>i5hrmXHVY8`8Cwi zF2EP-HA=XfGRH~Yh~j+?XFN($T+jB1;x&(uiCatkxc>+TfPqj#aS0YP(nqkPR+nDA zd#}E_a_8lKo&X@VkrzGFz~jv=afOr$YbG zk5x5{!WxZiN?+LquHT_zfkyZTVUl{i-rcZww1)mQf|{4bnP=ZYUpQTE&DR@q_1T6K z1@7nz=5W`sxuD?i@UZS|p3wz!wXT0a7tc;gVv8i@CTbA-JHcav+xqI7Dqq{T;(3uH z!-5mGk7=t{C+U|fctZkPNg9DdsypwQ*XEwsxp|D$PKE8p=27d&?s*rRF9`ONAW?nm1(Jn=~4@{{V|K)rJR z&h?FzyLZ;{$K<44YpAVBid#783SS|U`N_6k4Z;AvJglnq)T1jPWqMa{Y^>vwy$6+% zMo@WL;OMg|1wXCtMKN3l-PFBS-T7Iht}yrL11nJ+K$ss08Rw^a+v`-}|IV<#?ISNq zKf8A~dEJkxYK-&i+WzkS0c^^2h>aTBiU3b0Sj0#{GG&Mk{5ZjRTTbyyR5#e!@nfF6 zZ4LzU97;ogS{w{h^`HXtZzXY7HWs=n&(~Y^W|{wYuDqqxq>{pkBW7gXqbZ$*4vTR#XR3Y`w3&<|6olHyr1 zm`rsahk-lkDqdZsw=@Q($$^hFEo)1`zU@Y7Q$1Lun~W-GP_CDaxdkVpLM_^=)@0LP zTLx&X-FO}kWBowNfB{xR(c1HsrP%4+6;ewzl-R#0aWZcxb5QOMVNz%8BJ-2wji*18l~@WISf} zq8+szrF(k%G`l}P!OlcwbP_>{CcKJq4_i_)L?a7B2Kej0wU(n;K3~s`i&Q)N!$Crd zoCk@}QS2Q7LqxNv@YhS&wKY)x_Ph9HzY65H79yDaiJ=w;dX04|OQ;GwZelc)-h5U%A`W}#-A=_MlU zV_$Nd5aaJLi=hXYYkK86Kf_p4m06N>N{vw*xNh1A z@J$D+vw*K}hAZT!<{+ez`h;vaijVXOiwR_x)1NA5TcRNmg9%Z|a}j%>P{xMQm^LVw zy?&IavID%%j~HfVk)Sm)vVz_03vtpZXpME4sCNXi^mN7flR?2f240hsYR!*#qPVa4 zT!)}YI5W4v;UvouYq2=$!hr&wEHX#o1oD84$kOs@T0ek9E8=R9RN~aSX@bnRZWN|9 zKkURugS36*>+AX~yD_~pH{V*j`xxxrKWchxZ9^y7sK_HHtdJE38N$m(PuEgL=8%^> zh%*TjhA!mhWI$37X2J*sl89 zai$b2-+w&l1)U&8S_>7m!u7d0bQ&Utr18l1Eyy!FQt4B@(FO}A8p1ijK89GVTSzC~ zQZK6o8$zYg>pKU^GiJE`0a=26ocLVx-z|0SmShDSB$CPCheQb^yJ~G^rI%*lkwL=AgTU}q zFG$lKdDxPAB{BpS5bwAl2`BYKIiMv`!IoU}Yvt7gy61r}&E_pbh9qlgD--%EUKw2-EUR)+eV8Qbz=YwN-h*BYs9ER0eHsg>T zi!@SIYJr5v*iRDi7?4jWwK%K`fg(fv7c%~c}e>}VzqyDMAFpDs?1E+)FxEM z3(0~vUWm-wNPyW`YD5=ZC__RAVwTaPYf<-zJ&^1xOAbvcb0j$BHSL4EaDULH*rR>% z1C2epT+5-oRT)UBBXEl>w62%K~{3_H-kk>Ag zl$M*mUF|sb;-Rv^pZH-qO&O2~Tm_+eUyo^LhF z!c-JS;Za>^=O~Do8)=ahGcF#ZQr21jGGjX7U@-$&FCz$9Xad!XBJ#h{=J z+DsB_xOs(%;;g{z@}srK-Uqm1QoRy{h)?^SfOZD9J!FSRAqiQ*o!DoDXl+#{qZkT4 zLk-IuB7B4x84DUGJjQDrJu37(R=V27hm}!BDrP#QZ6}8%9%WV}#GkuA_XL(G*<5_Y zO&xe~fUR4M4BG_z=IDgvHzcV)fFZPjQzfvCWSs$rmlR8;IX<3{_B5VI3jxcK%Ix#j&Z1LNSWvw1<=H7jT_$S=8#80OYSfvY* zf)Ly56t(5NlDggsmR)9DN#_{NKpgivE5IpYh zxD{!M9f1NVikuIKPrJ0)tazQn&oCylR27MCIL49GjKHX*PUr!8ahZgL6rK1866LAG zDBkzznnGI4SPo9WsoreSl8$;^Kh_~3mS2#iEEmQhAbt>GAY37mgx_eZa-&ZF=ZL#e zp3tJ7=qxZbFG$WXJUa&$czl#Y@F;tR_tE*7!(CL=i!-+;ax`PF4;s@k81>4%yQ?RS z#OdP+4`))Z5EMwutZ3FrMd>wA=Z^q)Aa_f$77$sD0=jd1tk>n4% zZSrWd2*FcI5HM50@XpCR`Xsx~2q_?VCP9Qq%?V3Fn$gE5g=uUHoTx*oQYP*wgsy+! z_o4wEY&uRJ7g06??F=-{zIVQ*IH_J|`=8EtP`Y9O|BqC=B*$d4W{3j84hc5Kz|2ID z@MMT=;$Z*K<}^`yshd~^ty9DrXvDB3Ta?V*F}XS;I|;n%GSoR66W9sNn07{`rk%YS zWzKOQiSk1jRoFbjC4hW!r6eqkZPG z;%!^=Sn_`)&1aM<1<&o;IG!wm-6;1Er80?EOebhYnNq*&=t#)`661uDISqxPxG3Kc z0w#<3$Z9w>wP3qYJ|&wZv3E#W@McSgj?M9nCvq>PT%?0j zrNXYvhQ7aA1DMgfM|28C?;g2eSb<N!UZJw6`ghF> z=66X(L;ORCVEeh|Ym{NBUA!CqLD2D4O+7LtpDAaKB8Y+>UPc(kmTFOFNqr)?VawXH3I{vQ2a2 z;jyD8g5vlH*_wK^&Ql~fF$tU>>#8ioL%1$R=4TWx^0Y!x6udGH&SqFE`kWIM7=O`L z@IEIL%5Gd#m9Ec;j^_k(E%IW7PTa)HbAsa3N)=LWC4iHbwgOhE~*2d8+67< zqH}h0Z5Z^r6jGx&E$ zQ|h3Bs^kv@(9t9$2U>Nm_nU6|>Dq96MJ_I31-Lfc?ggEdfpNW6MK{xDfzB)h7^Z`_ z)a}jpMR}GShtK=@H$s){^sMcTqp;;u2vk`|A7sE8F ziG_>n7}@Y8`g&?vZ z$-R+1D7ZHm1&hupuO;}^@9tY?aj9UH*NzE zJj-O{Bsj#u++`0M)fjPSL>N&H!w<&GJ1cyg)+|7C!mJ2vfixR!Hb$IfhHrF(-Crck zN5mY@`mtogeILB+9K$y2^-hz*a~?Znu9JsGd)4fUX_T^ieB-!5>W@I*^-ziH2u0Y^ zh^)s9V-mgdGUZN`QN-&&9Y7)Ph!N zY`~3-eKV9K1WxJW*>X>onc|$pvVHxNTY;$XFNO7wQ+fdVL+wXY8#K$=R2{?bSgB#Ir94gB!SGuE-Hk27l zJA`c~Vs3G_85bA>^gn7R@?rOa=)3hFO)_4dpkhQ6ctY8Ez*3Sd9kUi!cxx!X7NugN zC(fu@gzjl=;nm|D1-)mAjiW+F8D)3N>e`7wRZ$2%28Lpb$eg!yk>MXNx<^9VZmzQq zhl}(=J`G*v;~~2u{REe#9ML_WFmZo6nWu}pQG(NOup4__CsBiTowOJ{sOQ8P#_BwDY)Ms>;fQCs zsIso5zEO|N5v3Yrq2bprq;LeO>B+od?E+Jpq z>#5kMgI#n0iwj8jU3XW<ZQLxd^vZjdCu8l+GzO*+ge2$jT0to$p2@+$DJt!=~aq zlI;UbCD$m#?TpQa1lmwSnbw?C#_?csCqqA1Gvg>4#+rdl{fwPqjCb9EThfY|V)BYI z19M}Jtj#m0YTfmy_r-NIO#h7#nD&x0)0m6>NRUTP=!{)bIu;~e49kXui}`Q+e&}#f z??^-Aa338d(5H+L3j{A4gDsyTW#~{StO~qRUKz!s;>5}m2jy4+HWgD8k&HAq-y$o_ zRJ+Ei8Jd_Ix>yri94@_4z=Auh>+&Tcwao(Qcwxt<)x$AWuBRev+6Gqj5Ur-w62Cw> zjY@p0`WVSI60%TzY2*ZaO?GoOBANPwrqEx}#-Ntow6Q8e+SzKP7buNghaQo>YmVv= z2ax59snxJ-yb~!=sGLcxefJ`sdJ^5PW%*QhIw{AJ zvLnw(Tf&z1_D`&o1wkX+mM)>31hyGjs(I;M_PZ`c^`u>aL;Zi zZ-x)eN3JKw+@PV^eAltfQ}t8c3?IDs;)`lyd3{5zZ!F#0P%pmt;?`!ki4CxzL%ZL+ z&BS|R@)O@I?7qBw=Z3s$qN{TLg9IEaKan0{gdyv=;IvpS-&WSp=&HgmG`&OvM@PIb z8Hz}gdpN`qhQ=k*b^|H&QB9q7F=Y61^b;E1NW`3Yk_ru&QD-#AoN8t1biL<$A%xR# zAOxoKFjDIV=s71@9ht=kL?y2~->Vi=9G-5;SsLqp6sn^FNh(`ZQY2ABHQ_uSfq&q~ zM|7t-WH?6y9be21eNC&~NDE@R&+ImA;iN@|}Q6Zl5dBIP%C07vZ+AQeZ$)Q>r0Cx#dKGo;99cqaXs_v&|o zxPOSeHeEPhs=Jv{5ekqjW+r#?tz0SDbsj^kf&F5l|J4nZ19=X_!0Tpf<~{f3R70`q3w18i|YS z*piV(9NE1YJ2Z1KY2o0UD#Q8}Lo?z&E^A9)hXY$6nGZVRqtiIDdnh`}pl7H^U`;wv zRvnbc1~5^Yu^%0HnGs6?HC?MZ%Vyj7**<**3MsWCwHvkmo|Djk_=*>`a~P`et_y&&$A(=bgDDJ(0O*>q%8h^aFV$ni2!%iU7H(2dr@eTn{?4mj4`|a6ar2C!K}82Mh$6R6}jeQ6X>8V zuj<^mRXd+%V70Cg!71iK8L_$+85I}_{B9=vq4&=gW#CAC%XNuUx}4WllsPFLStdAW zhHc}J<|hsm>)kmi&(u}5OyC}MA_@T$fgGsxcU_f@se{Uxog@yO1bEyTr*&0Y}|o7Zt)8>Y?t zY{J2#_&Tx0lXlbG<7Sf0Jbq&>n+-u~%nhVkzfVf%xM96tTfWYDH`&E8j)xt!oE1$n zyc9(+A%BX-Nw0`Igafc5#!M#AF{|2bluZ|Uv>8%5$?@KDhD}b%JtQno>r53$Qpi@K zBeAW+fGk!?r*cW!Uys3WLQ?CCY;H0a*%3>UBmK08@7BcO6fkgtXtF&okwR*EBOhfc z@@C(d7p6571dJi!tsVJDVQy)`5D)3ROzcrjME~L#O2h} z+OwtiN@=N7+Ao!!{e>U;m7rHDJ^K&;)~^IlmEL>yyFOeh^-HDarao6HJ^M@hPnC8{ zr3)XSn?QC-r3=5oh2xh>>gA{Pxbmm8aQIe+e0|SDL4QBK=MQ>b2i~B5w48Ljf&WSr z-}85uq1yF>`*CkM>_lBOequ+#fQYK%iV!}5XyWksb6XG05iw*cIJW~)b=>^%EE{Uf z!`V_bc=uF75B$KKz_J*LXzxlbDgK3W2vE11Yow9z%R{V=^nvP2UOa`0)05gJJ_njj zL32i<32;~mCQINBoSwFtnF-4%MF*-NM3=|#fvV&ue3W9;1Vt=~z8u6H2hFg;C>VNM zY{8YmZ9hDADySmfWN|lRNY+plg3AdjS$gomokM`foZwT_3U$8Q0uIKXtnAWPzMxm@ z*Xq@}v|4-R5a$(d2T7W^>-Eq4*ngz;+FtxsTCaat*6WAe_4={w`&hWSpRjZveSGpl zoOEz0$?IM>qI(jIH7H_3Wg%hGrAmTaskx~13GQhX^eMLrB1+Ogc7oVH#EtDzqo>xx zcmTy1MXK7FQmsZ~q1I}&<{1<9G6|ed(!ce1c$I6L;ytN$pBPN1wGwVG)SH*lWG_tz z$>PjR93^!PVWt~(W_Av1z=M&f_YsR@F9Krt?rSE6XY>+TEtI$h7|O)n!#N2X?%@Zx zWPl$ao`eFj@sWB*+>zY7@KYfH)aO61sE z4tD*=^WTeJ=#xpajdEA)kfS4_9_J_WW>}(XildN4slr z5ORq^-S&>3u6oJ7RK*qO^5c948z$xLsIyN$JA;2E2)j4@UeI?#D7;Ll5*b8$qRNeC zYj*D9{KBQn^~IT`Yu9fqzjE`|%B!#4UcGa7?Nj&GH}1dw#+#r1jJMtC`a8RO!CU*i zei#it7bofP;PCO$+co3=;>gMh#R%FB)E(8^c1^YA;9&Rk(xZVN?SN7Fsviff2a3gb zwbCSaxjdyRD|8bdU%(L#`=bY%be-oFA_YEVVA^nsL!Fy`dM)io~mMO1nTE+OPS2Qi$OvAK4;MJ_Vr=A_IX@Z0o-5H^%u6eIuoMO6|CUj9}4@_0b zF4PRkV2QwT@jD_Ow#;!)ou6kQN~d&#=!$w-U3AHePsWzOjo?U|pqW9SM_hERToF@t zb*Li2Hq8WH!5ei2WrIkYlp?qv_jDwPy5sbrYPGVWUR0HpDZG@Jki-bnj~kFXj3+RJ zF&Gmc8d}O;pN{*YfRESNBeqjP2!YY{6S~bUc-vQY4!V$ILF&hC7~d>ny2*ix21syC z@V!3hc|m9-4Mo|hb1Z5Od#rgeT?9JH6UqUG)NE)C)EU$$m zBi2C852zrvy73XYc09+OfhuO9)zsa6y+dOx0N_B->(h8!-ZY~6tm1YG;gFj(PxMDju@NDe)q<(Km{V%^u zy6l0f%*#&5W)TtRYHB9U5&`(vxo~(XYO_o z`i>qkVnSB5F+H`E<^T@f!evVwKzgSPPIp8lAZufgnj+k!<2dh*TDl{7e?U+$;02nb zF@bdTDQt@NTS*i`I)}d6s$DI2f^J-%a^!P1=S++1BEc_0L3fJ*WCokppXib1TPtJf z+%7cH4I}xLBK_E4Hx$+qL?iG8RsQUQM(y&}JI(3YcQ-fdr}?8gqg%D!o%-g@$_TB^ z1m8hMWPX;3_e6WF+_Uz&h_AoDO{%eHE32=rZ1A>5MyLQqZx;>vIhG+00G{mpt>R|GRMLkPz5U*Ov zXzbe63cpmls^6MY6{}EX#BRrI4r=OXKJ;pDFV#M?_2MEpjQ?2N+^lcCWO;7(N$^~= zbtyY2=EpB3)6I`E&&?w$-QZEn_?dm6RN zwXK&R*o3v-X-&UdEV9mxytQ0Ui@dBlT1D?;=x)x9o?4pW5f#4-zle4GOfZ_pilt+iQy z3JGO+SKA$0g=iS0L{Q3cM8h5LijnVws0DPbV4A8 z$RIQ2V~ruS^@UHOsXneoyz7ue=tz{HLoq@X`ko<+qYXtWk6Py0WqUCHYp`&~cKQJ? z-Q&fgq4)>6bmzZ>OLr`p&$@U|NjH~I5vch!T)d|x^EnppSgL+$mM@z^h?-4{RTPBn ztmW(k?oSeUx4f|1gTULSO&E!C+CH2`p*?%iHq%fVd_h`UUdZ%&QqN0Wiou^ro2Ah> zA)D7`Kx{;tjc!Bh&v-qD6AP!YRvhDoKVo{cjdsqc$!O@RNxN_m_oQWmFL}|Pqwh%+ zmRMXG0hhB78jIo>QVWwT$Fb#((FYLDri==+1x){)}~GGYUwT9 z)8|L+JfjoBKBg1Gyu;m^A0D`wVBhuWPrikW%|9X;U?n#L%w}wUR9;2K*E7$|$PIaZ zjKf2TU2|wPh@;0x=tk}PNA0CIZa%!dvc9o=hi}nqmlrQyY+Srpo;V|rd&{eLHgtLaQL}OO;k>%K^rq>()i`ti>rM0aIbLq* zmz%TmXYYDezdd*Maj(zmms<;G?)jD(^xQ&gZgyc|?oy4uU7DXg2Q)*4g~r0A#--Uy z%^H2X+`4#nn%**0Y0ft;Hx@2kyx6Mo*UNL4&rYaYhEPppNwqG|U7D-O*M+&}nQ6CT zX!r8AYCLW<8qG$dIoE17Ti)DU>*~v~YPPP@vrCOutI=p&nxCEDS(sb6xX_ySTiw~Y zOO4j0Mx!x@wsTj{j)9@~%dwhmU41z{2Mj~ym*cK#&I6U!d2lh*eL21Qa@&y3EU%vNn?fx|lD7v_^d%f43xjFxMyEp&VLbB$q&3)$1r|&KMpT67w z+-Kf;ZM*-{L1X*!Tld>%o`CDOSC;Q=INd#%p<=^3UdNv~3}SzG=*3-%M>q#S4|i)u ztZ%6SU4v5F_m8%4prEw(l-4p|?`j#NAdoBT>YIQ3nNPljlL9~SyIAjhx9FXxiuKM{ zqU#JWzf`*LFCEK}SX`nRU;r)B!tj)U&5PpRkt322$l^mE2k39H`dhL+^qU$2Z8ERKlI-Yyf<#DPu*X+uKGbg=)^}; z$70mQ;!IKDA&ojwuRZJzn4h9JZKDUl2|R{|X{0gt4el-ciWibLS*9uoYnFsQlCTYH z%;!gPYhz<=U0u!(<_&*a-CsEq>S@P8odBKe?rt`0#bIPyYAXb@Gek| z?DaL#NMGVWRp_Uryc%M76PZ34rEa(I{n?Cqb^)_m&k(&sbC?0n1DeTeT^W}Z?nQ89B7hp- ztu7tZk-Ju&pcsbivfT93woBYOdJ_eOhN)* z#xC=g+Kr>(pnVtzsXw|h)xb-V!zk{y2Y#~e#TPHSU5GJ=Z8%z|hyHeL7?5B>xm!8$ zsSVJaEyVVySbyX|z~H;oxOBP52D0`cjI68;=m?itZ3qI;B^TGwOM?Sny%EIzO}NOl z#E%dBSg~t7O(I$3U_C2ZtyhhuP9j76X)CtI$kT;Zy}5vOn6>G%ujkaph_*oy=TTil zo_qZx+N1nBM>n<(cWc8?3R$>{@jELwNFn|OH&FaDPnGCs|3_Epv{sBW!e0R=&xHbg z_@f`D<5uKb-q+wU_r~gn`9N*V?!yTUp@^u`jNXHGeQ2FrCAg%+F!XzrfJ*7hip3bz zAuh2$4#H{_25A)6mfpD8dgJC+CRL2mO$6X+usIP{W}QZZ%#f^n(iUUua94(eW4UU9 zD#SW^9U@|;@AtR;xM+kHAoG+4=O7`w`z~eeTX{5`_h?t3;AL_vAk@Ghet1wO{4*QT zp_dlD84Xj%D=Qm}Ed*_p6m+X?lrk1oi^WO0L}9I0EIx))tpcd4_)vO`P*L8EL7FoSMFizSeOi zrZ>kD^G95XIa&sgQBIig?g(|yFtFBL{7_BCJf*kNqQrQRhf-Om{s(YA>NOB?c!th)EDDZSNzx1 zm5HJc>89Ub)w;=zcGh0Mu^xn;OXL6ZD%1FXJf6mga?0D#M&DSSRPFcinV2Nkllik& zQ>(##9Bl;qrksST>X6i;I%rKzOe&P6s5Rzmvkld1EjBJKHd_<#6N!mQ)j-u2T-Z2> zeX41Z1gW2JJxZ>>WG-jd1WR?roeulS+;sM=u7;QPnI&q4Z7U}gUOuZoVVl+634tHN1ZjetE$1LocW9x(hm)8@JT=hA#AmLpU&alU%$3U zp>@ups2W%^s@a&kba7#x&ReV1YlOj6c?yT|+`KGV{_r2v*F_%Ln zB8S&;9&Q+R)%Gx@xux;@-MV@uiu;HMC(3`E&V4iEuUTUa0Ri=iqfa1OO}=m3hX+9) zq5r;rq#n$qe%!|;S~Ee2A9RM;Y-3BergQgr)>h3{lS-TLUzaXB6vc;U3^fqEwAPJb zq=U2+d#fc!N$U68KvwmI9AwNSHiW(MG~td z8pa*w+?){*^mCp8R#%Z@P}~mp#1CTs;KrSGRi=0C%*Nk$UNly01 zgi-LKoY-D~aB+YX*q+9QH~y3Z#(DKbm&#>RLV>1Kv%7Qc6^RHj|AWCyH%uruo?{wG zyR6?SXKi(U4R%1ajD;2ZU>CagZc&+cNFy0tcp^hN1LS}yU)xHY*(RXOV{Yfx3gLDI znD9#mnR7dsLQ|sWX@Iy}qvUKhFV`0?)?1BBIat65MsW_2t}i_@8Xw*ZX-AgoX^SG6YMM$`mRR)9t);j&G0Wui&@!H zo+=Rdq-D+uGaf#Sy>Qo09*QYckCRRz__N69W3l+c=*R}cUazQAqSdKAbP%>cO&{Z+ zV@a@~ri-bM;{{85YgKHqanh>e5IW-MD83zgNg5A3=`i-4R197*#zxV>D`688BBHP> zFWP2fX0~CoL=caa)qELw7-ks*rj9UWTH$rW?fD)FgpPAl)C+Jji54RX6&d;Bb_MYQ z$7r^0gVeTbRW%N3*N2hJ*DiA^7g^=FiiB?7^m@IcDVJ(iR2b4p1e(_uwrvl*)JH~G zyF7RO*3q54?Yqh4*6hsk(VZK+!__xmeY3l}ce}MRd;jR|H`fm1r8}?h-b&hKvatM+ zaxdD=xrJuKxQ?78WoYMM!SOZVVxHy^G8t;^v!&C)C`$Ic*zfXS?%g?fWnrV)*}a$^ zz1jKnr@|YT?_9jLJec=ejf2-;{nW-ma#Pmt#Nb# zd(739Ri17th`uh%P3R~{?dB;r86icv-58jg3UN9p?>o7>0JGC)cNe{Uru6PY0B6bX zE&y<&y#Ci0Pq7%>@Dc; zeP#ACyi97ZyOTb8!lM9zGn@%1dfU+d#6tlEZ-1#X+lx>z41U_w_97sLtLZv?GZiia3&6S9BxogQ75@IZ>NX5sk$( z^5WP#QWb{8^)KWwI@c$ax1E3`hiwg1-D0|uz-8rVXtqryQSYLHvEy!@imi2!N3sG< zR|vFCsylZ#mKSkX0IJa=DjFno*DEN(-s~`@lKP~#^L}>r2op8o?Q9q~$Z0S(W#+6C zRJ_hy&RSyKAW3nXdb?Q=Y(qo?6F6Tt#nPtr74V47b6SWAcbr`SaNtP@Rgp-y+>E8; z-UTrw36iD#0c}=QOD?T<3TX14CFw={odyT&>7ETuqA?)qx*xIZ*wu>p~?M8Z5ld2d1J6x8$N= zK~G|Xyx0nXC~Wz%TZpJ^KM12(VcCbnzCn}vYbXueK`a^W>;#Y97$rk^xvg5Vy~3UA zhVJ$yhr@*~2pG64pNajR?3jP=5~%%78pTHiFJpS?&S<_$1B^_}%xePWvmY=dXP1ovSJWYf zlG{h6_y`R(_WgCgKpU;?xa*Bj z7YsH-TXVD%`kHKnBK`yBO%*sTRjrHjuU)HC{FmmO0$A(5zi6&?I34Rw1|co^4${PH zAPtigJWGIO1+6&E9PGInIL3^V>VgO1?xLz-Db=oWF)Gc#%%k}vTWV^~Ntwnp7)t$) zedgj?2bsu-6yzpc;`X>TKzTj7)q(AmM2_>j2srW@rw@?tkzH3d6h|a>tSZa4wWxhnc7}n? zSa&aQ>=?G(gMOmwGND&PSgQlU%d39>xV6h>2I)c4Ec%s~Rdaq}VWHKWch->1ZDs{A z0C)AE$7~vb&LXI*=KSilmsIRC*}8Ozm54f*D9BtsC0Q`qQ$)j&e3C>xzt$c02le!E znoke|99q0@cmdhgD6DL(ROA{ER&@5n?7Cf~T{kmlPV~?Zk>4VDyAEe?7dpMyPPgJ0 zx}>A1k)*WnOD`?kvB#p>R3wy3TX5ZR|I?rbi_Mzk`cN^btUn?#0n?caQEX zGwHd4Fr{?ldsUqGq&$UGgq}|;*l7vz2M4j(y}4AU{~Pbuj4o!`*tH|E-yB?q+o6AY zG;nT(!>^ad_*ZAegG<#xb`4X1EIA6cJH~dDaSJWow(RijH>KA1qC>=(W$GA@WGo`? z`Kz%QSorpvvY8=y;JKZhVMqrLMAn-$a|>#0PM#!wEz}hqHj}IBCMI3^#0SnM-3(~E zdvW|jO4{Wn$Ul7UrXTvTmqzg$zey(|evMB1Jlt)0hr5dN_uX8?mD!tgbnMHvZc?WF zUy_`}Zz?`I_Iw5q(h~p1H|o^PB{-60w(Cv4=IhuLh(c990G~GP11iJA&`jeuqIjQE zPAF5;Hs%M==%xu>ngsWTuO-k(T%$9`s#@kRwSp$EMOt0nsq)S zR1YFMZn7(LI5Xjimq3fCL&xczLC{NdLxt(Zv5V=1Z>{Se%rI|RKh8LC#1fh>4I3F; zEamAZkUmc_${@DwmJWAu5r7}6^5WcFV{XolaOrRtB9+;~t^R`qWI5_qo|q&p{iKdh zf+3u%3rCLEtZyvc+bB3-lYI@?!1m=Mm>yXxS0>zDR?6lu8>|&xK05x#e-h8@z#spF zHGdFY>85PFvFLkj+ z3(C^3N|gALpma`JX6DTj(bQa6G?S7g3KPtX%y~-Wvbh2oQe`KYB%TFvED4_+LLk&B z@EDm%;V+%QKnL}3oPY5wDGZi~Pz8`5tvLvT<5R89kRwrLD!R~%net0C4p957=x4CM zGLel(O-%CYpCqRfGs$PeZBUEXBde>4$=ut=$_}v9Xk+teH=eZ=pG>m4y=ft#uW})sh>Q8YQbw`80S14n!yp8M6<#k$vO8DS_>1m(4e4h+{7)sQIME0aSIR%Qw%0<0cc^e zz{Cprc|tYTCRTu(*;Afa0Y;hK;fWPsV)uC`RxZjLm#$7sJXrJncx!^M7-oiv&QM`& zVu>@9R@T5X+COegEK9Bv_{NxJ6Kf`E={23RL_`Afa9$N-aHhPf5rKEC$YT4O63bAi z|3FJB6EV^jdPZtdrazh&ORQ4^#kxi{hvLPIvJF*|La>7!fiz_iP*iCnj>d_%Wv6`4 zzJHXk9JZqyO%gw0Y^MTCO4nOAUQap^+yEpW%wH(RM>B!^EYcgP1B_;c0z@+`bak??*byMqNt~&v=(IGia zU(~?}H`ydW0wd=@z>|ezG5n>q9m3<1qhH+LSxIQ!!@69ZO0KLFGaqYdRL8tnK+iIf+(`)Tz|9bOcY42BLIw+i4XvB znT_#WWvS>=0Ju*(P7k$o+*7j^ zL=SYRW|~DZL=w`7>m=$Kl-^Rx{%UGy(a$;vLw)PwEA&K+OMXIj8U1Yg)tgR7elk6} z?L-UTEEbYCv==sR`kjHxpv`!>PtrU*Op=#+bo7TAlo^m7ix~%-+j<3%fuGkt*NrGB&QZr!|vE$#?+vN1~J;{1jQy{+~YdRr~+4<6DEoV%s= z#ec$EYCrw#E!tB1G1*cZQ!V;A0|eV?Klq%Zk%}qBJCRT73{$H)Ki5 z?cUutKdsi6H}0<;yCE+dq*VM)MWH&1hB2xw_;Kj<5Kaj?zN(tyq_eYATup;EJ?u7W z(A-i3BI|zr56(u{KHBZWJ4dqk(kSfHr{CWUi<6KIU zx}cQpc2TJ_re~e0+oDd9IQapNmGUypSnan~U9J0R@d+%hlrjvRx$z&vZUQ5Tr`+Bw zFVab?O$Vsciap}?X5lp%W8Qi7dV8~cdECR*6?EEcjD34`=}o-f8i&$)6OYF}Z2|i1 z{Fv@<;pKwR-MHp}RuIq|*L)V87hT#g?u9v`MZv8MV_vYwf!dpAI+N(YtGW|x&-vRJt$UpL2enKo9c!PkyLWdb70J zdZtwRQ>D^JOYfB?O78*UbERkT&qS&8+}C_rsr1}4Ux`1zx>S1hi(mZW7oRG<_Z)C| z?(3e$&rg*~7oJJ|Ug)P^QF>2*eKimFsj+YY?(470?$2DDm{zo!8Cp7xl-x5r-&6NN~H_4>$jF#7v~p`_U7+zzjpc6l{=ri zaruo~-sM+rH(tHGaP#(!{?6@Iy7%~=e|f(#yE<53YJV67f8)dh5;fE~Y&4tAr%NTd zI_{}bsR>*fUk6-13T)%VE0s!5KLvE3daCr_-TQ$``Ij~pFVs`-H~;w0{{27x_&3)+ z);auNf7c)S`QQ4NzV(|va^X9^^N)S@cmDft|A~M8KWzTctDpL(U-g9-p1<~ofA!0M z?!r5}Km2>9Kl8#L-MI22zvH`io_Xs3`pdukmH&L^TfXPJ?%)0s4}avl3`Ja6I-}brk*e*dTb!?*mz@cs|}oyw2@tHovS_ucubfBkje{@eej-}qhs z;!k`>^UZhv_zx9L%ty?`e8`=c`QB1zetmuC;^!_s-g)WfOV_sV9L~J-(v5KS&FgEg zA09mJEG_Nq9IbiY98JvEQeD!A+ur^sjmy|W54iKGppbD zkq`geH~b&_-}o(m@3;NTzx#JT{~ga=d;1r^=1*VyWB>BM{`a5#2lsyDTYu-v3(fbw z`^)xzcm4Z+`LFc9@A}g}`-^}24<7#XFa4u%;z$2V_GtIpK2-e59e?|R@65mD;qk-&dF=N0-%|O{M?Q7eBR~Gj zulxT0d0}lXb=7bDdSc>@M;~~}JO6b1zdh$`-Ft4>EzrzYDeV+z$+zGjA_Tv`wP|i> z2O%%xI0vUy4q|#4Sb$mTr`fn;?M8@*X8fu z^}P?a4t@RfNUGfZxt}=wr8^T{fA@di@u^F%{H^5Zul3yis`;hkpI|4id2{ctX1{av zMLmD8_-CIOd-*$0{9Mlu&OS2!55M=sw?F;+FYeu!d}a16SN-+Zw|((tue#=3@y(kT zN{@W$XaD5J7cC9E;R);Tr{Dki@?~Fb+*N(uTYu|~16%$+^}>hrHz&XNcdwq=_DkRV z#jO)>dhm(c4xYUBU$V7-UcUU2_x@ma`3Ju_`QG`>R~C<5^R@0x2P!%Dmad~e@yAyV zedyX8K6HEKBftK?$_F0%spi>NxIg*LuRQRNAN=>fdEn(=TUZ?{-}=)Z zm_1bc)Xj%~dr!~8(A(br&WjpvUHrQ{^4F(+`@!X--}+>0Y1b!y>Fw`)>)~H`$p?S+ z+t2;n`^LY%`a2KY_pW@_I>>(K`M>ZV&oVyrJH2by{L9hZpZ|Y8^YwrH?4GYp-1Mav zKl$0`-TwT|A6oh3NB{AvPk(CLEhE=IGSmN^>soLB2j`*OeW|4vZ@O;gD_ft`-}Bh; z6UFS|AM79h@E^SRh0lNa(vSV~&+onIuEVFk_qtc64?ppa*Z##{f9#U)r?&k4=l49Z z{I`eixqr3mUC-J*F#5VjAE^D=P51v`@{f=H^@FS5w?1&o|GxW)!K=Ug^Kbas)=S?^ zfBCxk$EI?3OeX)bdDHb%-#+y5*L~BO-e*O18^3xyuJ`UoG;HQA3$Hl%PhWZ-`&9M#zx>L|^Y+pwZhyfCzWv2N*q0ms(sv#__^;plz2>X` z?ZH>R;JfKJTv~ejZ2UGdVrf0_LJH!Cmw$~Ue$ z`-@lo+;4vBi_?dfhhKBq=U(y9i~rAGJ^J~(w&(Nu;_trng_E1!_0DhnD`SL^7hN{= z8{?0D?AbTuQ$4rsJN}i%zyHin=B zqc46tjG^*-e^E`}Kj^;ke|_ZFH$C?=^!-P+-Do}jXMeHnZBN{{`uO`F_>+J5y-m07 zeBhIr(u3E$YTN6-dF$#!JHGq2=k0mPZ-3%_n@>+3`tFIT;REmPJ$}W%9Wp+dTX^k9 zzFa%J=O174#HT)gsP%z&J^Fm}^2)JKTz{MK`LW^GKd)!^10Q?OU3Wh3Ip4bOBm3_> z^o7HZegHV?)q1(STm_rYX9?y*^6xAOV%s;v?O#OZh9DU*Yz{1I7qFPCtE*@+AWyyb zh+Tv4_h5x{F9U}2+{-rM&Tl!+3p_vwxt5%JOBd-Q4ZWObIMsuzhFP-C-lCyv!5x!h z+Q@`uP$835Y zVJ{^rbpbSYLq$T=3eX`|I3X%%kqTWnDzy@f3@U9y%TSZK*cG6SGBt1{EioIUgZ+e% z17aqJ2_ZL&-&uSnUN?628W$8;+!DtWRCh#8dGie+rqX~J@tZc}2}39$n3eS*vl?IZwy zy$Cy-2mv{+3sz?vAzj-E>Dt4AxC>?Q-Gpqb?IL6^A=@`!3>5HE__z!{U~6F?w&S9= znr5>h``jz~gzvqG*#vYHuv3VBXIe)2w8LB|4?9*7<>9+R=7tPH9_}W+yymvv>#yG@ ze>YD5*1|zV-Y20nyz6CLwL+Hp-*?sI=(->3BKx*s$in%h$x8`IZTU{?zUzxW3FvQI z4adMK1uc7}CdO!4+!)|(bwgKkbf7? zuONiHU(^M<@j>zXDniHuq8&ibH8J;V;Di;wuO)=M9H=WHC_vdxjA52~h?}{`^fkYCe|`@|bvjAK(-K7a`<%3QmE7`i){efdPKK zC=Z%2OY~JDge(Jc6GF;_kk^VbKwSS^%oiBx`$WIXgplK6-KvC;-xuSo0j&_f4R|hK z{u7_aGuK+omwPf;Myt+Ss6cvTR$X3y+2e2M6J#yL2+2n=Te|OZh@(s<@EL&SdBFz{PA8 zm2Xl;v8iCU7}VN4m6=$YQHwoCDRz-wX&B}m?5y5s))@pNf;tyZ-I<=BStzEb=Cbp} z%v?GROi%NSXmozuC6`9fxX zs<@O3jP_Qg_O_5`SDM(aS5}w_QJA5DU0wsQJhA<&Yjk-m zsEtBPy^Bi|v)Re;8o}8w;Ws?`UO#oPx>SdhD%-N=vSAwT8g#K|VqxlJF`vCBQ=HAt zWpRDC$?Y0fYQ+z81lribp&R=1ss_MT%+SXgsCAK;oUj-WaNsp?F(T|m$Y%|ZxuM4i z*|#HEwkj;yU=>6EGyyS~Jh|^;Z}IC^HG%J*Ey`b+T+!`h-6$pH#-FJt0Z_ckmjx=z zQrBUx*9z`#@djl5`EAn{xCVermK(&|1^#y1`*tArC?HvQ%9;^XWhr$|Y6 zYBW&>CxND$0LzM2vjRa1Zob)jBGM+(#2S^WcBY<4jq)`#oWwAc=HB}WX1ewohKwF? zYF>G|zgMmaP~ubnmEDqkmwAx|JmAn$qX@%tLJ2}Zdg%h*4Y=%mgmmpEqzia6;K@KL z+Dyo{9B|vf(jV|HnJy<}+Yip2J9mZP!?zPcFWLg8Dd1Nvh(+D1_Ewlvwl$iT&t|PE zWSY_;M9m7T(-L?|fUicgX1NwUWwp>gZk3g&-fY%obdV5u(^8{Y0E`@Tv!c;wivVKY_KLqFuoZgV5rm04>%y5R!~0c2f%Az_>XGZI z3608QL{#$L0V!9uF8o>^73dQ#PZKP(V%1v>zJl;C5!ilz)!I=iY{RKFijHwV)9C)8 zkb9%VmRVO;+ipRMHNtjQR02x-of>wK&DWx*&au4TqrvXQYh{ zYI@Va*Kv!9r?lol3zmDP*O*fO)Qj!_r2$??@+`2M4Z{=z3>5KqvuzP%vqTdrLni0z z9o*dJGRk3!f^l48yDE~`4Dza-GNp2&z+h^0KX9B96;)=;>)bmnfTP9A>w_|$yqa=C zW1aRu>a~yMI)+lO+PYvmf}_?}SqWQ@16004KKJ2GT;jo)Tr$qyc<)2Yj~!4N;-fSw zxvce;0W>qud788zj;x*PnQ}95MmsmPlYhEV!72I2O(`L-Y+9VjBLBa=q?gu8tx9DL z6rnNYmcr$HR;`O%FIcaw!u8JMw6<_xAYiS!h-98Jf_b{*h8X6li_FAwIycrG&OD*5 zArHZw^aiakH~vgIJ<0Qtle|0bB>&egev*5gddE1$g+n{Vl3p^HvyUES^`=A1I`|)g zkqeyc<(P;tL)8xKJspbIz1%@mI=aE*f3XP+|2P2fq6QYR039%$RAM4JiRQGn3M zXXVkMO~B!8M5RFVgSujTUgga=+$+Uf9E$g@$89uk6K^g}g3D?WBdHnHq+PCCrr*jI z{&$cRzVl{&!35u+wdaDSkR!Xu(O6P=voFL{b(^`hDzoe=!y^nfOCg5dVs=Uk3lUn_ z6Gsd4-I5k^_>iRV1S=&#A5%eYwAG}JRd0cTH&Q4n!f+#{wxnL$GX{dG8&nLNXDeu?dLAe}q&a?-2|X zwk;{fBrGl+;_8p4waV<}R=uAx2nW@)4TmK#n0G(5n#>d#bp;aJko^HTA3wbtJz6L% z=IN?!xDaE}FzJjn5TYd5J!c?6nXV8Hljqno%xZu*@O44h76MCT&Ehc+D=F`)U{g-@v{71uuneVA+jFa(h0J5Zzy~m6ezYgsW+R+vR*ezHcPCsl83Qd;gN2y z1<&SR7Glb)>F(Slcd`ue~92-Ug!smfw3XOuVE%eAtSECPzQ61QqEE#4J)V|`0u}tr!`)FdB zhG(7hhF2-k`)&tMZprje%E~p1?!PxMjr+XRP5_ZgZ4n-y|0`qO31`+*jNAI^00%>e z%piu0NnK9qCM(~6x}{=hvY|UJ!wV#!34uW-taT^h?M%4_kpT5lS<_49POE@d<>=_4 z&Wgt`kFq2l@lx#HyTW>AsbmX@Ql!(aoPeWgi1E? zoEUG*)b)_-6dAHB1p?%z$;BnSk?$$jY~as)UDwdq9F~~M=8r*7Py$#yuf#}d2=Cr| z<}!1#fX_B|5y{N;Vv-r;6}&TMJ0w@ZNBoqL9NSB;j@&bP;vd-PMXwPr_qh-Ba&e8* z$#Tt>1M2iB0b|tQ{neH@rtRk?(^j+P5i*JL+EL4ad`jqfYtw}Iv5=?74cl$$bvmb) zYlg`}=YbqD;-6zJyQ{j*6xy%wVC!5|J&QG(?iy|Cj!j1xf+bGzxhv;NM(kPNM)q;%@*mjRMp?4ZdvP{5C@Z z2_nvWyE|d9y2zMdp!OwOj-A9vmr1i~m}iqUw^3KsKpwOQ^u7|*-xR%4_dp$NBch;w zoxHy)8>YF2>>K=0n5fP8pi`JX5rO%waqQC_66T>Z3H5WjYa3@}ke6kv+yaIPLaQ`% z+o)qW6UP7(oqo#dtg4$_U+b`P%Vs{m6~YJXW56(z3J$<6YRi3tVbZC7@kTv*B# zC#Q>rg=3j{pzJ#fOY`%YS^fZ8-GQp7O#SeFp7YWGhB3Bdcv`-TW z(@H=@0%0Lxw@lTh;p+8@k$nn1^1D}lR>C9e6uR$@K=-yd=)OxrH;{2YcM0Q@b;tvT z>N%t<_?8x0rpfC4RHqf4H7t|5HQl8UMZ;l^j4dRiH9+chKfR993aeDvfCA(Sp$|Z~ zKdJzn`$XI?q5Omka2jRY{l@Tp42@PnLZbnY5lsqQ_1Vq>-@49)DR)r_USr?}l*42F1h$p;}BeLR6o_5Oyq{NEg$K zi_(x#eea{<#?t*haX9TjjY-H#Y%^X@2gUmTpj<@;-!Urp^AYR)byH3xbt0W&?#T$| zZjZyb zq!`Kr7^8{YnDV|-8Xrjf)I1g!TcuRN8Z7_^K>peu<@`t&O(du=+5l|=M;Lq)<`Y0^ zh##OygabWy;ejkne~_f@*xHUS-ARTNd2Fi)*bd}m&*Z}V)Ka!kOixYaGI?%uUAD|h z%LrUvZUT)aRJg&bFgv?&qBwapGkL5yy^t#|El#BinLq=;082XRHE?CHf$7XtCYLT` z7m%@#U4x@rq~_JZnz_tOHeZO=RgI#-duQKnS&?lF2xy*c_n9JO?l*tzYE3KMHZyOrq{{j<%Mnp|CU{-tYS9;$xni>TkzI{98 zpcfZ%+>ks5EcQa!zyVGf2Y_-w%}ZoWVJ;+(D%pT)Prm!!{d~a&EW5h@KB#_$tS;RF zcr$_d`X?P{I(;J<@E3xcoGY%3J*MVk<)wtrCGpY7-3xA}a$3o`bb3y)zH8=-7F;wamDrSvTNQb-&0eYc=(tiGIpVE3#- zEwgTzjHhf8wU`~EVahF8crpr6+t`dh-nvDOH9+&)Egk}Th!-3l930!ww$_IZjh;7r z3G`B-SKL0gV{CD!#WgwTy#Us*@H#4~cMx6JbXtiqj8;oV)`T2%L-%R53ExJ zd~O5*cg7*$AM}VzS-DaY1Kk$b$<`YdvJ-1EnzUiM7S%cW37!INMr`J^8pv+6nsrOB zI9}FI|CW0V5lTqJ?NN4oa4_YsgMzRSML*HdO&vT9O<;p!1XlDm&^Tzc5myCF1n_s= z9f(EZ@u6W2Kcs0$L|znvA%0;KFNFXvBuW-+>%mo{UI*Elx~uR`SUXc&Z`{%=dJ1ESe*ptya9TEV zAX!MF!5l}gGKcCmqo(CTwl;l@Rp`M|%cW)Aq&BOxsMYQ%xXa%-(d$<&QHLv;>Nk5yIyb8t{R z7-kF36Vq^Nuz0N|u2*W%q587RYz&F+)LJgsZ&1gmntDBQlMjR7@D#bjzn235{{GiDXEhE#rZ;RVSc7KHQ_lCL9KY7;_gZb42I=Fp>`AXA?~h}z#T#s z-j~ZP&ZZ|b#S_`W(c&!h>H9zxk7jcGOi3>;Dtwyk0iw6chWdm%%c5<$)_p-gCHcy- zU6fj&p(oUJ=(jpLe0Ut{Z_j0>viWdj|9tF0#W+}9`q~bE4nd}M zYo$~K&H^9>aLhZQ2=q;()QSAn*I}^?2VNynT)GzSsKXUk+VMjVAPZ z(=hSEth-VI{Y12;a&U;flHbbdE$0IFY0)OG_B-dhaCl%y>3TCh?DW_#MvnbmamW78 zi(_AmSswaxpq2*f^3#SeSu3~TsMXQvT;_qGrIu0W_gXH~8)4gW(PqdR^r>>&m7v|3 zzgE$WLXsJC#(EB@=$y(iN*-@A$N~3FZS&ICA4l;J8;TtL0?yChC6z+}HX5;x4MRtK z*H{V>!!uJC(`wVKcq_kEy?o4K3i74V{gSe6K=hG?YsPTVtk_e}Hf%H0!q$GA5$o6Z-qlO8%ice_|! zcU+_FFvv@w9w|G(prg(brd8q2NfpB>TPw_78*tZ}NL7QC{UfCdFDBrCxK6Q~2=o*@ zSI{8|&Vg>vE%I+J_@}N-fa0 z@QuaOKsWXJ8tgS?V6eoLa{$GPe(IEUFxUb0b2_UAR|uaGVhjUiOOfw*SMg$9yecLz zl{VxpSO&KU(JzF~eIuiTDGfhXnJ`xaIizoVXmC)&4+$IXY}wE=$hY$ebnGisVrXg@ zv9t~G5obVQqZE|VsDg66lnSuIvXR3?g*h%gJkuZ$MGT}-A8Bhh$DM2>joLIeIF1bI z-pp)f4z9dTWG4J;C{{j7CQKDn_rm96f^V3p}}5u7MwRKw!Z2^a2G}y;Bu1HbdzwB7C||y zH+`2`@969DEJHuRMWB#V8+Tg+u{C-YDL%0NK$M>foGT;k6x8AFJP7K0<3PPELET#l z3GLDbdbMV_tYqnS1ywZ8Y371abd}b0vyyFp!`%>=+H4UJeUIadkDFb#X3N%D5Q1V#iSRYD6x>$HQdh~q zXsfHM0|B=aROwDLbmcpGH03O!(ngoaox_OJxdKB9!GNno&=;1dNpkF>@%Sr2mFu^ckWv!w}I80(^OgI!tDGFqML*DR|03l26@_Lzk*<5QPpdb*6xtJYBd@ zQ4%T0>)IaL-GOVHO)vwz1wdN@&**O%LZqCs)SxmwLqmX<`?lrtM{a$cQcJSet; z7=K9|#{ZH$!uhI6jGyLzMr~Z`Mz!XyGWev@sSnQYDjT2~-C2X1OBvb`#Jak*N*g-( zpqsi`X8qJ~>8Y0E(u##1B#fT2N&_@Z9eDR4>Y!So$B~CNni$bofKY6(;O$Ua!jDFW zhIk>$Em^pI9YAC}s&Z}^KJk47O$U91!~%e(LqkIvbOWen{TFWt$4b5nh?QSxD@m%X zv>G6;(Q;U;;a!?-;eQ7?;5~gDi`sC&@xFq}jhL&ORc5*Xh{uq-|@>sMZqPu2m>CR83`ma*iI%3m;fIiIK=!Hov->^Q=a0p*$gO z1R0>igi~&W@Ii`T8wGoPR=LK?r@OwAuxGP(kMJ_coI?nH zdj}1iL6Q&MA?^|2+biJnp>Tf!#|7gpPLcT1c8b~62xeavhuME6xnAD7 zL~Y$vuO8_9@tn9-&~4yqA%IFf;TjDF7YbJC6f1*ik;}?(BRxRV^*UfEq#XdU)^H#) z!?I;J7@Ue_z0N&}!5@Kw`A$u@E8rr}?lpi_;X#KU;PyzAnO3V>qc~UBLW?7eI)SYIif$WL3t~-h z-4x<>$Y*)|t;HG&^9io=rTz4Pj7tt_!IkxJAl5WjDVOznxmD+MR4-X8ZSI|87-eMY zc_2D1h;*^EX#{v^{8(jzGlmxSkBuKrY53u>((>*W-j0kM((nV330vmXMn*>9#YoaE zH#LDraj#pI;OiJMBbE@seY6OfupcSu+y6&Evx?+MoBzEdwxgRI@Cc|(2~ag$=G()f zDq4Dw@?dYl0!kbM|DyKMc7nbPa0_{*fdb3?(mbjo=9dPK@=UCVFE!dY0E~`-zqHiA z(4m2m1ca7^fZwndoAFJjj8G}3JfZ-)t(1O0s3nLZc zPP|ztV*FEN)cGzQx9p52wM~psyU|~fFchuB^_aI znU4hDv{7dx+TGguy88Q#kpKR+inK0rkD~m#Ew{9AboGp}m`l~}TANs{-+3}~oE3)e z8DF%kx%t(J!ST_(N9s)-@uq8X> zjO)4|OHE*m4283YubEYal~K!W0=_d?3T_3$rC{B;a3G_IOso?{B;Mj}pU;=_=EYZ{ z%oN?3m}sY1Wkj&*ia4zLtY9K|#}ciofJ#=&E;AH4!4O*9CQxu1iQ-aSFNH3EHbf>M z7}|M2L4Xb5!;M2a!)mvP2=!qdnwU453Cqj{iUK_5;EDr8jh@d$4xm-CY-nL_K0C3L z_dK>9knh3HI$eS2SZmaBYs?md9k5FRrKK!ZW59ngy<7uFdc$>)GXt+4zbzRvu?W$} z`lQN^Pk%tk-Na2XLXv*p*x=xhxMb^-Z6;`+h})<>Lg~MaJx5=N64iCpECdivnD=?; zC~edew9lt-J|6PdJ`dIhi72?o+YKAQFwiZ5Qb@Qy*p_g0W4KkRweKFmd`#P>=JTFT zZcEkDSM@d3Y#MAU&?#-_iOfVkTgZe9`vj|7Ay^n72jy%}h!O-#>iT0G*E}NB|^i)hoMrZY3@ngSYI+8CC0g&+9lb3vh4sf21-gIB@Q?I7gXfT%Ks}Uew zxIUedF33j|)B#=@N4g%CqzfCDq-$2u^i9p@11>cIh5-30^n#1B1#*F+jtS>MxPXkF61{P`x5b*)%kTw zkxoUJsjK2Bl8i+tI+r9#F#}XAKH-`Hr4tekBp%V(>Kz9ul^K*^Q#*WIijI0VzPk60{HR1m(pb zN4W^r`g{VU*ppiUC^&fmHbR9S#J#n)QJ}XHHNT+?0qmfa#h8T%nw1f+}JbA>o9nC^s)5_TQ@lp z%VW_qEqx76dvBQXhn5;h4Gs(gSi;iBU(^OzG@iUvOAQYkQhcJk(RNA_Pe(}N)o~k_DGZD~vXD`!qU(vIRPi)n&i16()tU;q$&^0ty>Vv;ouu z^t5e=p@)vj^0gy_Ax|hdP{F2nZ++S*xxa7;aIy;2j-`1xYVU}Z0~M)7u}NP}@)P#y z&1PV#8v$Co$@=8R&$4$uG)v4Gn?cg!yF-O(l+-wfsE=_Oy2;KQ>wSGv!cv2ReOaRM$J~_}$X#R2V-Zz#S z8rJZ`bLxqZBdO-$-`EWQ>AB@A(Fph(p~{l-l@gOcv?9JlxuVHd2iEjPomcSWJDJv1 zqzcBa;a|B)6DD21src8}3%yz9bfs#(06z4jQ)VV}c)@U*0Ffx*-uvjYz4n#z-T>pu z`M|~-4;OyHj|CdFjiwtaxNN8+XJ=WNHrL#mWzs|g;lqP4n!t=fMpcedbhf`Y5Sbyk zSsbP!@fn2hQRIbrAsWi8NrThZl;O5*-`=SSKXRK48z|nZfqk}n!3&_)j;Vv^8br$Q%=P~NZo~emqzOax> z&t!^)lZyyJy%KCwhg9KJEERU(@#|9)Fc_tV8hteY|M9}BQd0$?K+UN5s?_oYSU(Y5 zBt6?Mj_iV{gJl&+ty`0ru-zAHWikF(CW`C@)rqa!2J=hq6%BfCq$APBlJIs>wNeW5 znqWQ&I=)yl%AR(YrPA&SHQ`le8Z9TuJ5X;N_>)pByZUUu2_T3G(BVn|&{BsI+{v(L-B)hrA;|b5NFF$gv*otYVD~8vyTHIX;OB(EfciEsDs6CFTqqK#XdV$Fx>_8$xM#DOJumNPZ$B0ts#i z6l(cQ4h}$H%=O<%IvDrP3%kK9E5oEBUWO#NvE2e;(e@NqhyKe3RT zlB|?MNj1q9i3$qg1kYeEy?EqIg5iZLM+)&_!Wk->kCYHcq966FO$mAU!D{faM#K!h zhJp(ZWJ(6^PrT)nO%vZ@RNh;UnrZ$|EE5v*(dSJ%zF~njVty~B9Hj%GyfHDrHp8&Y zTk54$Qm53RK(kMej>UHA_k^$GP$(GSM^?b%G*+!8RF2^ipwR%(#JejfF|>faZtE$%e44rL z1}?bkmy+(hqLj~MZ|nvkt$2I=2(8||(KeZ*G>DSFC|n%iI|e1s!^u{RSK)o-6gtIB zh@BC{%e79Cws! z;JVsisUcL7^70^U`sLS@^7+OZ4=jl-Va_T1C2zWY3l?7DIpvyeJIp=Oa+k*%r9(FZ zsb}_aW8E;?nPrfLz^V|V(U}Gdb6ePJ=#1#l z8CxY3nnDCr7i?xG>sHkW+7|LPRk^d6^~TpRL^p`Inpifx-4pM)!5~fys7v6T#9UEN zVrigE08b$ck&n$-E^^dhR{~OkID+N9i|7p?4-2

M+h^LpR#28Xp0EN<_idLg(7p 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\"name\") ];\r\ + \n#[/system/script get \$expScript name];\r\ + \n:delay 2s;\r\ + \n\r\ + \n:set expSrc [/system/script print where name=\$expName];\r\ + \n/system/script print where name=\$expName;\r\ + \n#[:put [/system/script get \$expScript source]];\r\ + \n:put [\$expSrc];\r\ + \n:delay 2s;\r\ + \n:set FileName \$expName;\r\ + \n:set FilePath (\$DirPath . \$FileName);\r\ + \n\r\ + \n#:log warning \"\\n----------------------\\n\\\$expSrc = \$expSrc\\n____\ + ______________________________\";\r\ + \n:log info \"\\n\\\$FileName = '\$FileName'\";\r\ + \n:log info \"\\n\\\$FilePath = \$FilePath\";\r\ + \n#:log warning {/system/script/get source=[/system/script/get \$expScript\ + \_source];};\r\ + \n\r\ + \n#\$[/system script get [/system script find name=\"\$FileName\"] source]\ + \r\ + \n\r\ + \n#:local gettingScript \"\$FileName\";\r\ + \n#:execute script=\"[[:parse \\\"[:parse { :put (\"\$[/system script get \ + [/system script find name=\$FileName] source]\"); } \\\"]]\"; \\\r\ + \n\r\ + \n#:execute script={:put \"[:parse \\\"[:parse [/system/script get [/syste\ + m/script find name=\$gettingScript] source];\\\"]]\";} \\\r\ + \n# file=(\$FilePath . \".rsc\\00\");\r\ + \n:do {\r\ + \n:local fName (\$FilePath . \".rsc\");\r\ + \n#:delay 3s;\r\ + \n#/file add name=\$fName;\r\ + \n:put [:tostr [\$expSrc]];\r\ + \n/file set \$fName contents=[:put [\$expSrc]];\r\ + \n:delay 3s;\r\ + \n\$WaitForFile \$fName;\r\ + \n(\$FilePath . \".rsc\");\r\ + \n#:delay 2s;\r\ + \n\r\ + \n} on-error={\r\ + \n:log error \"\\nERROR \\\$FilePath = \$FilePath\";\r\ + \n}\r\ + \n#\$WaitForFile (\$FilePath . \".rsc\");\r\ + \n#:execute script={ :put [/system/script get [:put \$FileName] source]; }\ + ;\r\ + \n#/system/script export \$Sscript file=\$FilePath;\r\ + \n#/system/script/export [:put [/system/script/get \$expScript source]] fi\ + le=\$FilePath;\r\ + \n#:execute script={ :put [/system/script/get \$expScript source]; } \\\r\ + \n\r\ + \n#:log warning \"\\n\\\$expScript = \$expScript\";\r\ + \n# \\\r\ + \n# file=(\$FilePath . \".rsc\\00\");\r\ + \n#script={:put [/system/script/get \$FileName source];} file=(\$FilePath\ + \_. \".rsc\\00\");\r\ + \n#/system script export \$src file=(\$FilePath . \$FileName);\r\ + \n}\r\ + \n\r\ + \n:log warning \"\\nComplete!\";\r\ + \n\r\ + \n # :execute script={ :put [ /system/script/get Amster-GlobalConfig sou\ + rce ]; } \\\r\ + \n # file=(\$FilePath . \".conf\\00\");\r\ + \n # \$WaitForFile (\$FilePath . \".conf\");\r\ + \n # :onerror Err {\r\ + \n # /tool/fetch upload=yes url=(\$BackupUploadUrl . \"/\" . \$FileName\ + \_. \".conf\") \\\r\ + \n # user=\$BackupUploadUser password=\$BackupUploadPass src-path=(\ + \$FilePath . \".conf\");\r\ + \n # :set ConfigFile [ /file/get (\$FilePath . \".conf\") ];\r\ + \n # :set (\$ConfigFile->\"name\") (\$FileName . \".conf\");\r\ + \n " +add dont-require-permissions=no name=test1 owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="\ + \r\ + \n#:local runningVer [:find [/system/resource/get version] \"(\"];\r\ + \n\r\ + \n:local runningChannel;\r\ + \n:local runningVersion;\r\ + \n:local ParseCustom;\r\ + \n\r\ + \n:set ParseCustom do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Start [:find [ \$Str ] [:tostr \$2]];\r\ + \n :local End [ :tostr \$3 ];\r\ + \n :local Num [ :tonum \$4 ];\r\ + \n :local Return;\r\ + \n\r\ + \n :if ([:len \$Num] = 0) do={:set Num \"0\"}\r\ + \n :local rest [:pick \$Str (\$Start+\$Num) [:len \$Str]]; \r\ + \n :if ([:len \$End ] = 0) do={:set Return [:pick \$Str \$Num \$Start]} e\ + lse={\r\ + \n :set End [:find \$rest \$End];\r\ + \n :set Return [:pick \$rest 0 \$End];\r\ + \n }\r\ + \n:return \$Return;\r\ + \n}\r\ + \n \r\ + \n:global runningChannel [\$ParseCustom [/system/resource/get version] \"(\ + \" \")\" 1];\r\ + \n:log warning [\$runningChannel];\r\ + \n:set runningVersion [\$ParseCustom [/system/resource/get version] \" \" \ + \")\" 2];\r\ + \n:log warning [\$runningVersion];\r\ + \n\r\ + \n\r\ + \n\r\ + \n# :if ([:len \$open] = 0) do={\r\ + \n# :log error \"\$SMP Could not extract installed OS channel from vers\ + ion string: `\$runningOsAndChannel`.\";\r\ + \n# :error \"\$SMP error, check logs\";\r\ + \n# }\r\ + \n# :local rest [:pick \$runningOsAndChannel (\$open+1) [:len \$runningOs\ + AndChannel]];\r\ + \n# :local close [:find \$rest \")\"];\r\ + \n# :local channel [:pick \$rest 0 \$close];\r\ + \n# :return \$channel;\r\ + \n#}" +add dont-require-permissions=no name=01-Amster-Init-Script owner=vados \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="# Amster Initial Script\r\ + \n#\r\ + \n#:global GlobalEnvRemove true; /system/script {run Amster-GlobalEnvRemov\ + e; };\r\ + \n#:delay 1s;\r\ + \n:#global ReloadGlobal true; \r\ + \n\r\ + \n:global AmScriptsUrl \"https://ros.vados.ru/\";\r\ + \n:global defOwner \"vados\";\r\ + \n:global AmFirstRun;\r\ + \n\r\ + \n:set AmFirstRun do={\r\ + \n :global AmScriptsUrl;\r\ + \n :global defOwner;\r\ + \n #:global ParseKeyValueStore;\r\ + \n #:local AmUpdCRLF true;\r\ + \n #:local IfThenElse;\r\ + \n #:set IfThenElse do={:if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1\ + \_] = true) do={:return \$2}; :return \$3}\r\ + \n #/certificate/settings/set builtin-trust-store=fetch;\r\ + \n #/tool/fetch \"\$scriptsUrl/certs/Root-YE.pem\" dst-path=\"root-ye.p\ + em\";\r\ + \n #/certificate/import file-name=\"root-ye.pem\" passphrase=\"\";\r\ + \n # For basic verification we rename the certificate and print it by f\ + ingerprint. Make sure exactly this one certificate (\"Root-YE\") is shown.\ + \r\ + \n #/certificate/set name=\"Root-YE\" [ find where common-name=\"Root Y\ + E\" ];\r\ + \n #/certificate/print proplist=name,fingerprint where fingerprint=\"e1\ + 4ffcad5b0025731006caa43a121a22d8e9700f4fb9cf852f02a708aa5d5666\";\r\ + \n #:log info \"\\nAmScriptsUrl = \$AmScriptsUrl\";\r\ + \n :local Scripts {\r\ + \n \"Amster-GlobalConfig\"\r\ + \n \"Amster-GlobalFunc\";\r\ + \n# \"Amster-SFTP-backup\"\r\ + \n# \"Amster-BkpEmail-and-upd\";\r\ + \n };\r\ + \n# /system/script/set owner=(\$ScriptVal->\"name\") \\\r\ + \n# source=[ \$IfThenElse (\$ScriptUpdatesCRLF = true) \$SourceCRLF \$S\ + ourceNew ] \$Script;\r\ + \n :foreach Script in=\$Scripts do={\r\ + \n :put \"Installing \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n /system/script/add name=\$Script owner=\$defOwner source=([ /too\ + l/fetch (\"\$AmScriptsUrl\" . \$Script . \".rsc\") output=user as-value ]-\ + >\"data\");\r\ + \n /system/script/set source=[ :tocrlf [ get \$Script source ] ] \$\ + Script;\r\ + \n };\r\ + \n /system/script { run Amster-GlobalConfig; run Amster-GlobalFunc; }\r\ + \n}\r\ + \n:global AmUpdRun;\r\ + \n:set AmUpdRun do={\r\ + \n #:local UpdScript [ :tostr \$1 ];\r\ + \n #:global ParseKeyValueStore;\r\ + \n :global AmScriptsUrl;\r\ + \n :global defOwner;\r\ + \n :local Scripts {\r\ + \n# \"Amster-GlobalConfig\"\r\ + \n# \"Amster-GlobalFunc\"\r\ + \n# \"Amster-GlobalRemove\"\r\ + \n \"Amster-backup-SFTP\"\r\ + \n \"Amster-SFTP-backup\"\r\ + \n \"Amster-BkpEmail-and-upd\"\r\ + \n \"Amster-TG-Bot\"\r\ + \n \"Amster-TG-Notification\";\r\ + \n };\r\ + \n :foreach Script in=\$Scripts do={\r\ + \n :put \"updating \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n /system/script/add name=\$Script owner=\$defOwner source=([ /too\ + l/fetch (\"\$AmScriptsUrl\" . \$Script . \".rsc\") output=user as-value ]-\ + >\"data\");\r\ + \n /system/script/set source=[ :tocrlf [ get \$Script source ] ] \$\ + Script;\r\ + \n };\r\ + \n# /system/script/add name=\$Script owner=\$AmOwner source=([ /tool/fe\ + tch check-certificate=yes-without-crl (\"\$AmScriptsUrl\" . \$Script . \".\ + rsc\") output=user as-value ]->\"data\"); };\r\ + \n}\r\ + \n\r\ + \n\$AmFirstRun;\r\ + \n#\$AmUpdRun;\r\ + \n\r\ + \n#\$setUpdExist;\r\ + \n#:global setUpdExist;\r\ + \n#:global ParseKeyValueStore;\r\ + \n#:set setUpdExist do={\r\ + \n#:do {\r\ + \n#:global ParseKeyValueStore;\r\ + \n# :foreach Script in=[ /system/script/find where source~\"^#!rsc by Vad\ + os\\r\?\\n#comment=*\" ] do={\r\ + \n# :local ScriptVal [ /system/script/get \$Script ];\r\ + \n# :local ScriptInfo [ \$ParseKeyValueStore (\$ScriptVal->\"#comment\"\ + ) ];\r\ + \n# :local scriptName (\$ScriptVal->\"name\");\r\ + \n#:log warning (\"\\n\\\$scriptName = \". \$scriptName);\r\ + \n#: log warning (\"\\n\\\$ScriptInfo = \". \$ScriptInfo);\r\ + \n# }\r\ + \n#} on-error={}\r\ + \n#}\r\ + \n#\$name;\$owner;\$policy;\$dont-require-permissions;\$last-started;\$run\ + -count;\$source;\$invalid;\$comment;\$.id;\$.nextid;\$.dead;\$.about;\r\ + \n#;(evl / (evl /docommand=;(evl / (evl /foreachcounter=\$Script;do=;(evl \ + / (evl /localname=\$ScriptVal;value=(evl (evl /system/script/getnumber=\$S\ + cript))) (evl /localname=\$ScriptInfo;value=(evl (<%% \$ParseKeyValueStore\ + \_(> \$ParseKeyValueStore);(-> \$ScriptVal comment)))) (evl /localname=\$s\ + criptName) (evl /setname=\$scriptName;value=(-> #\$ScriptVal name)) (evl /\ + log/errormessage=(. \r\ + \n# \$ ScriptInfo = \$ScriptInfo)));in=(evl (evl /system/script/findwhere\ + =\$name;\$owner;\$policy;\$dont-require-permissions;\$last-started;\$run-c\ + ount;\$source;\$invalid;\$comment;\$.id;\$.nextid;\$.dead;\$.about;(~ \$so\ + urce (. ^#!rsc by Vados \r\ + \n# \? \r\ + \n#));5))));on-error=;(evl /)))\r\ + \n" +add dont-require-permissions=no name="Download cAP's Packages" owner=vados \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="# +------------------------------+\r\ + \n# | |\r\ + \n# | Mikrotik RouterOS Scripts |\r\ + \n# | _Download cAP's Packages_ |\r\ + \n# | |\r\ + \n# +------------------------------+\r\ + \n# https://github.com/sebastian13/mikrotik-routeros-scripts\r\ + \n#\r\ + \n\r\ + \n# Set a directory path providing the required space\r\ + \n# Add a microSD card https://wiki.mikrotik.com/wiki/Manual:System/Disks\ + \r\ + \n# or setup a RAM-Disk via '/disk/add type=tmpfs tmpfs-max-size=20M slot=\ + ram'\r\ + \n:local dir \"/tmpfs/capsman-packages\";\r\ + \n\r\ + \n\r\ + \n# Set Package Path if required, set policy to none if using 'upgrade-cap\ + s' script\r\ + \n/interface/wifi/capsman/set package-path=\$dir upgrade-policy=none;\r\ + \n/caps-man/manager/set package-path=\$dir upgrade-policy=none;\r\ + \n#require-same-version;\r\ + \n\r\ + \n:local installed [/system package get value-name=version [find where nam\ + e=\"routeros\"]]\r\ + \n\r\ + \n# Delete old downloads\r\ + \n# /file/remove [ find type=package name~\"capsman*\" ]\r\ + \n\r\ + \n# Starting from RouterOS version 7.13, drivers for older wireless and 60\ + GHz interfaces are\r\ + \n# part of the separate \"wireless\" package. The existing \"wifiwave2\" \ + package has been divided\r\ + \n# into distinct packages: \"wifi-qcom\" and \"wifi-qcom-ac\".\r\ + \n# RouterOS >=7.12 is required to upgrade to 7.13.\r\ + \n# https://forum.mikrotik.com/viewtopic.php\?t=202423\r\ + \n\r\ + \n:foreach architecture in={ \"arm\";\r\ + \n# \"arm64\";\r\ + \n \"x86_64\";\r\ + \n \"mipsbe\"} do={\r\ + \n :foreach package in={ \"routeros\";\r\ + \n# \"wifi-qcom-ac\";\r\ + \n# \"wifi-qcom\";\r\ + \n \"container\";\r\ + \n \"wireless\"} do={ :do {\r\ + \n :put \"\"\r\ + \n :put \"Download \$package \$installed for \$architecture\"\r\ + \n /tool fetch mode=https \\\r\ + \n url=\"https://download.mikrotik.com/routeros/\$installed/\$packa\ + ge-\$installed-\$architecture.npk\" \\\r\ + \n dst-path=\"\$dir/\$package-\$installed-\$architecture.npk\"\r\ + \n } on-error={ :put \"Error downloading the file. On a 404, note t\ + hat the package may not exist for the specified architecture!\" }\r\ + \n }\r\ + \n}\r\ + \n" +add comment="Script for setup routeros script system" \ + dont-require-permissions=no name=AM-setup owner=AM-setup policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-setup\r\ + \n# Script comment: Script for setup routeros script system\r\ + \n# Copyright (c) 2005-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch\r\ + \n#\r\ + \n# :local ScriptName [ :jobname ];\r\ + \n# Additional commands:\r\ + \n#\r\ + \n# For scripts:\r\ + \n#:global NewScript \"script_name\"; \r\ + \n#:global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n#/system/script/add name=\$NewScript owner=\$NewScript source=([/tool/fe\ + tch check-certificate=yes-without-crl (\$GlobalScriptsUrl . \$ScriptAdd . \ + \".rsc\") output=user as-value ]->\"data\");\r\ + \n#:global NewScript (a);\r\ + \n#:global ScriptInstallUpdate; \$ScriptInstallUpdate;\r\ + \n#\r\ + \n# For run from terminal:\r\ + \n#/system/script/add name=AM-setup owner=AM-Setup source=([/tool/fetch ch\ + eck-certificate=yes-without-crl \"https://ros.vados.ru/AM-setup.rsc\" outp\ + ut=user as-value ]->\"data\");\r\ + \n#\r\ + \n\r\ + \n:global ScriptUpdatesCRLF \"LF\";\r\ + \n#:if (\$ScriptUpdatesCRLF != false) do={\r\ + \n# :foreach Script in=[ /system/script/find where source~\"^#!rsc by Vad\ + os\" ] do={\r\ + \n# if (\$ScriptUpdatesCRLF = true) do={\r\ + \n# /system/script/set source=[ :tocrlf [ get \$Script source ] ] \$S\ + cript;\r\ + \n# } else={\r\ + \n# /system/script/set source=[ :tolf [ get \$Script source ] ] \$Scr\ + ipt;\r\ + \n# }\r\ + \n# }\r\ + \n#}\r\ + \n\r\ + \n# /import verbose=no \$ScriptName\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global SetupScript [ :jobname ];\r\ + \n:global RemoveMe true;\r\ + \n:global RunOnce (a);\r\ + \n:global InitSetup true;\r\ + \n:global tmpfs false;\r\ + \n\r\ + \n{\r\ + \n :local RmMe [:tobool \$1];\r\ + \n\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global SetupScript;\r\ + \n :global RemoveMe;\r\ + \n :global InitSetup;\r\ + \n :global tmpfs;\r\ + \n\r\ + \n :local UrlSuffix \$Identity;\r\ + \n :local UrlDir \"certs/\";\r\ + \n :local CertUrl (\$GlobalScriptsUrl . \$UrlDir . \$UrlSuffix .\"/\"); \ + \r\ + \n :local CertCommonName \"Root YE\";\r\ + \n :local CertFileName \"Root-YE.pem\";\r\ + \n :local CertFingerprint \"e14ffcad5b0025731006caa43a121a22d8e9700f4fb9c\ + f852f02a708aa5d5666\";\r\ + \n\r\ + \n :if (\$SetupScript = false) do={\r\ + \n :global SetupScript \"AM-setup\";\r\ + \n }\r\ + \n :if (\$RemoveMe != false) do={\r\ + \n :set RemoveMe true;\r\ + \n }\r\ + \n\r\ + \n :log warning (\"\\n\\\$GlobalScriptsUrl = \" . \$GlobalScriptsUrl);\r\ + \n \r\ + \n\r\ + \n :if (!(((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" ||\ + \_\\\r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \"fetch\" || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\r\ + \n [ :len [ /certificate/builtin/find where common-name=\$CertComm\ + onName ] ] > 0)) do={\r\ + \n :put \"Importing certificate...\";\r\ + \n /tool/fetch (\$CertUrl . \$CertFileName) dst-path=\$CertFileName\ + \_as-value;\r\ + \n :delay 1s;\r\ + \n /certificate/import file-name=\$CertFileName passphrase=\"\";\r\ + \n /certificate/set name=\"Root-YE\" [ find where common-name=\"Roo\ + t YE\" ];\r\ + \n :delay 1s;\r\ + \n };\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if ([ :len [ /certificate/find where fingerprint=\$CertFingerprint ] \ + ] != 1) do={\r\ + \n :error \"Something is wrong with your certificates!\";\r\ + \n };\r\ + \n\r\ + \n:local MkTmpfs do={\r\ + \n :local TmpFs [ /disk/find where slot=tmpfs type=tmpfs ];\r\ + \n :if ([ :len \$TmpFs ] = 1) do={\r\ + \n :if ([ /disk/get \$TmpFs disabled ] = true) do={\r\ + \n :log info (\"The tmpfs is disabled, enabling.\");\r\ + \n /disk/enable \$TmpFs;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :log info (\"Try to removing directory 'tmpfs'.\");\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/remove tmpfs;\r\ + \n :return true;\r\ + \n } delay=100ms max=10;\r\ + \n } on-error={ }\r\ + \n# \$RmDir \"tmpfs\";\r\ + \n :onerror Err {\r\ + \n /disk/add slot=tmpfs type=tmpfs tmpfs-max-size=([ /system/resource/g\ + et total-memory ] / 3);\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/get tmpfs;\r\ + \n :return true;\r\ + \n } delay=222ms max=10;\r\ + \n } on-error={ }\r\ + \n } do={\r\ + \n :log warning (\"Creating disk of type tmpfs failed: \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n# :return true;\r\ + \n}\r\ + \n:if (\$tmpfs = true) do={\r\ + \n :put \"Tmpfs variable is true. Checking for tmpfs/scripts directory...\ + \";\r\ + \n :if ([/file find name=\"tmpfs/scripts\"] != \"\") do={\r\ + \n :log info (\$0 . \"Directory tmpfs/scripts exsist.\");\r\ + \n :return \$true;\r\ + \n } else={\r\ + \n :log warning (\$0 . \"Directory tmpfs/scripts does't exsist.\");\r\ + \n :if ([ \$MkTmpfs ] = false) do={\r\ + \n :log error (\$0 . \"Directory tmpfs is not created!\");\r\ + \n :return false;\r\ + \n } else={\r\ + \n :onerror Err {\r\ + \n /file/add type=\"directory\" name=/tmpfs/scripts;\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/get /tmpfs/scripts;\r\ + \n :return true;\r\ + \n } delay=100ms max=10;\r\ + \n } on-error={ }\r\ + \n } do={\r\ + \n :log warning (\$0 . \"Making directory 'scripts' failed: \" . \$\ + Err);\r\ + \n :return false;\r\ + \n }\r\ + \n# :return true;\r\ + \n# [ /file/get \$FileName ]\r\ + \n }\r\ + \n# :log error (\$0 . \"Directory tmpfs/scripts does not exsist. Remove\ + \_global variable tmpfs!\");\r\ + \n# :global tmpfs (a);\r\ + \n# return false;\r\ + \n }\r\ + \n}\r\ + \n :put \"Renaming AM-GlobalConfig, if exists...\";\r\ + \n :local ConfOldName (\"AM-GlobalConfig-\" . [ /system/clock/get date ] \ + . [ /system/clock/get time ] . \".rsc\");\r\ + \n /system/script/set name=\$ConfOldName [ find where name=\"AM-GlobalCon\ + fig\" ];\r\ + \n :foreach Script in={ \"AM-GlobalConfig\"; \"AM-GlobalFunc\" } do={\r\ + \n :put \"Installing \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n /system/script/add name=\$Script owner=\$Script source=([ /tool/fetc\ + h check-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \ + \".rsc\") output=user as-value ]->\"data\");\r\ + \n \r\ + \n /system/script run \$Script;\r\ + \n :delay 2s;\r\ + \n };\r\ + \n\r\ + \n :local Script \"mod/AM-Certs-By-Ident\";\r\ + \n \r\ + \n :put \"Installing \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n /system/script/add name=\$Script owner=\$Script source=([ /tool/fetch \ + check-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \".\ + rsc\") output=user as-value ]->\"data\");\r\ + \n /system/script run \$Script;\r\ + \n :global tmpAddCerts;\r\ + \n :while ([ \$tmpAddCerts ] = true) do={\r\ + \n :delay 1s;\r\ + \n }\r\ + \n :delay 1s;\r\ + \n :put \"Removing \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n\r\ + \n :if ([ :len [ /certificate/find where fingerprint=\$CertFingerprint ] \ + ] > 0) do={\r\ + \n :put \"Renaming certificate by its common-name...\";\r\ + \n :global CertificateNameByCN;\r\ + \n \$CertificateNameByCN \$CertFingerprint;\r\ + \n };\r\ + \n\r\ + \n :if (\$RemoveMe = true) do={\r\ + \n :put \"Add Scheduler for remove this script...\";\r\ + \n :local OnEvent (\":delay 30s;\\n\" . \"/system/script/remove [ find \ + where name=\\\"\$SetupScript\\\" ];\\n\" . \\\r\ + \n \":delay 2s;\\n\" . \"/system/scheduler/remove [ find where name=\\\ + \"_RemoveSetup\\\" ];\\n\" . \\\r\ + \n \":delay 2s;\\n\" . \":global RemoveMe (a);\\n\");\r\ + \n :local Comment \"_RemoveSetup tmp scheduler for remove setup script.\ + \";\r\ + \n /system/scheduler/add name=\"_RemoveSetup\" comment=\$Comment start-\ + time=startup interval=30s on-event=\$OnEvent;\r\ + \n :log warning (\$0 . \"\\nI've done everything, and now I'm tired..\ + . I'm leaving.\\nThere will be an error below, this is normal.\");\r\ + \n }\r\ + \n :delay 1;\r\ + \n :put \"Loading configuration and functions...\";\r\ + \n :do { \r\ + \n :global ScriptInstallUpdate; \$ScriptInstallUpdate;\r\ + \n :put \"Normal removing temporary global environment variables.\";\ + \r\ + \n :global SetupScript (a);\r\ + \n :global InitSetup (a);\r\ + \n :global RemoveMe (a);\r\ + \n } on-error={\r\ + \n :local ErrorMsg \"Loading configuration and functions error!\";\r\ + \n :log error (\$0 . \$ErrorMsg . :error);\r\ + \n :error \$ErrorMsg;\r\ + \n }\r\ + \n}\r\ + \n" +add comment="Global Config Script" dont-require-permissions=no name=\ + AM-GlobalConfig owner=AM-GlobalConfig policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GlobalConfig\r\ + \n# Script comment: Global Config Script\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# Warning: Dont touch this line!\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global GlobalConfReady false;\r\ + \n#\r\ + \n#\r\ + \n# Global Url For Amster scripts to fetch\r\ + \n# most be defined in AM-Init-Script or AM-setup scripts\r\ + \n:global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n:global ScriptsUrlSuffix (a);\r\ + \n:global CheckSumsVerify true;\r\ + \n\r\ + \n:global ScriptUpdatesCRLF true;\r\ + \n:global CommentsInScripts true;\r\ + \n\r\ + \n# Debug output:\r\ + \n:global PrintDebug true; #false;\r\ + \n\r\ + \n# Debug output for specific script:\r\ + \n#:set (\$PrintDebugOverride->\"AM-GlobalFunc\") true;\r\ + \n\r\ + \n# Debug logs\r\ + \n# Other actions (disk, email, remote or support) can be used as well.\r\ + \n# I do not recommend using echo - use debug output instead (https://rsc.\ + eworm.de/main/DEBUG.html#debug-output).\r\ + \n#/system/logging/add topics=script,debug action=memory;\r\ + \n\r\ + \n:global MikrotikUpgradeUrl \"https://upgrade.mikrotik.com/routeros\";\r\ + \n:global Domain \"WORKGROUP\";\r\ + \n\r\ + \n:global EmailGeneralTo \"vados@vados.ru\";\r\ + \n#:global EmailGeneralCc \"\";\r\ + \n\r\ + \n# \$ScriptInstallUpdate mod/notification-telegram\r\ + \n:global TelegramTokenId \"8390868324:AAG3_d1hfVR2M6pGAgDK6ZvhOe4J97nyopw\ + \";\r\ + \n:global TelegramChatId \"-1003631309063\"; #\"454403445\";\r\ + \n# Use this to send notifications to a specific topic in group.\r\ + \n#:global TelegramThreadId \"30\";\r\ + \n# Using telegram-chat you have to define trusted chat ids (not group ids\ + !)\r\ + \n# or user names. Groups allow to chat with devices simultaneously.\r\ + \n:global TelegramChatIdsTrusted {\r\ + \n \"454403445\"\r\ + \n \"-1003631309063\"\r\ + \n \"-5124482807\"\r\ + \n \"1087968824\";\r\ + \n};\r\ + \n# \"-5294757340\";\r\ + \n\r\ + \n:global TelegramChatGroups \"(all)\";\r\ + \n#:global TelegramChatGroups \"(all|home|office)\";\r\ + \n\r\ + \n:global NotificationFunctions {\r\ + \n \"email\"\r\ + \n \"telegram\";\r\ + \n};\r\ + \n# Toggle this to disable symbols in notifications.\r\ + \n:global NotificationsWithSymbols true;\r\ + \n:global TerminalColorOutput true;\r\ + \n\r\ + \n:global DetectIpAddrDefault \"https://ipv4.mikrotik.ovh/\";\r\ + \n:global DetectIpAddrFallback \"https://api.ipify.org/\";\r\ + \n\r\ + \n## Update channel. Possible values: stable, long-term, testing, developm\ + ent\r\ + \n:global updateChannel \"stable\";\r\ + \n\r\ + \n:global CertRenewUrl (\$GlobalScriptsUrl . \"certs/\" . \$Identity . \"/\ + \");\r\ + \n:global CertRenewTime 2w;\r\ + \n:global CertRenewPass (a); #{\r\ + \n# \"\";\r\ + \n# \"v3ry-s3cr3t\";\r\ + \n# \"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n:global CertWarnTime 1w;\r\ + \n:global CertIssuedExportPass (a); # {\r\ + \n# \"\";\r\ + \n# \"cert1-cn\"=\"v3ry-s3cr3t\";\r\ + \n# \"cert2-cn\"=\"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n\r\ + \n# Script BackupAndUpdate mode, possible values: backup,\r\ + \n# updateOnce - Update once as \"osnotify\" and back \"osnotify\" mode (i\ + n terminal run :set OSUpdateMode updateOnce; \$OSUpdateMode;). \r\ + \n# osupdate - Update if available and creates backups before/after update\ + \_(ignores `forceBackup`)\r\ + \n# Set `forceBackup` to true to always create backups, even\ + \_without updates\r\ + \n# osnotify - Set `forceBackup` to always create backups on every run\r\ + \n:global DevInfoIncludeIP true;\r\ + \n:global BackupAndUpdateMode \"osnotify\"\r\ + \n\r\ + \n:global forceBackup true;\r\ + \n:global BackupPassword \"Peskar55\";\r\ + \n\r\ + \n# SFTP Backup Variables\r\ + \n:global BackupRandomDelay 0;\r\ + \n:global BackupUploadUrl \"sftp://amster.vados.ru/upload/\";\r\ + \n:global BackupUploadUser \$Identity;\r\ + \n:global BackupUploadPass \"root.mnuc.backuper\";\r\ + \n\r\ + \n:global BackupSendBinary true;\r\ + \n:global BackupSendExport true;\r\ + \n:global BackupSendGlobalConfig true;\r\ + \n:global BackupSendScripts true;\r\ + \n\r\ + \n# Remove local file after uploading if no errors\r\ + \n:global BackupRmLocal true;\r\ + \n\r\ + \n# Local Backups Root Directory \r\ + \n:global BackupLocalRoot \"tmpfs\";\r\ + \n\r\ + \n# Encrypt Backup\r\ + \n:global BackupEncrypt true;\r\ + \n\r\ + \n# Sensitive information in Backups\r\ + \n:global BackupSens true;\r\ + \n\r\ + \n# User Export\r\ + \n:global BackupUser true;\r\ + \n\r\ + \n# License Export (not for CHR, will silently skip)\r\ + \n:global BackupLicense false;\r\ + \n\r\ + \n# SSH Keys\r\ + \n:global BackupSshKeys true;\r\ + \n\r\ + \n# Certificate Export\r\ + \n:global BackupCerts true;\r\ + \n\r\ + \n# Certificate Password\r\ + \n:global BackupCertsPasswd \"\";\r\ + \n\r\ + \n# User-Manager Export\r\ + \n:global BackupUserMan false;\r\ + \n\r\ + \n# The Dude Export\r\ + \n:global BackupDude false;\r\ + \n\r\ + \n# User Files to export, comma-separated string or array of strings\r\ + \n# User Files are not removed on backup\r\ + \n# Any directory paths will be removed (/ -> _) on remote file\r\ + \n# Nonpresent files are silently skipped\r\ + \n:global BackupUserList \"autosupout.rif,autosupout.old.rif\";\r\ + \n\r\ + \n# load functions and any custom scripts\r\ + \n# [ /system/script/find where name=\"global-config-ov\ + erlay\" ], \\\r\ + \n# [ /system/script/find where name~\"^global-config-o\ + verlay\\\\.d/.\" ]\r\ + \n#\r\ + \n#:foreach Script in=([ /system/script/find where name=\"Amster-TGglobalV\ + ars\" ], [ /system/script/find where name=\"Amster-TGglobalFunc\" ]) do={\ + \r\ + \n# :onerror Err {\r\ + \n# /system/script/run \$Script;\r\ + \n# } do={\r\ + \n# :log error (\"Loading configuration from configs and functions\"\ + \_. [ /system/script/get \$Script name ] . \" failed: \" . \$Err);\r\ + \n# }\r\ + \n#}\r\ + \n#:log info \"\\ndevMode = \$devMode\";\r\ + \n#:log info \"\\ndevRunning = \$devRunning\";\r\ + \n:foreach Script in=[ /system/script/find where source~\"^#!rsc by Vados \ + GlobalRUN\\r\?\\n\" ] do={\r\ + \n :onerror Err {\r\ + \n /system/script/run \$Script;\r\ + \n } do={\r\ + \n :log error (\"Run script \" . [ /system/script/get \$Script name \ + ] . \" width marker \\\" GlobalRUN\\\" failed: \" . \$Err);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# signal we are ready\r\ + \n:set GlobalConfReady true;\r\ + \n:while (\$GlobalConfReady != true) do={:delay 500ms};\r\ + \n" +add comment="Global functions" dont-require-permissions=no name=AM-GlobalFunc \ + owner=AM-GlobalFunc policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GglobalFunc\r\ + \n# Script comment: Global functions\r\ + \n# Copyright (c) 2005-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch, scheduler\r\ + \n:local ExitOK false;\r\ + \n:local ScriptName [ :jobname ];\r\ + \n#:global checkRunJob \$SriptName;\r\ + \n#:global fixCRLF;\r\ + \n#:if (\$fixCRLF != false) do={\r\ + \n#:foreach fixScript in=([ /system/script/find where name~\"*\" ]) do={/s\ + ystem/script/set source=[ :tocrlf [ get \$fixScript source ] ] \$fixScript\ + }\r\ + \n#:if (\$fixCRLF != \"LF\") do={ /system/script/set source=[ :tocrlf [ g\ + et \$fixScript source ] ] \$fixScript};};\r\ + \n#}\r\ + \n:global GlobalFuncReady false;\r\ + \n\r\ + \n# Git commit id & info, expected configuration version\r\ + \n:global CommitId \"main/729/f42d43ae\";\r\ + \n:global CommitTAG \"unknown\";\r\ + \n:global CommitInfo \"Autocommit in repo: ros.vados.ru, generated by owne\ + r: root, in 00:05:03 01.07.2026.\";\r\ + \n\r\ + \n# The variables are received right here.\r\ + \n:global GlobalConfVersion 140;\r\ + \n\r\ + \n:global SumsDirAndFileName \"tmp/checksums.json\";\r\ + \n\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global IdentityShort [:pick \$Identity 0 18];\r\ + \n\r\ + \n:global devBoardName [/system/resource/get board-name];\r\ + \n:global devMode [/system/device-mode/get mode];\r\ + \n:global devCurArch [/system/resource/get architecture-name];\r\ + \n:global devCurVersion [/system/resource/get version];\r\ + \n\r\ + \n# Variables defined in AM-GlobalConfig.rsc\r\ + \n:global TerminalColorOutput;\r\ + \n:global CommentsInScripts;\r\ + \n:global DetectIpAddrDefault;\r\ + \n:global DetectIpAddrFallback;\r\ + \n:global updateChannel;\r\ + \n:global Domain;\r\ + \n:global EmailGeneralTo;\r\ + \n:global BackupAndUpdateMode;\r\ + \n\r\ + \n# Single functions, without nested functions.\r\ + \n:global MiniDateTimeStamp;\r\ + \n:global MicroDateTimeStamp;\r\ + \n\r\ + \n# Include globals \$PrintDebug and \$PrintDebugOverride\r\ + \n:global LogPrint;\r\ + \n\r\ + \n# DeviceInfo and Updates\r\ + \n:global IsCheckUpdates;\r\ + \n:global WaitCheckUpdates;\r\ + \n:global GetPubIp;\r\ + \n\r\ + \n:global GetArrDeviceInfo;\r\ + \n:global WaitArrDeviceInfo;\r\ + \n\r\ + \n:global devModel;\r\ + \n:global devRunningMode;\r\ + \n\r\ + \n:global devRbRevision;\r\ + \n:global devRbSerialNumber;\r\ + \n:global devRbVerAvail;\r\ + \n\r\ + \n:global runningChannelVersion;\r\ + \n:global runningChannel;\r\ + \n:global runningVersion;\r\ + \n\r\ + \n:global devBuildTime;\r\ + \n:global ROsVerAvail;\r\ + \n:global PkgUpdStatus;\r\ + \n:global currentUpdateChannel;\r\ + \n\r\ + \n:global ScriptInstallUpdate;\r\ + \n\r\ + \n:global DeviceInfo;\r\ + \n\r\ + \n:global OldMode;\r\ + \n\r\ + \n:global ParseKeyValueStore;\r\ + \n:global RandomDelay;\r\ + \n:global GetRandomNumber;\r\ + \n:global EscapeForRegEx;\r\ + \n:global CharacterMultiply;\r\ + \n:global CharacterReplace;\r\ + \n:global CleanFilePath;\r\ + \n:global CleanName;\r\ + \n\r\ + \n:global CommitBrief;\r\ + \n\r\ + \n:global EitherOr;\r\ + \n:global FormatLine;\r\ + \n:global GetRandom20CharAlNum;\r\ + \n:global GetRandom20CharHex;\r\ + \n:global GetRandomNumber;\r\ + \n:global Grep;\r\ + \n:global IfThenElse;\r\ + \n:global MAX;\r\ + \n:global MIN;\r\ + \n:global HumanReadableNum;\r\ + \n\r\ + \n:global IsFullyConnected;\r\ + \n:global IsDNSResolving;\r\ + \n:global IsTimeSync;\r\ + \n:global IsTimeSyncCached;\r\ + \n:global IsTimeSyncResetNtp;\r\ + \n\r\ + \n:global MkDir;\r\ + \n:global RmDir;\r\ + \n:global RmFile;\r\ + \n:global FileGet;\r\ + \n:global Unix2Dos;\r\ + \n:global UrlEncode;\r\ + \n:global WaitForFile;\r\ + \n:global ScriptLock;\r\ + \n:global SendTelegram;\r\ + \n:global NotificationFunctions;\r\ + \n:global SendNotification;\r\ + \n:global SendNotification2;\r\ + \n:global SymbolForNotification;\r\ + \n:global SymbolByUnicodeName;\r\ + \n:global ValidateSyntax;\r\ + \n:global CheckSumsVerify;\r\ + \n:global GlobalConfChanges;\r\ + \n:global GlobalConfMigration;\r\ + \n:global VersionToNum;\r\ + \n:global RequiredRouterOS;\r\ + \n:global ExitError;\r\ + \n:global ExitOnError;\r\ + \n\r\ + \n:global FetchHugeFiles;\r\ + \n:global FetchUserAgentStr;\r\ + \n:global FileExists;\r\ + \n:global CertificateAvailable;\r\ + \n:global CertificateDownload;\r\ + \n:global CertificateNameByCN;\r\ + \n:global CharacterMultiply;\r\ + \n\r\ + \n:global InspectVar;\r\ + \n:global InspectVarRet;\r\ + \n:global ParseCustom;\r\ + \n\r\ + \n:set IsCheckUpdates do={\r\ + \n :local isInfo [:tobool \$1];\r\ + \n :global updateChannel;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n :log info (\$0 . \" \\\$isInfo = \" . \$isInfo); \r\ + \n :local CurrentChannel [ /system/package/update/get channel ];\r\ + \n :if ([ :len \$updateChannel ] > 0 && \$CurrentChannel != \$updateChann\ + el) do={\r\ + \n :retry command={/system/package/update set channel=\$updateChannel} \ + \_delay=300ms max=5 on-error={};\r\ + \n :log info (\$0 . \" Set updates channel to: \$updateChannel\");\r\ + \n }\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local Channel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set currentUpdateChannel \$Channel;\r\ + \n :log info (\$0 . \" Update Channel: \" . \$Channel . \".\");\r\ + \n :local StatusPkg \"undefined\";\r\ + \n :log info (\$0 . \" Checking for updates...\");\r\ + \n :retry command={:set \$StatusPkg ([/system/package/update/check-for-up\ + dates without-paging as-value]->\"status\")} \\\r\ + \n delay=300ms max=5 on-error={};\r\ + \n :log info (\$0 . \" \\\$StatusPkg = \" . \$StatusPkg);\r\ + \n :if (\$StatusPkg = \"New version is available\") do={ :set PkgUpdStatu\ + s true; } else={ :set PkgUpdStatus false; }\r\ + \n :set ROsVerAvail [ :tostr [ /system/package/update/get latest-version \ + ] ];\r\ + \n :log info (\$0 . \" \\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if (\$isInfo != false || \$PkgUpdStatus = true) do={\r\ + \n :log info (\$0 . \" \\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if ([ :typeof \$arrDeviceInfo ] != \"array\") do={\r\ + \n :set arrDeviceInfo [:toarray \$arrDeviceInfo];\r\ + \n }\r\ + \n :retry command={:set (\$arrDeviceInfo->\"devUpdates\") [ /system/pac\ + kage/update/get ]} delay=500ms max=5 on-error={:log error (\"Error\")};\r\ + \n :return true;\r\ + \n } else={\r\ + \n :return \$PkgUpdStatus;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set WaitCheckUpdates do={\r\ + \n :global IsCheckUpdates;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n :local Status \"\";\r\ + \n :local Update;\r\ + \n :local WaitCount 0;\r\ + \n :if ([ \$IsCheckUpdates true ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n :set Status [ :tostr [ /system/package/update/get status ] ];\r\ + \n :while ((\$Status = \"\" || \$Status = \"finding out latest version...\ + \") && \$WaitCount < 60) do={\r\ + \n :delay 1s;\r\ + \n :set WaitCount (\$WaitCount + 1);\r\ + \n :set Status [ :tostr [ /system/package/update/get status ] ];\r\ + \n }\r\ + \n :set Update [ /system/package/update/get ];\r\ + \n :set currentUpdateChannel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set ROsVerAvail [ :tostr (\$Update->\"latest-version\") ];\r\ + \n :if ((\$Update->\"status\") = \"New version is available\") do={\r\ + \n :set PkgUpdStatus true;\r\ + \n } else={\r\ + \n :set PkgUpdStatus false;\r\ + \n }\r\ + \n :if ([ :typeof \$arrDeviceInfo ] != \"array\") do={\r\ + \n :set arrDeviceInfo [:toarray \$arrDeviceInfo];\r\ + \n }\r\ + \n :set (\$arrDeviceInfo->\"devUpdates\") \$Update;\r\ + \n :if (\$Status = \"\" || \$Status = \"finding out latest version...\") \ + do={\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set WaitArrDeviceInfo do={\r\ + \n :global GetArrDeviceInfo;\r\ + \n :while ([ \$GetArrDeviceInfo ] = false) do={\r\ + \n :delay 1s;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set GetArrDeviceInfo do={\r\ + \n :global LogPrint;\r\ + \n :global arrDeviceInfo;\r\ + \n :global devBoardName;\r\ + \n :global devModel;\r\ + \n :global devRunning;\r\ + \n :global devCurArch;\r\ + \n :global devRbRevision;\r\ + \n :global devRbSerialNumber;\r\ + \n :global devBuildTime;\r\ + \n :global runningChannelVersion;\r\ + \n :global runningChannel;\r\ + \n :global runningVersion;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n\r\ + \n :local DeviceInfo;\r\ + \n :local Resource [/system/resource/get];\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local VersionString [ :tostr (\$Resource->\"version\") ];\r\ + \n :local ChannelStart [ :find \$VersionString \" (\" ];\r\ + \n :local RouterBoard;\r\ + \n :local IsRouterBoard false;\r\ + \n :local RunningMode \"\";\r\ + \n\r\ + \n :set devCurArch [ :tostr (\$Resource->\"architecture-name\") ];\r\ + \n :set devBuildTime [ :tostr (\$Resource->\"build-time\") ];\r\ + \n :set runningChannelVersion \$VersionString;\r\ + \n :set runningVersion \$VersionString;\r\ + \n :set runningChannel \"\";\r\ + \n\r\ + \n :if ([ :typeof \$ChannelStart ] = \"num\" && \$ChannelStart >= 0) do=\ + {\r\ + \n :set runningVersion [ :pick \$VersionString 0 \$ChannelStart ];\r\ + \n :local ChannelRest [ :pick \$VersionString (\$ChannelStart + 2) [ :\ + len \$VersionString ] ];\r\ + \n :local ChannelEnd [ :find \$ChannelRest \")\" ];\r\ + \n :if ([ :typeof \$ChannelEnd ] = \"num\" && \$ChannelEnd >= 0) do={\ + \r\ + \n :set runningChannel [ :pick \$ChannelRest 0 \$ChannelEnd ];\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if ([:pick \$devBoardName 0 3] != \"CHR\" or [:pick \$devBoardName 0\ + \_3] != \"x86\") do={\r\ + \n :do {\r\ + \n :set RouterBoard [[ :parse \"/system/routerboard/get\" ]];\r\ + \n# :set RouterBoard [ /system/routerboard/get ];\r\ + \n :if ((\$RouterBoard->\"routerboard\") = true) do={\r\ + \n :set IsRouterBoard true;\r\ + \n }\r\ + \n } on-error={}\r\ + \n }\r\ + \n\r\ + \n :do {\r\ + \n :set RunningMode [ /system/device-mode/get mode ];\r\ + \n } on-error={\r\ + \n :do {\r\ + \n :set RunningMode [ /system/device-mode/get running ];\r\ + \n } on-error={\r\ + \n :set RunningMode \"\";\r\ + \n }\r\ + \n }\r\ + \n :set devRunning \$RunningMode;\r\ + \n\r\ + \n :if (\$IsRouterBoard = true) do={\r\ + \n :set devModel [ :tostr (\$RouterBoard->\"model\") ];\r\ + \n :set devRbRevision [ :tostr (\$RouterBoard->\"revision\") ];\r\ + \n :set devRbSerialNumber [ :tostr (\$RouterBoard->\"serial-number\") \ + ];\r\ + \n } else={\r\ + \n :set devModel \$devBoardName;\r\ + \n :set devRbRevision \"\";\r\ + \n :set devRbSerialNumber \"\";\r\ + \n }\r\ + \n\r\ + \n :set currentUpdateChannel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set ROsVerAvail [ :tostr (\$Update->\"latest-version\") ];\r\ + \n\r\ + \n :set (\$DeviceInfo->\"board-name\") \$devBoardName;\r\ + \n :set (\$DeviceInfo->\"architecture-name\") \$devCurArch;\r\ + \n :set (\$DeviceInfo->\"model\") \$devModel;\r\ + \n :set (\$DeviceInfo->\"revision\") \$devRbRevision;\r\ + \n :set (\$DeviceInfo->\"serial-number\") \$devRbSerialNumber;\r\ + \n :set (\$DeviceInfo->\"running-mode\") \$devRunning;\r\ + \n :set (\$DeviceInfo->\"build-time\") \$devBuildTime;\r\ + \n :set (\$DeviceInfo->\"running-channel-version\") \$runningChannelVers\ + ion;\r\ + \n :set (\$DeviceInfo->\"running-version\") \$runningVersion;\r\ + \n :set (\$DeviceInfo->\"running-channel\") \$runningChannel;\r\ + \n :set (\$DeviceInfo->\"routerboard\") \$IsRouterBoard;\r\ + \n :set (\$DeviceInfo->\"current-firmware\") [ :tostr (\$RouterBoard->\"\ + current-firmware\") ];\r\ + \n :set (\$DeviceInfo->\"upgrade-firmware\") [ :tostr (\$RouterBoard->\"\ + upgrade-firmware\") ];\r\ + \n :set (\$DeviceInfo->\"package-channel\") \$currentUpdateChannel;\r\ + \n :set (\$DeviceInfo->\"installed-version\") [ :tostr (\$Update->\"inst\ + alled-version\") ];\r\ + \n :set (\$DeviceInfo->\"latest-version\") \$ROsVerAvail;\r\ + \n :set (\$DeviceInfo->\"update-status\") [ :tostr (\$Update->\"status\"\ + ) ];\r\ + \n\r\ + \n :set arrDeviceInfo \$DeviceInfo;\r\ + \n :return \$DeviceInfo;\r\ + \n}\r\ + \n\r\ + \n:set DeviceInfo do={\r\ + \n :global GetArrDeviceInfo;\r\ + \n :global GlobalConfVersion;\r\ + \n :global Identity;\r\ + \n :global IfThenElse;\r\ + \n :global FormatLine;\r\ + \n :global CommitBrief;\r\ + \n\r\ + \n :local License [/system/license/get];\r\ + \n :local Snmp [ /snmp/get ];\r\ + \n :local Info [ \$GetArrDeviceInfo ];\r\ + \n\r\ + \n :if ([ :typeof \$Info ] != \"array\") do={\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :return ( \\\r\ + \n [ \$FormatLine \"Hostname\" \$Identity ] . \"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Snmp->\"location\") ] > 0) \\\r\ + \n ([ \$FormatLine \"Location\" (\$Snmp->\"location\") ] . \"\\n\") ]\ + \_. \\\r\ + \n [ \$IfThenElse ([ :len (\$Snmp->\"contact\") ] > 0) \\\r\ + \n ([ \$FormatLine \"Contact\" (\$Snmp->\"contact\") ] . \"\\n\") ] .\ + \_\\\r\ + \n \"Hardware:\\n\" . \\\r\ + \n [ \$FormatLine \" Board\" (\$Info->\"board-name\") ] . \"\\n\" . \ + \\\r\ + \n [ \$FormatLine \" Arch\" (\$Info->\"architecture-name\") ] . \"\\\ + n\" . \\\r\ + \n [ \$FormatLine \" Model\" (\$Info->\"model\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"revision\") ] > 0) \\\r\ + \n (\" \" . (\$Info->\"revision\")) ] . \"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"serial-number\") ] > 0) \\\r\ + \n ([ \$FormatLine \" Serial\" (\$Info->\"serial-number\") ] . \"\ + \\n\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$License->\"nlevel\") ] > 0) \\\r\ + \n ([ \$FormatLine \" License\" (\"level \" . (\$License->\"nlevel\ + \")) ] . \"\\n\") ] . \\\r\ + \n \"RouterOS:\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$License->\"level\") ] > 0) \\\r\ + \n ([ \$FormatLine \" License\" (\"level \" . (\$License->\"level\ + \")) ] . \"\\n\") ] . \\\r\ + \n [ \$FormatLine \" Channel\" (\$Info->\"package-channel\") ] . \"\ + \\n\" . \\\r\ + \n [ \$FormatLine \" Installed\" (\$Info->\"installed-version\") ] .\ + \_\"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"running-channel\") ] > 0) \\\r\ + \n ([ \$FormatLine \" Running\" ((\$Info->\"running-version\") . \ + \" (\" . (\$Info->\"running-channel\") . \")\") ] . \"\\n\") \\\r\ + \n ([ \$FormatLine \" Running\" (\$Info->\"running-version\") ] . \ + \"\\n\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"latest-version\") ] > 0 && \\\r\ + \n (\$Info->\"installed-version\") != (\$Info->\"latest-version\"))\ + \_\\\r\ + \n ([ \$FormatLine \" Available\" (\$Info->\"latest-version\") ] .\ + \_\"\\n\") ] . \\\r\ + \n [ \$IfThenElse ((\$Info->\"routerboard\") = true && \\\r\ + \n (\$Info->\"current-firmware\") != (\$Info->\"upgrade-firmware\")\ + ) \\\r\ + \n ([ \$FormatLine \" Firmware\" ((\$Info->\"current-firmware\") .\ + \_\" -> \" . (\$Info->\"upgrade-firmware\")) ] . \"\\n\") ] . \\\r\ + \n \"RouterOS-Scripts:\\n\" . \\\r\ + \n [ \$FormatLine \" Commit\" [ \$CommitBrief ] ] . \"\\n\" . \\\r\ + \n [ \$FormatLine \" Version\" \$GlobalConfVersion ]);\r\ + \n}\r\ + \n\r\ + \n:if ([ :typeof \$NotificationFunctions ] != \"array\") do={:set Notifica\ + tionFunctions ({})}\r\ + \n\r\ + \n:set GetRandom20CharAlNum do={\r\ + \n :global EitherOr;\r\ + \n :return [ :rndstr length=[ \$EitherOr [ :tonum \$1 ] 20 ] from=\"ABCDE\ + FGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\" ];\r\ + \n}\r\ + \n\r\ + \n:set GetRandom20CharHex do={:global EitherOr; :return [ :rndstr length=[\ + \_\$EitherOr [ :tonum \$1 ] 20 ] from=\"0123456789abcdef\" ]; }\r\ + \n:set GetRandomNumber do={:global EitherOr; :return [ :rndnum from=0 to=[\ + \_\$EitherOr [ :tonum \$1 ] 4294967295 ] ]; }\r\ + \n\r\ + \n:global checkRunJob;\r\ + \n:set checkRunJob do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLock; \r\ + \n :if ([ \$ScriptLock \$Script ] = false) do={:return false}\r\ + \n :foreach Job in=[ /system/script/job/find where script=\$Script ] do={\ + :set Job [ /system/script/job/get \$Job ];\r\ + \n :while ([ :typeof (\$Job->\"parent\") ] = \"id\") do={:set Job [ /sy\ + stem/script/job/get [ find where .id=(\$Job->\"parent\") ] ]}\r\ + \n :if ((\$Job->\"type\") = \"login\") do={:return true};}; :return fa\ + lse; \r\ + \n}\r\ + \n\r\ + \n:set InspectVar do={\r\ + \n :global InspectVarRet;\r\ + \n :put [ :tocrlf [ \$InspectVarRet \$1 ] ];\r\ + \n}\r\ + \n\r\ + \n:set CommitBrief do={\r\ + \n :global CommitId;\r\ + \n :global CommitInfo;\r\ + \n :if (\$CommitId = \"unknown\") do={\r\ + \n :return \"unknown\";\r\ + \n }\r\ + \n :return (\$CommitInfo . \"/\" . [ :pick \$CommitId 0 8 ]);\r\ + \n}\r\ + \n\r\ + \n:set InspectVarRet do={\r\ + \n :local Input \$1;\r\ + \n :local Level (0 + [ :tonum \$2 ]);\r\ + \n :global CharacterReplace;\r\ + \n :global IfThenElse;\r\ + \n :global InspectVarRet;\r\ + \n :local IndentReturn do={\r\ + \n :local Prefix [ :tostr \$1 ];\r\ + \n :local Value [ :tostr \$2 ];\r\ + \n :local Level [ :tonum \$3 ];\r\ + \n :global CharacterMultiply;\r\ + \n :return ([ \$CharacterMultiply \" \" (\$Level * 2) ] . \"-\" . \$Pre\ + fix . \"-> \" . \$Value);\r\ + \n }\r\ + \n :local TypeOf [ :typeof \$Input ];\r\ + \n :local Len [ :len \$Input ];\r\ + \n :local Return [ \$IndentReturn \"type\" \$TypeOf \$Level ];\r\ + \n :if (\$TypeOf = \"array\") do={\r\ + \n :foreach Key,Value in=\$Input do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"key\" \$Key (\$Level + 1) ] . \"\\n\" . \\\r\ + \n [ \$InspectVarRet \$Value (\$Level + 2) ]);\r\ + \n }\r\ + \n } else={\r\ + \n :if (\$TypeOf = \"str\") do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"len\" \$Len \$Level ]);\r\ + \n :if ([ :typeof [ :find \$Input (\"\\r\") ] ] = \"num\") do={\r\ + \n :set Input [ \$CharacterReplace \$Input (\"\\r\") \"\" ];\r\ + \n }\r\ + \n :if ([ :typeof [ :find \$Input (\"\\n\") ] ] = \"num\") do={\r\ + \n :set Input [ \$CharacterReplace \$Input (\"\\n\") \" \" ];\r\ + \n }\r\ + \n }\r\ + \n :if (\$TypeOf != \"nothing\") do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"value\" [ \$IfThenElse ([ :len \$Input ] > 80)\ + \_\\\r\ + \n ([ :pick \$Input 0 77 ] . \"...\") \$Input ] \$Level ]);\r\ + \n }\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set GetPubIp do={\r\ + \n :global DetectIpAddrDefault;\r\ + \n :global DetectIpAddrFallback;\r\ + \n\r\ + \n :local PubIpAddr false;\r\ + \n :local Url \$DetectIpAddrDefault;\r\ + \n :local JobName [ :jobname ];\r\ + \n\r\ + \n :do {\r\ + \n :log debug (\$0 . \" Trying to detect public IP using default serv\ + ice: \" . \$Url . \"...\");\r\ + \n :retry command={:set PubIpAddr ([ /tool/fetch http-method=\"get\"\ + \_check-certificate=yes-without-crl \$Url output=user as-value ]->\"data\"\ + )} \\\r\ + \n delay=50ms max=5 on-error={:log error (\$JobName . \" Failed to \ + detect public IP using default service: \" . \$Url . \".\")}\r\ + \n } on-error={\r\ + \n :log error (\$0 . \" Failed to detect public IP using default serv\ + ice: \" . \$Url . \".\");\r\ + \n :set Url \$DetectIpAddrFallBack;\r\ + \n :log debug (\$0 . \" Trying to detect public IP using fallback ser\ + vice: \" . \$Url . \"...\");\r\ + \n :retry command={:set PubIpAddr ([ /tool/fetch http-method=\"get\"\ + \_check-certificate=yes-without-crl \$Url output=user as-value ]->\"data\"\ + )} \\\r\ + \n delay=200ms max=5 on-error={:log error (\$JobName . \" Failed to\ + \_detect public IP using fallback service: \" . \$Url . \".\")}\r\ + \n };\r\ + \n :local IpLen ([ :len \$PubIpAddr ] -1);\r\ + \n :if (\$PubIpAddr = false) do={:set PubIpAddr \"not-detected\"} else=\ + {:set PubIpAddr [:pick \$PubIpAddr 0 \$IpLen]}\r\ + \n :return \$PubIpAddr;\r\ + \n}\r\ + \n\r\ + \n:set FormatLine do={\r\ + \n :local Key [ :tostr \$1 ];\r\ + \n :local Value [ :tostr \$2 ];\r\ + \n :local Indent [ :tonum \$3 ];\r\ + \n :local Spaces;\r\ + \n :local Return \"\";\r\ + \n :global CharacterMultiply;\r\ + \n :global EitherOr;\r\ + \n :set Indent [ \$EitherOr \$Indent 16 ];\r\ + \n :local Spaces [ \$CharacterMultiply \" \" \$Indent ];\r\ + \n :if ([ :len \$Key ] > 0) do={ :set Return (\$Key . \":\")}\r\ + \n :if ([ :len \$Key ] > (\$Indent - 2)) do={:set Return (\$Return . \"\\\ + n\" . [ :pick \$Spaces 0 \$Indent ] . \$Value);\r\ + \n } else={:set Return (\$Return . [ :pick \$Spaces 0 (\$Indent - [ :len \ + \$Return ]) ] . \$Value)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ParseCustom do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Start [:find [ \$Str ] [:tostr \$2]];\r\ + \n :local End [ :tostr \$3 ];\r\ + \n :local Num [ :tonum \$4 ];\r\ + \n :local Return;\r\ + \n :if ([:len \$Num] = 0) do={:set Num \"0\"}\r\ + \n :local rest [:pick \$Str (\$Start+\$Num) [:len \$Str]]; \r\ + \n :if ([:len \$End ] = 0) do={:set Return [:pick \$Str \$Num \$Start]} e\ + lse={\r\ + \n :set End [:find \$rest \$End];\r\ + \n :set Return [:pick \$rest 0 \$End];\r\ + \n }\r\ + \n:return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set LogPrint do={\r\ + \n :local Severity [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Message [ :tostr \$3 ];\r\ + \n :global PrintDebug;\r\ + \n :global PrintDebugOverride;\r\ + \n :global EitherOr;\r\ + \n :local Debug [ \$EitherOr (\$PrintDebugOverride->\$Name) \$PrintDebug \ + ];\r\ + \n :local PrintSeverity do={\r\ + \n :global TerminalColorOutput;\r\ + \n :if (\$TerminalColorOutput != true) do={:return \$1}\r\ + \n :local Color { debug=96; info=97; warning=93; error=91 };\r\ + \n :return (\"\\1B[\" . \$Color->\$1 . \"m\" . \$1 . \"\\1B[0m\");\r\ + \n}\r\ + \n :local Log ([ \$EitherOr \$Name \"\" ] . \": \" . \$Message);\ + \r\ + \n :if (\$Severity ~ (\"^(custom|debug|error|info)\\\$\")) do={\r\ + \n :if (\$Severity = \"debug\") do={:log debug \$Log}\r\ + \n :if (\$Severity = \"error\") do={:log error \$Log}\r\ + \n :if (\$Severity = \"info\" ) do={:log info \$Log}\r\ + \n } else={:log warning \$Log; :set Severity \"warning\"}\r\ + \n :if (\$Severity != \"debug\" || \$Debug = true) do={:put ([ \$PrintSev\ + erity \$Severity ] . \": \" . \$Message)}\r\ + \n}\r\ + \n\r\ + \n:set CertificateAvailable do={\r\ + \n :local CommonName [ :tostr \$1 ];\r\ + \n :local UseFor [ :tostr \$2 ];\r\ + \n\r\ + \n :global CertificateDownload;\r\ + \n :global ParseKeyValueStore;\r\ + \n\r\ + \n :if ([ :len \$UseFor ] = 0) do={\r\ + \n :log warning (\$0 . \"The intended use is undefined!\");\r\ + \n :set UseFor \"undefined\";\r\ + \n }\r\ + \n :if ([ /system/resource/get free-hdd-space ] < 8388608 && \\\r\ + \n [ /certificate/settings/get crl-download ] = true && \\\r\ + \n [ /certificate/settings/get crl-store ] = \"system\") do={\r\ + \n :log warning (\$0 . \"This system has low free flash space but \" . \ + \\\r\ + \n \"is configured to download certificate CRLs to system!\");\r\ + \n }\r\ + \n :if ([ :len \$CommonName ] = 0) do={\r\ + \n :log warning (\$0 . \"No CommonName given!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if (((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" || \\\ + \r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \$UseFor || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\r\ + \n [ :len [ /certificate/builtin/find where common-name=\$CommonName\ + \_] ] > 0) do={\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ + ) do={\r\ + \n :log info (\$0 . \"Certificate with CommonName '\" . \$CommonName . \ + \"' not available.\");\r\ + \n :if ([ \$CertificateDownload \$CommonName ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] > 1\ + ) do={\r\ + \n :log info (\$0 . \"There are \" . \$CertCount . \" Certificates with\ + \_CommonName '\" . \$CommonName . \"'. Should be ok.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :local CertVal [ /certificate/get [ find where common-name=\$CommonNam\ + e ] ];\r\ + \n :while ((\$CertVal->\"akid\") != \"\" && (\$CertVal->\"akid\") != (\$C\ + ertVal->\"skid\")) do={\r\ + \n :if ([ :len [ /certificate/find where skid=(\$CertVal->\"akid\") ] ]\ + \_= 0) do={\r\ + \n :local IssuerCN ([ \$ParseKeyValueStore (\$CertVal->\"issuer\") ]-\ + >\"CN\");\r\ + \n :log info (\$0 . \"Certificate chain for '\" . \$CommonName . \"' \ + is incomplete, missing '\" . \$IssuerCN . \"'.\");\r\ + \n :if ([ \$CertificateDownload \$IssuerCN ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :set CertVal [ /certificate/get [ find where skid=(\$CertVal->\"akid\ + \") ] ];\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CertificateDownload do={\r\ + \n :local CommonName [ :tostr \$1 ];\r\ + \n :global ScriptUpdatesBaseUrl;\r\ + \n :global ScriptUpdatesUrlSuffix;\r\ + \n :global CertificateNameByCN;\r\ + \n :global CleanName;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global RmFile;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :log info (\$0 . \"Downloading and importing certificate with \" . \\\ + \r\ + \n \"CommonName '\" . \$CommonName . \"'.\");\r\ + \n :local FileName ([ \$CleanName \$CommonName ] . \".pem\");\r\ + \n :do {\r\ + \n /tool/fetch check-certificate=yes-without-crl mode=https http-header\ + -field=({ [ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n (\$ScriptUpdatesBaseUrl . \"certs/\" . \$FileName . \$ScriptUpdate\ + sUrlSuffix) \\\r\ + \n dst-path=\$FileName as-value;\r\ + \n \$WaitForFile \$FileName;\r\ + \n } on-error={\r\ + \n :log warning (\$0 . \" Failed downloading certificate with CommonNam\ + e '\" . \$CommonName . \\\r\ + \n \"' from repository! Trying fallback to mkcert.org...\");\r\ + \n :do {\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if ([ :len [ /certificate/find where common-name=\"ISRG Root X1\"\ + \_] ] = 0 && \\\r\ + \n !(((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" \ + || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \"fetch\" || \ + \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\ + \r\ + \n [ :len [ /certificate/builtin/find where common-name=\"ISRG\ + \_Root X1\" ] ] > 0)) do={\r\ + \n :log error (\$0 . \" Required certificate is not available.\");\ + \r\ + \n :return false;\r\ + \n }\r\ + \n /tool/fetch check-certificate=yes-without-crl mode=https http-head\ + er-field=({ [ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n url=\"https://mkcert.org/generate/\" http-data=[ :serialize to=jso\ + n ({ \$CommonName }) ] \\\r\ + \n dst-path=\$FileName as-value;\r\ + \n \$WaitForFile \$FileName;\r\ + \n :if ([ /file/get \$FileName size ] = 0) do={\r\ + \n \$RmFile \$FileName;\r\ + \n :error false;\r\ + \n }\r\ + \n } on-error={\r\ + \n :log error (\$0 . \" Failed downloading certificate with CommonNam\ + e '\" . \$CommonName . \"'!\");\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n /certificate/import file-name=\$FileName passphrase=\"\" as-value;\r\ + \n :delay 1s;\r\ + \n \$RmFile \$FileName;\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ + ) do={\r\ + \n /certificate/remove [ find where name~(\"^\" . \$FileName . \"_[0-9]\ + +\\\$\") ];\r\ + \n :log warning (\$0 . \" Certificate with CommonName '\" . \$CommonNam\ + e . \"' still unavailable!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :foreach Cert in=[ /certificate/find where name~(\"^\" . \$FileName . \ + \"_[0-9]+\\\$\") ] do={\r\ + \n \$CertificateNameByCN [ /certificate/get \$Cert common-name ];\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CertificateNameByCN do={\r\ + \n :local Match [ :tostr \$1 ];\r\ + \n :global CleanName;\r\ + \n :local Cert ([ /certificate/find where (common-name=\$Match or fingerp\ + rint=\$Match or name=\$Match) ]->0);\r\ + \n :if ([ :len \$Cert ] = 0) do={\r\ + \n :log warning (\$0 .\" No matching certificate found.\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local CommonName [ /certificate/get \$Cert common-name ];\r\ + \n /certificate/set \$Cert name=[ \$CleanName \$CommonName ];\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CharacterMultiply do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Num [ :tonum \$2 ];\r\ + \n :if (\$Num = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :for I from=1 to=\$Num do={:set Return (\$Return . \$Str)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set FileExists do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :local Type [ :tostr \$2 ];\r\ + \n :global FileGet;\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={ :return false; }\r\ + \n :if ([ :len (\$FileVal->\"size\") ] = 0) do={ :return false; }\r\ + \n :if ([ :len \$Type ] = 0 || \$FileVal->\"type\" = \$Type) do={ :return\ + \_true; }\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set Grep do={\r\ + \n :local Input ([ :tostr \$1 ] . \"\\n\");\r\ + \n :local Pattern [ :tostr \$2 ];\r\ + \n :if ([ :typeof [ :find \$Input \$Pattern ] ] = \"nil\") do={:return []\ + }\r\ + \n :do {\r\ + \n :local Line [ :pick \$Input 0 [ :find \$Input \"\\n\" ] ];\r\ + \n :if ([ :typeof [ :find \$Line \$Pattern ] ] = \"num\") do={:return \ + \$Line}\r\ + \n :set Input [ :pick \$Input ([ :find \$Input \"\\n\" ] + 1) [ :len \$\ + Input ] ];\r\ + \n } while=([ :len \$Input ] > 0);\r\ + \n :return [];\r\ + \n}\r\ + \n\r\ + \n:set HumanReadableNum do={\r\ + \n :local Input [ :tonum \$1 ];\r\ + \n :local Base [ :tonum \$2 ];\r\ + \n :global EitherOr;\r\ + \n :global IfThenElse;\r\ + \n :local Prefix \"kMGTPE\";\r\ + \n :local Pow 1;\r\ + \n :set Base [ \$EitherOr \$Base 1024 ];\r\ + \n :local Bin [ \$IfThenElse (\$Base = 1024) \"i\" \"\" ];\r\ + \n :if (\$Input < \$Base) do={:return \$Input}\r\ + \n :for I from=0 to=[ :len \$Prefix ] do={\r\ + \n :set Pow (\$Pow * \$Base);\r\ + \n :if (\$Input / \$Base < \$Pow) do={\r\ + \n :set Prefix [ :pick \$Prefix \$I ];\r\ + \n :local Tmp1 (\$Input * 100 / \$Pow);\r\ + \n :local Tmp2 (\$Tmp1 / 100);\r\ + \n :if (\$Tmp2 >= 100) do={:return (\$Tmp2 . \$Prefix . \$Bin)}\r\ + \n :return (\$Tmp2 . \".\" . \\\r\ + \n [ :pick \$Tmp1 [ :len \$Tmp2 ] ([ :len \$Tmp1 ] - [ :len \$Tmp\ + 2 ] + 1) ] . \\\r\ + \n \$Prefix . \$Bin);\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set CleanFilePath do={\r\ + \n :local Path [ :tostr \$1 ];\r\ + \n :global CharacterReplace;\r\ + \n :while (\$Path ~ \"//\") do={ :set \$Path [ \$CharacterReplace \$Path \ + \"//\" \"/\" ]; }\r\ + \n :if ([ :pick \$Path 0 ] = \"/\") do={ :set Path [ :pick \$Path 1 [ :le\ + n \$Path ] ]; }\r\ + \n :if ([ :pick \$Path ([ :len \$Path ] - 1) ] = \"/\") do={ :set Path [ \ + :pick \$Path 0 ([ :len \$Path ] - 1) ]; }\r\ + \n :return \$Path;\r\ + \n}\r\ + \n\r\ + \n:set CleanName do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :local Return \"\";\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :if ([ :typeof [ find \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr\ + stuvwxyz0123456789\" \$Char ] ] = \"nil\") do={\r\ + \n :do {:if ([ :len \$Return ] = 0) do={:error true};\r\ + \n :if ([ :pick \$Return ([ :len \$Return ] - 1) ] = \"-\") do={:er\ + ror true}; :set Char \"-\"} on-error={:set Char \"\"}; \r\ + \n };\r\ + \n :set Return (\$Return . \$Char)};\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set RandomDelay do={\r\ + \n :local Time [ :tonum \$1 ];\r\ + \n :local Unit [ :tostr \$2 ];\r\ + \n :global EitherOr;\r\ + \n :global GetRandomNumber;\r\ + \n :global MAX;\r\ + \n :if (\$Time = 0) do={:return false}\r\ + \n :delay ([ \$MAX 10 [ \$GetRandomNumber ([ :tonsec [ :totime (\$Time . \ + [ \$EitherOr \$Unit \"s\" ]) ] ] / 1000000) ] ] . \"ms\");\r\ + \n}\r\ + \n\r\ + \n:set EscapeForRegEx do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :if ([ :len \$Input ] = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :local Chars (\"^.[]\\\$()|*+\?{}\\\\\");\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :if ([ :find \$Chars \$Char ]) do={:set Char (\"\\\\\" . \$Char)}\r\ + \n :set Return (\$Return . \$Char);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ExitError do={\r\ + \n :local ExitOK [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Error [ :tostr \$3 ];\r\ + \n :global IfThenElse;\r\ + \n :if (\$ExitOK = \"false\") do={\r\ + \n :log error (\$Name [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\\\$\") \ + \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with er\ + ror\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \ + \$Error) \".\" ]);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set ExitOnError do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :local Error [ :tostr \$2 ];\r\ + \n :global IfThenElse;\r\ + \n :log error (\$Name . \" \" . [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\ + \\\$\") \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with erro\ + r\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \$E\ + rror) \".\" ]);\r\ + \n}\r\ + \n\r\ + \n:set MiniDateTimeStamp do={\r\ + \n :local mikro [ :tobool \$1 ];\r\ + \n /system/clock;\r\ + \n :local vdate [get date];\r\ + \n :local vtime [get time];\r\ + \n :local vdoff [:toarray \"0,4,5,7,8,10\"];\r\ + \n :local MM [:pick \$vdate (\$vdoff->2) (\$vdoff->3)];\r\ + \n :local M [:tonum \$MM];\r\ + \n :if (\$vdate ~ \".../../....\") do={\r\ + \n :set vdoff [:toarray \"7,11,1,3,4,6\"];\r\ + \n :set M ([:find \"xxanebarprayunulugepctovecANEBARPRAYUNULUGE\ + PCTOVEC\" [:pick \$vdate (\$vdoff->2) (\$vdoff->3)] -1] / 2);\r\ + \n :if (\$M>12) do={:set M (\$M - 12)}\r\ + \n :set MM [:pick (100 + \$M) 1 3];\r\ + \n }\r\ + \n :local yyyy [:pick \$vdate (\$vdoff->0) (\$vdoff->1)];\r\ + \n :local dd [:pick \$vdate (\$vdoff->4) (\$vdoff->5)];\r\ + \n :local HH [:pick \$vtime 0 2];\r\ + \n :local mm [:pick \$vtime 3 5];\r\ + \n :local ss [:pick \$vtime 6 8];\r\ + \n :local Return;\r\ + \n :if (\$mikro = true) do={\r\ + \n :set Return ([:pick \$yyyy 2 4] . [:pick \$dd 0 1] . [:pick \$HH 0\ + \_1] . [:pick \$mm 3 4] . [:pick \$ss 6 7]);\r\ + \n } else={\r\ + \n :set Return ([\$yyyy] . [\$MM] . [\$dd] . [\$HH] . [\$mm] . [\$ss]);\ + \r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set WaitForFile do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :local WaitTime [ :totime \$2 ];\r\ + \n :global CleanFilePath;\r\ + \n :global EitherOr;\r\ + \n :global MAX;\r\ + \n :set FileName [ \$CleanFilePath \$FileName ];\r\ + \n :local Delay ([ \$MAX [ \$EitherOr \$WaitTime 2s ] 100ms ] / 9);\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/get \$FileName;\r\ + \n :return true;\r\ + \n } delay=\$Delay max=10;\r\ + \n } on-error={ }\r\ + \n :while ([ :len [ /file/find where name=\$FileName ] ] > 0) do={\r\ + \n :do {\r\ + \n /file/get \$FileName;\r\ + \n :return true;\r\ + \n } on-error={ }\r\ + \n :delay \$Delay;\r\ + \n :set Delay (\$Delay * 3 / 2);\r\ + \n }\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set FileGet do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :global WaitForFile;\r\ + \n :if ([ \$WaitForFile \$FileName 0s ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n :local FileVal false;\r\ + \n :do {\r\ + \n :set FileVal [ /file/get \$FileName ];\r\ + \n } on-error={ }\r\ + \n :return \$FileVal;\r\ + \n}\r\ + \n\r\ + \n:set RmDir do={\r\ + \n :local DirName [ :tostr \$1 ];\r\ + \n :global FileGet;\r\ + \n :log debug (\$0 . \" Removing directory: \". \$DirName);\r\ + \n :local DirVal [ \$FileGet \$DirName ];\r\ + \n :if (\$DirVal = false) do={\r\ + \n :log debug (\$0 . \" ... which does not exist.\");\r\ + \n :return true;\r\ + \n };\r\ + \n :if (\$DirVal->\"type\" != \"directory\") do={\r\ + \n :log error (\$0 . \" Directory '\" . \$DirName . \"' is not a direct\ + ory.\");\r\ + \n :return false;\r\ + \n };\r\ + \n :onerror Err {\r\ + \n /file/remove \$DirName;\r\ + \n } do={\r\ + \n :log error (\$0 . \" Removing directory '\" . \$DirName . \"' failed\ + : \" . \$Err);\r\ + \n :return false;\r\ + \n };\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set RmFile do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :global FileGet;\r\ + \n :log debug (\$0 . \" Removing file: \". \$FileName);\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={\r\ + \n :log debug (\$0 . \" ... which does not exist.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :if (\$FileVal->\"type\" = \"directory\" || \$FileVal->\"type\" = \"di\ + sk\") do={\r\ + \n :log error (\$0 . \" File '\" . \$FileName . \"' is not a file.\");\ + \r\ + \n :return false;\r\ + \n }\r\ + \n :onerror Err {\r\ + \n /file/remove \$FileName;\r\ + \n } do={\r\ + \n :log error (\$0 . \" Removing file '\" . \$FileName . \"' failed: \"\ + \_. \$Err); :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set MkDir do={\r\ + \n :local Path [ :tostr \$1 ];\r\ + \n :global CleanFilePath;\r\ + \n :global FileGet;\r\ + \n :global RmDir;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :local MkTmpfs do={\r\ + \n :global WaitForFile;\r\ + \n :local TmpFs [ /disk/find where slot=tmpfs type=tmpfs ];\r\ + \n :if ([ :len \$TmpFs ] = 1) do={\r\ + \n :if ([ /disk/get \$TmpFs disabled ] = true) do={\r\ + \n :log info (\$0 . \" The tmpfs is disabled, enabling.\");\r\ + \n /disk/enable \$TmpFs;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :log info (\$0 . \" Creating disk of type tmpfs.\");\r\ + \n \$RmDir \"tmpfs\";\r\ + \n :onerror Err {\r\ + \n /disk/add slot=tmpfs type=tmpfs tmpfs-max-size=([ /system/resource\ + /get total-memory ] / 3);\r\ + \n \$WaitForFile \"tmpfs\";\r\ + \n } do={\r\ + \n :log warning (\$0 . \" Creating disk of type tmpfs failed: \" . \$\ + Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :set Path [ \$CleanFilePath \$Path ];\r\ + \n :if (\$Path = \"\") do={\r\ + \n :return true;\r\ + \n }\r\ + \n :log debug (\$0 . \" Making directory: \" . \$Path);\r\ + \n :local PathVal [ \$FileGet \$Path ];\r\ + \n :if (\$PathVal->\"type\" = \"directory\") do={\r\ + \n :log debug (\$0 . \" ... which already exists.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ :pick \$Path 0 5 ] = \"tmpfs\") do={\r\ + \n :if ([ \$MkTmpfs ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :onerror Err {\r\ + \n /file/add type=\"directory\" name=\$Path;\r\ + \n \$WaitForFile \$Path;\r\ + \n } do={\r\ + \n :log warning (\$0 . \" Making directory '\" . \$Path . \"' failed: \ + \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set MAX do={:if (\$1 > \$2) do={:return \$1}\r\ + \n :return \$2;\r\ + \n}\r\ + \n:set MIN do={:if (\$1 < \$2) do={:return \$1}\r\ + \n :return \$2;\r\ + \n}\r\ + \n\r\ + \n:set CharacterMultiply do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Num [ :tonum \$2 ];\r\ + \n :if (\$Num = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :for I from=1 to=\$Num do={:set Return (\$Return . \$Str)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set CharacterReplace do={\r\ + \n :local String [ :tostr \$1 ];\r\ + \n :local ReplaceFrom [ :tostr \$2 ];\r\ + \n :local ReplaceWith [ :tostr \$3 ];\r\ + \n :local Return \"\";\r\ + \n :if (\$ReplaceFrom = \"\") do={:return \$String}\r\ + \n :while ([ :typeof [ :find \$String \$ReplaceFrom ] ] != \"nil\") do={\ + \r\ + \n :local Pos [ :find \$String \$ReplaceFrom ];\r\ + \n :set Return (\$Return . [ :pick \$String 0 \$Pos ] . \$ReplaceWith);\ + \r\ + \n :set String [ :pick \$String (\$Pos + [ :len \$ReplaceFrom ]) [ :len\ + \_\$String ] ];\r\ + \n }\r\ + \n :return (\$Return . \$String);\r\ + \n}\r\ + \n\r\ + \n:set IfThenElse do={\r\ + \n :if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1 ] = true) do={:return \ + \$2};\r\ + \n :return \$3;\r\ + \n}\r\ + \n\r\ + \n:set EitherOr do={\r\ + \n :global IfThenElse;\r\ + \n :if ([ :typeof \$1 ] = \"num\") do={:return [ \$IfThenElse (\$1 != 0) \ + \$1 \$2 ]}\r\ + \n :if ([ :typeof \$1 ] = \"time\") do={:return [ \$IfThenElse (\$1 > 0s)\ + \_\$1 \$2 ]}\r\ + \n# this works for boolean values, literal ones with parentheses\r\ + \n :return [ \$IfThenElse ([ :len [ :tostr \$1 ] ] > 0) \$1 \$2 ];\r\ + \n}\r\ + \n\r\ + \n:set ParseKeyValueStore do={\r\ + \n :local Source \$1;\r\ + \n :if ([ :pick \$Source 0 1 ] = \"{\") do={\r\ + \n :do {\r\ + \n :return [ :deserialize from=json \$Source ];\r\ + \n } on-error={ }\r\ + \n } \r\ + \n :if ([ :typeof \$Source ] != \"array\") do={:set Source [ :tostr \$1 ]\ + }\r\ + \n :local Result ({});\r\ + \n :foreach KeyValue in=[ :toarray \$Source ] do={\r\ + \n :if ([ :find \$KeyValue \"=\" ]) do={\r\ + \n :local Key [ :pick \$KeyValue 0 [ :find \$KeyValue \"=\" ] ];\r\ + \n :local Value [ :pick \$KeyValue ([ :find \$KeyValue \"=\" ] + 1) [\ + \_:len \$KeyValue ] ];\r\ + \n :if (\$Value=\"true\") do={:set Value true}\r\ + \n :if (\$Value=\"false\") do={:set Value false}\r\ + \n :set (\$Result->\$Key) \$Value;\r\ + \n } else={:set (\$Result->\$KeyValue) true}\r\ + \n }\r\ + \n :return \$Result;\r\ + \n}\r\ + \n:set RequiredRouterOS do={\r\ + \n :local Caller [ :tostr \$1 ];\r\ + \n :local Required [ :tostr \$2 ];\r\ + \n :local Warn [ :tostr \$3 ];\r\ + \n :global IfThenElse;\r\ + \n :global VersionToNum;\r\ + \n :if (!(\$Required ~ \"^\\\\d+\\\\.\\\\d+((alpha|beta|rc|\\\\.)\\\\d+|)\ + \\\$\")) do={\r\ + \n :log error (\$0 . \" No valid RouterOS version: \" . \$Required);\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ \$VersionToNum \$Required ] > [ \$VersionToNum [ /system/packag\ + e/update/get installed-version ] ]) do={\r\ + \n :if (\$Warn = \"true\") do={\r\ + \n :log warning (\$0 . \" This \" . [ \$IfThenElse ([ :pick \$Caller \ + 0 ] = (\"\\\$\")) \"function\" \"script\" ] . \\\r\ + \n \" '\" . \$Caller . \"' (at least specific functionality) requir\ + es RouterOS \" . \$Required . \". Please update!\");\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set ValidateSyntax do={\r\ + \n :local Code [ :tostr \$1 ];\r\ + \n :global LogPrint;\r\ + \n :onerror Err {\r\ + \n [ :parse (\":local Validate do={\\n\" . \$Code . \"\\n}\") ];\r\ + \n } do={\r\ + \n \$LogPrint debug \$0 (\"Valdation failed: \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set VersionToNum do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :local Multi 0x1000000;\r\ + \n :local Return 0;\r\ + \n :global CharacterReplace;\r\ + \n :set Input [ \$CharacterReplace \$Input \".\" \",\" ];\r\ + \n :foreach I in={ \"zero\"; \"alpha\"; \"beta\"; \"rc\" } do={\r\ + \n :set Input [ \$CharacterReplace \$Input \$I (\",\" . \$I . \",\") ];\ + \r\ + \n }\r\ + \n :foreach Value in=([ :toarray \$Input ], 0) do={\r\ + \n :local Num [ :tonum \$Value ];\r\ + \n :if (\$Multi = 0x100) do={\r\ + \n :if ([ :typeof \$Num ] = \"num\") do={\r\ + \n :set Return (\$Return + 0xff00);\r\ + \n :set Multi (\$Multi / 0x100);\r\ + \n } else={\r\ + \n :if (\$Value = \"zero\") do={ }\r\ + \n :if (\$Value = \"alpha\") do={ :set Return (\$Return + 0x3f00); \ + }\r\ + \n :if (\$Value = \"beta\") do={ :set Return (\$Return + 0x5f00); }\ + \r\ + \n :if (\$Value = \"rc\") do={ :set Return (\$Return + 0x7f00); }\r\ + \n }\r\ + \n }\r\ + \n :if ([ :typeof \$Num ] = \"num\") do={ :set Return (\$Return + (\$Va\ + lue * \$Multi)); }\r\ + \n :set Multi (\$Multi / 0x100);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ScriptLock do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local WaitMax [ :totime \$2 ];\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global ScriptLockOrder;\r\ + \n :if ([ :typeof \$ScriptLockOrder ] = \"nothing\") do={\r\ + \n :set ScriptLockOrder ({});\r\ + \n }\r\ + \n :if ([ :typeof (\$ScriptLockOrder->\$Script) ] = \"nothing\") do={\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n }\r\ + \n :local JobCount do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :return [ :len [ /system/script/job/find where script=\$Script ] ];\ + \r\ + \n }\r\ + \n :local TicketCount do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :local Count 0;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\") do={:set Count (\$Count \ + + 1)}\r\ + \n }\r\ + \n :return \$Count;\r\ + \n }\r\ + \n :local IsFirstTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Check [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if (\$Ticket = \$Check) do={:return true}\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\" && \$Ticket != \$Check) d\ + o={:return false}\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :local AddTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Add [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :while (true) do={\r\ + \n :local Pos [ :len (\$ScriptLockOrder->\$Script) ];\r\ + \n :set (\$ScriptLockOrder->\$Script->\$Pos) \$Add;\r\ + \n :delay 10ms;\r\ + \n :if ((\$ScriptLockOrder->\$Script->\$Pos) = \$Add) do={:return tru\ + e}\r\ + \n }\r\ + \n }\r\ + \n :local RemoveTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Remove [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Id,Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :while ((\$ScriptLockOrder->\$Script->\$Id) = \$Remove) do={\r\ + \n :set (\$ScriptLockOrder->\$Script->\$Id);\r\ + \n :delay 10ms;\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n :local CleanupTickets do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\") do={:return false}\r\ + \n }\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n }\r\ + \n :if ([ :typeof \$WaitMax ] = \"nil\" ) do={:set WaitMax 0s}\r\ + \n :if ([ :len [ /system/script/find where name=\$Script ] ] = 0) do={\r\ + \n :log error (\$0 . \" A script named '\" . \$Script . \"' does not ex\ + ist!\");\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$JobCount \$Script ] = 0) do={\r\ + \n \$LogPrint error \$0 (\"No script '\" . \$Script . \"' is running!\"\ + );\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$TicketCount \$Script ] >= [ \$JobCount \$Script ]) do={\r\ + \n :log error (\$0 . \" More tickets than running scripts '\" . \$Scrip\ + t . \"', resetting!\");\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n /system/script/job/remove [ find where script=\$Script ];\r\ + \n }\r\ + \n :local MyTicket [ \$GetRandom20CharAlNum 6 ];\r\ + \n \$AddTicket \$Script \$MyTicket;\r\ + \n :local WaitInterval (\$WaitMax / 20);\r\ + \n :local WaitTime \$WaitMax;\r\ + \n :while (\$WaitTime > 0 && \\\r\ + \n ([ \$IsFirstTicket \$Script \$MyTicket ] = false || \\\r\ + \n [ \$TicketCount \$Script ] < [ \$JobCount \$Script ])) do={\r\ + \n :set WaitTime (\$WaitTime - \$WaitInterval);\r\ + \n :delay \$WaitInterval;\r\ + \n }\r\ + \n :if ([ \$IsFirstTicket \$Script \$MyTicket ] = true && \\\r\ + \n [ \$TicketCount \$Script ] = [ \$JobCount \$Script ]) do={\r\ + \n \$RemoveTicket \$Script \$MyTicket;\r\ + \n \$CleanupTickets \$Script;\r\ + \n :return true;\r\ + \n }\r\ + \n \$RemoveTicket \$Script \$MyTicket;\r\ + \n \$LogPrint debug \$0 (\"Script '\" . \$Script . \"' started more than \ + once\" . \\\r\ + \n [ \$IfThenElse (\$WaitTime < \$WaitMax) \" and timed out waiting for\ + \_lock\" \"\" ] . \"...\");\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set SendNotification do={ :onerror Err {\r\ + \n :global SendNotification2;\r\ + \n \$SendNotification2 ({ origin=\$0; subject=\$1; message=\$2; link=\$3;\ + \_silent=\$4 });\r\ + \n} do={:global ExitOnError; \$ExitOnError \$0 \$Err}\r\ + \n}\r\ + \n\r\ + \n:set SendNotification2 do={\r\ + \n :local Notification \$1;\r\ + \n :global NotificationFunctions;\r\ + \n :foreach FunctionName,Discard in=\$NotificationFunctions do={\r\ + \n (\$NotificationFunctions->\$FunctionName) (\"\\\$NotificationFunctio\ + ns->\\\"\" . \$FunctionName . \"\\\"\") \$Notification;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set SymbolByUnicodeName do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :global EitherOr;\r\ + \n :global SymbolsExtra;\r\ + \n :local Symbols ({\r\ + \n \"abacus\"=\"\\F0\\9F\\A7\\AE\";\r\ + \n \"alarm-clock\"=\"\\E2\\8F\\B0\";\r\ + \n \"arrow-down\"=\"\\E2\\AC\\87\";\r\ + \n \"arrow-up\"=\"\\E2\\AC\\86\";\r\ + \n \"calendar\"=\"\\F0\\9F\\93\\85\";\r\ + \n \"card-file-box\"=\"\\F0\\9F\\97\\83\";\r\ + \n \"chart-decreasing\"=\"\\F0\\9F\\93\\89\";\r\ + \n \"chart-increasing\"=\"\\F0\\9F\\93\\88\";\r\ + \n \"cloud\"=\"\\E2\\98\\81\";\r\ + \n \"cross-mark\"=\"\\E2\\9D\\8C\";\r\ + \n \"earth\"=\"\\F0\\9F\\8C\\8D\";\r\ + \n \"fire\"=\"\\F0\\9F\\94\\A5\";\r\ + \n \"floppy-disk\"=\"\\F0\\9F\\92\\BE\";\r\ + \n \"gear\"=\"\\E2\\9A\\99\";\r\ + \n \"heart\"=\"\\E2\\99\\A5\";\r\ + \n \"high-voltage-sign\"=\"\\E2\\9A\\A1\";\r\ + \n \"incoming-envelope\"=\"\\F0\\9F\\93\\A8\";\r\ + \n \"information\"=\"\\E2\\84\\B9\";\r\ + \n \"large-orange-circle\"=\"\\F0\\9F\\9F\\A0\";\r\ + \n \"large-red-circle\"=\"\\F0\\9F\\94\\B4\";\r\ + \n \"link\"=\"\\F0\\9F\\94\\97\";\r\ + \n \"lock-with-ink-pen\"=\"\\F0\\9F\\94\\8F\";\r\ + \n \"memo\"=\"\\F0\\9F\\93\\9D\";\r\ + \n \"mobile-phone\"=\"\\F0\\9F\\93\\B1\";\r\ + \n \"pushpin\"=\"\\F0\\9F\\93\\8C\";\r\ + \n \"scissors\"=\"\\E2\\9C\\82\";\r\ + \n \"scroll\"=\"\\F0\\9F\\93\\9C\";\r\ + \n \"smiley-partying-face\"=\"\\F0\\9F\\A5\\B3\";\r\ + \n \"smiley-smiling-face\"=\"\\E2\\98\\BA\";\r\ + \n \"smiley-winking-face-with-tongue\"=\"\\F0\\9F\\98\\9C\";\r\ + \n \"sparkles\"=\"\\E2\\9C\\A8\";\r\ + \n \"speech-balloon\"=\"\\F0\\9F\\92\\AC\";\r\ + \n \"star\"=\"\\E2\\AD\\90\";\r\ + \n \"warning-sign\"=\"\\E2\\9A\\A0\";\r\ + \n \"white-heavy-check-mark\"=\"\\E2\\9C\\85\"\r\ + \n }, \$SymbolsExtra);\r\ + \n :local Magic [ :pick [ /system/clock/get date ] 4 10 ];\r\ + \n :local Special {\r\ + \n \"information-04-01\"=\"\\F0\\9F\\9A\\BB\";\r\ + \n \"large-orange-circle-04-01\"=\"\\F0\\9F\\8D\\8A\";\r\ + \n \"large-orange-circle-10-31\"=\"\\F0\\9F\\8E\\83\";\r\ + \n \"large-red-circle-04-01\"=\"\\F0\\9F\\8D\\92\" };\r\ + \n :if ([ :len (\$Symbols->\$Name) ] = 0) do={\r\ + \n :log warning (\$0 . \"No symbol available for name '\" . \$Name . \"\ + '!\");\r\ + \n :return \"\";\r\ + \n }\r\ + \n :return ([ \$EitherOr (\$Special->(\$Name . \$Magic)) (\$Symbols->\$Na\ + me) ] . \"\\EF\\B8\\8F\");\r\ + \n}\r\ + \n\r\ + \n:set SymbolForNotification do={\r\ + \n :global NotificationsWithSymbols;\r\ + \n :global SymbolByUnicodeName;\r\ + \n :global IfThenElse;\r\ + \n :if (\$NotificationsWithSymbols != true) do={:return [ \$IfThenElse ([\ + \_:len \$2 ] > 0) ([ :tostr \$2 ] . \" \") \"\" ]}\r\ + \n :local Return \"\";\r\ + \n :foreach Symbol in=[ :toarray \$1 ] do={:set Return (\$Return . [ \$Sy\ + mbolByUnicodeName \$Symbol ])}\r\ + \n :return (\$Return . \" \");\r\ + \n}\r\ + \n\r\ + \n:set Unix2Dos do={:return [ :tocrlf [ :tostr \$1 ] ]}\r\ + \n:set UrlEncode do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :if ([ :len \$Input ] = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :local Chars (\"\\n\\r !\\\"#\\\$%&'()*+,:;<=>\?@[\\\\]^`{|}~\");\r\ + \n :local Subs { \"%0A\"; \"%0D\"; \"%20\"; \"%21\"; \"%22\"; \"%23\"; \"\ + %24\"; \"%25\"; \"%26\"; \"%27\";\r\ + \n \"%28\"; \"%29\"; \"%2A\"; \"%2B\"; \"%2C\"; \"%3A\"; \"%3B\"; \ + \"%3C\"; \"%3D\"; \"%3E\"; \"%3F\";\r\ + \n \"%40\"; \"%5B\"; \"%5C\"; \"%5D\"; \"%5E\"; \"%60\"; \"%7B\"; \ + \"%7C\"; \"%7D\"; \"%7E\" };\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :local Replace [ :find \$Chars \$Char ];\r\ + \n :if ([ :typeof \$Replace ] = \"num\") do={:set Char (\$Subs->\$Repla\ + ce)}\r\ + \n :set Return (\$Return . \$Char);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set IsDNSResolving do={\r\ + \n :do {:resolve \"api.telegram.org\"} on-error={:return false}\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set IsFullyConnected do={\r\ + \n :global IsDNSResolving;\r\ + \n :global IsTimeSync;\r\ + \n :if ([ \$IsDNSResolving ] = false) do={:return false};\r\ + \n :if ([ \$IsTimeSync ] = false) do={:return false};\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set IsTimeSync do={\r\ + \n :global IsTimeSyncCached;\r\ + \n :global IsTimeSyncResetNtp;\r\ + \n :if (\$IsTimeSyncCached = true) do={:return true}\r\ + \n :if ([ /system/ntp/client/get enabled ] = true) do={\r\ + \n :if ([ /system/ntp/client/get status ] = \"synchronized\") do={\r\ + \n :set IsTimeSyncCached true;\r\ + \n :return true;\r\ + \n }\r\ + \n :local Uptime [ /system/resource/get uptime ];\r\ + \n :if ([ :typeof \$IsTimeSyncResetNtp ] = \"nothing\") do={\r\ + \n :set IsTimeSyncResetNtp \$Uptime;\r\ + \n }\r\ + \n :if (\$Uptime - \$IsTimeSyncResetNtp < 3m) do={:return false}\r\ + \n :log warning \"\$0 The ntp client is configured, but did not sync.\"\ + ;\r\ + \n :set IsTimeSyncResetNtp \$Uptime;\r\ + \n /system/ntp/client/set enabled=no;\r\ + \n :delay 20ms;\r\ + \n /system/ntp/client/set enabled=yes;\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ /system/license/get ]->\"level\" = \"free\" || \\\r\ + \n [ /system/resource/get ]->\"board-name\" = \"x86\") do={\r\ + \n :log debug \"\$0 No ntp client configured, relying on RTC for CHR fr\ + ee license and x86.\";\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ /ip/cloud/get update-time ] = true) do={\r\ + \n :if ([ :typeof [ /ip/cloud/get public-address ] ] = \"ip\") do={\r\ + \n :set IsTimeSyncCached true;\r\ + \n :return true;\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :log debug \"\$0 No time source configured! Returning gracefully...\";\ + \r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set FetchHugeFiles do={\r\ + \n :local ScriptName [ :tostr \$1 ];\r\ + \n :local Url [ :tostr \$2 ];\r\ + \n :local CheckCert [ :tostr \$3 ];\r\ + \n\r\ + \n :global CleanName;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global MkDir;\r\ + \n :global RmDir;\r\ + \n :global RmFile;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :set CheckCert [ \$IfThenElse (\$CheckCert = \"false\") \"no\" \"yes-w\ + ithout-crl\" ];\r\ + \n\r\ + \n :local DirName (\"tmpfs/\" . [ \$CleanName \$ScriptName ]);\r\ + \n :if ([ \$MkDir \$DirName ] = false) do={\r\ + \n \$LogPrint error \$0 (\"Failed creating directory!\");\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :local FileName (\$DirName . \"/\" . [ \$CleanName \$0 ] . \"-\" . [ \ + \$GetRandom20CharAlNum ]);\r\ + \n :onerror Err {\r\ + \n /tool/fetch check-certificate=\$CheckCert \$Url dst-path=\$FileName \ + \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$ScriptName ] }) as-va\ + lue;\r\ + \n } do={\r\ + \n :if ([ \$WaitForFile \$FileName 500ms ] = true) do={\r\ + \n \$RmFile \$FileName;\r\ + \n }\r\ + \n \$LogPrint debug \$0 (\"Failed downloading from \" . \$Url . \" - \"\ + \_. \$Err);\r\ + \n \$RmDir \$DirName;\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$WaitForFile \$FileName 5s ] = false) do={\r\ + \n \$LogPrint debug \$0 (\"The file downloaded from \" . \$Url . \" did\ + \_not show up.\");\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :local FileSize [ /file/get \$FileName size ];\r\ + \n :local Return \"\";\r\ + \n :local VarSize 0;\r\ + \n :while (\$VarSize != \$FileSize) do={\r\ + \n :set Return (\$Return . ([ /file/read offset=\$VarSize chunk-size=32\ + 768 file=\$FileName as-value ]->\"data\"));\r\ + \n :set FileSize [ /file/get \$FileName size ];\r\ + \n :set VarSize [ :len \$Return ];\r\ + \n :if (\$VarSize > \$FileSize) do={\r\ + \n :delay 100ms;\r\ + \n }\r\ + \n }\r\ + \n \$RmDir \$DirName;\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set FetchUserAgentStr do={\r\ + \n :local Caller [ :tostr \$1 ];\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global CommitBrief;\r\ + \n :global IfThenElse;\r\ + \n :local Resource [ /system/resource/get ];\r\ + \n :return (\"User-Agent: Mikrotik/\" . \$Resource->\"version\" . \" \" .\ + \_\$Resource->\"architecture-name\" . \\\r\ + \n \" \" . \$Caller . \"/Fetch (\" . \$GlobalScriptsUrl . \"; \" . [ \$\ + CommitBrief ] . \")\");\r\ + \n}\r\ + \n\r\ + \n:set ScriptInstallUpdate do={ :onerror Err {\r\ + \n :local Scripts [ :toarray \$1 ];\r\ + \n :local NewComment [ :tostr \$2 ];\r\ + \n :global InitSetup;\r\ + \n\r\ + \n :global CommitId;\r\ + \n :global ScriptUpdatesCRLF;\r\ + \n :global GlobalConfVersion;\r\ + \n :global Identity;\r\ + \n :global CertificateAvailable;\r\ + \n :global CheckSumsVerify;\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global SumsDirAndFileName;\r\ + \n :global GlobalScriptsUrlSuffix;\r\ + \n :global EitherOr;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global Grep;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global ParseKeyValueStore;\r\ + \n :global RequiredRouterOS;\r\ + \n :global SendNotification2;\r\ + \n :global SymbolForNotification;\r\ + \n :global ValidateSyntax;\r\ + \n :global CommentsInScripts;\r\ + \n\r\ + \n :if ([ \$CertificateAvailable \"Root YE\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading certificate failed, trying wit\ + hout.\");\r\ + \n }\r\ + \n\r\ + \n :foreach Script in=\$Scripts do={\r\ + \n :if ([ :len [ /system/script/find where name=\$Script ] ] = 0) do={\ + \r\ + \n \$LogPrint info \$0 (\"Adding new script: \" . \$Script);\r\ + \n /system/script/add name=\$Script owner=\$Script source=\"#!rsc by \ + Vados\\n\" comment=\$NewComment;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :local CommitIdBefore \$CommitId;\r\ + \n :local GlobalConfVersionBefore \$GlobalConfVersion;\r\ + \n :local ReloadGlobal false;\r\ + \n :local DeviceMode [ /system/device-mode/get ];\r\ + \n\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :local CheckSums ({});\r\ + \n :do {\r\ + \n :local Url (\$GlobalScriptsUrl . \$SumsDirAndFileName . \$GlobalScr\ + iptsUrlSuffix);\r\ + \n \$LogPrint debug \$0 (\"Fetching checksums from url: \" . \$Url);\ + \r\ + \n :set CheckSums [ :deserialize from=json ([ /tool/fetch check-certi\ + ficate=yes-without-crl \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$0 ] }) \$Url output\ + =user as-value ]->\"data\") ];\r\ + \n } on-error={ \$LogPrint warning \$0 (\"Err = \" . \$Err); }\r\ + \n }\r\ + \n\r\ + \n :foreach Script in=[ /system/script/find where source~\"^#!rsc by Vado\ + s\" ] do={\r\ + \n :local ScriptVal [ /system/script/get \$Script ];\r\ + \n :local ScriptInfo [ \$ParseKeyValueStore (\$ScriptVal->\"comment\") \ + ];\r\ + \n :local SourceNew;\r\ + \n\r\ + \n :foreach Scheduler in=[ /system/scheduler/find where on-event~(\"\\\ + \\b\" . \$ScriptVal->\"name\" . \"\\\\b\") ] do={\r\ + \n :local SchedulerVal [ /system/scheduler/get \$Scheduler ];\r\ + \n :if (\$ScriptVal->\"policy\" != \$SchedulerVal->\"policy\") do={\r\ + \n \$LogPrint warning \$0 (\"Policies differ for script '\" . \$Scr\ + iptVal->\"name\" . \"' and its scheduler '\" . \$SchedulerVal->\"name\" . \ + \"'!\");\r\ + \n }\r\ + \n }\r\ + \n :do {\r\ + \n :if (\$ScriptInfo->\"ignore\" = true) do={\r\ + \n \$LogPrint debug \$0 (\"Ignoring script '\" . \$ScriptVal->\"nam\ + e\" . \"', as requested.\");\r\ + \n :error true;\r\ + \n }\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :local CheckSum (\$CheckSums->(\$ScriptVal->\"name\"));\r\ + \n :if ([ :len (\$ScriptInfo->\"base-url\") ] = 0 && [ :len (\$Scri\ + ptInfo->\"url-suffix\") ] = 0 && \\\r\ + \n [ :convert transform=md5 to=hex [ :tolf (\$ScriptVal->\"sou\ + rce\") ] ] = \$CheckSum) do={\r\ + \n \$LogPrint debug \$0 (\"Checksum for script '\" . \$ScriptVal-\ + >\"name\" . \"' matches, ignoring.\");\r\ + \n :error true;\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len (\$ScriptInfo->\"certificate\") ] > 0) do={\r\ + \n :if ([ \$CertificateAvailable (\$ScriptInfo->\"certificate\") \"\ + fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading certificate failed, tryi\ + ng without.\");\r\ + \n }\r\ + \n }\r\ + \n :onerror Err {\r\ + \n :local BaseUrl [ \$EitherOr (\$ScriptInfo->\"base-url\") \$Globa\ + lScriptsUrl ];\r\ + \n :local UrlSuffix [ \$EitherOr (\$ScriptInfo->\"url-suffix\") \$G\ + lobalScriptsUrlSuffix ];\r\ + \n :local Url (\$BaseUrl . \$ScriptVal->\"name\" . \".rsc\" . \$Url\ + Suffix);\r\ + \n \$LogPrint warning \$0 (\"Fetching script '\" . (\$ScriptVal->\"\ + name\") . \"' from url: \" . \$Url);\r\ + \n :local Result [ /tool/fetch check-certificate=yes-without-crl \\\ + \r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$0 ] }) \$Url outp\ + ut=user as-value ];\r\ + \n :if (\$Result->\"status\" = \"finished\") do={\r\ + \n :set SourceNew [ :tolf (\$Result->\"data\") ];\r\ + \n } \r\ + \n } do={\r\ + \n \$LogPrint warning \$0 (\"Failed fetching script '\" . \$ScriptV\ + al->\"name\" . \"': \" . \$Err);\r\ + \n :if (\$Err != \"Fetch failed with status 404\") do={\r\ + \n :error false;\r\ + \n }\r\ + \n :if (\$ScriptVal->\"source\" = \"#!rsc by Vados\") do={\r\ + \n \$LogPrint warning \$0 (\"Removing dummy. Typo on installation\ + \?\");\r\ + \n /system/script/remove \$Script;\r\ + \n :error false;\r\ + \n }\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :if ([ :len (\$ScriptInfo->\"base-url\") ] = 0 && [ :len (\$Sc\ + riptInfo->\"url-suffix\") ] = 0 && \\\r\ + \n [ :len \$CheckSum ] = 0) do={\r\ + \n \$LogPrint warning \$0 (\"Added the script manually\? Skip u\ + pdates with 'ignore=true' in comment.\");\r\ + \n }\r\ + \n }\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$SourceNew ] = 0) do={\r\ + \n \$LogPrint debug \$0 (\"No update for script '\" . (\$ScriptVal-\ + >\"name\") . \"'.\");\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :local SourceCRLF [ :tocrlf \$SourceNew ];\r\ + \n :if (\$InitSetup != true && ((\$ScriptVal->\"name\") != \"AM-Globa\ + lConfig\" || (\$ScriptVal->\"name\") != \"AM-GlobalFunc\")) do={\r\ + \n :if (\$SourceNew = (\$ScriptVal->\"source\") || \$SourceCRLF = (\ + \$ScriptVal->\"source\")) do={\r\ + \n \$LogPrint debug \$0 (\"Script '\" . (\$ScriptVal->\"name\") \ + . \"' source did not change. End of lines set to CRLF.\");\r\ + \n if (\$ScriptUpdatesCRLF = false) do={\r\ + \n :put [ :tolf \$SourceNew ];\r\ + \n \$LogPrint debug \$0 (\"Script '\" . (\$ScriptVal->\"name\"\ + ) . \"' source update end of lines to LF.\");\r\ + \n }\r\ + \n :error false;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if ([ :pick \$SourceNew 0 14 ] != \"#!rsc by Vados\") do={\r\ + \n \$LogPrint warning \$0 (\"Looks like new script '\" . \$ScriptVa\ + l->\"name\" . \"' is not valid (missing shebang = '\$shebang') Ignoring!\"\ + );\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if (\$CommentsInScripts = true) do={\r\ + \n :local CommentLine [ \$Grep \$SourceNew (\"\\23 Script comment:\ + \_\") ];\r\ + \n :if ([ :len \$CommentLine ] = 0) do={\r\ + \n \$LogPrint debug \$0 (\"The search in new script '\" . (\$Scr\ + iptVal->\"name\") . \"' for comment in script body, is not found result Ig\ + noring!\");\r\ + \n :error false;\r\ + \n } else={\r\ + \n :if ([ :len (\$ScriptVal->\"comment\")] = 0 || [ :len (\$Scri\ + ptVal->\"comment\")] != [ :pick \$CommentLine ([ :find \$CommentLine \":\"\ + ] + 2) [ :len \$CommentLine] ]) do={\r\ + \n \$LogPrint debug \$0 (\"Exsiting comment for script '\" . (\ + \$ScriptVal->\"name\") . \"' is '\" . (\$ScriptVal->\"comment\") . \"'.\")\ + ;\r\ + \n :set (\$ScriptVal->\"comment\") [ :pick \$CommentLine ([ :f\ + ind \$CommentLine \":\"] + 2) [ :len \$CommentLine] ];\r\ + \n \$LogPrint debug \$0 (\"New comment (\" . (\$ScriptVal->\"c\ + omment\") . \") for script '\" . (\$ScriptVal->\"name\") . \"' has been ad\ + ded!\");\r\ + \n } else={\r\ + \n \$LogPrint debug \$0 (\"Comment for script '\" . (\$ScriptV\ + al->\"name\") . \"' did not change.\");\r\ + \n :error false;\r\ + \n }\r\ + \n } \r\ + \n }\r\ + \n\r\ + \n :local RequiredROS ([ \$ParseKeyValueStore [ \$Grep \$SourceNew (\ + \"\\23 requires RouterOS, \") ] ]->version);\r\ + \n :if ([ \$RequiredRouterOS \$0 [ \$EitherOr \$RequiredROS \"0.0\" ]\ + \_false ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"The script '\" . (\$ScriptVal->\"name\ + \") . \"' requires RouterOS \" . \\\r\ + \n \$RequiredROS . \", which is not met by your installation. I\ + gnoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n :local RequiredDM [ \$ParseKeyValueStore [ \$Grep \$SourceNew (\"\ + \\23 requires device-mode, \") ] ];\r\ + \n :local MissingDM ({});\r\ + \n :foreach Feature,Value in=\$RequiredDM do={\r\ + \n :if ([ :typeof (\$DeviceMode->\$Feature) ] = \"bool\" && (\$Devi\ + ceMode->\$Feature) = false) do={\r\ + \n :set MissingDM (\$MissingDM, \$Feature);\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len \$MissingDM ] > 0) do={\r\ + \n \$LogPrint warning \$0 (\"The script '\" . (\$ScriptVal->\"name\ + \") . \"' requires disabled \" . \\\r\ + \n \"device-mode features (\" . [ :tostr \$MissingDM ] . \"). I\ + gnoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$ValidateSyntax \$SourceNew ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Syntax validation for script '\" . (\$\ + ScriptVal->\"name\") . \"' failed! Ignoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n \r\ + \n :log info (\"Updating script: \" . (\$ScriptVal->\"name\") . \" Sc\ + ript comment: \" . (\$ScriptVal->\"comment\"));\r\ + \n\r\ + \n /system/script/set owner=(\$ScriptVal->\"name\") comment=(\$Script\ + Val->\"comment\") \\\r\ + \n source=[ \$IfThenElse (\$ScriptUpdatesCRLF = true) \$SourceCRL\ + F \$SourceNew ] \$Script;\r\ + \n :if (\$ScriptVal->\"name\" = \"AM-GlobalConfig\" || \\\r\ + \n \$ScriptVal->\"name\" = \"AM-GlobalFunc\" || \\\r\ + \n \$ScriptVal->\"name\" ~ (\"^(AM-GlobalFunc\\\\.d|mod)/.\")) d\ + o={\r\ + \n :set ReloadGlobal true;\r\ + \n }\r\ + \n } on-error={ }\r\ + \n }\r\ + \n :if (\$ReloadGlobal = true) do={\r\ + \n \$LogPrint info \$0 (\"Reloading global configuration and functions.\ + \");\r\ + \n :onerror Err {\r\ + \n :global InitSetup (a);\r\ + \n /system/script {run AM-GlobalConfig; run AM-GlobalFunc;};\r\ + \n } do={\$LogPrint error \$0 (\"Reloading global configuration and fun\ + ctions failed! \" . \$Err)}\r\ + \n }\r\ + \n :if (\$GlobalConfVersionBefore > \$GlobalConfVersion) do={\r\ + \n \$LogPrint warning \$0 (\"The configuration version decreased from \ + \" . \\\r\ + \n \$GlobalConfVersionBefore . \" to \" . \$GlobalConfVersion . \\\r\ + \n \". Installed an older version\?\");\r\ + \n }\r\ + \n :if (\$GlobalConfVersionBefore < \$GlobalConfVersion) do={\r\ + \n :global GlobalConfChanges;\r\ + \n :global GlobalConfMigration;\r\ + \n :local ChangeLogCode;\r\ + \n :if ([ :len \$ChangeLogCode ] > 0) do={\r\ + \n :if ([ \$ValidateSyntax \$ChangeLogCode ] = true) do={\r\ + \n :onerror Err {\r\ + \n [ :parse \$ChangeLogCode ];\r\ + \n } do={\$LogPrint warning \$0 (\"The changelog failed to run: \" \ + . \$Err)}\r\ + \n } else={\$LogPrint warning \$0 (\"The changelog failed syntax vali\ + dation!\")}\r\ + \n }\r\ + \n :if ([ :len \$GlobalConfMigration ] > 0) do={\r\ + \n :for I from=(\$GlobalConfVersionBefore + 1) to=\$GlobalConfVersion\ + \_do={\r\ + \n :local Migration (\$GlobalConfMigration->[ :tostr \$I ]);\r\ + \n :do {\r\ + \n :if ([ :typeof \$Migration ] != \"str\") do={\r\ + \n \$LogPrint debug \$0 (\"Migration code for change \" . \$I .\ + \_\" is not available.\");\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$ValidateSyntax \$Migration ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Migration code for change \" . \$I\ + \_. \" failed syntax validation!\");\r\ + \n :error false}\r\ + \n \$LogPrint info \$0 (\"Applying migration for change \" . \$I \ + . \": \" . \$Migration);\r\ + \n :onerror Err {\r\ + \n [ :parse \$Migration ];\r\ + \n } do={\$LogPrint warning \$0 (\"Migration code for change \" .\ + \_\$I . \" failed to run: \" . \$Err)}\r\ + \n } on-error={ }\r\ + \n }\r\ + \n }\r\ + \n :local NotificationMessage (\"The configuration version on \" . \$Id\ + entity . \" increased \" . \\\r\ + \n \"to \" . \$GlobalConfVersion . \", current configuration may nee\ + d modification. \" . \\\r\ + \n \"Please review and update global-config-overlay, then re-run glo\ + bal-config.\");\r\ + \n \$LogPrint info \$0 (\$NotificationMessage);\r\ + \n :if ([ :len \$GlobalConfChanges ] > 0) do={\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n\\nChanges:\ + \");\r\ + \n :for I from=(\$GlobalConfVersionBefore + 1) to=\$GlobalConfVersion\ + \_do={\r\ + \n :local Change (\$GlobalConfChanges->[ :tostr \$I ]);\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n \" . \\\ + \r\ + \n [ \$SymbolForNotification \"pushpin\" \"*\" ] . \$Change);\r\ + \n \$LogPrint info \$0 (\"Change \" . \$I . \": \" . \$Change);\r\ + \n }\r\ + \n } else={\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n\\nNews and\ + \_changes are not available.\");\r\ + \n\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$GlobalConfChanges ] > 0) do={ :set GlobalConfChanges; \ + } else={ :global GlobalConfChanges (a); }\r\ + \n :if ([ :len \$GlobalConfMigration ] > 0) do={ :set GlobalConfMigrati\ + on; } else={ :global GlobalConfMigration (a); }\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n\r\ + \n# load modules\r\ + \n:foreach Script in=[ /system/script/find where name ~ \"^(global-functio\ + ns\\\\.d|mod)/.\" ] do={\r\ + \n :local ScriptVal [ /system/script/get \$Script ];\r\ + \n :if ([ \$ValidateSyntax (\$ScriptVal->\"source\") ] = true) do={\r\ + \n :onerror Err {\r\ + \n /system/script/run \$Script;\r\ + \n } do={\r\ + \n \$LogPrint error \$0 (\"Module '\" . \$ScriptVal->\"name\" . \"' f\ + ailed to run: \" . \$Err);\r\ + \n }\r\ + \n } else={\r\ + \n \$LogPrint error \$0 (\"Module '\" . \$ScriptVal->\"name\" . \"' fai\ + led syntax validation, skipping.\");\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# Add and fix global scheduler\r\ + \n:put \"Add or/and fix Global scheduler...\";\r\ + \n:local OnEvent \"/system/script { run AM-GlobalConfig; run AM-GlobalFunc\ + ; }\";\r\ + \n:local Comment \"Every Day Set Global Environment Variables\";\r\ + \n:if ([ :len [ /system/scheduler/find where name=\"AM-SetGlobalEnv\" ] ] \ + = 0) do={\r\ + \n /system/scheduler/add name=\"AM-SetGlobalEnv\" comment=\$Comment on-ev\ + ent=\$OnEvent start-time=startup;\r\ + \n}\r\ + \n:if ([ /system/scheduler/get \"AM-SetGlobalEnv\" comment ] != \$Comment)\ + \_do={\r\ + \n /system/scheduler/set \"AM-SetGlobalEnv\" comment=\$Comment;\r\ + \n}\r\ + \n:if ([ /system/scheduler/get \"AM-SetGlobalEnv\" on-event ] != \$OnEvent\ + ) do={\r\ + \n /system/scheduler/set \"AM-SetGlobalEnv\" on-event=\$OnEvent;\r\ + \n}\r\ + \n# Add Update Script scheduler\r\ + \n:put \"Add or/and fix Update Script scheduler\";\r\ + \n:set OnEvent \":global ScriptInstallUpdate; \\\$ScriptInstallUpdate;\";\ + \r\ + \n:set Comment \"Every Day Check For Update Scripts\";\r\ + \n:if ([ :len [ /system/scheduler/find where name=\"AM-ScriptInstallUpdate\ + \" ] ] = 0) do={\r\ + \n/system/scheduler/add name=\"AM-ScriptInstallUpdate\" comment=\$Comment \ + start-time=startup interval=1d on-event=\$OnEvent;\r\ + \n}\r\ + \n\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady;\r\ + \n :retry { :if (\$GlobalConfReady != true) do={ :error (\"Global configs\ + \_not ready.\"); }; } delay=100ms max=5;\r\ + \n\r\ + \n:log warning (\"This script \$ScriptName Loaded \" . \\\r\ + \n [ \$IfThenElse (\$CommitId != \"unknown\") ([ \$CommitBrief ] . \" \ + \") ] . \\\r\ + \n \"on \" . \$devBoardName . \"\\nRouterOS: \" . \$devCurVersion . \"\ + \\nArchitecture: \" . \$devCurArch . \"\\nIdentity: \" . \$Identity);\r\ + \n # signal we are ready\r\ + \n:set GlobalFuncReady true;\r\ + \n:set ExitOK true;\r\ + \n\r\ + \n:log info (\$0 . \"Global configs ready!\");\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n}\r\ + \n" +add dont-require-permissions=no name=fetchCustom owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + \n\r\ + \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ + 0.30.30.10.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.10.pub\";\ + \n\r\ + \n\r\ + \n#\r\ + \n\ + \n#root.mnuc.backuper\ + \n\ + \n\r\ + \n\r\ + \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ + 0.30.30.81.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.81.pub\";\ + \n\r\ + \n\r\ + \n#:local Script AM-GlobalConfig;\r\ + \n\ + \n#/system/script/add name=\$Script owner=\$Script source=([ /tool/fetch c\ + heck-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \".r\ + sc\") output=user as-value ]->\"data\");\ + \n\r\ + \n#/usb1/mikro_bak_rsa.pub\r\ + \n\r\ + \n\ + \n#:local FetchFile AM-DOHCert.rsc\ + \n\ + \n\r\ + \n\r\ + \n:global GlobalScriptsUrl \"https://ros.vados.ru/tmp/\";\r\ + \n\ + \n:global MkDir;\ + \n\r\ + \n\ + \n:local FetchFile Manager@id_rsa.pub\ + \n:local localDir \"/\";\r\ + \n\r\ + \n\ + \n\ + \n\$MkDir \$localDir;\ + \n\ + \n\r\ + \n\r\ + \n/tool/fetch mode=https url=(\$GlobalScriptsUrl . \$FetchFile) dst-path=(\ + \$localDir . \$FetchFile);\ + \n" +add dont-require-permissions=yes name=MkTmpfs owner=vados policy=\ + ftp,read,write,policy,test,sniff,sensitive source=\ + ":global MkDir;\r\ + \n\r\ + \n\$MkDir /usb1/tmpfs;" +add dont-require-permissions=no name=CreateContainer owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + local contIF \"veth1\";\r\ + \n:local contIP \"10.10.10.10/24\";\r\ + \n:local contGW \"10.10.10.1\";\r\ + \n#/interface/veth/add name=\$contIF address=\$contIP gateway=\$contGW;\r\ + \n:local contBridgeIF \"Bridge-DOC\";\r\ + \n:local contBridgeIP \"10.10.10.1/24\";\r\ + \n/interface/bridge/add name=\$contBridgeIF;\r\ + \n/ip/address/add address=\$contBridgeIP interface=\$contBridgeIF;\r\ + \n/interface/bridge/port/add bridge=\$contBridgeIF interface=\$contIF;\r\ + \n/container/config/set ram-high=768M registry-url=https://registry-1.dock\ + er.io tmpdir=sata1/tmp\r\ + \n" +add dont-require-permissions=no name=carkeeperbot owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="/\ + container/mounts/add list=carkeeper_configs/.profile src=sata1/carkeeper/c\ + onfigs/.profile dst=/root/.ashrc;\r\ + \n/container/mounts/add list=carkeeper_configs/php.ini src=sata1/carkeeper\ + /configs/php.ini dst=/etc/php82/php.ini;\r\ + \n#/container/mounts/add list=carkeeper_configs src=sata1/carkeeper/config\ + s dst=/configs;\r\ + \n/container/mounts/add list=carkeeper_app src=sata1/carkeeper/app dst=/ap\ + p;\r\ + \n/container/mounts/add list=carkeeper_scripts src=sata1/carkeeper/scripts\ + \_dst=/scripts;\r\ + \n/container/mounts/add list=carkeeper_configs/sshd_config src=sata1/carke\ + eper/configs/sshd_config dst=/etc/ssh/sshd_config;\r\ + \n\r\ + \n/container/mounts/add list=carkeeper_tmp src=sata1/carkeeper/tmp dst=/va\ + r/tmp;\r\ + \n/container/mounts/add list=carkeeper_sync src=sata1/carkeeper/sync dst=/\ + var/sync;\r\ + \n" +add comment="Enable or disable ip addresses based on bridge port state" \ + dont-require-permissions=no name=mod/AM-IPbridge owner=mod/AM-IPbridge \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="#!rsc by Vados\r\ + \n# RouterOS script: mod/AM-IPbridge\r\ + \n# Script comment: Enable or disable ip addresses based on bridge port st\ + ate\r\ + \n# Copyright (c) 2005-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch\r\ + \n#\r\ + \n:local ScriptName [ :jobname ];\r\ + \n\r\ + \n# Add this Script scheduler\r\ + \n:if ([ :len [ /system/scheduler/find where name=\$ScriptName ] ] = 0) do\ + ={\r\ + \n:log warning (\"SystemScheduler for script: \" . \$ScriptName . \" is NO\ + T SET! Fix It Now!\");\r\ + \n:local OnEvent \"/system/script/run \$ScriptName;\";\r\ + \n:local Comment \"Every Day Check For Fake Bridges\";\r\ + \n/system/scheduler/add name=\$ScriptName comment=\$Comment start-time=sta\ + rtup interval=1d on-event=\$OnEvent;\r\ + \n}\r\ + \n\r\ + \n:foreach Bridge in=[ /interface/bridge/find ] do={\r\ + \n :local BrName [ /interface/bridge/get \$Bridge name ];\r\ + \n :if ([ :len [ /interface/bridge/port/find where bridge=\$BrName ] ] > \ + 0) do={\r\ + \n :if ([ :len [ /interface/bridge/port/find where bridge=\$BrName and \ + inactive=no ] ] = 0) do={\r\ + \n /ip/address/disable [ find where !dynamic interface=\$BrName ];\r\ + \n } else={\r\ + \n /ip/address/enable [ find where !dynamic interface=\$BrName ];\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n" +add comment="Use Telegram to chat with your Router and send commands" \ + dont-require-permissions=no name=mod/AM-TG-Bot owner=mod/AM-TG-Bot \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="#!rsc by Vados\r\ + \n# RouterOS script: mod/AM-TG-Bot\r\ + \n# Script comment: Use Telegram to chat with your Router and send command\ + s\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch\r\ + \n:local ExitOK false;\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady; :global GlobalFuncReady;\r\ + \n :retry { :if (\$GlobalConfReady != true || \$GlobalFuncReady != true) \ + \\\r\ + \n do={ :error (\"Global config and/or functions not ready.\"); }; } \ + delay=500ms max=50;\r\ + \n :local ScriptName [ :jobname ];\r\ + \n\r\ + \n :global Identity;\r\ + \n :global TelegramChatActive;\r\ + \n :global TelegramChatGroups;\r\ + \n :global TelegramChatId;\r\ + \n :global TelegramChatIdsTrusted;\r\ + \n :global TelegramChatOffset;\r\ + \n :global TelegramChatRunTime;\r\ + \n :global TelegramMessageIDs;\r\ + \n :global TelegramRandomDelay;\r\ + \n :global TelegramTokenId;\r\ + \n\r\ + \n :global CertificateAvailable;\r\ + \n :global EitherOr;\r\ + \n :global EscapeForRegEx;\r\ + \n :global FileExists;\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global LogPrintVerbose;\r\ + \n :global MAX;\r\ + \n :global MIN;\r\ + \n :global MkDir;\r\ + \n :global RandomDelay;\r\ + \n :global RmDir;\r\ + \n :global ScriptLock;\r\ + \n :global SendTelegram2;\r\ + \n :global SymbolForNotification;\r\ + \n :global ValidateSyntax;\r\ + \n :global WaitForFile;\r\ + \n :global WaitFullyConnected;\r\ + \n\r\ + \n :if ([ \$ScriptLock \$ScriptName ] = false) do={\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n \$WaitFullyConnected;\r\ + \n\r\ + \n :if ([ :len [ /system/scheduler/find where name=\$ScriptName ] ] = 0) \ + do={\r\ + \n /system/scheduler/add start-time=startup interval=30s name=\$ScriptN\ + ame on-event=\"/system/script/run \$ScriptName;\" comment=\"Telegram Chat \ + Bot\";\r\ + \n }\r\ + \n :if ([ :typeof \$TelegramChatOffset ] != \"array\") do={\r\ + \n :set TelegramChatOffset { 0; 0; 0 };\r\ + \n }\r\ + \n :if ([ :typeof \$TelegramRandomDelay ] != \"num\") do={\r\ + \n :set TelegramRandomDelay 0;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$CertificateAvailable \"Go Daddy Root Certificate Authority - \ + G2\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$ScriptName (\"Downloading required certificate \ + failed.\");\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n \$RandomDelay \$TelegramRandomDelay;\r\ + \n\r\ + \n :local Data false;\r\ + \n :for I from=1 to=4 do={\r\ + \n :if (\$Data = false) do={\r\ + \n :onerror Err {\r\ + \n :set Data ([ /tool/fetch check-certificate=yes-without-crl outpu\ + t=user \\\r\ + \n (\"https://api.telegram.org/bot\" . \$TelegramTokenId . \"/get\ + Updates\?offset=\" . \\\r\ + \n \$TelegramChatOffset->0 . \"&allowed_updates=%5B%22message%22%\ + 5D\") as-value ]->\"data\");\r\ + \n :set TelegramRandomDelay [ \$MAX 0 (\$TelegramRandomDelay - 1) ]\ + ;\r\ + \n } do={\r\ + \n :if (\$I < 4) do={\r\ + \n \$LogPrint debug \$ScriptName (\"Fetch failed, \" . \$I . \". \ + try: \" . \$Err);\r\ + \n :set TelegramRandomDelay [ \$MIN 15 (\$TelegramRandomDelay + 5\ + ) ];\r\ + \n :delay ((\$I * \$I) . \"s\");\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if (\$Data = false) do={\r\ + \n \$LogPrint warning \$ScriptName (\"Failed getting updates.\");\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :local JSON [ :deserialize from=json value=\$Data ];\r\ + \n :local UpdateID 0;\r\ + \n :local Uptime [ /system/resource/get uptime ];\r\ + \n :foreach Update in=(\$JSON->\"result\") do={\r\ + \n :set UpdateID (\$Update->\"update_id\");\r\ + \n \$LogPrintVerbose debug \$ScriptName (\"Update \" . \$UpdateID . \":\ + \_\" . [ :serialize to=json \$Update ]);\r\ + \n\r\ + \n :local Message (\$Update->\"message\");\r\ + \n :local IsAnyReply ([ :typeof (\$Message->\"reply_to_message\") ] = \ + \"array\");\r\ + \n :local IsMyReply (\$TelegramMessageIDs->[ :tostr (\$Message->\"reply\ + _to_message\"->\"message_id\") ]);\r\ + \n :if ((\$IsMyReply = 1 || \$TelegramChatOffset->0 > 0 || \$Uptime > 5\ + m) && \$UpdateID >= \$TelegramChatOffset->2) do={\r\ + \n :local Trusted false;\r\ + \n :local Chat (\$Message->\"chat\");\r\ + \n :local From (\$Message->\"from\");\r\ + \n :local Command (\$Message->\"text\");\r\ + \n :local ThreadId [ \$IfThenElse (\$Message->\"is_topic_message\") (\ + \$Message->\"message_thread_id\") \"\" ];\r\ + \n\r\ + \n :foreach IdsTrusted in=(\$TelegramChatId, \$TelegramChatIdsTrusted\ + ) do={\r\ + \n :if (\$From->\"id\" = \$IdsTrusted || \\\r\ + \n \$From->\"username\" = \$IdsTrusted || \\\r\ + \n \$Chat->\"id\" = \$IdsTrusted) do={\r\ + \n :set Trusted true;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if (\$Trusted = true) do={\r\ + \n :local Done false;\r\ + \n :if (\$Command = \"\?\") do={\r\ + \n \$LogPrint info \$ScriptName (\"Sending notice for update \" .\ + \_\$UpdateID . \".\");\r\ + \n \$SendTelegram2 ({ origin=\$ScriptName; chatid=(\$Chat->\"id\"\ + ); silent=true; \\\r\ + \n replyto=(\$Message->\"message_id\"); threadid=\$ThreadId; \\\ + \r\ + \n subject=([ \$SymbolForNotification \"speech-balloon\" ] . \"\ + Telegram Chat\"); \\\r\ + \n message=([ \$IfThenElse ([ :len (\$From->\"first_name\") ] >\ + \_0) (\"Hello \" . (\$From->\"first_name\") . \"!\\n\\n\") ] . \\\r\ + \n \"Online\" . [ \$IfThenElse \$TelegramChatActive \" (and a\ + ctive!)\" ] . \", awaiting your commands!\") });\r\ + \n :set Done true;\r\ + \n }\r\ + \n :if (\$Done = false && [ :pick \$Command 0 1 ] = \"!\") do={\r\ + \n :if (\$Command ~ (\"^! *(\" . [ \$EscapeForRegEx \$Identity ] \ + . \"|@\" . \$TelegramChatGroups . \")\\\$\")) do={\r\ + \n :set TelegramChatActive true;\r\ + \n } else={\r\ + \n :set TelegramChatActive false;\r\ + \n }\r\ + \n \$LogPrint info \$ScriptName (\"Now \" . [ \$IfThenElse \$Tele\ + gramChatActive \"active\" \"passive\" ] . \\\r\ + \n \" from update \" . \$UpdateID . \"!\");\r\ + \n :set Done true;\r\ + \n }\r\ + \n :if (\$Done = false && (\$IsMyReply = 1 || (\$IsAnyReply = false\ + \_&& \\\r\ + \n \$TelegramChatActive = true)) && [ :len \$Command ] > 0) do\ + ={\r\ + \n :if ([ \$ValidateSyntax \$Command ] = true) do={\r\ + \n :local State \"\";\r\ + \n :local File (\"tmpfs/telegram-chat/\" . [ \$GetRandom20CharA\ + lNum 6 ]);\r\ + \n :if ([ \$MkDir \"tmpfs/telegram-chat\" ] = false) do={\r\ + \n \$LogPrint error \$ScriptName (\"Failed creating directory\ + !\");\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n \$LogPrint info \$ScriptName (\"Running command from update \ + \" . \$UpdateID . \": \" . \$Command);\r\ + \n :execute script=(\":do {\\n\" . \$Command . \"\\n} on-error=\ + { /file/add name=\\\"\" . \$File . \".failed\\\" };\" . \\\r\ + \n \"/file/add name=\\\"\" . \$File . \".done\\\"\") file=(\$\ + File . \"\\00\");\r\ + \n :if ([ \$WaitForFile (\$File . \".done\") [ \$EitherOr \$Tel\ + egramChatRunTime 20s ] ] = false) do={\r\ + \n :set State ([ \$SymbolForNotification \"warning-sign\" ] .\ + \_\"The command did not finish, still running in background.\\n\\n\");\r\ + \n }\r\ + \n :if ([ \$FileExists (\$File . \".failed\") ] = true) do={\r\ + \n :set State ([ \$SymbolForNotification \"cross-mark\" ] . \ + \"The command failed with an error!\\n\\n\");\r\ + \n }\r\ + \n :local Content ([ /file/read chunk-size=32768 file=\$File as\ + -value ]->\"data\");\r\ + \n \$SendTelegram2 ({ origin=\$ScriptName; chatid=(\$Chat->\"id\ + \"); silent=true; \\\r\ + \n replyto=(\$Message->\"message_id\"); threadid=\$ThreadId; \ + \\\r\ + \n subject=([ \$SymbolForNotification \"speech-balloon\" ] . \ + \"Telegram Chat\"); \\\r\ + \n message=([ \$SymbolForNotification \"gear\" ] . \"Command:\ + \\n\" . \$Command . \"\\n\\n\" . \\\r\ + \n \$State . [ \$IfThenElse ([ :len \$Content ] > 0) \\\r\ + \n ([ \$SymbolForNotification \"memo\" ] . \"Output:\\n\" .\ + \_\$Content) \\\r\ + \n ([ \$SymbolForNotification \"memo\" ] . \"No output.\") \ + ]) });\r\ + \n \$RmDir \"tmpfs/telegram-chat\";\r\ + \n } else={\r\ + \n \$LogPrint info \$ScriptName (\"The command from update \" .\ + \_\$UpdateID . \" failed syntax validation!\");\r\ + \n \$SendTelegram2 ({ origin=\$ScriptName; chatid=(\$Chat->\"id\ + \"); silent=false; \\\r\ + \n replyto=(\$Message->\"message_id\"); threadid=\$ThreadId; \ + \\\r\ + \n subject=([ \$SymbolForNotification \"speech-balloon\" ] . \ + \"Telegram Chat\"); \\\r\ + \n message=([ \$SymbolForNotification \"gear\" ] . \"Command:\ + \\n\" . \$Command . \"\\n\\n\" . \\\r\ + \n [ \$SymbolForNotification \"cross-mark\" ] . \"The comma\ + nd failed syntax validation!\") });\r\ + \n }\r\ + \n }\r\ + \n } else={\r\ + \n :local MessageText (\"Received a message from untrusted contact \ + \" . \\\r\ + \n [ \$IfThenElse ([ :len (\$From->\"username\") ] = 0) \"without\ + \_username\" (\"'\" . \$From->\"username\" . \"'\") ] . \\\r\ + \n \" (ID \" . \$From->\"id\" . \") in update \" . \$UpdateID . \ + \"!\");\r\ + \n :if (\$Command ~ (\"^! *\" . [ \$EscapeForRegEx \$Identity ] . \ + \"\\\$\")) do={\r\ + \n \$LogPrint warning \$ScriptName \$MessageText;\r\ + \n \$SendTelegram2 ({ origin=\$ScriptName; chatid=(\$Chat->\"id\"\ + ); silent=false; \\\r\ + \n replyto=(\$Message->\"message_id\"); threadid=\$ThreadId; \\\ + \r\ + \n subject=([ \$SymbolForNotification \"speech-balloon\" ] . \"\ + Telegram Chat\"); \\\r\ + \n message=(\"You are not trusted.\") });\r\ + \n } else={\r\ + \n \$LogPrint info \$ScriptName \$MessageText;\r\ + \n }\r\ + \n }\r\ + \n } else={\r\ + \n \$LogPrint debug \$ScriptName (\"Already handled update \" . \$Upd\ + ateID . \".\");\r\ + \n }\r\ + \n }\r\ + \n :set TelegramChatOffset ([ :pick \$TelegramChatOffset 1 3 ], \\\r\ + \n [ \$IfThenElse (\$UpdateID >= \$TelegramChatOffset->2) (\$UpdateID +\ + \_1) (\$TelegramChatOffset->2) ]);\r\ + \n} do={\r\ + \n :global ExitError; \$ExitError \$ExitOK [ :jobname ] \$Err;\r\ + \n}\r\ + \n" +add comment="Script send Notifications in Telegram" dont-require-permissions=\ + no name=mod/AM-TG-Notifications owner=mod/AM-TG-Notifications policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: mod/AM-TG-Notifications\r\ + \n# Script comment: Script send Notifications in Telegram\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch, scheduler\r\ + \n:global FlushTelegramQueue;\r\ + \n:global GetTelegramChatId;\r\ + \n:global NotificationFunctions;\r\ + \n:global PurgeTelegramQueue;\r\ + \n:global SendTelegram;\r\ + \n:global SendTelegram2;\r\ + \n# flush telegram queue\r\ + \n:set FlushTelegramQueue do={ :onerror Err {\r\ + \n :global TelegramQueue;\r\ + \n :global TelegramMessageIDs;\r\ + \n :global CertificateAvailable;\r\ + \n :global IsFullyConnected;\r\ + \n :global LogPrint;\r\ + \n :if ([ \$IsFullyConnected ] = false) do={\r\ + \n \$LogPrint debug \$0 (\"System is not fully connected, not flushing.\ + \");\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ \$CertificateAvailable \"Go Daddy Root Certificate Authority - \ + G2\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading required certificate failed.\"\ + );\r\ + \n :return false;\r\ + \n }\r\ + \n :local AllDone true;\r\ + \n :local QueueLen [ :len \$TelegramQueue ];\r\ + \n :if ([ :len [ /system/scheduler/find where name=\"_FlushTelegramQueue\ + \" ] ] > 0 && \$QueueLen = 0) do={\r\ + \n \$LogPrint warning \$0 (\"Flushing Telegram messages from scheduler,\ + \_but queue is empty.\");\r\ + \n }\r\ + \n :foreach Id,Message in=\$TelegramQueue do={\r\ + \n :if ([ :typeof \$Message ] = \"array\" ) do={\r\ + \n :onerror Err {\r\ + \n :local Data ([ /tool/fetch check-certificate=yes-without-crl out\ + put=user http-method=post \\\r\ + \n (\"https://api.telegram.org/bot\" . (\$Message->\"tokenid\") .\ + \_\"/sendMessage\") \\\r\ + \n http-data=(\$Message->\"http-data\") as-value ]->\"data\");\r\ + \n :set (\$TelegramQueue->\$Id);\r\ + \n :set (\$TelegramMessageIDs->[ :tostr ([ :deserialize from=json v\ + alue=\$Data ]->\"result\"->\"message_id\") ]) 1;\r\ + \n } do={\r\ + \n \$LogPrint debug \$0 (\"Sending queued Telegram message failed: \ + \" . \$Err);\r\ + \n :set AllDone false;\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n :if (\$AllDone = true && \$QueueLen = [ :len \$TelegramQueue ]) do={\r\ + \n /system/scheduler/remove [ find where name=\"_FlushTelegramQueue\" ]\ + ;\r\ + \n :set TelegramQueue;\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n# get the chat id\r\ + \n:set GetTelegramChatId do={ :onerror Err {\r\ + \n :global TelegramTokenId;\r\ + \n :global CertificateAvailable;\r\ + \n :global LogPrint;\r\ + \n :if ([ \$CertificateAvailable \"Go Daddy Root Certificate Authority - \ + G2\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading required certificate failed.\"\ + );\r\ + \n :return false;\r\ + \n }\r\ + \n :local Data;\r\ + \n :onerror Err {\r\ + \n :set Data ([ /tool/fetch check-certificate=yes-without-crl output=us\ + er \\\r\ + \n (\"https://api.telegram.org/bot\" . \$TelegramTokenId . \"/getUpd\ + ates\?offset=0\" . \\\r\ + \n \"&allowed_updates=%5B%22message%22%5D\") as-value ]->\"data\");\ + \r\ + \n } do={\r\ + \n \$LogPrint warning \$0 (\"Fetching data failed: \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :local JSON [ :deserialize from=json value=\$Data ];\r\ + \n :local Count [ :len (\$JSON->\"result\") ];\r\ + \n :if (\$Count = 0) do={\r\ + \n \$LogPrint info \$0 (\"No message received.\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local Message (\$JSON->\"result\"->(\$Count - 1)->\"message\");\r\ + \n \$LogPrint info \$0 (\"The chat id is: \" . (\$Message->\"chat\"->\"id\ + \"));\r\ + \n :if ((\$Message->\"is_topic_message\") = true) do={\r\ + \n \$LogPrint info \$0 (\"The thread id is: \" . (\$Message->\"message_\ + thread_id\"));\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n# send notification via telegram - expects one array argument\r\ + \n:set (\$NotificationFunctions->\"telegram\") do={\r\ + \n :local Notification \$1;\r\ + \n :global Identity;\r\ + \n :global IdentityExtra;\r\ + \n :global TelegramChatId;\r\ + \n :global TelegramChatIdOverride;\r\ + \n :global TelegramMessageIDs;\r\ + \n :global TelegramQueue;\r\ + \n :global TelegramThreadId;\r\ + \n :global TelegramThreadIdOverride;\r\ + \n :global TelegramTokenId;\r\ + \n :global TelegramTokenIdOverride;\r\ + \n :global CertificateAvailable;\r\ + \n :global CharacterReplace;\r\ + \n :global EitherOr;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global ProtocolStrip;\r\ + \n :global SymbolForNotification;\r\ + \n :global UrlEncode;\r\ + \n :local EscapeMD do={\r\ + \n :local Text [ :tostr \$1 ];\r\ + \n :local Mode [ :tostr \$2 ];\r\ + \n :local Excl [ :tostr \$3 ];\r\ + \n :global CharacterReplace;\r\ + \n :global IfThenElse;\r\ + \n :local Chars {\r\ + \n \"body\"={ \"\\\\\"; \"`\" };\r\ + \n \"plain\"={ \"_\"; \"*\"; \"[\"; \"]\"; \"(\"; \")\"; \"~\"; \"`\"\ + ; \">\";\r\ + \n \"#\"; \"+\"; \"-\"; \"=\"; \"|\"; \"{\"; \"}\"; \".\"; \ + \"!\" };\r\ + \n }\r\ + \n :foreach Char in=(\$Chars->\$Mode) do={\r\ + \n :if ([ :typeof [ :find \$Excl \$Char ] ] = \"nil\") do={\r\ + \n :set Text [ \$CharacterReplace \$Text \$Char (\"\\\\\" . \$Char)\ + \_];\r\ + \n }\r\ + \n }\r\ + \n :if (\$Mode = \"body\") do={:return (\"```\\n\" . \$Text . \"\\n```\ + \")}\r\ + \n :return \$Text;\r\ + \n }\r\ + \n :local ChatId [ \$EitherOr (\$Notification->\"chatid\") \\\r\ + \n [ \$EitherOr (\$TelegramChatIdOverride->(\$Notification->\"origin\")\ + ) \$TelegramChatId ] ];\r\ + \n :local ThreadId [ \$EitherOr (\$Notification->\"threadid\") \\\r\ + \n [ \$EitherOr (\$TelegramThreadIdOverride->(\$Notification->\"origin\ + \")) \\\r\ + \n [ \$IfThenElse ([ :len (\$TelegramChatIdOverride->(\$Notification->\ + \"origin\")) ] = 0) \$TelegramThreadId ] ] ];\r\ + \n :local TokenId [ \$EitherOr (\$TelegramTokenIdOverride->(\$Notificatio\ + n->\"origin\")) \$TelegramTokenId ];\r\ + \n :if ([ :len \$TokenId ] = 0 || [ :len \$ChatId ] = 0) do={:return fals\ + e}\r\ + \n :if ([ :typeof \$TelegramMessageIDs ] = \"nothing\") do={:set Telegram\ + MessageIDs ({})}\r\ + \n :local Truncated false;\r\ + \n :local Text (\"*__\" . [ \$EscapeMD (\"[\" . \$IdentityExtra . \$Ident\ + ity . \"] \" . \\\r\ + \n (\$Notification->\"subject\")) \"plain\" ] . \"__*\\n\\n\");\r\ + \n :local LenSubject [ :len \$Text ];\r\ + \n :local LenMessage [ :len (\$Notification->\"message\") ];\r\ + \n :local LenLink ([ :len (\$Notification->\"link\") ] * 2);\r\ + \n :local LenSum (\$LenSubject + \$LenMessage + \$LenLink);\r\ + \n :if (\$LenSum > 3968) do={\r\ + \n :set Text (\$Text . [ \$EscapeMD ([ :pick (\$Notification->\"message\ + \") 0 (3840 - \$LenSubject - \$LenLink) ] . \"...\") \"body\" ]);\r\ + \n :set Truncated true;\r\ + \n } else={:set Text (\$Text . [ \$EscapeMD (\$Notification->\"message\")\ + \_\"body\" ])}\r\ + \n :if (\$LenLink > 0) do={\r\ + \n :set Text (\$Text . \"\\n\" . [ \$SymbolForNotification \"link\" ] .\ + \_\\\r\ + \n \"[\" . [ \$EscapeMD [ \$ProtocolStrip (\$Notification->\"link\") \ + ] \"plain\" ] . \"]\" . \\\r\ + \n \"(\" . [ \$EscapeMD (\$Notification->\"link\") \"plain\" ] . \")\ + \");\r\ + \n }\r\ + \n :if (\$Truncated = true) do={\r\ + \n :set Text (\$Text . \"\\n\" . [ \$SymbolForNotification \"scissors\"\ + \_] . \\\r\ + \n [ \$EscapeMD (\"The message was too long and has been truncated, c\ + ut off _\" . \\\r\ + \n ((\$LenSum - [ :len \$Text ]) * 100 / \$LenSum) . \"%_!\") \"plain\ + \" \"_\" ]);\r\ + \n }\r\ + \n :local HTTPData (\"chat_id=\" . \$ChatId . \"&disable_notification=\" \ + . (\$Notification->\"silent\") . \\\r\ + \n \"&reply_to_message_id=\" . (\$Notification->\"replyto\") . \"&mes\ + sage_thread_id=\" . \$ThreadId . \\\r\ + \n \"&disable_web_page_preview=true&parse_mode=MarkdownV2\");\r\ + \n :onerror Err {\r\ + \n :if ([ \$CertificateAvailable \"Go Daddy Root Certificate Authority \ + - G2\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading required certificate failed.\ + \");\r\ + \n :error false;\r\ + \n }\r\ + \n :local Data ([ /tool/fetch check-certificate=yes-without-crl output=\ + user http-method=post \\\r\ + \n (\"https://api.telegram.org/bot\" . \$TokenId . \"/sendMessage\") \ + \\\r\ + \n http-data=(\$HTTPData . \"&text=\" . [ \$UrlEncode \$Text ]) as-va\ + lue ]->\"data\");\r\ + \n :set (\$TelegramMessageIDs->[ :tostr ([ :deserialize from=json value\ + =\$Data ]->\"result\"->\"message_id\") ]) 1;\r\ + \n } do={\r\ + \n \$LogPrint info \$0 (\"Failed sending Telegram notification: \" . \$\ + Err . \" - Queuing...\");\r\ + \n :if ([ :typeof \$TelegramQueue ] = \"nothing\") do={:set TelegramQue\ + ue ({})}\r\ + \n :set Text (\$Text . \"\\n\" . [ \$SymbolForNotification \"alarm-cloc\ + k\" ] . \\\r\ + \n [ \$EscapeMD (\"This message was queued since _\" . [ /system/cloc\ + k/get date ] . \\\r\ + \n \" \" . [ /system/clock/get time ] . \"_ and may be obsolete.\") \ + \"plain\" \"_\" ]);\r\ + \n :set (\$TelegramQueue->[ :len \$TelegramQueue ]) { tokenid=\$TokenId\ + ;\r\ + \n http-data=(\$HTTPData . \"&text=\" . [ \$UrlEncode \$Text ]) };\r\ + \n :if ([ :len [ /system/scheduler/find where name=\"_FlushTelegramQueu\ + e\" ] ] = 0) do={\r\ + \n /system/scheduler/add name=\"_FlushTelegramQueue\" interval=1m sta\ + rt-time=startup \\\r\ + \n on-event=(\":global FlushTelegramQueue; \\\$FlushTelegramQueue;\ + \");\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n# purge the Telegram queue\r\ + \n:set PurgeTelegramQueue do={\r\ + \n :global TelegramQueue;\r\ + \n /system/scheduler/remove [ find where name=\"_FlushTelegramQueue\" ];\ + \r\ + \n :set TelegramQueue;\r\ + \n}\r\ + \n# send notification via telegram - expects at least two string arguments\ + \r\ + \n:set SendTelegram do={ :onerror Err {\r\ + \n :global SendTelegram2;\r\ + \n \$SendTelegram2 ({ origin=\$0; subject=\$1; message=\$2; link=\$3; sil\ + ent=\$4 });\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n# send notification via telegram - expects one array argument\r\ + \n:set SendTelegram2 do={\r\ + \n :local Notification \$1;\r\ + \n :global NotificationFunctions;\r\ + \n (\$NotificationFunctions->\"telegram\") (\"\\\$NotificationFunctions->\ + \\\"telegram\\\"\") \$Notification;\r\ + \n}\r\ + \n" +add dont-require-permissions=no name=testIfThenElseAndEitherOr owner=vados \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source=":local ExitOK false;\r\ + \n:local ScriptName [ :jobname ];\r\ + \n\r\ + \n:local ExitError;\r\ + \n#:local IfThenElse;\r\ + \n#:local EitherOr;\r\ + \n\r\ + \n:local one 11;\r\ + \n:local two 8;\r\ + \n#:log warning ([ \$IfThenElse (\$one != 0) \$one \$two ]);\r\ + \n:local Test; # [ :totime \"22s\" ];\r\ + \n\r\ + \n:local IfThenElse do={:if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1 ] \ + = true) do={:return \$2}; :return \$3;}\r\ + \n:local EitherOr do={\r\ + \n :if ([ :typeof \$1 ] = \"num\") do={:if ((\$1 != 0) = true) do={:retur\ + n \$1} else={:return \$2};}\r\ + \n :if ([ :typeof \$1 ] = \"time\") do={:if ((\$1 > 0s) = true) do={:retu\ + rn \$1} else={:return \$2};}\r\ + \n :if ([ :len [ :tostr \$1 ] ] > 0) do={:return \$1} else={:return \$2};\ + \r\ + \n}\r\ + \n\r\ + \n:log warning ([ \$IfThenElse (\$one != 0) \$one \$two ]);\r\ + \n\r\ + \n:set Test [ \$EitherOr \$Test \"\" ];\r\ + \n:log warning (\"\\\$ Test = \" . \$Test);\r\ + \n\r\ + \n:set ExitError do={\r\ + \n :local ExitOK [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Error [ :tostr \$3 ];\r\ + \n :local IfThenElse;\r\ + \n :if (\$ExitOK = \"false\") do={\r\ + \n :log error (\$Name [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\\\$\") \ + \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with er\ + ror\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \ + \$Error) \".\" ]);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n\r\ + \n#:log warning ([ \$IfThenElse (\$one != 0) \$one \$two ]);\r\ + \n\r\ + \n:set ExitOK true;\r\ + \n\$ExitError \$ExitOK \$ScriptName \"Err test custom\";" +add comment="Update and Backup send notification to EMail" \ + dont-require-permissions=no name=AM-Backup-UpdateEmail owner=\ + AM-Backup-UpdateEmail policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-Backup-UpdateEmail\r\ + \n# Script comment: Update and Backup send notification to EMail\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version>=6.43.7\r\ + \n# requires device-mode, fetch\r\ + \n#\r\ + \n# Updated: 15/04/2025\r\ + \n# Website: https://github.com/beeyev\r\ + \n# Notification e-mail (Make sure you have configured Email settings in T\ + ools -> Email)\r\ + \n:local ExitOK false;\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady; :global GlobalFuncReady;\r\ + \n :retry { :if (\$GlobalConfReady != true || \$GlobalFuncReady != true) \ + \\\r\ + \n do={ :error (\"Global config and/or functions not ready.\"); }; } \ + delay=500ms max=50;\r\ + \n :local ScriptName [ :jobname ];\r\ + \n\r\ + \n:local IfThenElse do={:if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1 ] \ + = true) do={:return \$2}; :return \$3;}\r\ + \n:local EitherOr do={\r\ + \n :if ([ :typeof \$1 ] = \"num\") do={:if ((\$1 != 0) = true) do={:retur\ + n \$1} else={:return \$2};}\r\ + \n :if ([ :typeof \$1 ] = \"time\") do={:if ((\$1 > 0s) = true) do={:retu\ + rn \$1} else={:return \$2};}\r\ + \n :if ([ :len [ :tostr \$1 ] ] > 0) do={:return \$1} else={:return \$2};\ + \r\ + \n}\r\ + \n:local ExitError do={\r\ + \n :local ExitOK [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Error [ :tostr \$3 ];\r\ + \n :if (\$ExitOK = \"false\") do={:log error (\$Name [ \$IfThenElse ([ :p\ + ick \$Name 0 1 ] = \"\\\$\") \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with er\ + ror\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \ + \$Error) \".\" ]);}\r\ + \n}\r\ + \n\r\ + \n:global LogPrint;\r\ + \n:global EditSchedulers;\r\ + \n:global DevInfoIncludeIP;\r\ + \n\r\ + \n:local SchedInterval \"7d\";\r\ + \n:local SchedStartTime \"03:41:27\";\r\ + \n:local SchedComment \"Every 7 Day Check For Update and Backup send notif\ + ication to EMail.\";\r\ + \n\r\ + \n# Function to add or edit schedulers for scripts, to ensure they are set\ + \_up correctly\r\ + \n# How to use:\r\ + \n# Call this function with the following parameters:\r\ + \n# 1. \$1 - Scheduler name (usually same as script name) [*]\r\ + \n# 2. \$2 - Interval (e.g. \"1d\" (default is \"1d\")) [**]\r\ + \n# 3. \$3 - Start Time (e.g. \"00:00:00\", default is current time) [**]\ + \r\ + \n# 4. \$4 - Comment (optional, default: \"Scheduler for script: \")\ + \_[**]\r\ + \n# 5. \$5 - On Event command (e.g. \"/system/script { run ; }\" (de\ + fault is \"/system/script { run \$SchedName; }\")) [**]\r\ + \n# 6. \$6 - Policy (e.g. \"ftp,reboot,read,write,policy,test,password,sni\ + ff,sensitive,romon\" (default is the same)) [**]\r\ + \n# 7. \$7 - Start Date (e.g. \"jan/01/1970\", default is current date) [*\ + *]\r\ + \n# Notes:\r\ + \n# * - Scheduler name is required, and is used to identify the scheduler\ + \_to create or edit. It is usually the same as the script name (\$ScriptNa\ + me).\r\ + \n# ** - If optional parameters are not provided, default values will be u\ + sed.\r\ + \n# This allows for quick setup with minimal parameters, while still \ + providing flexibility for customization.\r\ + \n# Example usage:\r\ + \n# :if ([ \$EditSchedulers \"my-script\" ] = false) do={:set ExitOK false\ + ; :error true;}\r\ + \n# :if ([ \$EditSchedulers \"my-script\" \"1d\" \"03:41:27\" \"My custom \ + scheduler comment\" false false \"jan/01/1970\" ] = false) do={:set ExitOK\ + \_false; :error true;}\r\ + \n\r\ + \n:set EditSchedulers do={\r\ + \n :local SchedName [ :tostr \$1 ];\r\ + \n :if ([ :len \$SchedName ] = 0) do={ :return false; }\r\ + \n :local Interval [ :tostr \$2 ];\r\ + \n :if ([ :len \$Interval ] = 0) do={ :set Interval \"1d\"; }\r\ + \n :local StartTime [ :tostr \$3 ];\r\ + \n :if ([ :len \$StartTime ] = 0) do={ :set StartTime [/system/clock/get \ + time]; }\r\ + \n :local Comment [ :tostr \$4 ];\r\ + \n :if ([ :len \$Comment ] = 0) do={ :set Comment \"Scheduler for script:\ + \_\$SchedName\"; }\r\ + \n :local OnEvent [ :tostr \$5 ];\r\ + \n :if ([ :len \$OnEvent ] = 0) do={ :set OnEvent \"/system/script { run \ + \$SchedName; }\"; }\r\ + \n :local Policy [ :tostr \$6 ];\r\ + \n :if ([ :len \$Policy ] = 0) do={ :set Policy \"ftp,reboot,read,write,p\ + olicy,test,password,sniff,sensitive,romon\"; }\r\ + \n :local StartDate [ :tostr \$7 ];\r\ + \n :if ([ :len \$StartDate ] = 0) do={ :set StartDate [/system/clock/get \ + date]; }\r\ + \n\r\ + \n :onerror Err {\r\ + \n # Check if scheduler exists, if not - create it\r\ + \n :if ([ :len [ /system/scheduler/find where name=\$SchedName ] ] = 0)\ + \_do={\r\ + \n :log warning (\$SchedName . \"System Scheduler for script: \" . \$\ + SchedName . \" is NOT SET! Fix It Now!\");\r\ + \n /system/scheduler/add comment=\$Comment interval=\$Interval name=\ + \$SchedName on-event=\$OnEvent policy=\$Policy start-date=\$StartDate star\ + t-time=\$StartTime;\r\ + \n }\r\ + \n # Check if scheduler parameters are correct, if not - fix them\r\ + \n :if ([ /system/scheduler/get \$SchedName comment ] != \$Comment) do=\ + {\r\ + \n /system/scheduler/set \$SchedName comment=\$Comment;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName interval ] != \$Interval) d\ + o={\r\ + \n /system/scheduler/set \$SchedName interval=\$Interval;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName on-event ] != \$OnEvent) do\ + ={\r\ + \n /system/scheduler/set \$SchedName on-event=\$OnEvent;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName policy ] != \$Policy) do={\ + \r\ + \n /system/scheduler/set \$SchedName policy=\$Policy;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName start-date ] != \$StartDate\ + ) do={\r\ + \n /system/scheduler/set \$SchedName start-date=\$StartDate;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName start-time ] != \$StartTime\ + ) do={\r\ + \n /system/scheduler/set \$SchedName start-time=\$StartTime;\r\ + \n }\r\ + \n } do={:log error (\$SchedName . \"Failed to add/modify scheduler for '\ + \" . \$SchedName . \"' failed: \" . \$Err); :return false};\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:if ([ \$EditSchedulers \$ScriptName \$SchedInterval \$SchedStartTime \$\ + SchedComment ] = false) do={\r\ + \n :log error (\$ScriptName . \"Failed to add/modify scheduler for \" . \ + \$ScriptName . \"!\");\r\ + \n :set ExitOK true;\r\ + \n :error true;\r\ + \n}\r\ + \n\r\ + \n# Local Function CheckOSUpdateMode\r\ + \n:local CheckOSUpdateMode;\r\ + \n:set CheckOSUpdateMode do={\r\ + \n :local NewMode [ :tostr \$1 ];\r\ + \n :if ([ :len \$NewMode ] = 0) do={\r\ + \n :log error (\$0 . \"/nFunction /\$CheckOSUpdateMode received empty v\ + alue!\");\r\ + \n :return false;\r\ + \n :error true;\r\ + \n }\r\ + \n\r\ + \n :global BackupAndUpdateMode;\r\ + \n :global OldMode [ :tostr \$BackupAndUpdateMode ];\r\ + \n :if (\$NewMode != \$BackupAndUpdateMode && \$NewMode = \"updateOnce\")\ + \_do={\r\ + \n :set BackupAndUpdateMode [ :tostr \$NewMode ];\r\ + \n :log info (\$0 . \"/nOk. /\$BackupAndUpdateMode set to: '\" . \$NewM\ + ode . \"'.\\nYou're a good boy! ))\\nI'm launching the update in 5 seconds\ + .\");\r\ + \n :delay 5;\r\ + \n /system/script { run \$ScriptName; };\r\ + \n } else={\r\ + \n :log warning (\$0 . \"/nFunction /\$CheckOSUpdateMode received value: \ + '\" . \$NewMode . \"', but is may be only \\\"updateOnce\\\"!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n :error false;\r\ + \n}\r\ + \n\r\ + \n# :if ([ :len [ /system/scheduler/find where name=\"running-from-backup\ + -partition\" ] ] > 0) do={\r\ + \n# \$LogPrint warning \$ScriptName (\"Running from backup partition, r\ + efusing to act.\");\r\ + \n# :set ExitOK true;\r\ + \n# :error false;\r\ + \n# }\r\ + \n\r\ + \n:global EmailGeneralTo;\r\ + \n:global BackupAndUpdateMode;\r\ + \n:global forceBackup;\r\ + \n:global BackupPassword;\r\ + \n:global BackupSens;\r\ + \n:global updateChannel;\r\ + \n\r\ + \n:global DevInfoIncludeIP;\r\ + \n\r\ + \n# Installs patch updates only (BackupAndUpdateMode = \"osupdate\").\r\ + \n# Works for `stable` and `long-term` channels.\r\ + \n# Updates only if MAJOR.MINOR match (e.g. 6.43.2 > 6.43.6 allowed, 6.44.\ + 1 skipped).\r\ + \n# Sends info if a newer (non-patch) version is found.\r\ + \n:local installOnlyPatchUpdates false;\r\ + \n:local scriptVersion \"26.04.30\";\r\ + \n:local copyrightUrl \"https://www.github.com/vados-dev/\";\r\ + \n#Script messages prefix\r\ + \n:local SMP \"BkpAndUpdate:\";\r\ + \n\r\ + \n:local exitErrorMessage \"\$SMP script stopped due to an error. Please c\ + heck logs for more details.\";\r\ + \n:log info \"\\n\\n\$SMP Script \\\"Mikrotik RouterOS automatic backup & \ + update\\\" v.\$scriptVersion started.\";\r\ + \n:log info \"\$SMP Script Mode: `\$BackupAndUpdateMode`, Update channel: \ + `\$updateChannel`, Force backup: `\$forceBackup`, Install only patch updat\ + es: `\$installOnlyPatchUpdates`\";\r\ + \n\r\ + \n## vv FUNCTIONS vv ##\r\ + \n:global WaitCheckUpdates; \$WaitCheckUpdates;\r\ + \n:global MiniDateTimeStamp;\r\ + \n:local MDTSFix [\$MiniDateTimeStamp];\r\ + \n:global Identity;\r\ + \n:global IdentityShort;\r\ + \n:global devBoardName;\r\ + \n:global runningVersion;\r\ + \n:global runningChannel;\r\ + \n:global devModel;\r\ + \n:global devRbSerialNumber;\r\ + \n:global ROsVerAvail;\r\ + \n:global PkgUpdStatus;\r\ + \n\r\ + \n# Checks if two RouterOS version strings differ only by the patch versio\ + n\r\ + \n# :put [\$FuncIsPatchUpdateOnly \"6.2.1\" \"6.2.4\"] # Output: true\r\ + \n# :put [\$FuncIsPatchUpdateOnly \"6.2.1\" \"6.3.1\"] # Output: false\r\ + \n:local FuncIsPatchUpdateOnly do={\r\ + \n :local ver1 \$1;\r\ + \n :local ver2 \$2;\r\ + \n # Extract the major and minor components from a version\r\ + \n :local extractMajorMinor do={\r\ + \n :local ver \$1;\r\ + \n :local dot1 [:find \$ver \".\"];\r\ + \n :if (\$dot1 = -1) do={:return \$ver}\r\ + \n :local major [:pick \$ver 0 \$dot1];\r\ + \n :local rest [:pick \$ver (\$dot1 + 1) [:len \$ver]];\r\ + \n :local dot2 [:find \$rest \".\"];\r\ + \n :local minor \$rest;\r\ + \n :if (\$dot2 >= 0) do={:set minor [:pick \$rest 0 \$dot2]}\r\ + \n :return (\$major . \".\" . \$minor);\r\ + \n }\r\ + \n\r\ + \n# Compare the major and minor components of both version strings\r\ + \n:if ([\$extractMajorMinor \$ver1] = [\$extractMajorMinor \$ver2]) do={:r\ + eturn true}\r\ + \n :return false;\r\ + \n}\r\ + \n# Creates backups and returns array of names\r\ + \n# Possible arguments:\r\ + \n# \$1 - file name, without extension\r\ + \n# \$2 - password (optional)\r\ + \n# \$3 - sensitive data in config (optional, default: false)\r\ + \n# Example:\r\ + \n#:put [\$FuncCreateBackups \$backupName]\r\ + \n:local FuncCreateBackups do={\r\ + \n :local backupName [ :tostr \$1 ];\r\ + \n :local BackPass \$2;\r\ + \n :local BackSens \$3;\r\ + \n\r\ + \n #Script messages prefix\r\ + \n :local SMP \"BkpAndUpdate:\";\r\ + \n :local exitErrorMessage \"\$SMP script stopped due to an error. Please\ + \_check logs for more details.\";\r\ + \n :log info (\"\$SMP global function `FuncCreateBackups` started, input:\ + \_`\$backupName`\");\r\ + \n\r\ + \n # validate required parameter: backupName\r\ + \n :if ([:typeof \$backupName] != \"str\" or [:len \$backupName] = 0) do=\ + {\r\ + \n :log error \"\$SMP parameter 'backupName' is required and must be a \ + non-empty string\";\r\ + \n# :log warning \"\$SMP parameter '\\\$backupName' is required and mus\ + t be a non-empty string.\\nI set '\\\$backupName' to 'default-backup'\";\r\ + \n# :set \$backupName \"default-backup\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n\r\ + \n :local backupFileSys \"\$backupName.backup\";\r\ + \n :local backupFileConfig \"\$backupName.rsc\";\r\ + \n :local backupNames {\$backupFileSys;\$backupFileConfig};\r\ + \n\r\ + \n ## Perform system backup\r\ + \n :if ([:len \$BackPass] = 0) do={\r\ + \n :log info (\"\$SMP starting backup without password, backup name: `\ + \$backupName`\");\r\ + \n /system backup save dont-encrypt=yes name=\$backupName;\r\ + \n } else={\r\ + \n :log info (\"\$SMP starting backup with password, backup name: `\$ba\ + ckupName`\");\r\ + \n /system backup save password=\$BackPass name=\$backupName;\r\ + \n }\r\ + \n :log info (\"\$SMP system backup created: `\$backupFileSys`\");\r\ + \n ## Export config file\r\ + \n :if (\$BackSens = true) do={\r\ + \n :log info (\"\$SMP starting export config with sensitive data, backu\ + p name: `\$backupName`\");\r\ + \n # Since RouterOS v7 it needs to be explicitly set that we want to ex\ + port sensitive data\r\ + \n :if ([:pick [/system resource get version] 0 1] < 7) do={\r\ + \n :execute \"/export compact terse file=\$backupName\";\r\ + \n } else={\r\ + \n :execute \"/export compact show-sensitive terse file=\$backupName\ + \";\r\ + \n }\r\ + \n } else={\r\ + \n :log info (\"\$SMP starting export config without sensitive data, ba\ + ckup name: `\$backupName`\");\r\ + \n /export compact hide-sensitive terse file=\$backupName;\r\ + \n }\r\ + \n :log info (\"\$SMP Config export complete: `\$backupFileConfig`\");\r\ + \n :log info (\"\$SMP Waiting a little to ensure backup files are written\ + \");\r\ + \n :delay 20;\r\ + \n :if ([:len [/file find name=\$backupFileSys]] > 0) do={\r\ + \n :log info (\"\$SMP system backup file successfully saved to the file\ + \_system: `\$backupFileSys`\");\r\ + \n } else={\r\ + \n :log error (\"\$SMP system backup was not created, file does not exi\ + st: `\$backupFileSys`\");\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n :if ([:len [/file find name=\$backupFileConfig]] > 0) do={\r\ + \n :log info (\"\$SMP config backup file successfully saved to the file\ + \_system: `\$backupFileConfig`\");\r\ + \n } else={\r\ + \n :log error (\"\$SMP config backup was not created, file does not exi\ + st: `\$backupFileConfig`\");\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n :log info (\"\$SMP global function `FuncCreateBackups` finished. Creat\ + ed backups, system: `\$backupFileSys`, config: `\$backupFileConfig`\")\r\ + \n :return \$backupNames;\r\ + \n}\r\ + \n# Sends an email\r\ + \n# Parameters:\r\ + \n# \$1 - to (email address)\r\ + \n# \$2 - subject\r\ + \n# \$3 - body\r\ + \n# \$4 - file attachments (optional; pass \"\" if not needed)\r\ + \n#\r\ + \n# Example:\r\ + \n# \$FuncSendEmailSafe \"admin@domain.com\" \"Backup Done\" \"Backup comp\ + lete.\" \"backup1.backup\"\r\ + \n:local FuncSendEmailSafe do={\r\ + \n :global EmailGeneralTo;\r\ + \n :local emailSubject \$2;\r\ + \n :local emailBody \$3;\r\ + \n :local emailAttachments \$4;\r\ + \n :local SMP \"Bkp&Upd:\";\r\ + \n :local exitErrorMessage \"\$SMP script stopped due to an error. Please\ + \_check logs for more details.\";\r\ + \n :log info \"\$SMP Attempting to send email to `\$EmailGeneralTo`\";\r\ + \n # SAFETY: wait for any previously queued email to finish\r\ + \n :local waitTimeoutPre 60;\r\ + \n :local waitCounterPre 0;\r\ + \n :while (([/tool e-mail get last-status] = \"resolving-dns\" or [/tool \ + e-mail get last-status] = \"in-progress\")) do={\r\ + \n :if (\$waitCounterPre >= \$waitTimeoutPre) do={\r\ + \n :log error \"\$SMP Email send aborted: previous send did not compl\ + ete after \$waitTimeoutPre seconds\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n :log info \"\$SMP Waiting for previous email to finish (status: \$[/\ + tool e-mail get last-status])...\";\r\ + \n :delay 1; \r\ + \n :set waitCounterPre (\$waitCounterPre + 1);\r\ + \n }\r\ + \n # Send the email\r\ + \n :do {\r\ + \n /tool e-mail send to=\$EmailGeneralTo subject=\$emailSubject body=\$\ + emailBody file=\$emailAttachments;\r\ + \n } on-error={\r\ + \n :log error \"\$SMP Email send command failed to execute. Check logs \ + and verify email settings.\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n # Wait for send status to change from \"in-progress\" / \"resolving-dn\ + s\"\r\ + \n :local waitTimeout 60;\r\ + \n :local waitCounter 0;\r\ + \n :local emailStatus \"\";\r\ + \n :log info \"\$SMP Waiting for email to be sent, timeout in `\$waitTime\ + out` seconds...\";\r\ + \n :while (\$waitCounter < \$waitTimeout) do={\r\ + \n :set emailStatus [/tool e-mail get last-status];\r\ + \n :if (\$emailStatus != \"in-progress\" and \$emailStatus != \"resolvi\ + ng-dns\") do={\r\ + \n :log info \"\$SMP Email send status received: \$emailStatus\";\r\ + \n # exit loop\r\ + \n :set waitCounter \$waitTimeout;\r\ + \n } else={:delay 1; :set waitCounter (\$waitCounter + 1)}\r\ + \n }\r\ + \n # Final decision based on last status\r\ + \n :if (\$emailStatus = \"succeeded\") do={\r\ + \n :log info \"\$SMP Email successfully sent to `\$EmailGeneralTo`\";\ + \r\ + \n } else={\r\ + \n :log error \"\$SMP Email failed to send. Status: `\$emailStatus`. Ch\ + eck logs for more details and verify email settings.\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n}\r\ + \n# Global variable to track current update step\r\ + \n# They need to be initialized here first to be available in the script\r\ + \n:global buGlobalVarTargetOsVersion;\r\ + \n:global buGlobalVarScriptStep;\r\ + \n:local scriptStep \$buGlobalVarScriptStep;\r\ + \n:do {\r\ + \n /system/script/environment remove buGlobalVarScriptStep;\r\ + \n} on-error={}\r\ + \n:if ([:len \$scriptStep] = 0) do={\r\ + \n :set scriptStep 1;\r\ + \n}\r\ + \n## ^^ FUNCTIONS ^^ ##\r\ + \n#\r\ + \n# Initial validation\r\ + \n## Check email settings\r\ + \n:if ([:len \$EmailGeneralTo] < 3) do={\r\ + \n :log error (\"\$SMP Parameter `\\\$EmailGeneralTo` is not set, or cont\ + ains invalid value. Script stopped.\");\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n# Values will be defined later in the script\r\ + \n:local emailServer \"\";\r\ + \n:local emailFromAddress [/tool e-mail get from];\r\ + \n:log info \"\$SMP Validating email settings...\";\r\ + \n:do {\r\ + \n :set emailServer [/tool e-mail get server];\r\ + \n} on-error={\r\ + \n # This is a workaround for the RouterOS v7.12 and older versions\r\ + \n :set emailServer [/tool e-mail get address];\r\ + \n}\r\ + \n:if (\$emailServer = \"0.0.0.0\") do={\r\ + \n :log error (\"\$SMP Email server address is not correct: `\$emailServe\ + r`, check `Tools -> Email`. Script stopped.\");\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n:if ([:len \$emailFromAddress] < 3) do={\r\ + \n :log error (\"\$SMP Email configuration FROM address is not correct: `\ + \$emailFromAddress`, check `Tools -> Email`. Script stopped.\");\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n# Script mode validation\r\ + \n:if (\$BackupAndUpdateMode != \"backup\" and \$BackupAndUpdateMode != \"\ + osupdate\" and \$BackupAndUpdateMode != \"updateOnce\" and \$BackupAndUpda\ + teMode != \"osnotify\") do={\r\ + \n :log error (\"\$SMP Script parameter `\\\$BackupAndUpdateMode` is not \ + set, or contains invalid value: `\$BackupAndUpdateMode`. Script stopped.\"\ + );\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n# Update channel validation\r\ + \n:if (\$updateChannel != \"stable\" and \$updateChannel != \"long-term\" \ + and \$updateChannel != \"testing\" and \$updateChannel != \"development\")\ + \_do={\r\ + \n :log error (\"\$SMP Script parameter `\\\$updateChannel` is not set, o\ + r contains invalid value: `\$updateChannel`. Script stopped.\");\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n# Verify if script is set to install patch updates and if the update cha\ + nnel is valid\r\ + \n:if ((\$BackupAndUpdateMode = \"osupdate\" or \$BackupAndUpdateMode = \"\ + updateOnce\") and \$installOnlyPatchUpdates = true) do={\r\ + \n :if (\$updateChannel != \"stable\" and \$updateChannel != \"long-term\ + \") do={\r\ + \n :log error (\"\$SMP Patch-only updates enabled, but update channel `\ + \$updateChannel` is invalid. Only `stable` and `long-term` are supported. \ + Script stopped\");\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n \r\ + \n :if (\$runningChannel != \"stable\" and \$runningChannel != \"long-ter\ + m\") do={\r\ + \n :log error (\"\$SMP Script is set to install only patch updates, but\ + \_the installed RouterOS version is not from `stable` or `long-term` chann\ + el: `\$runningChannel`. Script stopped\");\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n}\r\ + \n#\r\ + \n\r\ + \n:local rawTime [/system clock get time];\r\ + \n:local rawDate [/system clock get date];\r\ + \n\r\ + \n:local deviceOsVerAndChannelRunning [/system/resource/get version];\r\ + \n\r\ + \n:local backupNameTemplate (\"backup_v\" . \$runningVersion . \"_\" .\ + \_\$runningChannel . \"_\" . \$MDTSFix);\r\ + \n:local backupNameBeforeUpdate (\$backupNameTemplate . \"_before_update\"\ + );\r\ + \n:local backupNameAfterUpdate (\$backupNameTemplate . \"_after_update\")\ + ;\r\ + \n\r\ + \n## Email body template\r\ + \n:local mailSubjectPrefix \"\$SMP Device - `\$IdentityShort`\";\r\ + \n:local mailBodyCopyright \"Mikrotik RouterOS automatic backup & update \ + (ver. \$scriptVersion) \\n\$copyrightUrl\";\r\ + \n:local changelogUrl \"Check RouterOS changelog: https://mikrotik.com\ + /download/changelogs/\";\r\ + \n:local mailBodyDeviceInfo \"\";\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"Device information:\")\ + ;\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\n-------------------\ + --\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nName: \$Identity\")\ + ;\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nModel: \$devModel\"\ + );\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nBoard: \$devBoardNa\ + me\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nSerial number: \$de\ + vRbSerialNumber\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nRouterOS version: v\ + \$deviceOsVerAndChannelRunning\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nBuild time: \$[/sys\ + tem/resource/get build-time]\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nRouterboard FW Avai\ + lable: \$ROsVerAvail\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nDevice date-time: \ + \$rawDate \$rawTime (\$[/system/clock/get time-zone-name ])\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nUptime: \$[/system/\ + resource/get uptime]\");\r\ + \n## IP address detection\r\ + \n:if ([ \$DevInfoIncludeIP ] = true) do={\r\ + \n :global GetPubIp;\r\ + \n :local PublicIpAddress [ \$GetPubIp ];\r\ + \n :set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nPublic IP address\ + : \" . \$PublicIpAddress . \".\");\r\ + \n :log info \"\$SMP Public IP address detected: \$PublicIpAddress\";\r\ + \n} \r\ + \n\r\ + \n:local mailAttachments [:toarray \"\"];\r\ + \n:if (\$scriptStep = 1 or \$scriptStep = 3) do={\r\ + \n :if (\$scriptStep = 3) do={\r\ + \n :log info (\"\$SMP Waiting for one minute before continuing to the f\ + inal step.\");\r\ + \n :delay 1m;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n## STEP 1: Create backups, check for new RouterOS, and send email\r\ + \n## Steps 2\C3\A23 run only if auto-update is enabled and a new version i\ + s available\r\ + \n:if (\$scriptStep = 1) do={\r\ + \n :global BackupPassword;\r\ + \n :global BackupSens;\r\ + \n :local isNewOsUpdateAvailable false;\r\ + \n :local isLatestOsAlreadyInstalled true;\r\ + \n :local isOsNeedsToBeUpdated false;\r\ + \n :local isUpdateCheckSucceeded false;\r\ + \n :local isEmailNeedsToBeSent false;\r\ + \n :local mailSubjectPartAction \"\";\r\ + \n :local mailPtBodyAction \"\";\r\ + \n :local mailPtSubjectBackup \"\";\r\ + \n :local mailPtBodyBackup \"\";\r\ + \n # Checking for new version\r\ + \n :if (\$BackupAndUpdateMode = \"osupdate\" or \$BackupAndUpdateMode = \ + \"osnotify\" or \$BackupAndUpdateMode = \"updateOnce\") do={\r\ + \n :log info (\"\$SMP Setting update channel to `\$updateChannel`\");\r\ + \n /system/package/update/set channel=\$updateChannel;\r\ + \n :log info (\"\$SMP Checking for new RouterOS version. Current instal\ + led version is: `\$runningVersion`\");\r\ + \n \$LogPrint debug \$ScriptName (\"Checking for updates...\");\r\ + \n \r\ + \n# /system/package/update/check-for-updates without-paging as-value;\ + \r\ + \n# /system/package/update check-for-updates;\r\ + \n :delay 5s;\r\ + \n :set PkgUpdStatus [ /system/package/update/get status ];\r\ + \n :if (\$PkgUpdStatus = \"New version is available\") do={\r\ + \n :set ROsVerAvail [/system/package/update/get latest-version];\r\ + \n }\r\ + \n\r\ + \n :if (\$PkgUpdStatus = \"New version is available\") do={\r\ + \n :log info (\"\$SMP New RouterOS version is available: `\$ROsVerAva\ + il`\");\r\ + \n :set isNewOsUpdateAvailable true;\r\ + \n :set isLatestOsAlreadyInstalled false;\r\ + \n :set isUpdateCheckSucceeded true;\r\ + \n :set isEmailNeedsToBeSent true;\r\ + \n :set mailSubjectPartAction \"New RouterOS available\";\r\ + \n :set mailPtBodyAction \"New RouterOS version is available, curren\ + t version: v\$runningVersion, new version: v\$ROsVerAvail. \\n\$changelogU\ + rl\";\r\ + \n } else={\r\ + \n :if (\$PkgUpdStatus = \"System is already up to date\") do={\r\ + \n :log info (\"\$SMP No new RouterOS version is available, the lat\ + est version is already installed: `v\$runningVersion`\");\r\ + \n :set isUpdateCheckSucceeded true;\r\ + \n :set mailSubjectPartAction \"No os update available\";\r\ + \n :set mailPtBodyAction \"No new RouterOS version is available, t\ + he latest version is already installed: `v\$runningVersion`\";\r\ + \n } else={\r\ + \n :log error (\"\$SMP Failed to check for new RouterOS version. Pa\ + ckage check status: `\$PkgUpdStatus`\");\r\ + \n :set isEmailNeedsToBeSent true;\r\ + \n :set mailSubjectPartAction \"Error unable to check new os versio\ + n\";\r\ + \n :set mailPtBodyAction \"An error occurred while checking for a \ + new RouterOS version.\\nStatus returned: `\$PkgUpdStatus`\\n\\nPlease revi\ + ew the logs on the device for more details and verify internet connectivit\ + y.\";\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n # Checking if the script needs to install new os version\r\ + \n :if ((\$BackupAndUpdateMode = \"osupdate\" or \$BackupAndUpdateMode = \ + \"updateOnce\") and \$isNewOsUpdateAvailable = true) do={\r\ + \n :if (\$installOnlyPatchUpdates = true) do={\r\ + \n :if ([\$FuncIsPatchUpdateOnly \$runningVersion \$ROsVerAvail] = tr\ + ue) do={\r\ + \n :log info \"\$SMP New RouterOS version is available, and it is a\ + \_patch update. Current version: v\$runningVersion, new version: v\$ROsVer\ + Avail\";\r\ + \n :set isOsNeedsToBeUpdated true;\r\ + \n } else={\r\ + \n :log info \"\$SMP The script will not install this update, becau\ + se it is not a patch update. Current version: v\$runningVersion, new versi\ + on: v\$ROsVerAvail\";\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nThis update wil\ + l not be installed, because the script is set to install only patch update\ + s.\");\r\ + \n }\r\ + \n } else={\r\ + \n :set isOsNeedsToBeUpdated true;\r\ + \n }\r\ + \n }\r\ + \n # Checking If the script needs to create a backup\r\ + \n :if (\$forceBackup = true or \$BackupAndUpdateMode = \"backup\" or \$i\ + sOsNeedsToBeUpdated = true) do={\r\ + \n :log info (\"\$SMP Starting backup process.\");\r\ + \n :set isEmailNeedsToBeSent true;\r\ + \n :local backupName \$backupNameTemplate;\r\ + \n # This means it's the first step where we create a backup before the\ + \_update process\r\ + \n :if (\$isOsNeedsToBeUpdated = true) do={\r\ + \n :set backupName \$backupNameBeforeUpdate;\r\ + \n #Email body if the purpose of the script is to update the device\r\ + \n :set mailSubjectPartAction \"Update preparation\";\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nThe update proces\ + s for device '\$Identity' is scheduled to upgrade RouterOS from version v.\ + \$runningVersion to version v.\$ROsVerAvail (Update channel: \$updateChann\ + el)\");\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nPlease note: The \ + update will proceed only after a successful backup.\");\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nA final report wi\ + th detailed information will be sent once the update process is completed.\ + \");\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nIf you do not rec\ + eive a second email within the next 10 minutes, there may be an issue. Ple\ + ase check your device logs for further information.\");\r\ + \n }\r\ + \n :do {\r\ + \n :set mailAttachments [\$FuncCreateBackups \$backupName \$BackupPas\ + sword \$BackupSens];\r\ + \n :set mailPtSubjectBackup \"Backup created\";\r\ + \n :set mailPtBodyBackup \"System backups have been successfully crea\ + ted and attached to this email.\";\r\ + \n } on-error={\r\ + \n :set isOsNeedsToBeUpdated false;\r\ + \n :set mailPtSubjectBackup \"Backup failed\";\r\ + \n :set mailPtBodyBackup \"The script failed to create backups. Pleas\ + e check device logs for more details.\";\r\ + \n :log warning \"\$SMP Backup creation failed. Update process will b\ + e canceled if automatic update is enabled\";\r\ + \n }\r\ + \n }\r\ + \n :if (\$isEmailNeedsToBeSent = true) do={\r\ + \n :log info \"\$SMP Preparing to send email...\";\r\ + \n :local mailStep1Subject \$mailSubjectPrefix;\r\ + \n :local mailStep1Body \"\";\r\ + \n # subject\r\ + \n :if (\$mailSubjectPartAction != \"\") do={:set mailStep1Subject (\$\ + mailStep1Subject . \" - \" . \$mailSubjectPartAction)}\r\ + \n :if (\$mailPtSubjectBackup != \"\") do={:set mailStep1Subject (\$ma\ + ilStep1Subject . \" - \" . \$mailPtSubjectBackup)}\r\ + \n # body\r\ + \n :if (\$mailPtBodyAction != \"\") do={:set mailStep1Body (\$mailStep1\ + Body . \$mailPtBodyAction . \"\\n\\n\")}\r\ + \n :if (\$mailPtBodyBackup != \"\") do={:set mailStep1Body (\$mailStep1\ + Body . \$mailPtBodyBackup . \"\\n\\n\")}\r\ + \n :set mailStep1Body (\$mailStep1Body . \$mailBodyDeviceInfo . \"\\n__\ + ________\\n\" . \$mailBodyCopyright);\r\ + \n :set mailStep1Body (\$mailStep1Body . \"\\n[*** TEST START ***]\\n\\\ + \$DeviceInfo: \" . [ \$DeviceInfo ] . \"\\n[*** TEST END ***]\\n\");\r\ + \n # Send email with backups\r\ + \n :do {\$FuncSendEmailSafe \$EmailGeneralTo \$mailStep1Subject \$mailS\ + tep1Body \$mailAttachments} on-error={\r\ + \n :set isOsNeedsToBeUpdated false;\r\ + \n :log error \"\$SMP The script will not proceed with the update pro\ + cess, because the email was not sent.\";\r\ + \n }\r\ + \n }\r\ + \n :if ([:len \$mailAttachments] > 0) do={\r\ + \n :log info \"\$SMP Cleaning up backup files from the file system...\"\ + ;\r\ + \n /file remove \$mailAttachments;\r\ + \n :delay 2s;\r\ + \n }\r\ + \n :if (\$isOsNeedsToBeUpdated = true) do={\r\ + \n :global OldMode;\r\ + \n :log info \"\$SMP everything is ready to install new RouterOS, going\ + \_to start the update process and reboot the device.\";\r\ + \n :do {\r\ + \n :local nextStep 2;\r\ + \n :if (\$isCloudHostedRouter = true) do={\r\ + \n :log info \"\$SMP The device is a cloud hosted router, the secon\ + d step updating the Routerboard firmware will be skipped.\";\r\ + \n :set nextStep 3;\r\ + \n }\r\ + \n :local scheduledCommand (\":delay 5s; /system/scheduler remove BKP\ + UPD-NEXT-BOOT-TASK; \\\r\ + \n :global buGlobalVarScriptStep \$nextStep; :global buGlobalVarTarg\ + etOsVersion \\\"\$ROsVerAvail\\\"; \\\r\ + \n :delay 10s; /system/script { run \$ScriptName; }\");\r\ + \n /system/scheduler add name=BKPUPD-NEXT-BOOT-TASK on-event=\$schedu\ + ledCommand start-time=startup interval=0;\r\ + \n /system/package/update install;\r\ + \n } on-error={\r\ + \n # Failed to install new os version, remove the task and variables\ + \r\ + \n :do {\r\ + \n /system/scheduler remove BKPUPD-NEXT-BOOT-TASK;\r\ + \n :set BackupAndUpdateMode \$OldMode;\r\ + \n :delay 2;\r\ + \n :do {\r\ + \n :global buGlobalVarTargetOsVersion (a);\r\ + \n } on-error={\r\ + \n :set \$mailSubjectPrefix (\$mailSubjectPrefix . \"ERROR Remove\ + \_global variable \\\$buGlobalVarTargetOsVersion failed!\");\r\ + \n }\r\ + \n# :if ([ :len \$OldMode ] > 0) do={\r\ + \n# :do {\r\ + \n# :global OldMode (a);\r\ + \n# } on-error={\r\ + \n# :set \$mailSubjectPrefix (\$mailSubjectPrefix . \" ERROR Re\ + move global variable \\\$OldMode failed!\");\r\ + \n# }\r\ + \n# }\r\ + \n } on-error={\r\ + \n :log error \"\$SMP Failed to install new RouterOS version. Pleas\ + e check device logs for more details. \\\r\ + \n \\nAnd Failed to remove task and variables! Check it!\";\r\ + \n :set \$mailSubjectPrefix (\$mailSubjectPrefix . \" ERROR Remove \ + task and global variables!\");\r\ + \n }\r\ + \n :log error \"\$SMP Failed to install new RouterOS version. Please \ + check device logs for more details.\";\r\ + \n :local mailUpdateErrorSubject (\$mailSubjectPrefix . \" - Update f\ + ailed\");\r\ + \n :local mailUpdateErrorBody \"The script was unable to install new \ + RouterOS version. Please check device logs for more details.\";\r\ + \n # Send email with error\r\ + \n \$FuncSendEmailSafe \$EmailGeneralTo \$mailUpdateErrorSubject \$ma\ + ilUpdateErrorBody \"\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n## STEP 2: (Post-reboot) Upgrade RouterBOARD firmware\r\ + \n## Runs only if auto-update is enabled and a new RouterOS version was fo\ + und\r\ + \n:if (\$scriptStep = 2) do={\r\ + \n :log info \"\$SMP The script is in the second step, updating Routerboa\ + rd firmware.\";\r\ + \n :log info \"\$SMP Upgrading routerboard firmware from v.\$deviceRbCurr\ + entFw to v.\$deviceRbUpgradeFw\";\r\ + \n /system routerboard upgrade;\r\ + \n :delay 2;\r\ + \n :log info \"\$SMP routerboard upgrade process was completed, going to \ + reboot in a moment!\";\r\ + \n ## Set task to send final report on the next boot\r\ + \n /system scheduler add name=BKPUPD-NEXT-BOOT-TASK on-event=\":delay 2; \ + :global buGlobalVarScriptStep 3; \\\r\ + \n :global buGlobalVarTargetOsVersion \\\"\$buGlobalVarTargetOsVersion\\\ + \"; :delay 10s; /system/script { run \$ScriptName; }; \\ \r\ + \n :delay 5s; /system/scheduler remove BKPUPD-NEXT-BOOT-TASK;\" start-ti\ + me=startup interval=0;\r\ + \n :delay 2;\r\ + \n /system reboot;\r\ + \n}\r\ + \n\r\ + \n## STEP 3: Final report (after second reboot, with delay).\r\ + \n## Runs only if auto-update is enabled and a new RouterOS version was fo\ + und.\r\ + \n:if (\$scriptStep = 3) do={\r\ + \n :log info (\"\$SMP The script is in the third step, sending final repo\ + rt.\");\r\ + \n :local targetOsVersion \$buGlobalVarTargetOsVersion;\r\ + \n :do {\r\ + \n# /system/script/environment remove [find name=\"buGlobalVarTargetOsV\ + ersion\"];\r\ + \n :global buGlobalVarTargetOsVersion (a);\r\ + \n } on-error={\r\ + \n :set \$mailSubjectPrefix (\$mailSubjectPrefix . \" ERROR Remove glob\ + al variable \\\$buGlobalVarTargetOsVersion failed!\");\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$OldMode ] != 0) do={\r\ + \n :do {\r\ + \n# /system/script/environment remove [find name=\"OldMode\"];\r\ + \n :global OldMode (a);\r\ + \n } on-error={\r\ + \n :set \$mailSubjectPrefix (\$mailSubjectPrefix . \" ERROR! Remove\ + \_global variable \\\$OldMode failed!\");\r\ + \n }\r\ + \n }\r\ + \n :if ([:len \$targetOsVersion] = 0) do={\r\ + \n :log warning \"\$SMP Something is wrong, the script was unable to ge\ + t the target updated OS version from the global variable.\";\r\ + \n }\r\ + \n :local mailStep3Subject \$mailSubjectPrefix;\r\ + \n :local mailStep3Body \"\";\r\ + \n :if (\$targetOsVersion = \$runningVersion) do={\r\ + \n :log info \"\$SMP Successfully verified new RouterOS version: target\ + : `\$targetOsVersion`, current: `\$runningVersion`\";\r\ + \n :set mailStep3Subject (\$mailStep3Subject . \" - Update completed - \ + Backup created\");\r\ + \n :set mailStep3Body (\$mailStep3Body . \"RouterOS and routerboard upg\ + rade process was completed\");\r\ + \n :set mailStep3Body (\$mailStep3Body . \"\\nNew RouterOS version: v.\ + \$targetOsVersion, routerboard firmware: v.\$deviceRbCurrentFw\");\r\ + \n :set mailStep3Body (\$mailStep3Body . \"\\n\$changelogUrl\\nBackups \ + of the upgraded system are in the attachment of this email.\\n\\n\$mailBod\ + yDeviceInfo\\n\\n\$mailBodyCopyright\");\r\ + \n :set mailAttachments [\$FuncCreateBackups \$backupNameAfterUpdate \$\ + BackupPassword \$BackupSens];\r\ + \n } else={\r\ + \n :log error \"\$SMP Failed to verify new RouterOS version: target: `\ + \$targetOsVersion`, current: `\$runningVersion`\";\r\ + \n :set mailStep3Subject (\$mailStep3Subject . \" - Update failed\");\r\ + \n :set mailStep3Body (\$mailStep3Body . \"The script was unable to ver\ + ify that the new RouterOS version was installed, target version: `\$target\ + OsVersion`, current version: `\$runningVersion`\\nCheck device logs for mo\ + re details.\\n\\n\$mailBodyDeviceInfo\\n\\n\$mailBodyCopyright\");\r\ + \n }\r\ + \n \$FuncSendEmailSafe \$EmailGeneralTo \$mailStep3Subject \$mailStep3Bod\ + y \$mailAttachments;\r\ + \n :if ([:len \$mailAttachments] > 0) do={\r\ + \n :log info \"\$SMP Cleaning up backup files from the file system...\"\ + ;\r\ + \n /file remove \$mailAttachments;\r\ + \n :delay 2;\r\ + \n }\r\ + \n :log info (\"\$SMP Final report email sent successfully, and the scrip\ + t has finished.\");\r\ + \n}\r\ + \n:log info (\"\$SMP the script has finished, script step: `\$scriptStep` \ + \\n\\n\");\r\ + \n\r\ + \n} do={:global ExitError; \$ExitError \$ExitOK [ :jobname ] \$Err}\r\ + \n" +add dont-require-permissions=no name=testGetPubIP owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + global DevInfoIncludeIP;\r\ + \n:global CharacterReplace;\r\ + \n:global EscapeForRegEx;\r\ + \n:global UrlEncode;\r\ + \n\r\ + \n:if ([ \$DevInfoIncludeIP ] = true) do={\r\ + \n :global GetPubIp;\r\ + \n :local Char (\"\\n\\r\");\r\ + \n :local PublicIpAddress [\$GetPubIp];\r\ + \n# :if ([ :pick \$PublicIpAddress ([ :len \$PublicIpAddress ] - 2) ] = \ + \"\\n\") do={:error true}; :set error falseon-error={:set PublicIpAddress \ + (\$PublicIpAddress . Char)};\r\ + \n#:if ([ :pick \$PublicIpAddress ([ :len \$PublicIpAddress ] - 1) ] = \"/\ + n\") do={\r\ + \n#:log warning \"N is = //n\"; \r\ + \n# :set PublicIpAddress [ \$CharacterReplace \$PublicIpAddress \$Char \"\ + \" ];\r\ + \n#}\r\ + \n# :set PublicIpAddress [ \$UrlEncode \$PublicIpAddress ];\r\ + \n#:local IpLen ([ :len \$PublicIpAddress ] -1);\r\ + \n#:set PublicIpAddress [:pick \$PublicIpAddress 0 \$IpLen];\r\ + \n :log warning \"Public IP address detected: \$PublicIpAddress , len = \ + \$IpLen\";\r\ + \n}\r\ + \n" +add comment="download, import and update firewall address-lists" \ + dont-require-permissions=no name=AM-FwAddrLists owner=AM-FwAddrLists \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="#!rsc by Vados\r\ + \n# RouterOS script: AM-FwAddrLists\r\ + \n# Script comment: download, import and update firewall address-lists\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# https://ros.vados.ru/doc/fw-addr-lists.md\r\ + \n:local ExitOK false;\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady; :global GlobalFuncReady;\r\ + \n :retry { :if (\$GlobalConfReady != true || \$GlobalFuncReady != true) \ + \\\r\ + \n do={ :error (\"Global config and/or functions not ready.\"); }; } \ + delay=500ms max=50;\r\ + \n :local ScriptName [ :jobname ];\r\ + \n\r\ + \n :global GlobalScriptsUrl;\r\ + \n :local ListsDirName (\"fw-addr-lists.d/\" . [ /system identity get nam\ + e ] . \"/\");\r\ + \n :local ListsUrl (\$GlobalScriptsUrl . \$ListsDirName);\r\ + \n :local ListsIndex \"index.lst\";\r\ + \n :local AddrListsTimeOut 1d;\r\ + \n :local CheckCerts false;\r\ + \n\r\ + \n :global CertificateAvailable;\r\ + \n :global EitherOr;\r\ + \n :global FetchHugeFiles;\r\ + \n :global HumanReadableNum;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global NetMask4;\r\ + \n :global NetMask6;\r\ + \n :global ScriptLock;\r\ + \n \r\ + \n :local FindDelim do={\r\ + \n :local ValidChars \"0123456789.:/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghi\ + jklmnopqrstuvwxyz-\";\r\ + \n :for I from=0 to=[ :len \$1 ] do={\r\ + \n :if ([ :typeof [ :find \$ValidChars [ :pick (\$1 . \" \") \$I ] ] \ + ] != \"num\") do={\r\ + \n :return \$I;\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :local GetBranch do={\r\ + \n :global EitherOr;\r\ + \n :return [ :pick [ :convert transform=md5 to=hex [ :pick \$1 0 [ \$Ei\ + therOr [ :find \$1 \"/\" ] [ :len \$1 ] ] ] ] 0 2 ];\r\ + \n }\r\ + \n \r\ + \n :if ([ \$ScriptLock \$ScriptName ] = false) do={\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :local ListComment (\"managed by \" . \$ScriptName);\r\ + \n :local Url (\$ListsUrl . \$ListsIndex);\r\ + \n :local tmpIndex false;\r\ + \n\r\ + \n :for I from=1 to=5 do={\r\ + \n :if (\$tmpIndex = false) do={\r\ + \n :set tmpIndex [ :tolf [ \$FetchHugeFiles \$ScriptName \$Url \$Chec\ + kCerts ] ];\r\ + \n :if (\$tmpIndex = false && \$I < 5) do={\r\ + \n :log warning (\$ScriptName . \" Failed downloading address-list \ + index, \" . \$I . \". try from: \" . \$Url);\r\ + \n :delay ((\$I * \$I) . \"s\");\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n :if (\$tmpIndex = false) do={\r\ + \n :log warning (\$ScriptName . \" Failed downloading address-list inde\ + x from: \" . \$Url);\r\ + \n :set tmpIndex \"\";\r\ + \n }\r\ + \n\r\ + \n :foreach List in=[ :deserialize \$tmpIndex delimiter=\"\\n\" from=dsv \ + options=dsv.plain ] do={\r\ + \n# :set List (\$List->0);\r\ + \n :local ListName;\r\ + \n :local Delim [ \$FindDelim \$List ];\r\ + \n :if (\$Delim = 0) do={ :set List \"\"; };\r\ + \n :log warning (\$ScriptName . \" Parsing address-list index entry: \"\ + \_. \$List);\r\ + \n :if ([ :len \$List ] = 0) do={\r\ + \n :log error (\$ScriptName . \" Failed parsing address-list index en\ + try: \" . \$List);\r\ + \n } else={\r\ + \n :set ListName [ :pick \$List 0 \$Delim ];\r\ + \n }\r\ + \n :log warning (\$ScriptName . \" Parsing address-list index entry: \"\ + \_. \$ListName . \" - Ok.\");\r\ + \n :local ListUrl (\$ListsUrl . \$ListName);\r\ + \n :local CntAdd 0;\r\ + \n :local CntRenew 0;\r\ + \n :local CntRemove 0;\r\ + \n :local IPv4Addresses ({});\r\ + \n :local IPv6Addresses ({});\r\ + \n :local Failure false;\r\ + \n :local Data false;\r\ + \n :local TimeOut \$AddrListTimeOut;\r\ + \n\r\ + \n :for I from=1 to=5 do={\r\ + \n :if (\$Data = false) do={\r\ + \n :set Data [ :tolf [ \$FetchHugeFiles \$ScriptName \$ListUrl \$Ch\ + eckCerts ] ];\r\ + \n :if (\$Data = false && \$I < 5) do={\r\ + \n :log warning (\$ScriptName . \" Failed downloading for list '\ + \" . \$ListName . \"', \" . \$I . \". try from: \" . \$ListUrl);\r\ + \n :delay ((\$I * \$I) . \"s\");\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if (\$Data = false) do={\r\ + \n :set Data \"\";\r\ + \n :set Failure true;\r\ + \n :log warning (\$ScriptName . \" Failed downloading for list '\" . \ + \$ListName . \"' from: \" . \$ListUrl);\r\ + \n } else={\r\ + \n :log warning (\$ScriptName . \" Downloaded \" . [ \$HumanReadableN\ + um [ :len \$Data ] 1024 ] . \"B for list '\" . \$ListName . \"' from: \" .\ + \_\$ListUrl);\r\ + \n }\r\ + \n\r\ + \n :foreach Line in=[ :deserialize \$Data delimiter=\"\\n\" from=dsv op\ + tions=dsv.plain ] do={\r\ + \n :set Line (\$Line->0);\r\ + \n :local Address;\r\ + \n :if ([ :pick \$Line 0 1 ] = \"{\" && [ :pick \$Line ([ :len \$Line\ + \_] - 1) ] = \"}\") do={\r\ + \n :do {\r\ + \n :set Address [ :tostr ([ :deserialize from=json \$Line ]->\"ci\ + dr\") ];\r\ + \n } on-error={ }\r\ + \n } else={\r\ + \n :set Address ([ :pick \$Line 0 [ \$FindDelim \$Line ] ] . (\$L\ + ist->\"cidr\"));\r\ + \n }\r\ + \n :do {\r\ + \n :local Branch;\r\ + \n :if (\$Address ~ \"^[0-9]{1,3}\\\\.[0-9]{1,3}\\\\.[0-9]{1,3}\\\\\ + .[0-9]{1,3}(/[0-9]{1,2})\?\\\$\") do={\r\ + \n :local Net \$Address;\r\ + \n :local CIDR 32;\r\ + \n :local Slash [ :find \$Address \"/\" ];\r\ + \n :if ([ :typeof \$Slash ] = \"num\") do={\r\ + \n :set Net [ :toip [ :pick \$Address 0 \$Slash ] ]\r\ + \n :set CIDR [ :pick \$Address (\$Slash + 1) [ :len \$Address ]\ + \_];\r\ + \n :set Address [ :tostr (([ :toip \$Net ] & [ \$NetMask4 \$CID\ + R ]) . [ \$IfThenElse (\$CIDR < 32) (\"/\" . \$CIDR) ]) ];\r\ + \n }\r\ + \n :set Branch [ \$GetBranch \$Address ];\r\ + \n :set (\$IPv4Addresses->\$Branch->\$Address) \$TimeOut;\r\ + \n }\r\ + \n :if (\$Address ~ \"^[0-9a-zA-Z]*:[0-9a-zA-Z:\\\\.]+(/[0-9]{1,3})\ + \?\\\$\") do={\r\ + \n :local Net \$Address;\r\ + \n :local CIDR 128;\r\ + \n :local Slash [ :find \$Address \"/\" ];\r\ + \n :if ([ :typeof \$Slash ] = \"num\") do={\r\ + \n :set Net [ :toip6 [ :pick \$Address 0 \$Slash ] ]\r\ + \n :set CIDR [ :pick \$Address (\$Slash + 1) [ :len \$Address ]\ + \_];\r\ + \n }\r\ + \n :set Address (([ :toip6 \$Net ] & [ \$NetMask6 \$CIDR ]) . \"/\ + \" . \$CIDR);\r\ + \n :set Branch [ \$GetBranch \$Address ];\r\ + \n :set (\$IPv6Addresses->\$Branch->\$Address) \$TimeOut;\r\ + \n }\r\ + \n :if (\$Address ~ \"^[\\\\.a-zA-Z0-9-]+\\\\.[a-zA-Z]{2,}\\\$\") d\ + o={\r\ + \n :set Branch [ \$GetBranch \$Address ];\r\ + \n :set (\$IPv4Addresses->\$Branch->\$Address) \$TimeOut;\r\ + \n :set (\$IPv6Addresses->\$Branch->\$Address) \$TimeOut;\r\ + \n }\r\ + \n } on-error={ }\r\ + \n }\r\ + \n \r\ + \n\r\ + \n :foreach Entry in=[ /ip/firewall/address-list/find where \\\r\ + \n list=\$ListName comment=\$ListComment ] do={\r\ + \n :local Address [ /ip/firewall/address-list/get \$Entry address ];\ + \r\ + \n :local Branch [ \$GetBranch \$Address ];\r\ + \n :local TimeOut (\$IPv4Addresses->\$Branch->\$Address);\r\ + \n :if ([ :typeof \$TimeOut ] = \"time\") do={\r\ + \n :log warning (\$ScriptName . \" Renewing IPv4 address \" . \$Add\ + ress . \\\r\ + \n \" in list '\" . \$ListName . \"' with \" . \$TimeOut . \".\ + \");\r\ + \n /ip/firewall/address-list/set \$Entry timeout=\$TimeOut;\r\ + \n :set (\$IPv4Addresses->\$Branch->\$Address);\r\ + \n :set CntRenew (\$CntRenew + 1);\r\ + \n } else={\r\ + \n :if (\$Failure = false) do={\r\ + \n :log warning (\$ScriptName . \" Removing IPv4 address \" . \$A\ + ddress . \\\r\ + \n \" from list '\" . \$ListName . \".\");\r\ + \n /ip/firewall/address-list/remove \$Entry;\r\ + \n :set CntRemove (\$CntRemove + 1);\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :foreach Entry in=[ /ipv6/firewall/address-list/find where \\\r\ + \n list=\$ListName comment=\$ListComment ] do={\r\ + \n :local Address [ /ipv6/firewall/address-list/get \$Entry address ]\ + ;\r\ + \n :local Branch [ \$GetBranch \$Address ];\r\ + \n :local TimeOut (\$IPv6Addresses->\$Branch->\$Address);\r\ + \n :if ([ :typeof \$TimeOut ] = \"time\") do={\r\ + \n :log warning (\$ScriptName . \" Renewing IPv6 address \" . \$Add\ + ress . \\\r\ + \n \" in list '\" . \$ListName . \"' with \" . \$TimeOut . \".\ + \");\r\ + \n /ipv6/firewall/address-list/set \$Entry timeout=\$TimeOut;\r\ + \n :set (\$IPv6Addresses->\$Branch->\$Address);\r\ + \n :set CntRenew (\$CntRenew + 1);\r\ + \n } else={\r\ + \n :if (\$Failure = false) do={\r\ + \n :log warning (\$ScriptName . \" Removing IPv6 address \" . \$A\ + ddress . \\\r\ + \n \" from list '\" . \$ListName . \".\");\r\ + \n /ipv6/firewall/address-list/remove \$Entry;\r\ + \n :set CntRemove (\$CntRemove + 1);\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :foreach BranchName,Branch in=\$IPv4Addresses do={\r\ + \n :log warning (\$ScriptName . \" Handling branch: \" . \$BranchName\ + );\r\ + \n :foreach Address,Timeout in=\$Branch do={\r\ + \n :log warning (\$ScriptName . \" Adding IPv4 address \" . \$Addre\ + ss . \\\r\ + \n \" to list '\" . \$ListName . \"' with \" . \$Timeout . \".\ + \");\r\ + \n :onerror Err {\r\ + \n /ip/firewall/address-list/add list=\$ListName comment=\$ListCo\ + mment \\\r\ + \n address=\$Address timeout=\$Timeout;\r\ + \n :set CntAdd (\$CntAdd + 1);\r\ + \n } do={\r\ + \n :log warning (\$ScriptName . \" Failed to add IPv4 address \" \ + . \$Address . \\\r\ + \n \" to list '\" . \$ListName . \"': \" . \$Err);\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :foreach BranchName,Branch in=\$IPv6Addresses do={\r\ + \n :log warning (\$ScriptName . \" Handling branch: \" . \$BranchName\ + );\r\ + \n :foreach Address,Timeout in=\$Branch do={\r\ + \n :log warning (\$ScriptName . \" Adding IPv6 address \" . \$Addre\ + ss . \\\r\ + \n \" to list '\" . \$ListName . \"' with \" . \$Timeout . \".\ + \");\r\ + \n :onerror Err {\r\ + \n /ipv6/firewall/address-list/add list=\$ListName comment=\$List\ + Comment \\\r\ + \n address=\$Address timeout=\$Timeout;\r\ + \n :set CntAdd (\$CntAdd + 1);\r\ + \n } do={\r\ + \n :log warning (\$ScriptName . \" Failed to add IPv6 address \" \ + . \$Address . \\\r\ + \n \" to list '\" . \$ListName . \"': \" . \$Err);\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :log info (\$ScriptName . \" list: \" . \$ListName . \\\r\ + \n \" (\" . [ \$HumanReadableNum (\$CntAdd + \$CntRenew) 1000 ] . \ + \")\" . \\\r\ + \n \" -- added: \" . [ \$HumanReadableNum \$CntAdd 1000 ] . \\\r\ + \n \" - renewed: \" . [ \$HumanReadableNum \$CntRenew 1000 ] . \\\r\ + \n \" - removed: \" . [ \$HumanReadableNum \$CntRemove 1000 ]);\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitError; \$ExitError \$ExitOK [ :jobname ] \$Err;\r\ + \n}\r\ + \n" +add dont-require-permissions=no name=awg-env owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="/\ + container/envs/add list=awg-proxy-env key=AWG_LISTEN value=\":55055\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_REMOTE value=\"109.107.17\ + 7.79:55055\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_JC value=\"9\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_JMIN value=\"105\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_JMAX value=\"920\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_S1 value=\"90\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_S2 value=\"136\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_S3 value=\"45\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_S4 value=\"27\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_H1 value=\"472437748-4724\ + 38637\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_H2 value=\"707808381-7078\ + 09250\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_H3 value=\"1609078552-160\ + 9079495\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_H4 value=\"1012329484-101\ + 2329741\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_I1 value=\"\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_I2 value=\"\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_I3 value=\"\ + \"\ + \n/container/envs/add list=awg-proxy-env key=AWG_I4 value=\"\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_I5 value=\"\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_SERVER_PUB value=\"o5IgCl\ + 1/G6xbl6j7sPaP4ZNYREeYOmqZjKbm+v0b9jU=\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_CLIENT_PUB value=[/interf\ + ace/wireguard/get [find name=wg-awg-proxy] public-key]" +add dont-require-permissions=no name=awg-container-add owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="/\ + container/add file=sata1/tmp/awg-proxy-amd64.tar.gz interface=veth-awg-pro\ + xy envlist=awg-proxy-env \\\ + \n hostname=awg-proxy root-dir=sata1/awg-proxy logging=yes shm-size=4M \ + start-on-boot=yes\ + \n/container/start [find where tag~\"awg-proxy\"]" +add dont-require-permissions=yes name=awg2-webui owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + put \"Download image, and save him to usb1/tmp\"\ + \n/tool/fetch url=\"https://ros.vados.ru/docker/awg2-webui-chr.tar\" dst-p\ + ath=sata1/tmp/awg2-webui.tar\ + \n\ + \n:put \"1. Create veth interface\"\ + \n/interface veth add name=veth-awg address=10.9.9.2/24 gateway=10.9.9.1\ + \n\ + \n:put \"2. Create bridge and add veth\"\ + \n/interface bridge add name=br-awg\ + \n/interface bridge port add bridge=br-awg interface=veth-awg\ + \n\ + \n:put \"3. Assign IP to bridge\"\ + \n/ip address add address=10.9.9.1/24 interface=br-awg\ + \n\ + \n:put \"4. NAT: masquerade container traffic\"\ + \n/ip firewall nat add chain=srcnat src-address=10.9.9.0/24 action=masquer\ + ade\ + \n\ + \n:put \"5. Port forward: UDP 443 \? container (AmneziaWG QUIC mimicry)\"\ + \n/ip firewall nat add chain=dstnat protocol=udp dst-port=443 action=dst-n\ + at to-addresses=10.9.9.2 to-ports=55055 in-interface=ether1\ + \n\ + \n:put \"6. Port forward: HTTPS 9443 \? container Web UI\"\ + \n/ip firewall nat add chain=dstnat protocol=tcp dst-port=9443 action=dst-\ + nat to-addresses=10.9.9.2 to-ports=443 in-interface=ether1\ + \n\ + \n:put \"Create environment list..\"\ + \n/container envs add list=awgui-env key=WEB_USER value=vados\ + \n/container envs add list=awgui-env key=WEB_PASS value=zoomzoomrulezz\ + \n/container envs add list=awgui-env key=SECRET_KEY value=RanDomSecret32Sy\ + mboLs2325272931q\ + \n/container envs add list=awgui-env key=AWG_ENDPOINT value=10.30.30.10:44\ + 3\ + \n\ + \n:put \"Load and start container...\"\ + \n/container add file=sata1/tmp/awg2-webui.tar interface=veth-awg hostname\ + =awg2-webui \\\ + \nroot-dir=sata1/awg2-webui layer-dir=sata1/docker/awg2-webu/ envlist=awgu\ + i-env start-on-boot=yes logging=yes shm-size=4M\ + \n\ + \n:put \"Start...\"\ + \n/container start 0\ + \n\ + \n:put \"Verify...\"\ + \n/container print\ + \n\ + \n:put \"Should show: status=running\"\ + \n\ + \n/log print where topics~\"container\"\ + \n\ + \n:put \"Should show: AWG started, nginx running\"\ + \n" +add dont-require-permissions=no name=awg-proxy-go-Client owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="\ + \n:put \"[::::::::::::::: START :::::::::::::::]\"\ + \n:put \"\"\ + \n:put \"Add ram disk tmpfs\"\ + \n#/disk add type=tmpfs tmpfs-max-size=100M slot=ram\ + \n\ + \n:put \"veth and network:\"\ + \n /interface/bridge add name=docker port-cost-mode=short\ + \n /ip/address/add address=192.168.100.1/24 interface=docker\ + \n\ + \n /interface/veth add address=192.168.100.3/24 gateway=192.168.100.1 name\ + =awg-proxy-go-client\ + \n /interface/bridge/port add bridge=docker interface=awg-proxy-go-client\ + \n\ + \n:put \"add config-client.json\"\ + \n/file/add name=sata1/mounts/awg-proxy-go/config-client.json\ + \n/container/mounts/add src=sata1/mounts/awg-proxy-go/config-client.json d\ + st=/app/config.json list=awgcfg-client\ + \n\ + \n#:put \"Download image, and save him to sata1/tmp/awgproxy-0.0.1-0034-am\ + d64.tar.gz\"\ + \n#/tool/fetch url=\"https://ros.vados.ru/amneziawg/img/awgproxy-0.0.1-003\ + 4-amd64.tar.gz\" dst-#path=\"tmp/awgproxy-0.0.1-0034-amd64.tar.gz\"\ + \n\ + \n:put \"Add container\"\ + \n/container/add file=\"usb1/dst/awgproxy-0.0.1-0036-amd64.tar\" interface\ + =awg-proxy-go-client hostname=awg-proxy-go-client root-dir=sata1/awg-proxy\ + -client logging=yes start-on-boot=yes workdir=sata1/docker/ name=\"awg-pro\ + xy-go-client\" mountlists=awgcfg-client\ + \n\ + \n:put \"Start container\"\ + \n#/container/start awg-proxy-go-client\ + \n\ + \n:put \"Add user:\"\ + \n /user/add address=192.168.100.2 group=write name=awg-proxy-go password=\ + peskar55\ + \n\ + \n:put \"Add forwarding port web-ui\"\ + \n /ip firewall nat add action=dst-nat chain=dstnat dst-port=8089 in-inter\ + face=ether1 protocol=tcp to-addresses=192.168.100.3 to-port=8088\ + \n\ + \n:put \"Add native WireGuard-server wg-awg, add address.\"\ + \n /interface/wireguard/add name=wg-awg-client\ + \n /ip/address/add address=172.17.0.1/24 interface=wg-awg-client\ + \n\ + \n:put \"\"\ + \n:put \"[::::::::::::::: END :::::::::::::::]\"\ + \n" +add comment=awg2wg dont-require-permissions=no name=awg2wg-uninstall owner=\ + Manager policy=ftp,read,write,policy source="\r\ + \n :put \"Uninstalling AmneziaWG...\"\r\ + \n :log info \"Uninstalling AmneziaWG...\"\r\ + \n /ip/route/remove [find where comment=awg2wg-tunnel]\r\ + \n :do { /tool/netwatch/remove [find where comment=Monitor-AWG2WG] } on-e\ + rror={}\r\ + \n :do { /ip/route/remove [find where comment=Monitor-AWG2WG] } on-error=\ + {}\r\ + \n /container/stop [find where interface=veth-awg2wg]\r\ + \n :delay 7s\r\ + \n /interface/list/member/remove [find where interface=wg-awg2wg]\r\ + \n /container/remove [find where interface=veth-awg2wg]\r\ + \n /container/envs/remove [find where list=\"awg2wg-env\"]\r\ + \n /ip/firewall/nat/remove [find where out-interface=\"wg-awg2wg\"]\r\ + \n /ip/address/remove [find where address=\"172.20.0.1/30\"]\r\ + \n /interface/veth/remove [find where name=\"veth-awg2wg\"]\r\ + \n /interface/wireguard/peers/remove [find where interface=\"wg-awg2wg\"]\ + \r\ + \n /ip/address/remove [find where interface=\"wg-awg2wg\"]\r\ + \n /interface/wireguard/remove [find where name=\"wg-awg2wg\"]\r\ + \n :do { /file/remove [find where name~\"awg2wg.+tar\"] } on-error={}\r\ + \n :do { /file/remove [find where name=\"disk1/awg2wg\"] } on-error={}\r\ + \n :put \"Uninstall AmneziaWG Proxy complete!\"\r\ + \n :log info \"Uninstall AmneziaWG Proxy complete!\"\r\ + \n /system/script/remove [find where name=awg2wg-uninstall]\r\ + \n" +add dont-require-permissions=no name=amneziawg-mikrotik-c owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="\ + \n:global GlobalScriptsUrl \"https://ros.vados.ru/\";\ + \n:global imagesPath \"amneziawg/img/\";\ + \n:local Image \"awg-proxy-amd64-7.20-Docker.tar.gz\";\ + \n\ + \n:global DstDir \"sata1/dst/\";\ + \n\ + \n:local Url=(\$GlobalScriptsUrl . \$ImagesPath . \$Image);\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n" +add comment=Generator dont-require-permissions=no name=awg-VDSina owner=vados \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="# ============================================================\ + \n# AWG Proxy -- MikroTik RouterOS (protocol: v2)\ + \n# 2026-06-30 22:08:12\ + \n# ============================================================\ + \n\ + \n# 0. Check prerequisites\ + \n:if ([:len [/system/package/find where name=\"container\" disabled=no]] \ + = 0) do={\ + \n :put \"Container package is not installed or disabled. Install it and \ + reboot.\"\ + \n :error \"Usage: /system/device-mode/update container=yes\"\ + \n}\ + \n\ + \n# Auto-detect storage\ + \n:global awgDisk \"disk1\"\ + \n{\ + \n :local usbFree 0\ + \n :do { :set usbFree [/disk/get [find where mount-point=\"usb1\"] free] \ + } on-error={}\ + \n :if (\$usbFree > 0) do={\ + \n :set awgDisk \"usb1\"\ + \n :put (\"Using USB: usb1 (\" . (\$usbFree / 1048576) . \" MB free)\")\ + \n } else={\ + \n :if ([:len [/disk/find where slot=\"usb1\"]] > 0) do={\ + \n :put \"USB found but not mounted. Format as ext4\?\"\ + \n :put \"Press 'y' to confirm:\"\ + \n :local key [/terminal/inkey timeout=30]\ + \n :if (\$key = 121 || \$key = 89) do={\ + \n /disk/format-drive usb1 file-system=ext4 label=usb1\ + \n :delay 3s\ + \n :do { :set usbFree [/disk/get [find where mount-point=\"usb1\"] \ + free] } on-error={}\ + \n :if (\$usbFree > 0) do={\ + \n :set awgDisk \"usb1\"\ + \n } else={ :put \"Format failed, using disk1\" }\ + \n } else={ :put \"Using disk1\" }\ + \n } else={ :put \"No USB, using disk1\" }\ + \n }\ + \n}\ + \n\ + \n# 1. Uninstall script\ + \n/system/script/add name=awg-VDSina-uninstall comment=awg-VDSina source={\ + \n :put \"Uninstallimg AmneziaWG...\"\ + \n :log info \"Uninstallimg AmneziaWG...\"\ + \n :local prevDNS \"\"\ + \n :do { :set prevDNS [/container/envs/get [find where list=\"awg-VDSina-\ + env\" key=\"AWG_PREV_DNS\"] value] } on-error={}\ + \n :local storageDisk \"disk1\"\ + \n :do { :set storageDisk [/container/envs/get [find where list=\"awg-VDS\ + ina-env\" key=\"AWG_STORAGE\"] value] } on-error={}\ + \n :local prevTmpdir \"\"\ + \n :if (\$storageDisk != \"disk1\") do={\ + \n :do { :set prevTmpdir [/container/envs/get [find where list=\"awg-VD\ + Sina-env\" key=\"AWG_PREV_TMPDIR\"] value] } on-error={}\ + \n }\ + \n /ip/route/remove [find where comment=awg-VDSina-tunnel]\ + \n /ip/route/remove [find where comment=awg-VDSina-dns]\ + \n /container/stop [find where interface=veth-awg-VDSina]\ + \n :delay 7s\ + \n /container/remove [find where interface=veth-awg-VDSina]\ + \n /container/envs/remove [find where list=\"awg-VDSina-env\"]\ + \n :if (\$prevDNS != \"\") do={\ + \n :do { /ip/dns/set servers=\"\$prevDNS\" } on-error={}\ + \n :put (\"DNS restored to: \$prevDNS\")\ + \n }\ + \n :if (\$storageDisk != \"disk1\") do={\ + \n /container/config set tmpdir=\$prevTmpdir\ + \n :put (\"Container tmpdir restored to: \$prevTmpdir\")\ + \n }\ + \n /ip/firewall/nat/remove [find where out-interface=\"wg-awg-VDSina\"]\ + \n /ip/firewall/nat/remove [find where src-address=\"172.19.75.208/30\"]\ + \n /ip/address/remove [find where address=\"172.19.75.209/30\"]\ + \n /interface/veth/remove [find where name=\"veth-awg-VDSina\"]\ + \n /interface/wireguard/peers/remove [find where interface=\"wg-awg-VDSin\ + a\"]\ + \n /ip/address/remove [find where interface=\"wg-awg-VDSina\"]\ + \n /interface/wireguard/remove [find where name=\"wg-awg-VDSina\"]\ + \n :do { /file/remove [find where name~\"awg-VDSina.+tar\"] } on-error={}\ + \n :do { /file/remove [find where name=(\$storageDisk . \"/awg-VDSina\")]\ + \_} on-error={}\ + \n :do { /file/remove [find where name=(\$storageDisk . \"/ru_ranges_time\ + out.rsc\")] } on-error={}\ + \n :if (\$storageDisk != \"disk1\") do={\ + \n :do { /file/remove [find where name~(\$storageDisk . \"/pull\")] } o\ + n-error={}\ + \n }\ + \n :put \"Uninstall AmneziaWG Proxy complete!\"\ + \n :log info \"Uninstall AmneziaWG Proxy complete!\"\ + \n /system/script/remove [find where name=awg-VDSina-uninstall]\ + \n}\ + \n\ + \n# 2. Network infrastructure\ + \n/interface/veth/add name=veth-awg-VDSina address=172.19.75.210/30 gatewa\ + y=172.19.75.209\ + \n/ip/address/add address=172.19.75.209/30 interface=veth-awg-VDSina\ + \n/ip/firewall/nat/add chain=srcnat action=masquerade src-address=172.19.7\ + 5.208/30\ + \n\ + \n# 3. WireGuard interface (MikroTik derives the public key automatically)\ + \n/interface/wireguard/add name=wg-awg-VDSina private-key=\"4CHyNhbOs923/E\ + yNDguMXJXdghL2I3UyzXPwrANVgHs=\" listen-port=14710 disabled=yes\ + \n/interface/wireguard/peers/add interface=wg-awg-VDSina public-key=\"o5Ig\ + Cl1/G6xbl6j7sPaP4ZNYREeYOmqZjKbm+v0b9jU=\" preshared-key=\"RNvF7T1cg5tyXcY\ + QnD9N5BEp6e20vVJQTv0GMjyvHU0=\" endpoint-address=172.19.75.210 endpoint-po\ + rt=51820 allowed-address=0.0.0.0/0 persistent-keepalive=10\ + \n/ip/address/add address=10.9.0.3/32 interface=wg-awg-VDSina\ + \n\ + \n# 4. Container environment variables\ + \n/container/envs/add list=awg-VDSina-env key=AWG_LISTEN value=\":51820\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_REMOTE value=\"109.107.1\ + 77.79:55055\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_JC value=\"9\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_JMIN value=\"105\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_JMAX value=\"920\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_S1 value=\"90\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_S2 value=\"136\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_H1 value=\"472437748-472\ + 438637\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_H2 value=\"707808381-707\ + 809250\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_H3 value=\"1609078552-16\ + 09079495\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_H4 value=\"1012329484-10\ + 12329741\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_SERVER_PUB value=\"o5IgC\ + l1/G6xbl6j7sPaP4ZNYREeYOmqZjKbm+v0b9jU=\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_CLIENT_PUB value=[/inter\ + face/wireguard/get [find name=wg-awg-VDSina] public-key]\ + \n/container/envs/add list=awg-VDSina-env key=AWG_S3 value=\"45\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_S4 value=\"27\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_I1 value=\"\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_I2 value=\"\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_I3 value=\"\ + \"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_I4 value=\"\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_I5 value=\"\"\ + \n\ + \n# 5. DNS\ + \n{ :local rawDNS [/ip/dns/get servers]; :local prevDNS \"\"; :foreach s i\ + n=[:toarray \$rawDNS] do={ :if ([:len \$prevDNS] > 0) do={ :set prevDNS \"\ + \$prevDNS,\$s\" } else={ :set prevDNS \$s } }; /container/envs/add list=aw\ + g-VDSina-env key=AWG_PREV_DNS value=\"\$prevDNS\" }\ + \n# NOTE: replaces existing DNS configuration\ + \n/ip/dns/set servers=109.107.177.79\ + \n/ip/route/add dst-address=109.107.177.79/32 gateway=wg-awg-VDSina distan\ + ce=1 comment=awg-VDSina-dns\ + \n\ + \n# 6. Download, create and start container\ + \n/container/envs/add list=awg-VDSina-env key=AWG_STORAGE value=\$awgDisk\ + \n:if (\$awgDisk != \"disk1\") do={\ + \n :local tmpPath (\$awgDisk . \"/pull\")\ + \n /container/envs/add list=awg-VDSina-env key=AWG_PREV_TMPDIR value=[/co\ + ntainer/config/get tmpdir]\ + \n /container/config set tmpdir=\$tmpPath memory-high=200M\ + \n}\ + \n{\ + \n :local arch [/system/resource/get architecture-name]\ + \n :local ver [/system/resource/get version]\ + \n :local dotPos [:find \$ver \".\"]\ + \n :local rest [:pick \$ver (\$dotPos + 1) [:len \$ver]]\ + \n :local endPos [:find \$rest \".\"]\ + \n :if ([:typeof \$endPos] = \"nil\") do={ :set endPos [:find \$rest \" \ + \"] }\ + \n :local minor [:tonum [:pick \$rest 0 \$endPos]]\ + \n :local suffix \"\"\ + \n :if (\$minor <= 20) do={ :set suffix \"-7.20-Docker\" }\ + \n :local file \"\"\ + \n :if (\$arch = \"arm64\") do={ :set file (\"awg-proxy-arm64\" . \$suffi\ + x . \".tar.gz\") }\ + \n :if (\$arch = \"arm\") do={ :set file (\"awg-proxy-arm\" . \$suffix . \ + \".tar.gz\") }\ + \n :if (\$arch ~ \"x86\") do={ :set file (\"awg-proxy-amd64\" . \$suffix \ + . \".tar.gz\") }\ + \n :if (\$file = \"\") do={ :error \"Unsupported architecture: \$arch\" }\ + \n :local url \"https://github.com/timbrs/amneziawg-mikrotik-c/releases/l\ + atest/download/\$file\"\ + \n :local filePath \$file\ + \n :if (\$awgDisk != \"disk1\") do={\ + \n :set filePath (\$awgDisk . \"/pull/\" . \$file)\ + \n :do { /file/make-directory (\$awgDisk . \"/pull\") } on-error={}\ + \n }\ + \n :if ([:len [/file/find where name=\$filePath]] = 0) do={\ + \n :local freeStorage 0\ + \n :if (\$awgDisk = \"disk1\") do={\ + \n :set freeStorage [/system/resource/get free-hdd-space]\ + \n } else={\ + \n :do { :set freeStorage [/disk/get [find where mount-point=\$awgDis\ + k] free] } on-error={}\ + \n }\ + \n :if (\$freeStorage < 262144) do={\ + \n :put (\"WARNING: Low disk space (\" . (\$freeStorage / 1024) . \"K\ + B free). Need at least 256KB.\")\ + \n :put \"See: https://github.com/timbrs/amneziawg-mikrotik-c#insuffi\ + cient-disk-space\"\ + \n :error \"Insufficient disk space\"\ + \n }\ + \n :put (\"Fetching: \$url -> \" . \$filePath)\ + \n /tool/fetch url=\$url dst-path=\$filePath http-max-redirect-count=10\ + \n } else={\ + \n :put (\"File already exists: \" . \$filePath)\ + \n }\ + \n :local rootDir (\$awgDisk . \"/awg-VDSina\")\ + \n /container/add file=\$filePath interface=veth-awg-VDSina envlist=awg-V\ + DSina-env hostname=awg-VDSina root-dir=\$rootDir logging=no start-on-boot=\ + yes comment=awg-VDSina\ + \n :if (\$minor > 20) do={ [:parse \"/container/set [find where interface\ + =veth-awg-VDSina] shm-size=4M\"] }\ + \n /file/remove \$filePath\ + \n :local freeMem [/system/resource/get free-memory]\ + \n :if (\$freeMem < 16777216) do={\ + \n :put (\"WARNING: Low memory (\" . (\$freeMem / 1048576) . \"MB free)\ + . 16MB+ recommended.\")\ + \n }\ + \n /container/start [find where interface=veth-awg-VDSina]\ + \n :do { /file/remove [find where name=\"console-dump.txt\"] } on-error={\ + }\ + \n :put \"Waiting for container to start...\"\ + \n :delay 5s\ + \n /interface/wireguard/enable wg-awg-VDSina\ + \n :put \"WireGuard interface enabled\"\ + \n :put \"Installation complete!\"\ + \n :put \"Ping test: 109.107.177.79 via AmneziaWG tunnel. repeat 10 times\ + \"\ + \n /ping 109.107.177.79 count=10 interval=1\ + \n :put \"\"\ + \n :put \"======= NEXT STEPS =======\"\ + \n :put \"\"\ + \n :put \"Route specific traffic through the AmneziaWG tunnel:\"\ + \n :put \"\"\ + \n :put \" Route a single host:\"\ + \n :put \" /ip/route/add dst-address=8.8.8.8/32 gateway=wg-awg-VDSina \ + comment=awg-VDSina-tunnel\"\ + \n :put \"\"\ + \n :put \" Route a subnet:\"\ + \n :put \" /ip/route/add dst-address=100.0.0.0/8 gateway=wg-awg-VDSina\ + \_comment=awg-VDSina-tunnel\"\ + \n :put \"\"\ + \n :put \" Remove a route:\"\ + \n :put \" /ip/route/remove [find where comment=awg-VDSina-tunnel dst-\ + address=8.8.8.8/32]\"\ + \n :put \"\"\ + \n :put \" Show all tunnel routes:\"\ + \n :put \" /ip/route/print where comment=awg-VDSina-tunnel\"\ + \n :put \"\"\ + \n :put \" Uninstall:\"\ + \n :put \" /system/script/run awg-VDSina-uninstall\"\ + \n :put \"=========================\"\ + \n}\ + \n\ + \n# 7. Verification\ + \n# /container/print\ + \n# /interface/wireguard/print\ + \n# /interface/wireguard/peers/print\ + \n" +add comment="wg2awg GH repo in client mode" dont-require-permissions=no name=\ + "wg2awg client" owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="\ + \n:local Arch \"amd64\";\ + \n:local Version \"v0.0.1\";\ + \n:local Name wg2awg;\ + \n:local WorkDisk \"sata1\";\ + \n:local WorkDir (\$WorkDisk . \"/\" . \$Name);\ + \n:local Bridge (\"Bridge-\" . \$Name);\ + \n:local VetchMTU \"1280\";\ + \n:local Vetch (\"vetch-\" . \$Name);\ + \n:local VetchNet \"10.9.0.0\";\ + \n:local VetchGate \"10.9.0.1\";\ + \n:local VetchAddr \"10.9.0.3/32\";\ + \n:local Env (\$Name . \"-env\");\ + \n:local ConfDir (\"/etc/\" . \$Name . \"/\");\ + \n:local ConfFile (\$Name . \".conf\");\ + \n:local Mounts (\$WorkDisk . \"/mounts/\" . \$Name . \"/\");\ + \n:local RootDir (\$WorkDisk . \"/\" . \$Name);\ + \n:local Registry \"https://registry-1.docker.io\";\ + \n:local FetchFile (\$Name . \"-\" . \$Arch . \"-oci.tar\");\ + \n:local FetchUrl (\"https://github.com/vados-dev/wg2awg/releases/download\ + /\" . \$Version . \"/\" . \$FetchFile);\ + \n:local FetchDir (\$WorkDisk . \"/dst/\");\ + \n:local AwgListen \":44044\";\ + \n:local AwgRemote \"109.107.177.79\";\ + \n\ + \n#:put \"\"\ + \n#put \"Add ram disk tmpfs\"\ + \n#/disk add type=tmpfs tmpfs-max-size=100M slot=ram\ + \n\ + \n\ + \n:log warning \"[::::::::::::::: START :::::::::::::::]\"\ + \n:log warning \"Add veth and nat...\";\ + \n\ + \n/interface/veth/add name=\$Vetch address=\$VetchAddr gateway=\$VetchGate\ + ;\ + \n\ + \n:do {\ + \n:log warning (\"Remove interface: name=\" . \$Vetch . \", address=\" . \ + \$VetchAddr . \", gateway=\" . \$VetchGate);\ + \n/interface/veth/remove [find name=\$Vetch];\ + \n:log warning (\"Add interface: name=\" . \$Vetch . \", address=\" . \$Ve\ + tchAddr . \", gateway=\" . \$VetchGate);\ + \n/interface/veth/add name=\$Vetch address=\$VetchAddr gateway=\$VetchGate\ + ;\ + \n:log warning (\"Remove ip address: address=\" . \$VetchGate . \", interf\ + ace=\" . \$Vetch);\ + \n/ip/address/remove [find address=\$VetchGate interface=\$Vetch];\ + \n:log warning (\"Add ip address: address=\" . \$VetchGate . \", interface\ + =\" . \$Vetch);\ + \n/ip/address/add address=\$VetchGate interface=\$Vetch;\ + \n:log warning (\"Remove firewall nat rule: chain=srcnat action=masquerade\ + \_src-address=\" . \$VetchNet . \"/24\");\ + \n/ip/firewall/nat/remove [find chain=srcnat action=masquerade src-address\ + =(\$VetchNet . \"/24\")];\ + \n:log warning (\"Add firewall nat rule: chain=srcnat action=masquerade sr\ + c-address=\" . \$VetchNet . \"/24\");\ + \n/ip/firewall/nat/add chain=srcnat action=masquerade src-address=(\$VethN\ + et . \"/24\");\ + \n} on-error={}\ + \n\ + \n#:do {\ + \n#/file add type=directory name=\$Mounts\ + \n#} on-error={}\ + \n\ + \n\ + \n:do {\ + \n:log warning (\"Remove WorkDir: \" . \$WorkDir . \"...\");\ + \n/file remove [find name=\$WorkDir];\ + \n:log warning (\"Add WorkDir: \" . \$WorkDir . \"...\");\ + \n/file add type=directory name=\$WorkDir;\ + \n:log warning (\"Remove config file \" . \$Mounts . \$ConfFile . \"...\")\ + ;\ + \n/file remove [find name=(\$Mounts . \$ConfFile)];\ + \n:log warning (\"Add config file \" . \$Mounts . \$ConfFile . \"...\");\ + \n/file add type=file name=(\$Mounts . \$ConfFile) contents=\"[Interface]\ + \nPrivateKey = 4CHyNhbOs923/EyNDguMXJXdghL2I3UyzXPwrANVgHs=\ + \nAddress = \$VetchAddr\ + \nMTU = \$VetchMTU\ + \nDNS = 109.107.177.79\ + \nPreUp = ip route add 109.107.177.79/32 via \$VetchGate dev \$Vetch\ + \nPostDown = ip route del 109.107.177.79/32 via \$VetchGate dev \$Vetch\ + \nJc = 9\ + \nJmin = 105\ + \nJmax = 920\ + \nS1 = 90\ + \nS2 = 136\ + \nS3 = 45\ + \nS4 = 27\ + \nH1 = 472437748-472438637\ + \nH2 = 707808381-707809250\ + \nH3 = 1609078552-1609079495\ + \nH4 = 1012329484-1012329741\ + \nI1 = <\ + r 4>\ + \nI2 = \ + \nI3 = \ + \nI4 = \ + \nI5 = \ + \n\ + \n[Peer]\ + \nPublicKey = o5IgCl1/G6xbl6j7sPaP4ZNYREeYOmqZjKbm+v0b9jU=\ + \nAllowedIPs = 0.0.0.0/0\ + \nEndpoint = \$AwgRemote\ + \nPersistentKeepalive = 10\ + \nPresharedKey = RNvF7T1cg5tyXcYQnD9N5BEp6e20vVJQTv0GMjyvHU0=\";\ + \n} on-error={}\ + \n\ + \n\ + \n:do {\ + \n:log warning (\"Remove mounts list \$Env...\");\ + \n/container/mounts/remove [find list=\$Env];\ + \n:log warning (\"Add mounts list \$Env...\");\ + \n/container/mounts/add src=(\$Mounts . \$ConfFile) dst=(\$ConfDir . \$Con\ + fFile) list=\$Env mode=ro;\ + \n:log warning (\"Remove Environment keys.\");\ + \n/container/envs/remove [find list=\$Env];\ + \n:log warning (\"Add Environment keys.\");\ + \n/container/envs/add list=\$Env key=AWG_CONFIG value=(\$ConfDir . \$ConfF\ + ile);\ + \n/container/envs/add list=\$Env key=AWG_LISTEN value=\$AwgListen\ + \n/container/envs/add list=\$Env key=AWG_REMOTE value=\$AwgRemote\ + \n/container/envs/add list=\$Env key=AWG_MODE value=client\ + \n} on-error={}\ + \n\ + \n:do {\ + \n:log warning (\"Remove \" . \$WorkDisk . \"/tmp\");\ + \n/file remove [find name=(\$WorkDisk . \"/tmp\")];\ + \n:log warning (\"Add \" . \$WorkDisk . \"/tmp\");\ + \n/file add type=directory name=(\$WorkDisk . \"/tmp\");\ + \n:log warning (\"Container add tmpdir \" . \$WorkDisk . \"/tmp\");\ + \n/container config set tmpdir=(\$WorkDisk . \"/tmp\");\ + \n} on-error={}\ + \n\ + \n:do {\ + \n:log warning (\"Remove \" . \$FetchDir . \$FetchFile . \".\");\ + \n/file remove [find name=(\$FetchDir . \$FetchFile)];\ + \n:log warning (\"Download image from \" . \$FetchUrl . \" and save him to\ + \_\" . \$FetchDir . \$FetchFile . \".\");\ + \n/tool/fetch url=\$FetchUrl dst-path=(\$FetchDir . \$FetchFile);\ + \n} on-error={}\ + \n\ + \n#:log warning (\"Container set registry \" . \$Registry . \" tmpdir= \" \ + . \$WorkDisk . \"/tmp\");\ + \n#/container/config/set registry-url=\$Registry tmpdir=(\$WorkDisk . \"/t\ + mp\");\ + \n\ + \n#:do {\ + \n:log warning (\"Container add width: file=\" . \$FetchDir . \$FetchFile \ + . \", interface=\" . \$Vetch . \", hostname=\" . \$Name . \", mountlists=\ + \" . \$Env . \", envlist=\" . \$Env . \", root-dir=\" . \$RootDir . \", wo\ + rkdir=\" . \$WorkDir . \", name=\" . \$Name . \", logging=yes, start-on-bo\ + ot=yes\");\ + \n/container/add file=(\$FetchDir . \$FetchFile) interface=\$Vetch hostnam\ + e=\$Name mountlists=\$Env envlist=\$Env root-dir=\$RootDir workdir=\$WorkD\ + ir name=\$Name logging=yes start-on-boot=yes;\ + \n#} on-error={}\ + \n\ + \n\ + \n#/container/start [find where interface=\$Vetch];\ + \n\ + \n#/interface/bridge add name=\$Bridge port-cost-mode=short;\ + \n#/ip/address/add address=192.168.100.1/24 interface=\$Bridge;\ + \n\ + \n#/interface/veth add address=192.168.100.3/24 gateway=192.168.100.1 name\ + =awg-proxy-go-client\ + \n#/interface/bridge/port add bridge=docker interface=awg-proxy-go-client\ + \n\ + \n#:put \"add config-client.json\"\ + \n#/file/add name=sata1/mounts/awg-proxy-go/config-client.json\ + \n#/container/mounts/add src=sata1/mounts/awg-proxy-go/config-client.json \ + dst=/app/config.json list=awgcfg-client\ + \n\ + \n#:put \"Download image, and save him to sata1/tmp/awgproxy-0.0.1-0034-am\ + d64.tar.gz\"\ + \n#/tool/fetch url=\"https://ros.vados.ru/amneziawg/img/awgproxy-0.0.1-003\ + 4-amd64.tar.gz\" dst-#path=\"tmp/awgproxy-0.0.1-0034-amd64.tar.gz\"\ + \n\ + \n#:put \"Add container\"\ + \n#/container/add file=\"usb1/dst/awgproxy-0.0.1-0036-amd64.tar\" interfac\ + e=awg-proxy-go-client hostname=awg-proxy-go-client root-dir=sata1/awg-prox\ + y-client logging=yes start-on-boot=yes workdir=sata1/docker/ name=\"awg-pr\ + oxy-go-client\" mountlists=awgcfg-client\ + \n\ + \n#:put \"Start container\"\ + \n#/container/start awg-proxy-go-client\ + \n\ + \n#:put \"Add user:\"\ + \n#/user/add address=192.168.100.2 group=write name=awg-proxy-go password=\ + peskar55\ + \n\ + \n#:put \"Add forwarding port web-ui\"\ + \n#/ip firewall nat add action=dst-nat chain=dstnat dst-port=8089 in-inter\ + face=ether1 protocol=tcp to-addresses=192.168.100.3 to-port=8088\ + \n\ + \n#:put \"Add native WireGuard-server wg-awg, add address.\"\ + \n#/interface/wireguard/add name=wg-awg-client\ + \n#/ip/address/add address=172.17.0.1/24 interface=wg-awg-client\ + \n\ + \n:put \"\"\ + \n:put \"[::::::::::::::: END :::::::::::::::]\"\ + \n" +add dont-require-permissions=no name=tmp_print owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="/\ + interface/wireguard/add name=wg-VDSina private-key=\"4CHyNhbOs923/EyNDguMX\ + JXdghL2I3UyzXPwrANVgHs=\" listen-port=21641 \ + \n :put \"======= AWG PROXY SETUP =======\"\ + \n :put \"\"\ + \n :put \"Run awg-proxy on your external device (10.30.30.33).\"\ + \n :put \"\"\ + \n :put \"1. Download the binary for your platform:\"\ + \n :put \" Linux arm64: https://github.com/timbrs/amneziawg-mikrotik-c/\ + releases/latest/download/awg-proxy-linux-arm64\"\ + \n :put \" Linux arm: https://github.com/timbrs/amneziawg-mikrotik-c/\ + releases/latest/download/awg-proxy-linux-arm\"\ + \n :put \" Linux amd64: https://github.com/timbrs/amneziawg-mikrotik-c/\ + releases/latest/download/awg-proxy-linux-amd64\"\ + \n :put \"\"\ + \n :put \"2. Set environment variables and run:\"\ + \n :put \"export AWG_LISTEN=\\\":51820\\\"\"\ + \n :put \"export AWG_REMOTE=\\\"109.107.177.79:55055\\\"\"\ + \n :put \"export AWG_JC=\\\"9\\\"\"\ + \n :put \"export AWG_JMIN=\\\"105\\\"\"\ + \n :put \"export AWG_JMAX=\\\"920\\\"\"\ + \n :put \"export AWG_S1=\\\"90\\\"\"\ + \n :put \"export AWG_S2=\\\"136\\\"\"\ + \n :put \"export AWG_H1=\\\"472437748-472438637\\\"\"\ + \n :put \"export AWG_H2=\\\"707808381-707809250\\\"\"\ + \n :put \"export AWG_H3=\\\"1609078552-1609079495\\\"\"\ + \n :put \"export AWG_H4=\\\"1012329484-1012329741\\\"\"\ + \n :put \"export AWG_SERVER_PUB=\\\"o5IgCl1/G6xbl6j7sPaP4ZNYREeYOmqZjKbm+\ + v0b9jU=\\\"\"\ + \n :put (\"export AWG_CLIENT_PUB=\\\"\" . [/interface/wireguard/get [find\ + \_name=wg-VDSina] public-key] . \"\\\"\")\ + \n :put \"export AWG_S3=\\\"45\\\"\"\ + \n :put \"export AWG_S4=\\\"27\\\"\"\ + \n :put \"export AWG_I1=\\\"\\\"\"\ + \n :put \"export AWG_I2=\\\"\\\"\"\ + \n :put \"export AWG_I3=\\\"\\\"\"\ + \n :put \"export AWG_I4=\\\"\\\"\"\ + \n :put \"export AWG_I5=\\\"\\\"\"\ + \n :put \"\"\ + \n :put \"chmod +x awg-proxy-linux-* && ./awg-proxy-linux-\"\ + \n :put \"\"\ + \n :put \"3. (Optional) Create a systemd service for auto-start.\"\ + \n :put \"\"\ + \n :put \"Route specific traffic through the AmneziaWG tunnel:\"\ + \n :put \"\"\ + \n :put \" Route a single host:\"\ + \n :put \" /ip/route/add dst-address=8.8.8.8/32 gateway=wg-VDSina comm\ + ent=VDSina-tunnel\"\ + \n :put \"\"\ + \n :put \" Route a subnet:\"\ + \n :put \" /ip/route/add dst-address=100.0.0.0/8 gateway=wg-VDSina com\ + ment=VDSina-tunnel\"\ + \n :put \"\"\ + \n :put \" Remove a route:\"\ + \n :put \" /ip/route/remove [find where comment=VDSina-tunnel dst-addr\ + ess=8.8.8.8/32]\"\ + \n :put \"\"\ + \n :put \" Show all tunnel routes:\"\ + \n :put \" /ip/route/print where comment=VDSina-tunnel\"\ + \n :put \"\"\ + \n :put \" Uninstall:\"\ + \n :put \" /system/script/run VDSina-uninstall\"\ + \n :put \"================================\"" +/user group +add name=backup policy="telnet,ssh,ftp,read,write,policy,test,sniff,sensitive,\ + !local,!reboot,!winbox,!password,!web,!api,!romon,!rest-api" +add name=manager policy="local,ssh,ftp,read,write,policy,api,rest-api,!telnet,\ + !reboot,!test,!winbox,!password,!web,!sniff,!sensitive,!romon" +/app +set cinny firewall-redirects=8094:80:tcp:web +set goaway container-command-lines=goaway:none:docker.io/pommee/goaway:latest +set home-assistant container-command-lines=\ + home-assistant:none:lscr.io/linuxserver/homeassistant +set n8n firewall-redirects=5678:5678:tcp:web +set nextcloud container-command-lines="db:none:docker.io/postgres:17,redis:non\ + e:docker.io/valkey/valkey:/bin/sh -c 'valkey-server --port 6379 --appendon\ + ly yes --requirepass \$VALKEY_PASSWORD',server:none:docker.io/nextcloud:ap\ + ache" +set pihole environment="pihole:FTLCONF_dns_listeningMode=all,pihole:FTLCONF_we\ + bserver_api_password=password" +set redlib firewall-redirects=8087:8080:tcp:web +set solr container-command-lines=solr:none:docker.io/solr:latest +set uptime-kuma container-command-lines=\ + uptime-kuma:none:docker.io/louislam/uptime-kuma:1 +/app settings +set disk=*7 lan-bridge=Bridge-LAN registry-mirrors="https://reg.vados.ru/:http\ + s://reg.vados.ru/,https://registry-1.docker.io:https://registry-1.docker.i\ + o,https://ghcr.io:https://ghcr.io" show-in-webfig=no +/caps-man aaa +set called-format=ssid interim-update=20m20s mac-caching=16m40s +/caps-man access-list +add action=reject allow-signal-out-of-range=10s disabled=no interface=all \ + signal-range=-120..-80 ssid-regexp="" +add action=accept allow-signal-out-of-range=10s disabled=no interface=all \ + signal-range=-79..120 ssid-regexp="" +/caps-man manager +set ca-certificate=auto certificate=auto enabled=yes +/caps-man manager interface +set [ find default=yes ] forbid=yes +add disabled=no interface=Bridge-LAN +/caps-man provisioning +add action=create-dynamic-enabled hw-supported-modes=g,gn,b \ + master-configuration=2G name-format=prefix-identity name-prefix=2G +add action=create-dynamic-enabled hw-supported-modes=an,ac,a \ + master-configuration=5G name-format=prefix-identity name-prefix=5G +add action=create-dynamic-enabled disabled=yes hw-supported-modes=ac,an,a \ + master-configuration=2G name-format=prefix-identity name-prefix=5ghz-ac +add action=create-dynamic-enabled disabled=yes hw-supported-modes=an \ + master-configuration=5G name-format=prefix-identity name-prefix=5ghz-an +/certificate settings +set builtin-trust-store=all crl-store=system crl-use=yes +/container config +set layer-dir=/sata1/docker/layers memory-high=200.0MiB memory-max=4096.0MiB \ + registry-url=https://registry-1.docker.io tmpdir=/sata1/tmp username=\ + amsternl +/container envs +add key=AWG_CONFIG list=wg2awg-env value=/etc/wg2awg/wg2awg.conf +add key=AWG_LISTEN list=wg2awg-env value=:44044 +add key=AWG_MODE list=wg2awg-env value=client +add key=AWG_REMOTE list=wg2awg-env value=109.107.177.79 +/container mounts +add dst=/etc/wg2awg/wg2awg.conf list=wg2awg-env mode=ro src=\ + /sata1/mounts/wg2awg/wg2awg.conf +/ip smb +set enabled=no +/interface bridge port +add bridge=Bridge-LAN interface=LAN +/ipv6 settings +set allow-fast-path=no disable-ipv6=yes disable-link-local-address=yes +/interface detect-internet +set detect-interface-list=WAN internet-interface-list=Internet \ + lan-interface-list=LAN wan-interface-list=WAN +/interface list member +add interface=ether1 list=WAN +add interface=Bridge-LAN list=LAN +/interface wifi cap +set discovery-interfaces=*DF +/interface wifi capsman +set ca-certificate=auto certificate=auto interfaces=Bridge-LAN package-path=\ + /tmpfs/capsman-packages require-peer-certificate=no upgrade-policy=none +/ip address +add address=10.30.30.10/24 interface=Bridge-LAN network=10.30.30.0 +/ip dhcp-client +add disabled=yes interface=ether1 name=client1 +/ip dhcp-server +add add-arp=yes address-pool=dhcp always-broadcast=yes bootp-support=none \ + dhcp-option-set=domain-search-set disabled=yes interface=*3 lease-script=\ + lease-script lease-time=10m name=nucDHCP server-address=10.30.30.10 +/ip dns +set allow-remote-requests=yes doh-max-concurrent-queries=100 \ + doh-max-server-connections=20 servers=10.30.30.33,109.107.177.79 \ + verify-doh-cert=yes +/ip firewall service-port +set ftp disabled=yes +set tftp disabled=yes +set sip disabled=yes +set pptp disabled=yes +set rtsp disabled=no +/ip route +add check-gateway=none disabled=no distance=1 dst-address=0.0.0.0/0 gateway=\ + 10.30.30.81 routing-table=main scope=30 target-scope=10 +/ip service +set ftp disabled=yes +set reverse-proxy disabled=yes +set ssh port=2222 +set www-ssl port=4443 +set api-ssl disabled=yes +/ip service webserver +set acme-plain=no crl-plain=no graphs-plain=no graphs-secure=no index-plain=\ + no index-secure=no rest-secure=no scep-plain=no webfig-plain=no \ + webfig-secure=no +/ip ssh +set forwarding-enabled=both publickey-authentication-options=verify-required \ + strong-crypto=yes +/ip upnp +set enabled=yes +/ip upnp interfaces +add interface=*3 type=external +add interface=*B5 type=internal +/ipv6 firewall address-list +add address=2400:cb00::/32 list=CLOUDFLARE-IP-Address +add address=2606:4700::/32 list=CLOUDFLARE-IP-Address +add address=2803:f800::/32 list=CLOUDFLARE-IP-Address +add address=2405:b500::/32 list=CLOUDFLARE-IP-Address +add address=2405:8100::/32 list=CLOUDFLARE-IP-Address +add address=2a06:98c0::/29 list=CLOUDFLARE-IP-Address +add address=2c0f:f248::/32 list=CLOUDFLARE-IP-Address +/special-login +add port=serial0 +/system clock +set time-zone-name=Europe/Moscow +/system identity +set name=MikroNUC +/system note +set show-at-login=no +/system ntp client +set enabled=yes +/system ntp server +set enabled=yes manycast=yes multicast=yes +/system ntp client servers +add address=10.30.30.81 +add address=0.ru.pool.ntp.org +add address=1.ru.pool.ntp.org +add address=2.ru.pool.ntp.org +add address=3.ru.pool.ntp.org +/system package update +set check-certificate=yes-without-crl ip-version=ipv4 +/system scheduler +add comment="Every Day Set Global Environment Variables" disabled=yes name=\ + AM-SetGlobalEnv on-event=\ + "/system/script { run AM-GlobalConfig; run AM-GlobalFunc; }" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Every Day Check For Update Scripts" disabled=yes interval=1d \ + name=AM-ScriptInstallUpdate on-event=\ + ":global ScriptInstallUpdate; \$ScriptInstallUpdate;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Every Day Check For Fake Bridges" disabled=yes interval=1d name=\ + mod/AM-IPbridge on-event="/system/script/run mod/AM-IPbridge;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Telegram Chat Bot" disabled=yes interval=30s name=mod/AM-TG-Bot \ + on-event="/system/script/run mod/AM-TG-Bot;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment=\ + "Every 7 Day Check For Update and Backup send notification to EMail." \ + disabled=yes interval=1w name=AM-Backup-UpdateEmail on-event=\ + "/system/script { run AM-Backup-UpdateEmail; }" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-date=2026-06-25 start-time=03:41:27 +/tool e-mail +set certificate-verification=no from=MikroNUC port=587 \ + server=smtp.mail.me.com tls=starttls user=vadossio@icloud.com +/user aaa +set accounting=no diff --git a/MikroNUC/history/20260817/20260817114001-Diff-Config.rsc b/MikroNUC/history/20260817/20260817114001-Diff-Config.rsc new file mode 100644 index 0000000..9429fa4 --- /dev/null +++ b/MikroNUC/history/20260817/20260817114001-Diff-Config.rsc @@ -0,0 +1,12 @@ +5393c5393,5394 +< set ca-certificate=auto certificate=auto enabled=yes +--- +> set ca-certificate=CAPsMAN-CA-2C900E832E04 certificate=CAPsMAN-2C900E832E04 \ +> enabled=yes +5407c5408 +< set builtin-trust-store=all crl-store=system crl-use=yes +--- +> set builtin-trust-store=untrusted +5487a5489,5490 +> /system gps +> set set-system-time=yes -- 2.52.0