From efe676ebe36ff569f69907426227fd8ae4dfb761 Mon Sep 17 00:00:00 2001 From: vados-dev Date: Sun, 28 Jun 2026 07:41:08 +0300 Subject: [PATCH] Backup for MikroGig created successfully at Jun 28 07:41:08. --- .../20260628/20260628074104-Binary.backup | Bin 0 -> 169095 bytes MikroGig/20260628/20260628074104-Config.rsc | 2761 +++++++++++++++++ .../20260628/20260628074104-Diff-Config.rsc | 79 + 3 files changed, 2840 insertions(+) create mode 100644 MikroGig/20260628/20260628074104-Binary.backup create mode 100644 MikroGig/20260628/20260628074104-Config.rsc create mode 100644 MikroGig/history/20260628/20260628074104-Diff-Config.rsc diff --git a/MikroGig/20260628/20260628074104-Binary.backup b/MikroGig/20260628/20260628074104-Binary.backup new file mode 100644 index 0000000000000000000000000000000000000000..dd27b9c63532e706989fa10fb86f450afa7e139d GIT binary patch literal 169095 zcmV+aKLEh*sF8DrlmY<8zKVXK_V0MT%jII<-BC9H0002|`~Q9*)C2%n!s&MBV4Aq* z@tNnl=f#~*B5-mxs!QiEQn_LV5M|`6c1D1?@7=;Pg}thRF0InibOjakSWt@kl0+4# zbb`m#?|^~liuo}a&P-F0%jp-aB~4>CRxSRJAJ_}@HTW3t_<*ZWafMwa0!+1&Jn~<# zbBytxo_qrFJ#f3n1clwEsOCw-kcl$v(I^GvAkh04>Vk!JLRZe5K}~kr!az)ner(5% z27mb0qkcE_%Sky!xuT7R#_6SNk}C<76k5#trz>i>3W)#?3Vt;=ZHXCfPH^Y@eFG@e}B%mXQy!3AnHI6?ghFYG%co>?8o9t>T_GaZI%FOC+x3ZxW zWIF%Ti7$$IKOZuFRp6M*%DKdE(V`sem6t2*R9tKzI1crBdOnS{iv z|{?A$x70& z%}|#W0-4n(4QH}%OX&@)B&w0$50!)A6{C=~EH$RKCXTRO1wh=3)B6r58NM^T1yhQT zkoXGR{&|tvGdI}-=PQ8CCMuM&sQYv}vUAlx^U?E}-it$rqCsS<)m^Z&0v1%V&$clI z%Q#pEvifowjcHzJ@Pw06-0LWR?~W2@dCd^cyLH;WK4f0fT9XnE!8yj-@x}EMpjkC> z1p4aHQS|+&8BU1aLWK#@W}D5GYy;nt2-Tc>t0%IX8h~!-c%1IW7tzkfgj;xGksq!r z94zBIom2tkHV+mNR|{jo!@$cTu^$PmV@IsXD~8~xgsH}>MEgyo0X~j zNs)zgj_-d0zB=5572dyDpWS+JLCGK|11I8`bns7=9nW;YA z6gl_F1zKN$q!LwfXy2Dy_9kGzi_Bfd*)4hQivQ>ixT8HtOuU&D=FYQ2;KdRuQz zNk~OonSH)^G&eGM_S7n%DtyF0Bq)Hc4p{U-v{!rv3fqeZm@SrceCu~S(~dbKB0>k?J9XB~FHjxm ze+Zr2cDQVPN}^ISUw7M2#R^6MF9mBPR7?G04n2xz*&(>I#N@}UKt1=9mB?vZqht&g zDdg$?-$(c1(L;Wg5i4BStmiNChUVrVdCHs06B0p!&A2<}JcCcnsxxuEkCn>U7sXY&#Hrmj0Xf2B@7=ir&+#vv zLo>YEYK;3Wp#gPBkJ&#b-_tml!pRtHN33N}ODJ zeMAA-f>i+zMkUDj#H)&dRn|fHpH$$()|y4rJ^`~K^f7Ego2T2 zRr(8offcKsguF_nO(4Pf8Qd>?lolbduVvjSJUj^cbv~l z3Bs2gJSXh^_AVHrlt%qLGE|~1jB%-3P%1aGcP-#-Qz)n@dmqQ#-ah6RfFWbD>@*zE zrTz$`zgpJ*2_dAI0o#Qql$l-O_Ksv!a=KAE=e~@(XgvnanR9Vvu;U-5hyKd0X(Ib{c&f!lZ_S^}Ieqk`l2@crqB~OTSX$ zO-CF=1v)$hdmJ9m)5&~OMnT6Zf6i@O_n>FX9eGdEg6G((BmO7Q?y!fR%r7B0+;JWU z*zcWb`wbY&W9^3@PbEvP7FP}Y8aa|h?{|B|*7vFVPh6=s8QV`vl93>H;m-1e@NV3# zu+j)NqkcOW9EKeEPbh6^g#k)9tcr5dpaT=p)o9wyu7l5eW-YkvD+-0-oD9ZyOuK5! zu?3cyi;BMTZ_$RC;w%^~#Y%$CS4<6o5ntV~qSOARIM9K@WASXOb#4G>DCCsBLpm|j zIe5Un1v^=)Rvpo?B!osruXSTbxVc3o$Jfd0$D+>mGz?&#qFSG3Ijg_T4H40s?$od` zspIJk&B0g$=j)B04u@!SO<1q*gZaQJ_D6l`x?p>1h$G1uRggXE^IW3+w__In=7Y}h z&UlpwOL`fl!E7gkOrB!=Xw7{&3n%ay3$@Oy3FAlEc_eN@N>`exBf*pJV%XWnk!Q}Z#^;nrB6Y>2u zBY3ZVE;+RJB|)84PfUzrhWo&TdTE-2s zm#A6ABXf_-g%4PAZ|JW8g*(6}k%jycIVBKN5#;BsxcH7npQVN)SRh*J=SBuL70MtJ zwP0S@=2D{jZ4M_DTw%+YHHEA3-5BVLl*3ptUZzeyLuEV*V8&WaiBG*1tkbL7A)Yx+OgJ4bkX{#Fw3o*%-tw*7FS*}E8e2gtc zI3nnvP83CJ^yTJVD5VAvwRs&=B;~b@sf*C+tT>G5>5Q))t?MigYuts7*-IBsBL%OU z*$0wp{igv`W*i98H^$fnlBSX9kjL1UEh3>Xyz@b*O%jKi9>(eq^s*I}{snE?_0<-O zdN3=En31MY#C77XG*Qga`B)I;JjirA0L#P<_@|aqZ6k;Sp z{;H`R?<8`>nR~800dtGs&-mTJ4CC`t^o%eF2hnE$E~EU9Mz2;7de0#=fkq(}wQy8p zsUM<4d)+-C0dH5jGom42qg<{bU!nz}LgV>;eqPlzd0;{y+U=(rH|};I%6t8T!5WfB z%}kF49GHv{2=WP~s#QMjr1QG=x5gr%ODXqn4^%8F%X^lsKDxgz6Ia7E5N<5&M)ej%WPEvrG|M^VvC z52bXgo3$fyx^ayd&K|g@+7F;+4_<(kthjg&Z}lQz%WT zHOj_|Zzxm>xD%{gHCY>xqr~oZ3B&#DAlQSQ4EG5ji``Gb<26Yb2}F zP|LdfS>~cN%v@IAoFR%PNF$BXM&kULIIVx&x9iIu5om5i>HTn8HI=~Fc{;Imqd_q- zWRhIV9|`w(bJM;y_XyXO((v}+(~kpc=bl@QJ_N*VrCHbIxLxhYTUYU|KI4(n1f;p! z!Zu;O0bozGoPefIY_e!_Q`n-(x|#+G-dt5i2B2XiF%m=|j}vWtnCFKpv4fB{V+<&$ zG>{cA{Y!76q_>dKyZB>IPGPjGOi!@Zm79CNDw=xXd6j+ZqH144RPQ8=8 zz5`Aux7n4#Ev0N5ei5i49}xKY`^bOmgvzDTzVF%xX8~Dai$9837ungZ``jH+{P}?H z%Dv{L)Pc!^^Q?8L^v;QRIrT<+w>l+QCRTLbE43-!Ps>Nzsw`XPHj5Nr zHrpC1HM|$}P15-&7r4^O{x2v_NhKA(mAf-Tu|n$D8Y$asf4MkyxJ$Pne9a$t;tD=n z#)L$HIrfapy0w@3OslidhgY8jf2rcsaW6_PoxkSSm1R9TLtv7$UN+JOI0DDy(ZyJ_ zOj?@qyb2xlG_5l14)A}jRek`-^DgH<(5qQCBJ~}N`EkPNVRCK*0u_ytLNZw6iR`T6 zJ6}HIn}I||gp_5iONjtRR|@wI`D;SVq%wX2rq=YG=GrZmW)+9!F|=&>HsFYDVNIAd zeS|T^La@qsAAWJ_Bj?Ww+(hn>tWP!>r6-%@qHfU`b2tfwN~WYw3Mm#6QvfxdvbTzO z;vLvE!CpMj`X*XYKsEF%*v;Qg8z3J*b=7Ff2 zmp_NqmJID+R7$jJTf4?72=XVcumL;&6HUUnla4YRM6rDbktGP`z(s)QSw)fD7!hVG z`{*)K`yEsuv5Ejqd{u+2l+epS$XZ)9v8+|$%F+SucgdL8S6Bn$xpz0=mCi08W+eQM z=yYn4C_{|=eRn^*2i-i8a6#8BNt&mH#6tkP3ePwX{wjCAn*qiYMjr$Z7HX`&2^X;AeUg^y)9X-9+ zC!`ghs~Rp2f${D<^ng+Xln|0{Dc{tn+{rKNzGLj%`tX{6>`f(s?)*y?Gq-yQ#rdQJ z^3&U`#_v3oVA&Dgf|H!IrnxsDVckLy^X4jevr+-g4^W4_sJVMvly#kHShjFD{9R+F z{R8}wiiL4Eg!?2SVy8*wTXA^kTuv94ZxFdl@BOlcT-jCW4I#8n{?t8cFJ-&J+c!Vf zVmtxM3B@y?VP~4t&6lkF!OC8sS_DqQVb2v@k@y!jUuWa{MDB5A+cOs*+NZW+W5IwJ zg#b`tPG6g_brhYUoF}(Xtm+1*lRoRpgpdGLzIW_gIINn`bGES|Nxxx0Jr7TmqQ1%J zfGs=qL2sGa&+UL#im<-7Kwe_QICS)6wl20r(2fB?kWyPZmZYUe&w z#}ycUwx6SgFhHc@L;E$?-P!7XV*y%a_3o~5up`68rSu;+8)nGSndvVW(~8a0WZX}` z@9P(ja)w$(D+-7z07v1^vK#E!>imcW*P1ob1Sq~utinUxh=M=Yl1Wc37^U~I!^%eO zfe2Qyo3zOvi*ktZD-vj@H*_Lj1wz7*-pyG8a?cX2>k6O)7DC?rFpZ}q=Ud#v@R{-# z?Bw23H~IcoX8nsxBXp;}isdv=5be+go+>7LbxolFZ*F`imMEEGz>84W%(poi+ZeiU*Nf-xm=Y|| zHo;6K zU`?k#4}UDLi*K|laf>eKNQuyUdNhec_&dO!Oej=8T6I^wLgs|_6n{X+}!RZ|7 ztN~@AtjX5aZGczw>~cENX7$50iLumN+^St7dO|ipo+=T5fiX(YJcHK~ti=f0n!!HJ z6HP;ow=}s4DtUj?7vnftDEBI&H3;uEp=ii7MFif)0<&*vKI3UN5Eo*WsQ+MAoiN^ z*-U0{%S;19LDp$_9V^fq#KM}OKsmKtn6;ogyO|BGfWeB$o+R3MsI&jHsF!xWgw0XF z^;5k5BZ9VJFaBfoQza<;$5%bjKO(X{Sj$9k`@ldvha~_0s;u^6TDCRU7Frh0m~I!S zlM55dD#I851H$zKyT%riCm2Api<|ux1`1H(UO50Pac11YcHiBsC(d9I{Aq0SY$guO zRb$GZTkVeUkMZHMm;$Z41rW`H3=(oUY;)%98A#&H21b3V#%9tt^DP0^4csA~j|yJ^ zR#bjH!BqDNq|tk<+<#a*X3IG5tCiLVc%+OrF>|W_AiHR#8z3RcpxtOE;ua=Ypj#9IkfE9$&)F6{-fP=jWhV&u`lgwdk(@v?LFx_Jw;N@QIRBm@fr|d4z|D zote834p^JR!ChE6GV;|rN#5Yua!XV2V0nn3vCZ|DKre^^h37_plaiFTu^)d+r}LW) z^u|pB!2cfj?aN=Im&gax;SP7ZXyHkKoV@PhUQ7?MMRPjd9)b6YSndjmN9`k_L04_` zt!p-KJt;bO$9?gAe0R40WIg|@**G#qns3=ImY!Nq-n%L%Hz~(d;=^qp(9wz+n5bUW zHF#qjoHT9FiRpFGhe_0&Zgzpnz$cI@~Eb|t*b*5i+5lVI#Xm8x+SQ4@5 zm`2s34MDv|WD6;Fht_oV^l_%JmGk#r9O{xn+X4rTzXjl*Hk&iIjcLc7v z2)KgZ2!dfz`TVi2>@=f!9q9n2S{-Ne)q6|*_?iQ$MhTMjYXpi(4Qmi$KzB=coW%y) zMANy@q12}9OO|l=E#jIO>=l(-{NnqPEN_D->)ERpcoAd)kq7w%@~v|q)H5Oq+iQ24 z&z|O9md;NIs*c+F7##wXsrmBCe}$G~m{=opijU&_;<2tFBU-S~u zA&4oq(;NbjeG$~j6c1|E5Jx9V4x}&P+0-wjISwu^8jIl{D`&muSY?7h(aeO-MDg<4 zv)OQANH=6sb!?-~-2r^?f-XL2``EK7s^kKP(+^k^0V`$wBPS6?(URn1dEj;sZlZ*k zHXzbpN?g_jK@5L9Lkk)ct&*T-T%?+vN2sbxeFW%OA`15W)`$1eqm|d=ys7KSrdaJr zF_|aW*mK8cAp>LQ#{okxCqI-Wf?~`emjoGSFoRr1K1iP3fzPoeJ+A90K(CsjSVFce z?D87m*ATaQH??%>)oyRqQjWyz(zfPmi)mnAKB}ud+8li-5;7G~ricLR`Ve#&9orqa zfgte7m8==@X(A0$dnpc^a!qzM&$2D6Aw?W+q?>o^3`7N%ccSiy#ciHF5A0(br#C5Y zT)yay1-oUGd)I3%XR+@fR1P2h9|9Wy{Z&3KUqxV_Y_JCm&bxsX$ujHV(-k}IN2K-; z?3~w81&=2h%J3ziwN!oelzTf79`uJG3MMi%-lfU%Q634t$vh(b^ zk5;WNE=r+m7!&#FN)iS1^0H-_cRH=$yXRwCu2Nqd4*_(!jV6QT8^Wk25>`t&dX**14$RpEtE+lQ>! zt#36)T0-v$#j)8lfYiyjqvhko`%qqcH&Wbi3&)RM41Rsn-qw+af6IEZuOW)|W|hEd zCcyeFwj=Fz;xivxE!XyYX$g^VgI@FLFu3hRGO?J{TtI|=(2{d^53`Esw~5v^&=L4unT`VYM>KpT zlRqKV7eaS;&AB2nB??SXw5&E`;>d>@P8%e>LL7d`NpHh+J>zJ8lD}4smeH%9pCR(g zaLy~Xkx>9T!&&)=dCd69(*O6ws>$INH)vDfeAtMTjYloA(D@ zA!u62?wEji1+oB=Y57D)&bo|5RZx*1wkFB1X;JOQ$y;AC5fPMp$;%FqX&-RiI{q9D zb-iHMt8!MeEgPGMY9p+p?y`I(O!UskH`0X9%A$==UL-1QqQBecD+)Wz-8ar%Jp;_e z==zHk#k9LrZG>g>H}~%S&hEDEx8QlfT^JsAPQ7m+KRN>1GddE%sVvK}IyxaFDcGdM z9r8Urq2LQuNHd)j=>aP2pcWHq0g#5iXBp|>l9T>@%BJbX=iOMlTwHr)?AM2g#W9SQ zry0+49}U> zAO0rdGeK`2e$|#U<)`p5fk}b_S(aNsWD;2~08w7tTO{5j&16m`sCUZ~&>C2-PcgM# zey*t?@JYk>a`Gn_qg#^*&|;SsF}kB$UEe17%)d%CzS${SC-pXt-xx*iQEr(*0kPzM zyxH>z4JvNn#X>Z8hN`19VHnUH+MUD;emX}sYpmX?*!ca%4P#ndqs?Mu0R+qn>ST$& zE8}=LySpb!|65~!H8gKszr)p#8Lr^u!-Jb|n*+cn_s(=&`zx14XfH3DULu(me%@U> zvQ0P!S8Us_E+zsyI#z*j#3PF(W1Q=;;e>G)3GbEIpedU2A*Xct|2lXoFO~`5rD^VZ zv*&Zal04ral@T8tu32B`Mx+!CTt}7?Ws8>Pelv~zlIYY_CCn%xiz@bA!S*V1u$c}j zS-J1>lri_Vy`Eq)8oSW1aAB-_(wx;g+t9vb)Ux^o6&KFhm36~Cjk%5wS*vI`9Dx=m z5UU+R5AWXpRzbOduVLMVJ^f>^ERj63#BrUPtD(urEtAUVpC~E}N2x6iIF3#3A}nun z9`HMRk1ZUhPc)QOJ=_fUe#A4`E-8<>a4xHdm?B?Q9Jj(^*~SZV<@xpK(9F|-rf-;c zlDGRb70Bd?u^wkJxYvvXUJ=vsU8>+@-`09-3)YO54AMa^zfnSj(sZi|WAoEbnQ4ql zNfpM5>o!&rPYv3IaLxqg|Cw+LmR?z$e?h18RMed()lRhHsD`bCTZAKej;LD*5|Mv4 zG`vSvPo;`MK;dSMLnc{rYX+zvD{NMirdLIT;qd%D!2O#iTL@pov^^gpKhamoyaz`yc$ZyTG|rO}M(T!&yC%{baZhv$4r6TsU> zV1zY3*{z9--d$gS2~#=qAm&=O3g%-uekj`We_Dp8J*Ta78(6j4oeAB}-(%IVi zB%ayw6t-B-zI6b2#D)5XYTj6=FpV*U@39@PG8O$3b;faepexr>(8cz|R{4}6z`{4u zEhLI-^JFK&fGy54x|*ST@CaT!1-N2q{pb2j54~0;7xKKliBH(TBdV~00=%8)h%@WC zaGKkWGLo}|Tanv-oldC)my>V5@6!x8A}E9Os?9(%+q*;L+XN}wk_Z|jMh*E_6qR3> zx_R{*BcI`ZxEV2525%z<7pONaI&QG9PR&;jcvU&PFt}gPte}{Z$x>8v!+e`@Ctq7$ zz1#A5e)D`_xI#)gtQ;#-K>z>!?qE0=RNtI?H;)p72irI}dT%8y_a^w*Q#`3J6A@Z) ziL@VM+@e%XuBFz=F-;M>z`y4$pi8|+eJfB}gt9>ix-(1@wXjggl?<-I8Hw-h2LX<&lUHyx4f#YAZvg_ah%n4=t+cR*7>qhJ-zU z>8yCwk8YxPWK7g};(;7`B(#?LGqCbvJW6BEXJrLIe`LmDjcr(px#u2wNmQMLFNYi8 zQtDkBQsmz-{1GbHqAFqCb)BWu`^qa-s?Oki92q)GD2l<-M^U(O@xEE>jM(NeUYRix zG8ltzf5Qlde9^dTTj;VJeS}s4bIEaDI#^M5?N!!n3Qv5z?&*bW4uKDNT#eA6lu4Mh ziL{Cch`C9uUobUZYJs~S0l0sVsccm_#~0Cs^O=pNF~@2}1HIH!SJ>tvFTpO=ixtr$uE;eBz^V!ClE{kPK#~@8Fv( z1D&8|Sj$=97jahhxFcULu*QqKkbjbD|Gi?8=FE=342x120g5w(#I)kPr`RsFNp+@x zpv4!=%K7ygkQ3m&){Ejz4((tFvg9PcQ~KQFaD_k9lRNl-&pop<1s(Z>Sy=m8#J1yX z*Mz^SS8cj{>e0*>1&<>kjA7tJocWCkQX(Hi{@I?8Wt&kg7^XmDD}MD|}V|r=b)N z_URTex&)rI5`s`S{IAXs^wg1zMM-qo!W`bkKJQca?mB&UYKFX!<@E7e8{9S^zL-=2>zzS*tShRl zW5e3UI;}ujvD5zb@4-9`H%r&$KbHnr<`@XNSD<*D84u0EBFqMmJMS6*tRNBuSYTM} zI^6C8egoJK?=O4A*Rbd7Y)I&xDE?z(!)PWX!dPo6%XUb}a)n{TUrR%n9>V)4)C+=| zi;2E(oZh%_udyJR*L(2?>DcoS!3!nff8o6TV2OkQ$5tr(!I$1JMishO>($h3bz*-$ z+QDPh3>+B-ryuB%(?)m_WI>!t5`DcuDM4}J67l{^n)SN6%8;DNTz+JrN}46R9+ku39GLh z=tZDAb^W@U+Rw>Q+~3RH?D!(~WJx$9 zPDS2NTtSZB3EC|V$z?i?oJinP(Lu+QEGfQgLFGOTF-ZGn6E@YM_InHbNF8Ov6FY%f zu2SwqVTlw2N8JO))E~_QK&Y;ob^KkBDEhhSX3Ad5~ea2lul*&x%GwF!_y z1Z~kJqCx2UCX=y(@ZU$>SC2W%D28$ zlouZJ5qk{bn0QFav!4AdCSlxf@HJ{3n$0dqG*?A~ka~PWq^a!j)moGX{R&cO2;QkEku!;?z3gru& zLIZ2#!a2Zn8w8hbu(ZLmR7$?#|36DZikZYh1U#f~PK<&{302e`z#zH18;O#Ia3lK6 z1ee}R{}Zv79^6vbdro5z%v0}4z_vfPR(GC&^ZSyQ%FTO3zcg=A|6@7ZtO^k#3klU6|AHwqO*$Ke?R{KNXCG zD(a^G+b!{v3x*T@CaW1QRXID(?Dk-U{`)PxnVP2d3`ti!NY&_@Mc zvp*46;CCTM=SN?#(ZM#;Cr{3yORQNoV<^w#hAOwPjx!fty-z+EK|x$ORu!+w>v6{i{)U@ z^!A0FP)@{>wXSz%Bd$g#-c$4Udn^&~%!-HtAg!>3osadTjS(qzEDz47FnaAEU;>A~ z+eMxC+VhJDGv@aQXmBn+(C7T5Ml2{cN=Iltc$Uqe2X#(|gVl&d>QoMgs5rJLGW-V% z0ExoN=?zt&XY>)$Os)EZ@)DZjb|qv;Hx6T6WV%=losO z0AP59^IKn-Fc5Ft!%yNo^#?!r+biI~cA7@kBm_&<3H>G(fBD&hYNvrRB^I>5mlkjg zF03tZ@}^#e2m)(GYPpUf4bJQkj;%{?YdWfQuc(YPlMI{l+1DyM0<<2H<7Epm8jtfq zBak~;OkKrdY$rMxRPvE>vKZF8*gOSm1phiGh07_ZE9l?`HxcDgCd$**?Ea#K2aVwX zIv;+;(d8Lk$r+MfX#Cmhn;D|T=8WXiINP`)LV*b*_)+b$iS2`Z0e-Jv~<%lDYmlh6=i^ypH(m6vqS#D(lII z+zX{^=f&j-lE@^>uPqHja^JnboxUjL({thRv0_7ssgtz;}**mVm}@U@9jiLVi&ylKB z%JsjI1kt#NFz(7E6~o=OYJI;Da@Fj+EJutDw zR0U6eZ3lVAt-b-F$YgZ^MMBimVRvkrYhF@?VNPLCOx=0WLj5FyobeVc!>zNcwwbWC z7LeFZdIHS&)^6^$UV>BqxHyl=oOaVqLO4FU#P?dfg`^sXVHb*l$|1ETpiRGWW`TJO z6qD_p{u8-uvEZPK0d`Js6~Sg%w@CPcDU&-}ozn6T8R_cYOqB`ru%@!Z``U2E&pO|P4hbUHHK$}};>7e3aUy&?LTqv1SON2aE%K!|zpSF>Am z9aEevgCHG;+hS2e-?`U+;J%u3%|F_?4g2GgpyaGv8LD~plo50flyf!g%g4B%wtjt0 zn{KZlX}Ky0>d1y$&Dz9pek;T7g$79$p+&JG&U1U#7;dbPf?y9;$;^r96McIBKn<0q zN1mf|I-Thc+OH{<;2yNDRb8+f7_cu^dC+^ygjUIwqzPyJwj1n? zh2@X2Cg=*X${Qey-ng*ovmgbe22uusRq}TsvM5>!ycOd>UF)nan;s!b&+@0QWjf_% zodJqkJ^gW^GSjnhp{F6x$xO9;~^ya^&*b#02M-B>HvY?w}Dj7_p6(8B9p+BOkntUV@! zu9oST1yh*SbHbAA#NKP1R-#2gUmOZ~nhbfuqT2Pl}fhwn2Hgv;)ClJFz z>oB$V%t`w{S8vKrvcHl08R{jr$ywqW(9Hdfdt#-7hjuiPR@eOusGgEMemVbfB^SJO zrz}&C>G(|x2qjO*UKD;h_4#^u?d6O;h!eiwT%!&S?*%2f6XoqF$|nAJ z-DK%Q2a<=4&zV}HLh{JV&EbB*O*28HiV@{&ZkAEz6jD!gT`3> zk+6>?&?ZEQEUUGis;he@M;LwC)FGqa#Bc5e_u@2*L?gUbEH2{MfM_ zQ6k8~%8W>wO`^YfMiQhZ5JN5_h=5CPP@o57oaNK_IB-4ZD}1>2OwI{QLcD zw0C6KykRkOV4+l3lP5+H|4>QI;gs*$CuDa2jblSGOUYmK=MIUkocg3(Ut-zVBOVzA zHw$>MPz7sub;HMs!)UbPh zWj90-%B2TsN?kvO)tS`Ovit>@9U)!8L3zp($0t-1y(T}%x?IZ-bz6M5DS^n?l&9!{ zItF=6bM;q!0Uj=V`6C3KfG;@PKsg0Wbj?f;dIkfu)w)N)fiO%#$SGAtlV#7yb=H z4h-QwFn-d3(nL0t^Y8*kSMBaD8~T-k(GS_3(hX2QdQ|%xyf~+6=X;R)Yy&n^{I{Mm&EnrEmb z4_3cy2AygPU)Ln&yfBhY#ouYL>U7ed*rQ=qgf&Hg1!_t(^WR^IghdFHh^#rRr9XpE)IWBT|)y^02WUJqFU=z$)3L= z6k*d>_ab4CLB;F1Z!ZRYV^XPX)?sk}&6FRh{oyfP`f*RYg6SpA~ybd7wHbI+ud9rVIK5h_6Ze zHDP3jd3H>chrorbGPIu}h8JUpobTggoB+Dj+(7ObM}Ix&vI+Gw4i(6CeP_L_I&>q# ziHO8xtvf#>o~XAKN14URr<`g*myWoohayL)EZ;zXxQbPLcs8M;_n7M~`)6PnkZu+c z536I)5PmSD0nX@Hu4}<8Gii{b%v%zK-jvWNt#zdzo#Ihgj=a} zs6O-)@i9%vch&&^KLEgeLKGBTe7WF47OE+^HJPb{*@{G&QFz@8ALoePu$RRzuUp>K z2<1v%I(BAuhW&EJw@%r%gMKBFsT~SVOv3Vq)BR5UCALhz*hU?5ZRBs9=GMxm$Aj?~ zNB%8;==YcyxK;*LCEn$V{sl*T_FI2(f)43X)&{+&*ZP7}jIq9JkfJ{#c^k(?u-~gV zb&hCgvX$TB)QAw4kD#kLgoS4f1IV$lm|FPx-*7drc((PSH1xvIykz`9v!b$3qpP0= z3B}@{${-%GIbPbHLj+2n1oYWwfmcO_gQRdI$cHeJq$@;6|5fC0dYq$i)e5ILWZR(9 zQ%?qmj~U+>Sd(~!Xw0hcz)A#}oIi=Hh4(cpfVaOopCaI{qQuQzqy6Yvh+=JjVYBU)xuW-d zQ`eLqNw^%>QgkQ*H%PkVWxMXg`#C^THf>~<(DM3I4u9ewY@PF<$nlmSl?iYSxEshh zvSy?QvmR`mqqYstA|pcSTUoHiYG*6mLXB;CYj}s4PAwJUhK*SflRfHDxfLZd#BtU!nVCb-PBXaC(g>-Lfr0hZO6vx(OLx5sHLKrntcXI z1Uz{n`We?i3}Hn)!7Jrh&|G-CSoddf@d>roXa$=m;r*d=5Yc){NluB@AXste(b=Mi zF8&LKdS6dk?+O5v=0F;OB#;Q|)JkI^I=t3?yWwq&PrIM}C}E`G9(nb&5Gi z5EZsYtVxTbJlIol{EaZgY7brZcWY6*55r&B6~V7-fKq2%*+MAH1WY)w=8o6CZua2z z)4WM~JTOqgua?6^idDr)Ni^X9)EqCh4Eer!1%6b_W;Y-u`n%!hZ1dig_MwXo~_)gu_SxPRkT!(ak4Uw69YagVaFZ>iaLpXa6qCJEVvm5$ojKA-Awub`%?|j z(}hmT8$vVHHN*0(!!N@9yMSieqd;6+dWa8u=nkWi0*ed6DflEXrW(TwKUdVjN=|lq zjpJy^0uqHNd79K3eP5Lr1tKq)QY>uWL4Zfq3s}_ttzb8LZKv3RYVdzAl6it z$IGy7ii}%=@d>dKk7!e;2F=b;!Nkb(O1-4L^Ri7l+E%`Hwt8B=Hq+;E*`JDpRF7w* zc`@nZ6-RbS>PJlK1ZUR7n@9bgst4$5`AJng{tS4GyD=knO>1sZX-bd*k6Nn59J;?R zZR!=|P%Ju1i~zl#hdNXw|4d6o68ZJv^ge0JtDEs~KgG~CEo(>6)CQ2~lgR;URlHBK z;k=I~!Nxm$dIOqT$$~NdZ?C%kZ6hZ>Nf_F?faoN>M1Rm$X+gB4<`bzaOW9~BZSR+OhTRc;f-1E$IIPP~}<9wijl`$FAYjlW^v zub9%TI|1!CSRuyKshU~e+1mTzOx>OQwrsOdpIK5|@3>GRW4q3FoDjryXQ5-=^5UFG z(8O}A+I*^#OJ{?B>_-_s%Yy{?g`$NFd+OFbr^FX8G&pT9%MWr}vSQ9nf$x^*`Q(4X z$R@LD2?#q}yuzose|mG=O8EB)h;Ef^Lep{j5=E!=Qw3=lq?izv1qg0-pfD7D+6!xt z%=`ksVQ|k{H^@fnLs&WuIm)s47;PyEa>6DrB~Fpj?ecp&cTi99qf-=BfTx(!qyDx2 z1mFk@$3H7>OMNN?_7CnV`!KgfCTcJ){V1ll6|y5=cADeRdhV~4y)M%p{fLi|z3ADy zwXMfmvbIgEBq)dstM||{x5aI!I}RlTE4XN`HiEtSowWtfFavMG89$c%V`aDNj?2Nh zSxJS#uLA0W@4~5QPY^Mv_*VU5Kz>+(*GHno@2 z9jR*N(;APN96lAm$PL#nY7qQWu6dgxT*||6s>~LAxh9k^S(Fr9oa~`7;In9?NCvxP z`+!^Bcnf$%vrBz6*N-QSX1k@5mD@$W`981OAu_$)Am)`yDFv69$C<*)Q9b>v5FgVA zpu;qjsNN2zHpm=;s^AOK3`5-i9ewz=Upyb6QxSEdr6n7mMOh`8S;fIdKfM7NEUnP6 zYyw6L%4daqlVUF;!2QMa&C8VBxOhZ_x|SB%kst*K_bI!tO+IR8Mhb00FG3k_EVFa= z;Vx8nkhH!r+bDSHY7(;$DR?4nw>@IdaxN;+ukqV{3M2tsD*}MMhv3@wr1gG>PZw9I z#Z`w5x(M>&lR{lbY@N`7uBNYO#6{%nWV^?iP++^K+Yg3gmi!+%d)%5{Zy~g8DVXZ+i|@m;tDO zQN{i8f3L;lkmJ6v&YG1(OMG+-c`Zfy_<@nexzn98Wdiy9#k#8P|P*92h{dshr^# zOL0C;8N8D$)?R>eXTHDT*b4^x#Fv9}&I%=24Ua&OdvH%jYGBKuhSPaw@>!2X*e)^2 zdhO*EB6YL|PP;!iw9J%ApIf7P4nwIb!y@BRQ>VP+wc)Jk)c5}@hoIyy@eX}Te_gPP zC8vj_zVCtH*b9L$6tG*$lyh%y+mgrA`LfgY?-ApGi5Kq#pPDFNoTmcHbfaG+8;Yj^;7;W^`cpUS)PDd1r z@SLjZV7hVV?8FSvG zJIaqkaCMa*x^1uS1l|{TY9fmxK)W+=8d<+7rSo-r)yWkfn;_Sd#c0GT@$i#p;A#Y9 z{Gb_K7SyI6ak#zG9l6ydBDpl*$GqiL!q@e$^5+@KicIlZCPCKnL2LiCxCQj(gl|!6 zgHEQjIXNIN`wmc}7w>(dXSAYf3ecym3kM@+$<^b-{~Sgbo)_9Oj@e`;+xx*-X%GKS zIqAX-*2A6HcnLUhQma=FmP7#`VIeZ&1J;R%7`3v94u)Lj>wV}pSCUBVGhX!i3Q}8+ z`XJvdGN3>G5#K4sn2bi4eUarAtVXCRNoayTxD$e3^>WMctn4tpZtb3$Y#ACXT=7hu8UyMe8!ytVt!-Ne@>Fe@`?0p25>JfqK(X}%$Df1zDW_Aw{Y z4`;J~x^!y-Q#%iC{L8WIKMrB{bzNkV6%?;xNC(H_kY8BmpzpPXHF-Ct8>G%p+LR}N zTOsiHc%{K@(90&`3yxx2vwu@|WG_3YM^KPu_?gY?H1@`g#<~eK*jlWLA_plQDb@u*#*<`wS5>!br{3>5<%B8d!#^hN6*0sk zt8yXW%d(4UA_=>fGbbYS4ROXfR`X?E6pQ?r0dLfxWu(pTKJFd8QQ`Tsw^b&&4NLL)Tk?f;EL6DLTWo; zzR9vKZB#w_7Kp?F(t z#^YP)?6vtkMQ-8Fy@wNcdh)H*i6FE;1oR`uV~fvyy2W*Euy|v$U&tzDOuUco%A1}k zW*|!@dp{$D^3yxyT1z|)9mI)Z?s8ol7x1dIXy`<2;A6*zUFvXuTW=<`^vKj*M_#KD zCuo|Q;%6hA*;mDed#9WX2g%cx=oIMPLnqOJ;!>_FZYrVb%l&B}z-^$hw{147UQ*U@ zg}^4iuV#GOgQQ>b87h8pZ+`hXsVa2fO7G4Wip@;hxo>9vV}u{RC2Ooxr@xlRWbZVrS4C`Nt0b?{ z9RQR}+dtFThw=#O3sLE^;XPGMH8)nY=ALCp zq>%p&z^vHVNcrZ^Up)OBW*T)Yns4&9!j)c4H)4%GY=oF;C(?TwfKb$3T8LSD47*_J z1LyPAB-SUH56l@AzmM*di?~FUBNH{>dbeRuU_U@DCbjZ+{20p=cnCoMmM5z;{TlPP zDvfYOLPn_`jlqt#(*j_0j6Ts&y=52(qAUUUAm7(SQ+opF1srL0g8_{&D()gBEo|0H!L2XN zC;VhAJ^dHTQX`)A+rDTw?FB3`=7P&S2RdV*M+b7F8lR#YNh{I4nSOZVzVOa~x&ZYD53PZSAOxZ;&`QOQn88U}ZKQ(@92QK)$M&YleE&YZ0WD^2 zvIRpj{9{q03mW1RrZN}fz%%M%7c_?2I7soT)!CS^XNFJ}@&_`m&ObTOMcJHNN#=>2~;Avke zB_~BvhMEq`Sa{>M$A6?=zv(Lfj%MR3VMqyg-b#_(%~tKvomk`uh9lJyr;ODk>t14= z-9fVJYHgR)jKBP|qz1I92w;E&QM-vA12nFQ@d&9l(m(vJyIPW9PF6C47iH=30>Qii zT|JWQXEYRyIKnOX3#9rL@%d?$ds?gZtWVxecM1ex0>zgN_XnyjARG1gAa(Q4`sLeR zWjl5Hi*=~yb%e?L<88O6HZd_w4Xb%7rN33VxaMokIB|Z)H#eBs7;+x?3P2#P3Z4K9 zCC**Rb58~mmEshVNxdYqcIY(G3@_6MVmwy7puJc&8ok$1j-8( z>@2muG}xO5>nwUW;WjSG*Rvv|Ec~|jp)QMBT`M>4xO!Wy1L6G9h$Yw#X6*2!Xu7&S zHjdFcsn`KUm|O9DIy0%u*eh%ckWItxy3`jKx#Rb~N*|PBPF?Weaiz);_C6d7qPF41 z!*4a92UTCM1|8aWA88l<}Y&wEPSKen29R?OMp+91`&Eg0ILp( z9n*3>U8_Blp}`=H+#QrkMOb&wW_)`Y4!<&fs)MB$-;_S$G_!p)#}TIZIJv8(qk>iF z%DsBGrxQB7%c?%>?om)i25FxfH0@>N6k4S`il2tgg~-R?2;4ASxO%ruwbTcxfiiiW zVag33fDNE{Vo*tm{;4Ub!}VnDarU9HLQeA$9E}$fp|`0uv3uPsbaSj(j6lkM!x&pU zZl}q;Lt7#6#1%PA%=g0D0Fc*|ae+OQ$*G&Hpqd8J!zFD+L%>@YV9kleWsbe&HwNkz z=B7ND%Z7UP^p(}1Nr<#_uGCK47p|DPg-##quEP9Ev&lAhhoU7h%;kwK_wv(2Xj8;R z#z>1RnT#)QPPGop%!f(QJ=iffRB~vv0uT(M30(#&ehFR-Zr4E)YJyVpIXObzwU7ix zmLExPnxiuDih#|4MmyiEa9plu&HW^~hY6&7A2gY|TDLvl#mzB2wF8)VzBQ+W^^3{0 zft~5wWx$A7xteWg6cqWOU~=VSpbcD3$H2wgH(2M6}w7JLnXu#nDKW=*(<}DgAXtp z_*Eig8cWcH*+~o`a5sTXkUT_v)a!$2P7Okom_Ct(<8z{Ojw z!IECp)LNN!k#C(bNJ~dFPCOP}fa@74baBzWV=1aWt+XOhub{nozX#WosixoeoK1mX zmyxfPVg($yjDt-37t5|xvQHJb<^<)mM|P6;+3>nt@1Rd^QU=nymy$MK z3L?{o^Rv2h;r6O{VL-BC?>(E8NO0vEMtdu-2lzZDV$p=n5nQ8n-i~hhRN2@>mhv5U zPF;#*!^FV+>CyI1fn?&7c{NDlZoBHDj<4No94zeny9Ep>yH>235RN8xKk*^aF9hT5 z$sa#TP+lfFuchA_dID~h^d;@3PP}18sgD3d7Bb0v4BqcwNXzh}>lT2qv)i zwsVA`6rBm=&3KcEj^t?^OP6eTqb=TPkFR?B*BNdogFYkRoMyYdb4GwF4?+k+f8^|> ze|+gp9L9kGW0sEZrz!_wMwC1er^aBIz0gkYvVF22i;obnpp>g%^(!LLh?jZBvXfbx2sdOBl!NDq-uk9LP#N$T62JQkE8l3NGroqP zBT8zG6VU9WN46^CoH0f^B&wa5Ci!vWoP^($S;GB>Qh7l5@t71zxlgC0qYX1CDrd1> z%wcbsKMy-Ji2g!i%RU!?sDMMoe)w@Q6DHWSQl!rsJkNtgfbD z4+sT7-NlBQaL4O)+qJm2o(1;cqmf`XYIx+q+^*+ztnCT{W2gebrY_^y-dEZ)kzUc& zn8{-UIu6ZC5HZ-qxD_~ln6Pc~fwk09FOQ2UBy}A{rQ+s?9!=OrtNzsf7m&?z8r$BB z(3qk;poM0%#Ns9%-bjV5r%BAz98QQBT;AjRqO*}U@`Ni&c;2{ZGIEW;giMTsT39lF zF8N}doV^}KhcVqXpiVr47BjWpk#QdIgqJe&T8CQi$P%a9U!FR|$Nfz=q5FrE0Qd?g zhgD(v@`h+3-t#Oic;%E!2BU{4#Twc@<_WjU4a3gO9)#c!bJJgu^hD&$V^mIKJG4P@ zX4s&wjQi+{IGqz;I5{f;P3jwQ^SuF|u@C|#RnYP&Hz+XUIS|q)YtMk*oo0@uUh20= zsU{XuDj{f4jdlb}svR7rbH&B$>6;tzpo;I=_* zEWXYtfC`nPe~v&2hXPM#-ge)t|0vq_M0`gB;0EBDOu@J|Gjs#Wv5VC{2DXD3s(xsTp`_d9Nes+Lk<@uN)h&M4s3ulP%J+DCIMVslPu<1e>*- zs58y$h3^_U{OB+%XE_AEa^>waXfwIG17YP0ktr~4qvhn`nhgz|yKFNQh>IjNi% zHEn%po-OFZ?UV%t&eXM|5_sSs$GnuzRV@G=GX&<4@%3Yxt3Jm`eHm?jf$;|!l>+9P z;*f$L6a$u-t0$F^vvY(I!YOw@IBeej@Scb#W*aO3Li^&p5~kor{on1ZxGW?qvI7Dp&!zWo2kOW zhDffX3FKY54#(wB6ye!8CU0V5XKdSIs5Z?mIlKFl0jk&C;YftTBH^3`Qk>a`+ZjqiJGOY^!5#rr!2<#4*VC zQ=3+GOEzzm^Wsc8XgDWUMtt!a^+l550?a6X%}Jx2C(KGj#+wuZ^CFOj^Ab1wX9}vX z$F98nITfZF1NvhXh);DaO`B%FyR^eSRg#v5TZOrhM3ye=aE_SKa|_M|oy&B_FIIkD z$HvrvA>YR=buQe{>}Xcml+Y$p*=!yQGN>qzTme&O1;w-i>5570`5jmk7A>o~wTInT zBbDwL@^LX~;l^zy1Yt?~oec{xP(FWoE1f5M%bQ z&M8}-P6U>ABAs9rQ&sk(PBmjWms6^OGQN3fPyTeGb*b9$W|z&*1BSyI1 z^|uHCh5V`~*|?CQ=&--xf$6T=SWCcyMbMfdiFWU+!1#eJ2vRNu@d= zTq(CiIW9YSu_U~0?|k4w2FRs{1t*Wnt`VOXfNvPPMU;Hx`O{~BW<{J@G_{>tASkt9 z{3Z&uhv;d`dTFa6nUQ2|f!%LnnA*cm zF#W#R2O;TE&kL9?@F3zgU~WHv@C$tkM9MRr z*&z!2$qU}G;Q|_Cf#~3mV$bnLExqJE4p4p{!w?ZJ*qOHKAtwTv=@opA)m2zxB^Qrr zZ!|YZq)xMx1B+hoC5DYMcPi8PMDbYaaY=x1xKgOWmXv_R#=23QmM`tc`Zk|eLD5U? zUHZd5aC2}N{NA4i3yiD|gcofw(g>8>KBxquw8qdSn}ikGQ{~*#vzlFdK+J$-0vtt zFi#MAd{_za&jZg*cs;rl3s?sY=VfQUoVkM;_>5oj$=`ZnOgd|lAu_dSOc*nts8{}8 zT&YqxqHIGKHuKDb$Yb19u+_j5F+4#4hiL&(uAuf~uf3Su1=jx(3OYJ!?HLQ!az<_n znFpI$GVq0dcp8*@2o`fbl{KjgK928an|MN4M|X$c(+m^l9em($P!7PRCuZ8{YN#yZ z!vqgWD&&TY?4~__xi$UHe4ntajt&;MeXcS^jN@c+VpArLF387wo7njwiYau}WJw(0 z4-*MXQef9pW#S;@UX^bWVD#eOi_ zX~QBcX#f65nPyt@_MA4f>kH2x8WgU9E^6h)s}Y4g>dtG!2)d*|dZYmFY?D+5O%1-h zj#GuztituPUAev$<1RaG85{DFkc@?>Qo zqJ2X$xW0gGy_RZ;x}kp7MN(yReVyfaMb#O6+12i8CC_MSjF)ub==4jJPGBWn`JPsA z$ygX{k-$*yps4vdKm28W8W8DEk zQd(PddcuP9i-VmyOqgK%eyJ`#@G9Zx!Hiv}6h4CEXY^~;$ol~HqDsYkl6>nYYGarK zdTO*G89vBUw-V{ROUy)%`hJv)EQxxi@4-sx%}v<}cziM>j$?R*S?8%E5_I4SeoPM% z?ZS*$L?VCL3jnxF}XPKYm|207`ldNTa2> z_slXihryg08X(`M2^`_!@Vsq|YuSuFB{L+>(Gt#)JD_5{*)UQJf#zvNN+to|hR#6Bx$<<=y;=yIUQka@BFlA<5F-( z#yK%Mxy9JQ1EOQ)^f$C!_lk<0tpY^rM@=cd?Zg|c%BJ-oU7-ul>NWJ!C@##0X3Vfo zQ_dkG0V8?FNnwS3&w7#?^+4>2CdG3ynsHt?j3PXEK^UechnSv0j}H^}e$kew4A!PR z^`i5QN6Chac7@ZWx#64+65q7Yy8=s!*0|7`R!H72D-@D#;!4W{Er$nHWex%bz2b|V$YLXdBD zTVJ+3RM^A@=aXvFg*vH}$?e&}=EjW6X2CM@l+h0!H2;?+G4y>D2z7{eC=Ff41gG-;jq?0s$y|lwPJ#&5fz>xD=gCX<+5|_KjRO zvszZ&+zuG`|0Y@71L$oJ)uzl+)%zSfU<-XjLAXYRzorYINBbzubM~Qo6)!!pATCx7 zl}R>65x}cLB6VL_2eGe_mv(V@NDh(JE|Qa_>Tv14qXYUa6Kdx`^8zhSGZ_aV+>S3= z=NWbyAj)jJH;AWxFFH!&@Cn5Jlk&=Ln2={E8#tA9VM7NEUGnjM!Wb6$Pi1g*Boy3L zS_^R5Ox(Ys0*we;m>e=3C5EZ5P-Gd;*5{}>4wlwPMH7pVsGtG_$q7{PGL(-|StuaL z_gp-B{;3PtN~!5Ba?a1Q+8MKv&8AML~^oY*?ks{ z=2Olus?W3xr?+MRi-Eq+F-O~u!wqZBl<6`N{NLI2C8R2)pfupneW7O&lSg+%YDiha zHP>p7%1Ure_i%!?t@v6Nixd&gxNr5SMG=O_4@oM>C0<9RIP6cI*>gM{v8W1VKUsP0#Q!R_D^uWnzz|)D>3Pwl<`|ei;;J#j73VI zUQaf6JW6$|()Z4=OSCp;6R3gM zYLV6)UFW(emKBA!+yvvxZzB~%h|vSUgRF>t&na8|T8}|c{V9nca2JW#hQ1z}=wPbM z>4#U_Q$POggI+6t`Ov>W2#GDI9RL6311N?wK5)Ikp z2a#Xa*D@AJ>tG3ChS5w)jLBi$){1CwZcYUuDe#bU08R%Ba#d*Xs=$9rMG;n5VXIro z(MuVl6mYYe#Y}ZghKHjG#LoM?;8yaLZ|5795CfjEmB(I3jT&+#($qT><1h49)Ykp(TH2uSd91D7Gt}XsXm!p9(GG~u(_&_Y*mvZZi zXp%&(o)rseG#3usKLtTiOnaSa=h8*8Q9#Ux6kA>Hi!-3zZ~zM7kPy(^Dq;^NaOKVk zYgkQL28ba+6WKfVC11!ajuA&ogf?|)_U0!`-<K+Xu$R=Dv*ZhY?9Wp(2}30jb@~e8Mm;99zvss8t0$1DnEhNqPBj8zGaQ^5 zb}KvCKx+W7^C_*Ck6CqEj2TSK^EGq@sja-i7VwazvYNiSIU5TcN9J3m5bSgscgiDW zYvjTAEAvoN?Z{K##9?X3`;KPTn9@pbRC<$`b-&^t+fsGy)@&hC-k&Ok6VE%l(g>>8>{lA~5@IvNr zhH){IOS24;uy{|%Yari+-|~92;ylioaQx5EIg!KD!4pNfJWz0ExiqW+6p3&XffL@l zf}BuA#m6&pKWu`#PhNXX;Tb`ml!9Q5PZ!fz4DxDqe|YYOA=zc0T{Oe}gG5Gv zdps~oIoe3h=Tg(A;ZFU!9&1>vyP%w7Z$m$B)o;fxS!2JG)1i{_yYWt-RGJKE%VepG z<6Jpym^7)bYZ^1pmhFe0CW5|lK%3f(Wxta3nzP(S&I!&rd>Yrs_ZLmj8LiL(QVTne z`83Qy@EE-H@COfsHfU_ZOBMG+e@y3oUQW1lbgZs8;NL!F{d0Jlx{SO?$Rh$rtq(Mi z7JBeF{s0KWBR%1Iy{#2GXWFEAH?6@4e!Foo1f;5#vgOb@i38t&%L&_g_VO}74ZkDUL(ESgR{ zR?)@-v?8~zh(lbLkVcCf){SbU=pa9R4xM)d9(h+O57m6*BaUw3)z{*|Mm9^!<0{sP z9)M7B8FJX|*;uHpYucnKMbbB$1d4a^qXq0VXe0pHIUuN(T6arWD>Yo`d?>)NYeMq2 zStY9}CCJ>=I$ZnVU(eNDb3WfIIQ$7riBneJ$RPLjHbgjzloh+^o8USXo9^e0`K8)~ z+Knu4cvnnz2_|Z`dsOE4Zh@1V1UQHYx1R*`$|-k18kVPg9RS5UxTA z>V)@qU!|51GZlB-==zpl{$i0G4aRW+y84>8n@D2=(N32`!Lg57-b6;Igjpr z;LSkpz;FT;VUI8ek#1>*LNGe|K5B<;HI7>YdZPufzB2HG(%E9O?5o zv+@J9ktK(J9$Tr8#re_`?%#f@*@8pc#17IhSdt%>Xg58~oUSS|dElL^PRVOFS>=`Q&orm%uO#)2aFVH5S_doSCI8mc^) zi@cl3oPU#1?wOU%I;yhwZNO3Z39>pZF-)^k-vBIOSDX*v%XWPgO%=~*J2zd?ofyr# zf~3ZGl=fUZhdR&T-%(10kR30cRN}bO%MZ&{eEpx@@@!xYxv0D-xOh}qhfLl$w>@A&YjAf zTM~24DKJWhxr?Ox<*Dq^({j;iO<{&Y7Fy=@0U&4{ns+$hRKd*=1fS!WWoeSs>;Juu z;RM;!jj4i~i6RtAS`Oq@8zoic-+YVpx{Dk%$jU@56$awWdR$%;=d`hQkFk*5qdC{+ z$0FP-DgNYxOT~!Hh zxSd}Uf1Bte)~|RlW{StzkE8qu2hfUHKaf`VCP?4A;E4D& zA}?VrW>NBKB&wI)xhUNuk{`j+G06)>nf{3F3f~=N;o3>sU@!N{4357Kz(9GTvOF;y zYyQ)5aGbVub@(vt1{&~UTw!bM!!tMkRq*yLS^0mmslyu$!V%S z6uJu@6^(V~`EcA?nLupt+`@>LPI_oX8Hah+@W?T+-ixyY@uvb82wl}(1tt@WUhMzM ze`Y%72G8mFjCA<@$ad%6v^b^-A}M>N&&g%pYvrRr|CA~BIx5w}X|#kCy$tP#3TO8e zoJ8VO1Pz`JmXHi!Mhe5SM&Vl^dU|$O^@^ia6qd#-2A9AqkmD2wAzr0yBnA8m=O7z? zO*vTxO|WC)SYN7u(TKVv737MM-t@ipM==vTM~ZZD{V8yg^%Y4qhVH8m%2!j|3>!(> za(Hbm+(61IWA7qRFvcSu3lH`rAt+d_jd6zqWlLkRVm_Z7kSsCw;5@r^FW6UYCb5iA z@#5aRt3=k3E@In0_a+IQ+>raZc*OmAa>R9xyIc36Hs2zpi&a?%fLGmX17^jY{r}Q* zXNOz*TaEwC+2Q>-8vz^;3F4GfdH+yo5A{%=vUEDNmJnpT`I|p$8}~@m{iPQXNzMWj z2G^0||Hzb;%3nZGvZXcILT8x^C3{uS$ZExW2cr4ac8+~@*hAO|R7A(z@qF4h0j}{@ z3$_jnS~vzwLTsV3c0-+Dv~&U-=3mWSw4G4DiZpCuW;4};k2LdKD~FlD&Z_CS-_%cl z$bVYrq@nJ#bpt;|Br(sqgjI3jaF%G@5Dc`zig_w)D%sChiWw0(B9a`m-tD9qC@z@R z2(?YUy2yq7nPj|S--Gb(w_8-vsqCTM?EiNEu`uEK9dfE2Y{ut-4dKup@(@Q6iZ$hN zU8|ZtBK<6MYA3ho!#z8b`m!{*R0(~B*t zR!Z~22EU#cl6{xgSQbmB@NfMDzOmEV@Z8If%b|2Ect$x~K3+Z`8W9L{l11>3U=oWEPeKWcU_Xc@8L}F<{1t z11zdaSmI8h+-3zwaXnt^`_(K0(t+$&iKDufg9x56q;4mpQP!Iino3F7>o`@IAA&zc-mH%VTjLVS8g`=ZfLPQ zr?C#Pf`Tdzz8xKBWazbC?^;f1h-Vc{M3)*Hgb5>LDF#%zpP5R?>J1?b5eTh1#2Mh~ zML+3zhHnBcbUG#43B0QSFOSsewa|36g#9ym3H+UcXB%lXoc_luT=-b9WUL?56f*mn zdZ{;zq0|VXS*{9dyBj*W{c;5vl55OaeCza_FP3o}Za@_~UbyaBQ>zN|v#T~5$vac; zSur+55{n**4{vTEQ%0BwE?%qprq5!oIP_z+2E{WAa?nG2=5}gg=J?_;(&VDxsIv)1 zh!cAbM%(>3fhCX}nO0)Vp%NamL?M}ZCX%qXz-eV&)e&vQCX7HsJ^n{<6PR^Nq$n$; z^pK0X)hnz$Vfbw0_`=Xb2zf+MS&vo|U|Us8-Y$&QCOr;L17x3ZjbpWyd!CVF2^G^5 zvSnr82_dz`zEss@mJQ3tcW%%84h*pPk&2x)Uu@l0UUop$3KBIWhR>@&)8NG_xv5fI3jA=tGxni zmPdg@rR+~Gd_oR)_B+28-+~M zRFxy*8%z%ip}j_TdxaR9_dyn$=_d)+WA%NEt}dfq*&PlCRAa9Iu?JETLs+xF!jw6EnFkEp0XXho72xk2 zV8Z!QVdv;SqPl_IC%m!od4BAR?c^wFO2xfqj;*nZE&z;eop)(D$mWH&BamZ92zt(( zjD+}$l5E@D@+6s>tuUm_ES<7_m84dSceJw^~5K-AQgA%Oiy!FwN{1EkcvHQ?S{k_pUf^DbwIDQs|{&;U3ev(2bMJE zxNYk^LX{Is2eMr2XmM1^P8}-sGF2ci_(Ojl(M;0_YZL!b|MxmkJ&ycb7RnG(LAZoL zBEG3lOL#+@L7|J^@|+18ZgKW!lH~&+FD}G(-D&1w zqQ)Dc49SQY5|0ST2m7Blfx>FPUdiKvY@|Ba7hm_QxC5`;>J;s)|0)}#6ysVh?C~9d zIlSEDrt_|Um*#gXD=^`9zr0+}rFI(s1k{`UiO5#RUb}ScpNMO3L8JWuV*7ly$;f5F zr;qeSoy%j)AYE~*)PthE&gDfA!W*tF0=g!YixQw#fCf9gHpE8cf^F~dmlv{z(%D+D zAG1!MF3`!{;(mw7)DKG$V(iI1uS=2jPvkpx7cmFj?ktQ$gbdW zi7`jg?2_DzYl|1w3fShSEmnXdDx?ti*~_HHe=<5e-=XjF_`=U5uFOU+XNiJs-gL@; zB9HC^YCG@=bR9y7(x-&hlHcC+GHf#?-7r5B(4}3L)sxpU=9=LZa$n*jKSYT`yykPE z4)K<)SPyluzeu3hVZ>zwczx{$d4LzPr6*+;xV`^QjZ8QMuBbQUAUt3w^|zy{@gN}n zIK-fP+x-djO1UEcxTki-mvg0M`C=I$!=otF5eYvdMtWs22)V75v>SNG{C2eu9gcKc zlj6eKUj`IiR4)G9#|zl3w{*Xq@XF4?-c2cXO?ElBr%*K*3*HZAF#M~`eKsp5U=x{- zqGvMsw4p5p^F$jqtaEaiX9e_k+IcLeC$4esvpnyNO-*zpk9BcKnqEM8LHPR$anK|w zgJ!vL<3Ec2A|ElpWKHI6V4W#pQ!I!fqcP!ff=l+#BY&_QZN?M-eSFM*`>??^YN&w= zbL;CqESo|3V*D1X?K!v&ZdfVq(?bKc7 zfK8+^JZ1$EJwC|+;`ml^hpKj|U81$%Uy=(QQQcJ9vq;H2U zMGqHXHhgrjZ8ux^lI#VzLr z07)uh>UDdjGW9+__p_A^?Ade%WTkEV)I4hx=KDntZgo61@xvTJ2pxX<%yJ0rhPP6}X2JY46tX{d$kw%4S0*Ai z^i0%4yrmO+!dmIsxjE?_VG3y0g=P*_rM`;1n$^O5e%+t04lQubd_?bs*b-JxJ) zU^d?(WWL~=?!JG(l-SW0Xwo^Zn=COyYQsz{i$6omuuo6pbzp9>*5bmf9NC%18*uRM zW!(||ngA+LzGKK25&Zaedna6$ki!tx+7wC8XGU7&w~^1LWaXTHhqk|?p*cz9+5`=| zHFN-NM?0*S6II^%!N2oH)Fi&H42`LaqC^?pQdKzHYOTM4CPTi3VaNVOb9bkVfQOl$ z=Qj`Dc$`;G9ee&hs-$@>!z{!UpJ2ih9|e@#w)r{5L#VYfxEZ6v$gDVH{UnpgF3_6& zL?6CRx{C_!xDZxvoY{AI)GOU`5ww zN(uOcIpF{EHDE^&y6f7GOqtiIUY;yAMiEMAHtOXmTA{>}*&#a3p$>DI()f6AeU#M< zIa2g69N&TlrC&lmzUf}0#u8Zl8;s6a2yv3JguJgj?TSifQqVkY@M`?8pyC7=TSt|& z5uM)?lKqpnkeNg1x_9X{Y^-Q+JN7#Qwk>R}Pr5EHr{zNCY4cN-7%i(0%xlraNY#Yg zVh0>lM@q3F9aKmC7E`c=mDGi@ry$$jiB+J&zA|88R@FqCxiEc0#n#!@7@x#wQ`h_x z!|sk)2%FBkXTTVnek@0gO;qz9edE-Rm<{|}UVYen{gRc&T=ObMn=0T_n$`*(uh6HY z2OC6v;S$BIrjWPLha?o|JH%oIR+&f^zdYGjK?5D)C~%1FTkn|3DyjISQ|?cZVxav0 z%*YX_J09~th<@LFakpoHGALvaAf!vpjBV0HgAl$1foI(>X8YH7=>sw?oCQ`JJfSqM z+=u*oH(9Ba{0=A?r$ysQ<|W+lM7h91`X1F59XVD?LUzRb!`7VIymnK7=?M=H|ZWdEoW=l^`kn37GZtXspOHo|Y z#k)*5XCFE0U;4bGV}b z9U=NM)jZjR{(~O+ki5hf)SNvfJrlpG)EWH9!gMp|o(IR5(s4nuW!j>KQ^j~loQ8k&?9&2F zC*fNjlh=eRz=+0E(V;F`Jb3C+jf|=Xkn=El3KkU!2p6l63x0@RC(1ibjApB6F3&kf znW&#r>CEgD*nK|&DfvW~^b{UMWaD<~CpNVZYLgXiRfSBTY^?ph=UYcW{qDrq2@WRJ zkuGL?U7euoj1IT5>*y9W-P(wiKC7{2O)~6H6RW=!r!aT54!+|5%K9&O+ECt#-}90h z$UK0xSuWOtbyjBb4_RL_e~V=16Vk&H!H!|-$)CW*$yQEf)*4Ge9F#5eVC0yVMBVxb zo;Ao_CLCu9ioV!ObbofqLk7$VDYGX)ZHu}gtl{y3z>_8!l5DBH^aMxzzfVK zX7k&1Yh;3DucL_D=n-pkfb$Yodr#yapd1!5*ek%Ak33|AUBf(-8B=9|lZ&psmmeB} z6pE+fl_PfNSRCf&JM(asv0o0EU>FLJHpB5HM7PAa>(MXkXiS+&ZnT|j3SnS#?Z0+| ztHCsL-oy|3`2!`MO~aOv#Hxi3N-2-*L@Ok@EqAMvsin`$76PacvBKIUp+uUJRC=J@ zINcxOXg~e{I}7ViBeqvyIA_m+N*AXMzF`mDl(G85kfZY3O-AGSG}lPk-1`AWL;2d% zp6nGL=;E7d`DCEsTAWz}3j}+>kg*jt-{QSZ2A~J&+}uwy21Zopo#=%?5HR8ZWkkX%SwbtK2T^4-glE|L`5H_sET*MRTOrZ&QRZ*l16+wDsz#sG6?SaRMmvcrWbC0 zVJUXTNmO&Fj$NzFlRP#@Rvkl^CAsO4Yw~wbTIJN-3h4sRKF;bOM@gRWi+~)O`3ZNM%3Xcy!T|%Y{gY5Yw9+PHUC^3S z5gL6aiAz6-7QA(lG@bKzaLabXw+f{4h30;dh0@foIZ!Qh?4DKPZ2GUE4(5eOd#9Fl zcA?lPP#P-D(p!@b7wjz3|9YgH+}DLNxsN1^@SIjqEVOq_2#SBRBbi2Q%Y1#tGYGO~ z$Zy5bGJFn#e1&117K06oBV4$r=SZ1SMjvn<&^p$?DZPdku^peR3y^8X4qtq%k>RuQ z7dqudSYwwn!^GIB(C>BAc@y+;($N4TCty_WtOyq!qc~%QDf-=!StP_ljXN_|4&86|UXy>FJ-hn@nNbp;u!?3iY`#ftppl)+^PO>xkjejU+OP zZ2CizQSTm&OGbyX!a(eZ85_a|sZS-kzqgVy5ceazi(IoeaMiTmLT(n^0zyor2Q}Dp zz&y`O^dWx90PUjTA9Erj%4|2=c?DduZxhJaS$yK@%1U{Iyw2JAe8_+iV^c~kBMN0@ zSX}S2ME-%hEnXu*bUz0J1nu&Aw)m6%#-j}5t|?95A3o~GLT=pk*)`MoV;6h-k>^0` z25PqiV>jz`v+Kzd9FI&K-fQ%JZVDMIUUP5fJ71GFDsyj3W4^R4ic};<4Vku)>lXht zCNJLv9X((-fdxd}7ED0pi{$*DDysa%M{;v}J=3EhzBRpC21iMacU%Yb+#@8ikGI(N zyd0*b2&f8E`$_vCab;?-lRY~}cDhey9({+0nlib%dFFTdd5#QlPYSA?|6hkyBrdBB zxYr9ugqRDtd2s@kpBNDRx}hEM32P8yin0D35Z_LS>ig+we80;bsPW6gg++qiyKs-b z?!^*vp zYJ(o@t`mh&w;~p`!EI%4)$gCQaFtd-d4+yhns%3jl8>FB(l;vN*A7u`Ggl!B?6zo?ZfGSy3#+<=ZEbHPPmI&mprsC-x zLOktlNzT-KGRVURSiBYq9LqCv~feSpeF*+Mo<$E=sZECIv0;t3;doaZ!c zlMkOzDOqyx0{fC@ji+`3b8t_IE95qktN7=hz&f~M&u0G^`{;KyoDx$>o!@Z+kKAI* zF8WM9bT*FoDC)V60=YHCn8^YS0>qNeEny$Aqw1^v4w+nT5$Y&1u!u8;b z*=E5f@+1r24o#`HU<|*WaPw%ceti&F$6=CSOC~zj_a5UlFu!|6ntkspDm~*x7;=&C zLPs>|S9|u6R_&1RU><+Ie7ZsKe%fnfXXg2WqznsrrX6Mq6K3BR`5pu|mPPN8pH#Mv z25oK6I5%niUO?Sv*#IWtf5z{eu>aoMR3L!-Owe`bU%NPk^jyA6%O4#M_J#_?-rm$8 zX#I^_WJ}v2iZPD0B#KO!QG0M5qNtv?6ufV9{{B$?)ekt~?Oolew|Q#~ZMob(V5N`c zq=oOasvpnj@ed}kbn^&f&-&^o9|mmQ20Z?u!DD?%a7b9>26?T@iZ>+N2Kq09SK)nQ z`v*EAFc7u_2(TaTGjvv}oC4jLz@1@BxRdq~`<&OeLl6^nKj{GCqdkXcgOjDvmJRvxo~LE(9j(=@tFU}FOqN@flz6S{rX?0g{%}N z%J?^vwcY}R-VSbS+r{`bsB>S5&>+$ z`ij+^PaUxEBq6Inw&xi$cqy~@b~aA$up5I*j`t|mA{31G{SQ?OuKZU*7B2JfRzSaQ zR-KOHoSOW#3YJ$oe*5gAx@#Y$kqZP|Fz4tt^q7rL3XEwZe$MHaz2eQ4Z|9Uxurk#a zANefBnG0XWlU3WZ_Ee)<9lEIF_c5H~76E=M*?Ry$t(K zB2i!F#p=McUUTwGJ+bJ;c}L6bs^c|`^_dT$taXTTkQLa9cIq)?t;ELt#G!S9r!Cs? z_wKwpHw*B3k`v6rya$s*%J;kQBfd=7y=v&1!?S^RRQdDqBA5m$1;(H2?K3gckWdh= zsbC*ZG=cHpnijJ`1-+)BLSqvtSS;YR`0=xnK+30dp0%>W?BN(K_O^p`eNmljs(G@K zvc_C9zY&X_{s5JZW%@ER&NPA}dNa2|%|qC>D4gNx6{^7F)usx2^hBYzGgb3^unU3a zT&sjyVOyU>V<`#bkX|RYR!~A9S{*fl$5G7D}+w;^xZ!f2|_(#{-T_F)My$KS$(K);H6Dv0`$ z8`i>|2+wD&*c<#lkElB$FR^%%1faFxsVFeKfs{OEcH`*EdxNj+o40XSg6A^y7p>K2 zLyQ0mVw1B4e)?7=R(^#8;;{{sAWMul|E->SgZ`zZCToQ;e|vd#_By^hy|%RTYn56u>45z@^gYo3E*x~=I-AC(T0TyCXaf72NJI; zrJM40)|(#)0zc{SWhako@oqM>6XvR!GpnK6mQ(~V=?S4IhA})I!aA7E*{6lTH=AAO zXM{#acgL5L7dI8))gH*bO%%lTU)`LIz$P=2F|nO@DYyCZ(&AH};%0K|38}$5>e$u} zU6?K0;n6#AzNYnRA2!Ik6z#{(U3T~1YWwGWAYkRguJ|KO?-2%rG>!dIC{~Q_4=N5T z#O8YfQUlHB5&hWl)pyA%>_d=L5dLTFEq;w}DQ~XF0T(81v^aNvzyWIK^Sylg3pZC< zvilwG7usMV3a#f31$=_GGGCZN7FX!ZbA^RqUg zX+g_R8fc6$DQsE!XBqFw`A4112t#h&Q%4OZ=@Sm7*JumC&>G`?q>ad6k_Q}% z6h{a!4XRFl8c@2L<6ehG>w`E*bUp!_^$>%X@o~#q&434h#;XWnu1Fiy5n%~!TB^bw zpWHe6Ng}C`kd9c-(fpESu=9^P^W`d?>!4>QGQCk-V@0B;@6dOOhYeo^r2*7t_%wVM zJa&ZvYo^9;osW8|s-Z9g+8Yl+B!BM4@h@7X(4aL5#*^QHJX58n7~>F+{>hxHvI?^NXx*uaJrnm;$iIcq zaeJHQ4Oeow^iI*uJZk~@49MGpXqNZ<7jSvCMpTz3Mwx8onGfz|pW^IGNxj2o6kTyD zO*R|Dt?9tDNbp}_N1N!Tq_7likT=SihTauksL8Xz*rr0iYJqW`%2ful=MbN21O?kwp_?)kRx=0V@rL?jat2hr-}w^Ey^|&WHtfSpL|OO1BdM zj?k5Yk_t&#(}cS=1m$5u3+iOv!6Pl;<3vRWK#|z?jb=a-h5j#P5NvC(#T;c_k)__3 zaRt4;b?R}4oqr!i0i$12?Zz4*Q*A*E#MU=|L_NoG?QiY6mgb~|<%=SPdlXh9!Y1sl z-czak{N8v?(Q8or+9_9uLXo}t#_ZnXn}a9Z9;kcuUhAhT9QkLZo~qW$--|x$?MYK5 zAxI-z@!751B23rm>&`x%I;xZ60|RB+(#29(JfNpZK-}s|_O=`I;UnCxoTb8m-xYAY^b7kDyhvff=}k zIHsmkVIIyqE-J{9mw6lEQ`CAQ2qQWo%$PAD9?~c*^e923jSm6_hjgB zKDgY1!|P1Yf-c(8o!cU5bOS-qfGLTq$xjxXTyp6*(RL2CZTg6irJqzU{mgeGkn<*s zf_@r6k`%bX0gL|?U|`%jALW$UOB6rD1pQ?{Y_2ff3^1g`E@g# zI~H5J7*lq|UdkUtgi~rknc#>D`dA1xaB;s}YWdqkivDelA5M|lO;)fvP!acE#Vpqy z1Sj@QBAPK7JafEjFy+h$cEqCCA{sGmX7b9J14r#c#n{8B;2nD7dx)Z;>sh=6dsk`i zN0ou#{aF&6+Hx1RT|Mv5;{4wCNWI9h`~q_~{P_$Is*9g^4(IHJjw?)WOT@&QHI~%1wVagl7pAW8JX(o*Ie{M`-lg3d9oGj!Nhfbh-7m4`~oXzAQEx>j8nxUzd zDKl6KPVsVyb;EW`gzJsVQ&7Xss8Jr@x=h}Bih2E}m6L*|meWLNqS&EkVX@1P^qpse zct$pHEqeW!epf?_YBP$z3IyPzu*m!+ksQW*`$MQf2JLS|0O(%NtwG}Qi)Btn=w0(# z#v~gq>}1XqVg5=tLazX3aiRS=(%aXRq`@fDzao~f(E&1CG;!D=G#Qsg(zZG{p4-;m ztFqMXU-3L)QXN;X;`-~OZR<^wam)`yP_gic*OM?%R{1FH&17-&7o}LWIjPmsuAPba73+>xRSpx}frVYyNAY)cQCkpv6Y6Btg0#lG2W|QB!z6?Ac@g53XZ?!)U=)^n*afpHLFrp$meCD7lI-gWwH!lpLHP2UtT zfT=oCiK5@gB#SHuubBN-P=?QK`?P$&bS5nK3GhIzr zB3u`LPIP&;wt=etGu}t0eCEOw{!tQlSL%pNC~~(6ds)%xPxqNDIaR}CsNt3e*DkS2 z9B>@9Jh|u+Lg>4ikUCrb!$KS>{MGKWcRHx?=%iVj{h)BpzmN~ndWk&1k7 zG+bFN^DFxtWB;9u@N&egn+{m0By~D@gzaLH%ugnwE6`MWRXH3B2TyZ5<%8oyoT_VH062!vt< z49sk^yn5UMffX1fDF^NMS8G)Qq5J)Q&#j&69Q$*_2MGUEEW_J;v@Nygx#h{#dbD^#jm?NRM?JEuh9S`qz znQ-!2VRWo}wQutl@hc!o{Jv%p29iW2nrTB)R>|Ou1s@#@#K)Q@Z%ZqZtfLhjzjBnj zRdHv!;~PPxt&f<7lr{UaOTiWOsdE7%$Y-I{fd3v~nFsR>`+pp*=SxhT*uKw21()X~ zbv|_as0O62O&%6R1W8JSz1JnE7v_Z35~6)$>`h{%I@+GFI8-#ZnDmu;UxUin7!GAR zg)F3fKIKmw+GzrpU~fLa%b6VR5k+v5x-Ic(t}}7VrBjJ_LU3c%P5{UwgRJOG^6 z34L-d>KjL3-zWwo^WEt%ECJuV#K%q^QKxF<9b$YfN(;xJPNAcN4R#ZJ3!avdF)cNE zoHb1+w;{PiQg}|7(X{N~r=043#=FpX>{=NH5B&T?WiM0th!hc)5g)HA1Ju{;=U8Dl zeTX5nY6B^rwln6A6fr!RJQ|o|2kpoJg!=;46 zNWGW?=-5iI*2S%OPdSz65HbRWSeA#-#Fq<30_{T4 zQ50)DWpKY+N;H7sDeqWm6rtdaI(?D4K$~XfiZ8Lx3R(>(6DB#4HHIA zhRndFoO8h>4O|k?gYigw^+l4%EU2?Kc?g7dCYuLZw%Qp-{(2#+bI!1RsS}Xs$MoE< zFG;>jOfskf-yGntzo1D@p?zaQVzp(@Npdu8N4%M{aX~Gl`~oV(Vy{5Urk!YDaAtU5#EkQ11VfCe_ngc933t_-Aw< zozmFa6Qv}w9D$-=M1RG-n~J;PWnNoOd-eD6?ruAvJpJ<)WmGHkYW$T}GLHE`0sHzX zmy|3_d3zi;xa3bwO1o4b$yd+Zn;g2ZB&xJBBXel>m6wd{^ngs97&)o`p>^s0D_DsL zC{b0;F*sUqHDxP9B4M`w}_R|Nb}rZzF1$aKktrLY8+N@jnGnQsX#RLdtjZlbWq~{Cut-6GQQoQhR@0} zGGi1ik6TDcK7YwlL1LW>T0GZxj+Nk+3rVAsi-hevchciZ^;7SPt;l1={TUJyU^)xg zF~>)Gp%MK3XK|>YTprSj)iR)Wl?~AS$2L4J4>Q)Tv(`TEQ!{-_BCFI<~|Ok!SvB0 zq@VF6F1!5Q9LaJ00eIO@H^ZJfL&YyU}Ge6X6$aHpl- znWsVTvzx*Ez81XJGSQ(Dj5H{uu-qRNht8q~P;A=A>7GrrqFaR^FTnBmFXno%YLL$h z;K^7*IX3j{<~!NWx|SN-7qiU;5rMCmrXVXzuqe_-`(h}L!> zCei>c!Ct{{*!70#C?ZW8mXEA-`gu%7a5mX=@8fkKj3&FE{J5;Jufye8EYv|2Eg0Pn zjPs+r?xgS&&Y+{*Lux=JM{}PEUy#!+c-CP1wrj*C4k6IOW5XcQiS&SS6e1O1b>1EEMJGbT8uQ@V)G2>5bPUJm7rjch;1? zUwR*+6smAZbzGK1uEpnlObfP~vlU6%(vXg6o#I)YAmf;ngBI9c=D13}V!FQaLKDgh zt}XM@>YBJ!W>BdwPxF(7{W7-NL2%G&=ia5aYEFh$6W2U}SAq2q=cIwn+U*M1!nOa> z8fTT4v%$hM{omL(3h#_z!2%Kgz)2|vSmFpk%&mQa1P|aK?d(MHuywBPR=kZOu~+s6 zQ$6aEFX^+s;_b8sR%E&c0LNP~`3I#@<#Y8@+I1UEf`A#9;iSTkIWk_<&5c$&V&cPQ24mwInYDFl#Gol=V8D{ z!x|~chyBe8iKpEeLZXrr#g*AqhGjx{9kuh4O&I(wQw2XrRUEPV{Br-eG{F6IDg=Q)jVQ1H>KLeHzFOg7PR z%iKcO8p|lpk^4oPACqNZ`}F&JfmfMYQ-8|otS8q!H$Lcz53a!_N%TP{GXq#y$U2p= zsA0Id%+l&?ZjoO}*G3%rNHk$(wVC{97@SWwp1X7_kT7SYwI!Tf^1s!rI`=FA%g&p+ zIgQ~fzjh*hoj=X&nHP@jhBAx>JYyxqfOW9vRyPA_hvvcOkDE1ct;o~%okf=K)CV~f zX#MaW8zqdsRjld{R@$iXi4X^>j#odXsFN)gUVA!e^D?Ku~ zmHgL!c)Z#aDg)w-Lj1uB>HKTW{&5gNNJ2V-;W>3d1D{^cU}EDxVN0Z})$YdR>Asp% zR6uRLSEN?CMFBzPxrfWf5COpx9X19kR+fExt^$vv%LmKX@c>#k>c=^llgXQ`zT% z6bcx%e-Ow|2^j{cqg|Q+Q&$46<~d})-oI)wqo(=NHG3sD>w95?hv&p57x6sCDA1>g zW(+WIO9jr+#^^1YX5YQ%&jUolG!KAB=bedy(u-&IdaDkfVwp>opfM;DQF7v*8O+9U zU)jfW3fu^5Mx>ldde1i~Vi%|KSu*&$$ihL5y>EO-wFg`_S2RWAcHOo8&5BCg)_28K zyTvg45M-FUs&42z*c$VvDG}mCBEesKhm`(Ow1wada`I02{I7tgAHg0bo7xpm&W4As$ zVP(}2;xd1%T;`wVoXne4#GGF%kr|Untov*vrXeY94b|VI>xBfI12BL>goCyt1Sa}j zT@<`39B&6h>-V zd^8s8#b!zW;O`_Gk{y@tT~rs4zgi3F0KaPnAP%A%!$0Nw>T`Zg-}RiO2lDPIWy~;i zZ=%Nq!ksd}!hO9)FWZ)&{|Q+Ay&HH7Rym)AUd%$tumq5*ELh~BVmdLtKvcDg@xJ_J zkND&`N5=<=AkG7MY6O5W)Go%mN}YZZ`&Um3QX{(2Np@USZ1rzxA%KKX99jVg!+X}I zi6~G```}<7WhmhSNLFftev@gNv*m3N%i-EN(K{eK4cW zt-zbMYs7Hq!S4vB(%f>GR5HG(L^3Wck7m&hp+k}v0p%3PH-Po6{+!s%yDV_*?RV}$ zy|A9}TSZF%s|;txQBalrIf70Gvdyos7OGH3>0OdYTsB(u)`GT?IjOs9^^s%LhFaSR zg@k2F)m@n{i8D>UkW9jdwEwn@?X3ZyPkhzJN3237z-vKN~j@-7a@?@pWX$- zYKZSL#ox9S&!Xw!;`{&OBR>)Wd_B}W@#}Q;BAHXwZ$vArTUhJjhp=9uo7A|1hQ(1( zJ2`{IePk!sGCgJ?Vim->c{)&kq9X{=Rc)DN1dVASa^mE|@)(-A*|Wz+u)5PZmQZ82 zCkp)$t1=gKAnx)yPLD`Z#~BH*D3Si}ha=JGvd|`!#dATjP|ea&b@Jd!V0MGt!;#nL z@55nenkP3t?ke#Zz%W$~^_Nh4pb?92r>Lg;1@nxA4SO=>7Ru!Ai$@AyVyX$wyf&(U zz<3GtbZ*z`IfY^xrZn}tkfhM%rykBz$nrWrZpsuj%$SM~?-+sumA#A?Zy}-1`(mL8 zIZT}=B!Q(FKE&s=0N_UT(LM z)(Bo-i*|5hd2N!SSf*gY-Arr?`4_?2-22;$Kvl51`D+A zutW@cYxzgZmXh(7NK^`pmQ*tuAo0?){n;yn$qfgQ%K}}m;wD6N8;{T0n^KY`5O;QC z;Zlb!Mn=j%h^!Ka#i8wM<0MV7&g>U*6Q6)Lqu&#?n6|GkgQ5isfol&9kePoR%MerN zn2HOY5K?4c;cS^d*xkbMo2j^dWxX8I0j%SHl@Zz@K1pg`+CQ)$2(Gkm~JSc&tB z9-o%)@sHC4X}Y(M6y-EMH0nd>M3I)~XtI}RYF!q#X^BR~F3i2-BUS0M4p+Chb9O3F z>2GWFAjN!PfATy2b@DNz`)-rglRe1Hmo|^XnCQRrTt{R?fynDzlj2E+@(>JD8Ka4) zZHEd>pb!^`7SrKMI&)!HUgkhR4+-lPRo!3Y?wE?d$^s6(Ot{3<;#)(d!Z`e^4oI$J zDhFJq_#56FEolhXIrAsOm#tUO;fJUqf|-;HieNxz01(~J z+d)pP>w#|yE5Fw43;p>D=t4AqTfq{LU*Olq4&zl$drR3qs19UV#ZiinK1MPtSkOOO zz$`sDMNZu$=0+WsA&_d?i0@et)svPv!mJI-gQfN9v74U`)5b{KYE$PSTTHD66T;wU zBfr)6dK6YpeipK@v0=ue@0k7r{5vq5P*V8YzK3K3QjJ%tQ}=jhO6#)*k^NdEY9Fm6 zWZo%_6Q}moz1V|Tb>$d*@#6;t*XnY;dr#sg1mSL1Id56u7$}fyL5}VJ-K2HpSgnY< zew-)%f9D_4SKe%hwwL#&)xO*Vl=>ZeK1C2`lsv|z-Z93-y*rX<>hpH`=F)Oiw}|1v zLF<)qnsvkaog+a3M>>$QZHwZ(5~ldNnGYg7l7Ka)0E$C(&9%Fn zY=lTt^5F3>p%;^qjf4N>dey5IlXTRm_-K}01oNnrT_m2O+|2+9Ej>}w()3F^A0Atdo zz8Xf!>`DB3A;4Gayb`7Uh(=9*{A;7*Prf>jl#(-xGa#@;UecoRFcP3@w2R*!%c4J| zL4=D|?!rOGIwse9an@2-(DvaZRbEaei%E!-RhzNMvR|7+$Eb;HQMvT{KCv)M+8n-h zz26%%|2W8tFe>MV%*LsccV+pIWLWmx%HXI-sJh#J%4oZdg{L^1C_?E8Nga=gjS|s+ zi1o$sH}YHP;&wL~RGv>B$3=Arlz0Jj#GhYTb*PN%Rk1SD!(29gcqPHkm#_Ky^`9=z ztsgqO?N4)4$ubj-jHEb`(XE|8dG|-hB0SdDqoy9VCn-vT;Pd+GEkUkEEHg_Nt4B@moTwX!EmZ+32hr2Y<_AlO@}nZV#ss)gKpdXQeRjy)ao4Ayg{I}Ze ziIV}9GAHbAv*hIj8r+b!`WetSBCrMUWJ_+xBggpzgtpREJdyyMOmC{=JLFne;W${6I7h4dTe+j8AbgS6K_#g<&kR_$MOwn_Q^4B8hO z5Gb3O_qI2F(H9dLMA1J__n+RRlJL+tMuyp|FM~xgWGZ4)=RdmiSrqFV3wGBH1o`qO zOKv=c$r}F5iYz;gO6Z%{3+$ym+-r4^>}|Dm5!t8zU}x1Fx&N+LviNA$b=G;$J%k3n zQ_aeWX?W_z?a9;lSs+i#gP>gf7M-Z^zwIr(?+n>_!A-hD?wz$S^(qHUnMTr5 zU|)V%PUjUk{cLbAbFb3_NFDKQe+&aYL-To`&4Q_@1wu9dPaRn8BHL|S-AU0h#~;<< zinEb(Q=p-aKy*W+x(qbck$DO^Z1{=BS->N_DA%;RDkq?yo`deH$iQ(A3}67!=X9(; zma%=ZEA%f)G%ruoUPI(o^D+N$lAVEqZe6!8k@#0%AkLr>@Ll+NB!A-#HniM)Hb|Q3 zqu+%5`!{-A73)?#D$xNj+8n9XjZ2Uy9_`O*Ss=#w0PqDXq%I-<9uXb`O+T-F;zog| z?L(Esn}hF+niBYz6U0oW-oFRY&lf$s5jOuz6tyA4WMO&2pHtkarS`MN0f=s&>krYy zyyZ!CeoK&m(^dz<_XJk_^8JM(+TK583iv2#jW$-3yixR**Y}Yoo1){U6k`4Oc2NX^ z&9CGNdez9*{w0!mKb)T|DC{5PFT9*N=3xOd5wR|>t#|fC-g_ZE$?-fyo`j3TQpHon zz!!~H<1oCQ9_XXhYix}hO%5lcj&;&PM#4d~!a_1Srql~5wE|!6U`IBmNDOkhNnREK zyQXQA!z1OA@tp#EI3L;pC-@W*>Znm~E@Vbb9Cnt%z;1tH&{G5sX38Sb{H_-Lsl9k% ztdVcV$wRrGAt6yAzjolr;$QZp-EO#+eF5Gnn>|$Y6pTg z1`x;go(Zn%b+m}^pho|Id^o0SpVbf%$_kuql8L`th?ZOC~`_jv*>YGWsb-WebYhJo2&muXn3eK8j3?-e}wF`M-XBv#KG7hIzCAxRG! zr3b^I)pd>ZEmRS#WI1^Vk(fha!=(ifk{y%jAE^E z@i-0H7o0-(>Csviaf|LS>xov~`2Gc^hIOL)1VXZ-8e<)}h z=5%Dhh7P`eC`V^B+vRpU|2wQLe{RONtTx~(NN5&)d3q{C<&t3$P)mXTYkc?91)p3i zp)c7=4+7T_bR2`(;=1*qMw6i;3V@B~jhC`^ zAZfKCYA%n4dqkKYMogz7s|f5^pn9+`6pb7k8!50MQj)IQXy?;swGffJ*L4$oUim3E z|BLCKGJ2yL0OJ5*O1`8jWUi)oj^o9RbM_{)UQ6V_Hm-4lnZkw}HgS^x3h~6*7jMAm zs(K#^=;j_k?QPWxn>=acgN}%=ZE-^l_a%sjjj!b1F=a2(ES5bD=+|f;?mi#M^m0UT z(@I|hN8v6xSW=Ke&u2oeux1Jn!&L}&6wXcXyWQ6as$OnkTTirf{u7^Fq)uviUU;3? z+4j5)5n3iG9F0dW|L?-UUVy$n^=W*$wA>F){bA5Rm+>F#0)z^21 zD!$Nx-ti7qGmo(QaXN-1v6S^yPy7BQ8)@^G5`9h(pwKx?)5Y(_A1A$lHo3#yPlx76 zT3BaqR(Dnrs|r+1-q7ecJU?i&sn79`a7uVoXpPa>kvIYQV^jGApD^BAC|W?Fha_zX zYo+Q=XQ7JI-M4tm*k}}KNKl%^5q|dYZ(fW@1*}5tK#tJDG4)hl2Fy z`*7z81b3Dmx^;PO|Aj`MYhxt{s)V7wL$q!^vtd0f!Z=_SWSVSVz@kVGNy zVABx^4tEj7!1X5NpCnXhav?*NguFK&5gAb4g*4 zsiJhlQ70|;8O=&E4M}?VX8o(wnR*N-?dG_=o{)S+F6SH{osIb5xNeBqPmLRDf^Yjm zd{rAWT1?>8!^7ubQiKW5!ayecIdl5_f#L`Z^|;kD_%#FgZO<$s=A~cA<`Rz~1E1!^ z2XkS9x<~;3KLEhv>kronr*chmp3GB(^Px!`55w2d z*f{u27^Q~u8U;~okU&mcyPW%bVI>j2LMQE04N8|?Hh-UeQKiJ+$}{J)qe+nuO?AUB z%s%ndcO87?5o(``cPDp1D*7B)G*Kmxf*aQf1dXKn2mL%~<(4D;ac=v|xlfyT&H@Q- zvh0>=wj5Ybth&jD&(IMykd};L=HJuhtys@ATBq(lG}6z`oae%+t&+~s8IWIF1PRLY zj-zk%0G++PB~vOh=O5DT1xe$a1IiJlmlXyP?~Uwb!=7|;a0Z2X-$Kr~^pBhQ`7@Hy zS+7+Cxxkl$&GWwZ`N?5mT(`Xp?fr3dZ5o(>Q^9UD7pE4O8YSm}NYFQS^g^D!0y>PS zzgj4GYzWiJnYbWQg@KXG$Jal$a_Y}4XFeYvTVuad95=Eg3n|0G zkQa3?%yuOh_oCJdx-?Nlk?YUk@UmPOXbAUj7#2Vc-c6;HF={uqgNYJ;bDr-xvWHU19 zqL~MnS^H+$wCpU^{Z(PuFW&;Lc53{H0SHxnC)d{!h_%15k(%MBj$Uw)X{5zEwmNr zwm+h@_uLy&n)|@K;F;j2s#%N<9!=k{q<0ltl!#HUn%3+?8w$xQXbeik$y@7G2B=|& zIju3@BYLH)Eta=H{xW5$kF_%8LUbYFHz8GS*TW5KCd*6uT=AQIY=1UszM zLdBJ4<}RkYIZFun$Vcp=f|gK6N?v9cD}39=dosTCKxE+REeeB3J4%LnNcV#%6_3$^ z17m&Aq3WjhgVZvz(*2GibQvs0(*o{FkHj#c$QS){Bl(c8@Q+%3P}zwv-S|%;VD>DVtvp2CZ$Cez@#w z%)U|2e{;SMG)*MS%b)RR0j&sUt#=Gd7RXiQ1HKA&?Yz?;&c-NS>#&YeC9_I5L4qHQ zqeopV@FD%WiIT~bTR|s`8Kt(H28yy%6pH^J1;gd{UNuPUn4yft$<%#cAOzhHx?azX zo_$WmMv6AoKp@Z*r0+W5xm00}q-lxyyunq%vNX;K z%uWnyK9B(Hy(LjfN;VNq2IRhR`~YR~^4fNfhN-C!qv@w;x<@nQ8wOA6RWuXoPUA5V3D(JV&H8XS9Vq4FMM#S1SL$gAaRqBT9 zf+ybd#*^YbpV(wN$qw%QeUg<>OJ9^ZsSOyeE$|luw(KK6CtT&9EdtlTEJ9xF=LK^( z+fN;Ma*-`j&Ak2|#zQYznOr?}AzSG(NXyTdOKVj z-`ld#J^{34JSKEG?u$54Si&@5`sIK8qsg_X;Zy{Z1!z9ee-w&Hv1{A_cbsGtwv zT1@g(mq}8z687-G>?FYUr)91bJ;L(IzU3A%Sbo}{wcUyW#H=$&ORPXta5{jQws?*k zRLb0pai3h;1ucss{s3#5iH3bL6MqwgVAb2drs7PO%8ExOP)c#7dROP9^Y0!r@Cy`D zc+fpSg(m)Z1{k#y*0LwlC6<9}AghwYC*~6M5g{uzni42^FPp>)HZA0oR2|_&@{2pj zd~W2NBDz*bk1>lwjh|sqVqwafo0H&&!S%NW>6F0nVNc3Q?cBWQ`FIxLjvMU}fe^Lz z{1yyO?#x&XY>~t#SY*%l0H;v5g3r>RSZ~>oynA1V^RyZM#{I^^xoxw7{LQ$O#l0c( zcWy5@Y}SQf=^UtKW*@pvv)FBRVue5(+a^>C91 zX>e3YQU_(92QY&@4bFd2i8v0KfxO&>FrmF0YdG1;m1Ae05b~EUxk%iUtw)=pGGS^u zD+*{_S|wwE2a-fO2{OBay@q83BV}CMf#a@%SVF6&=ZgW|as?@vN`gKjH^_|X{1iT= zulWbLRMojuKT^y0K}><;_bK-2q&b1HSq6zj`&JM4O7fX88-o=>UL}Fjib|g112RUh zoCzjqY~m7QTU1zq9{~H&#hp8_$IyK3b_^GA8|ud3!%={?2(zEj=F1rHKGBlM|fWk7&dU@5uSRsFK?^du65X6Zce)zD^ka& zDP%**qzn#yR~OTYLudD%lxs>wVz72+Doi*~Uuz?T?cUmE}~>*$2O(WS=g4w=*G#-IQ<|M)Fk_^Y10U$-))^NIVSue@4;RS z4}iHgzzc95G`rM@&g)GLxmHG1PVY;5PRXlcNFL#g)u3{Wp)oSIWNY(CUMX)!?$-55Z~jWiI(B|I+bIn0L5 literal 0 HcmV?d00001 diff --git a/MikroGig/20260628/20260628074104-Config.rsc b/MikroGig/20260628/20260628074104-Config.rsc new file mode 100644 index 0000000..3fde352 --- /dev/null +++ b/MikroGig/20260628/20260628074104-Config.rsc @@ -0,0 +1,2761 @@ +# 2026-06-28 06:41:05 by RouterOS 7.23.1 +# software id = 2961-C4ZZ +# +# model = RBD52G-5HacD2HnD +# serial number = HCX084RAVFA +/interface bridge +add admin-mac=18:FD:74:6E:58:14 auto-mac=no name=Bridge-LAN port-cost-mode=\ + short +add name=br-awg +/interface wireless +# managed by CAPsMAN +set [ find default-name=wlan2 ] country=belarus mode=ap-bridge name=wlan1 \ + ssid=vados wps-mode=disabled +# managed by CAPsMAN +set [ find default-name=wlan1 ] country=belarus mode=ap-bridge name=wlan2 \ + ssid=vados5g wps-mode=disabled +/interface ethernet +set [ find default-name=ether1 ] mac-address=48:8F:5A:8E:1A:BC +/interface veth +add address=10.9.9.2/24 container-mac-address=00:00:00:00:00:00 dhcp=no \ + gateway=10.9.9.1 gateway6="" name=veth-awg +/interface list +add name=WAN +add name=LAN +add exclude=LAN include=WAN name=Internet +/interface wireless security-profiles +set [ find default=yes ] supplicant-identity=MikroTik +/routing table +add disabled=no fib name=r_to_vpn +/system logging action +add email-start-tls=yes email-to=vados@vados.ru name=email target=email +/system script +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/700/f14f095d\";\r\ + \n:global CommitTAG \"unknown\";\r\ + \n:global CommitInfo \"Autocommit in repo: ros.vados.ru, generated by owne\ + r: root, in 13:30:03 22.06.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 comment="Download custom Certificates By Routers Identity " \ + dont-require-permissions=no name=mod/AM-Certs-By-Ident owner=\ + mod/AM-Certs-By-Ident policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-Certs-By-Ident\r\ + \n# Script comment: Download custom Certificates By Routers Identity \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# CloudFlare\r\ + \n#/tool/fetch mode=https url=https://cacerts.digicert.com/DigiCertGlobalR\ + ootCA.crt.pem;\r\ + \n#/certificate/import file-name=DigiCertGlobalRootCA.crt.pem passphrase=\ + \"\";\r\ + \n#/tool/ fetch mode=https url=https://cacerts.digicert.com/DigiCertGlobal\ + G2TLSRSASHA2562020CA1-1.crt.pem;\r\ + \n#/certificate/import file-name=DigiCertGlobalG2TLSRSASHA2562020CA1-1.crt\ + .pem passphrase=\"\";\r\ + \n# Quad9\r\ + \n#/tool/fetch mode=https url=\"https://cacerts.digicert.com/DigiCertGloba\ + lG3TLSECCSHA3842020CA1-1.crt.pem\"\r\ + \n#/certificate/import file-name=DigiCertGlobalG3TLSECCSHA3842020CA1-1.crt\ + .pem passphrase=\"\";\r\ + \n:global tmpAddCerts true; \r\ + \n\r\ + \n:global GlobalScriptsUrl;\r\ + \n:local UrlSuffix [/system/identity/get name];\r\ + \n:local UrlDir \"certs/\";\r\ + \n:local CertUrl (\$GlobalScriptsUrl . \$UrlDir . \$UrlSuffix .\"/\");\r\ + \n\r\ + \n:log error \$CertUrl;\r\ + \n\r\ + \n:local CertName \"DigiCert-Global-Root-CA.pem\";\r\ + \n/tool/fetch mode=https url=(\$CertUrl . \$CertName);\r\ + \n/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n/certificate/set name=\"DigiCert-Global-Root-CA\" [ find where common-na\ + me=\"DigiCert Global Root CA\" ];\r\ + \n\r\ + \n:set CertName \"DigiCert-Assured-ID-Root-CA.pem\";\r\ + \n/tool/fetch mode=https url=(\$CertUrl . \$CertName);\r\ + \n/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n/certificate/set name=\"DigiCert-Assured-ID-Root-CA\" [ find where commo\ + n-name=\"DigiCert Assured ID Root CA\" ];\r\ + \n\r\ + \n:set CertName \"DigiCert-Assured-ID-Root-G2.pem\";\r\ + \n/tool/fetch mode=https url=(\$CertUrl . \$CertName);\r\ + \n/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n/certificate/set name=\"DigiCert-Assured-ID-Root-G2\" [ find where commo\ + n-name=\"DigiCert Assured ID Root G3\" ];\r\ + \n\r\ + \n:set CertName \"DigiCert-Assured-ID-Root-G3.pem\";\r\ + \n/tool/fetch mode=https url=(\$CertUrl . \$CertName);\r\ + \n/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n/certificate/set name=\"DigiCert-Assured-ID-Root-G3\" [ find where commo\ + n-name=\"DigiCert Assured ID Root G3\" ];\r\ + \n\r\ + \n:set CertName \"DigiCert-Global-G2-TLS-RSA-SHA256-2020-CA1.pem\";\r\ + \n/tool/fetch mode=https url=(\$CertUrl . \$CertName);\r\ + \n/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n/certificate/set name=\"DigiCert-Global-G2-TLS-RSA-SHA256-2020-CA1\" [ f\ + ind where common-name=\"DigiCert Global G2 TLS RSA SHA256 2020 CA1\" ];\r\ + \n\r\ + \n:set CertName \"DigiCert-Global-G3-TLS-ECC-SHA384-2020-CA1.pem\";\r\ + \n/tool/fetch mode=https url=(\$CertUrl . \$CertName);\r\ + \n/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n/certificate/set name=\"DigiCert-Global-G3-TLS-ECC-SHA384-2020-CA1\" [ f\ + ind where common-name=\"DigiCert Global G3 TLS ECC SHA384 2020 CA1\" ];\r\ + \n\r\ + \n:set CertName \"DigiCert-Global-Root-G2.pem\";\r\ + \n/tool/fetch mode=https url=(\$CertUrl . \$CertName);\r\ + \n/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n/certificate/set name=\"DigiCert-Global-Root-G2\" [ find where common-na\ + me=\"DigiCert Global Root G2\" ];\r\ + \n\r\ + \n:set CertName \"DigiCert-Global-Root-G3.pem\";\r\ + \n/tool/fetch mode=https url=(\$CertUrl . \$CertName);\r\ + \n/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n/certificate/set name=\"DigiCert-Global-Root-G3\" [ find where common-na\ + me=\"DigiCert Global Root G3\" ];\r\ + \n\r\ + \n:set CertName \"DigiCert-TLS-ECC-P384-Root-G5.pem\";\r\ + \n/tool/fetch mode=https url=(\$CertUrl . \$CertName);\r\ + \n/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n/certificate/set name=\"DigiCert-TLS-ECC-P384-Root-G5\" [ find where com\ + mon-name=\"DigiCert TLS ECC P384 Root G5\" ];\r\ + \n\r\ + \n:set CertName \"DigiCert-TLS-RSA4096-Root-G5.pem\";\r\ + \n/tool/fetch mode=https url=(\$CertUrl . \$CertName);\r\ + \n/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n/certificate/set name=\"DigiCert-TLS-RSA4096-Root-G5\" [ find where comm\ + on-name=\"DigiCert TLS RSA4096 Root G5\" ];\r\ + \n\r\ + \n:set CertName \"DigiCert-Trusted-Root-G4.pem\";\r\ + \n/tool/fetch mode=https url=(\$CertUrl . \$CertName);\r\ + \n/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n/certificate/set name=\"DigiCert-Trusted-Root-G4\" [ find where common-n\ + ame=\"DigiCert Trusted Root G4\" ];\r\ + \n\r\ + \n:set CertName \"GTS-Root-R4.pem\";\r\ + \n/tool/fetch mode=https url=(\$CertUrl . \$CertName);\r\ + \n/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n/certificate/set name=\"GTS-Root-R4\" [ find where common-name=\"GTS Roo\ + t R4\" ];\r\ + \n\r\ + \n:set CertName \"Go-Daddy-Root-Certificate-Authority-G2.pem\";\r\ + \n/tool/fetch mode=https url=(\$CertUrl . \$CertName);\r\ + \n/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n/certificate/set name=\"Go-Daddy-Root-Certificate-Authority-G2\" [ find \ + where common-name=\"Go Daddy Root Certificate Authority - G2\" ];\r\ + \n\r\ + \n:set CertName \"ISRG-Root-X1.pem\";\r\ + \n/tool/fetch mode=https url=(\$CertUrl . \$CertName);\r\ + \n/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n/certificate/set name=\"ISRG-Root-X1\" [ find where common-name=\"ISRG R\ + oot X1\" ];\r\ + \n\r\ + \n:set CertName \"Root-YE.pem\";\r\ + \n/tool/fetch mode=https url=(\$CertUrl . \$CertName);\r\ + \n/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n/certificate/set name=\"Root-YE\" [ find where common-name=\"Root YE\" ]\ + ;\r\ + \n\r\ + \n:set CertName \"Starfield-Root-Certificate-Authority-G2.pem\";\r\ + \n/tool/fetch mode=https url=(\$CertUrl . \$CertName);\r\ + \n/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n/certificate/set name=\"Starfield-Root-Certificate-Authority-G2\" [ find\ + \_where common-name=\"Starfield Root Certificate Authority - G2\" ];\r\ + \n\r\ + \n:set CertName \"USERTrust-RSA-Certification-Authority.pem\";\r\ + \n/tool/fetch mode=https url=(\$CertUrl . \$CertName);\r\ + \n/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n/certificate/set name=\"USERTrust-RSA-Certification-Authority\" [ find w\ + here common-name=\"USERTrust RSA Certification Authority\" ];\r\ + \n\r\ + \n:global tmpAddCerts (a);\r\ + \n\r\ + \n#/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n#/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n#:set CertName \"GTS-Root-RX.pem\";\r\ + \n#/tool/fetch mode=https url=(\$AmScriptsUrl . \"certs/\" . \$CertName);\ + \r\ + \n#/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n#internet\r\ + \n#/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n#:set CertName \"ISRG-Root-X2.pem\";\r\ + \n#/tool/fetch mode=https url=(\$AmScriptsUrl . \"certs/\" . \$CertName);\ + \r\ + \n#/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n\r\ + \n\r\ + \n#/ip/dns/set allow-remote-requests=yes doh-max-concurrent-queries=100 do\ + h-max-server-connections=20 use-doh-server=https://security.cloudflare-dns\ + .com/dns-query verify-doh-cert=yes;\r\ + \n#/ip/dns/static remove [find where address=1.1.1.1];\r\ + \n#/ip/dns/static add address=1.1.1.1 name=security.cloudflare-dns.com com\ + ment=\"cloudflare-dns IPv4 1\";\r\ + \n#/ip/dns/ static remove [find where address=1.0.0.1];\r\ + \n#/ip/dns/static add address=1.0.0.1 name=security.cloudflare-dns.com com\ + ment=\"cloudflare-dns IPv4 2\"\r\ + \n#/ip dns static add address=2606:4700:4700::1111 name=security.cloudflar\ + e-dns.com type=AAAA\r\ + \n#/ip dns static add address=2606:4700:4700::1001 name=security.cloudflar\ + e-dns.com type=AAAA\r\ + \n#/ip/dns set servers=\"1.0.0.1, 8.8.4.4\";\r\ + \n#/ip/dns set servers=\"\";" +add dont-require-permissions=no name=fetchCustom owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="\ + \r\ + \n:global GlobalScriptsUrl;\r\ + \n/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@10\ + .30.30.86.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.86.pub\";\ + \n" +add dont-require-permissions=no 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\"\r\ + \n#/tool/fetch url=\"https://ros.vados.ru/docker/awg2-webui-arm.tar\" dst-\ + path=usb1/tmp/awg2-webui.tar\r\ + \n\r\ + \n:put \"1. Create veth interface\"\r\ + \n/interface veth add name=veth-awg address=10.9.9.2/24 gateway=10.9.9.1\r\ + \n\r\ + \n:put \"2. Create bridge and add veth\"\r\ + \n/interface bridge add name=br-awg\r\ + \n/interface bridge port add bridge=br-awg interface=veth-awg\r\ + \n\r\ + \n:put \"3. Assign IP to bridge\"\r\ + \n/ip address add address=10.9.9.1/24 interface=br-awg\r\ + \n\r\ + \n:put \"4. NAT: masquerade container traffic\"\r\ + \n/ip firewall nat add chain=srcnat src-address=10.9.9.0/24 action=masquer\ + ade\r\ + \n\r\ + \n:put \"5. Port forward: UDP 443 \? container (AmneziaWG QUIC mimicry)\"\ + \r\ + \n/ip firewall nat add chain=dstnat protocol=udp dst-port=443 \\\r\ + \n action=dst-nat to-addresses=10.9.9.2 to-ports=55055 in-interface=eth\ + er1\r\ + \n\r\ + \n:put \"6. Port forward: HTTPS 9443 \? container Web UI\"\r\ + \n/ip firewall nat add chain=dstnat protocol=tcp dst-port=9443 \\\r\ + \n action=dst-nat to-addresses=10.9.9.2 to-ports=443 in-interface=ether\ + 1\r\ + \n\r\ + \n:put \"Create environment list..\"\r\ + \n/container envs add list=awgui-env key=WEB_USER value=vados\r\ + \n/container envs add list=awgui-env key=WEB_PASS value=zoomzoomrulezz\r\ + \n/container envs add list=awgui-env key=SECRET_KEY value=RanDomSecret32Sy\ + mboLs2325272931q\r\ + \n/container envs add list=awgui-env key=AWG_ENDPOINT value=10.30.30.10:44\ + 3\r\ + \n\r\ + \n:put \"Load and start container...\"\r\ + \n/container add file=usb1/tmp/awg2-webui.tar \\\r\ + \n interface=veth-awg \\\r\ + \n root-dir=usb1/docker/awg2-webui \\\r\ + \n envlist=awgui-env \\\r\ + \n start-on-boot=yes \\\r\ + \n logging=yes\r\ + \n\r\ + \n:put \"Start...\"\r\ + \n/container start 0\r\ + \n\r\ + \n:put \"Verify...\"\r\ + \n/container print\r\ + \n\r\ + \n:put \"Should show: status=running\"\r\ + \n\r\ + \n/log print where topics~\"container\"\r\ + \n\r\ + \n:put \"Should show: AWG started, nginx running\"\r\ + \n" +/user group +add name=backup policy="telnet,ssh,ftp,read,write,policy,test,sniff,sensitive,\ + !local,!reboot,!winbox,!password,!web,!api,!romon,!rest-api" +/container config +set layer-dir=/usb1/docker memory-high=2048.0KiB memory-max=2048.0KiB \ + registry-url=https://registry-1.docker.io tmpdir=/tmp username=amsternl +/container mounts +add dst=/etc/amnezia/amneziawg list=awg0.conf src=/usb1/wg +/disk settings +set auto-media-interface=lo +/interface bridge port +add bridge=Bridge-LAN interface=ether1 +add bridge=Bridge-LAN interface=ether2 +add bridge=Bridge-LAN interface=ether3 +add bridge=Bridge-LAN interface=ether4 +add bridge=Bridge-LAN interface=ether5 +add bridge=Bridge-LAN interface=wlan2 +add bridge=Bridge-LAN interface=wlan1 +add bridge=br-awg interface=veth-awg +/ip neighbor discovery-settings +set discover-interface-list=Internet +/ipv6 settings +set allow-fast-path=no disable-ipv6=yes disable-link-local-address=yes \ + forward=no +/interface detect-internet +set detect-interface-list=static internet-interface-list=Internet \ + lan-interface-list=LAN wan-interface-list=WAN +/interface list member +add interface=*B list=WAN +add interface=ether2 list=LAN +add interface=ether3 list=LAN +add interface=ether4 list=LAN +add interface=ether5 list=LAN +add interface=wlan2 list=LAN +add interface=wlan1 list=LAN +add interface=ether1 list=Internet +add interface=Bridge-LAN list=LAN +/interface wireless cap +# +set bridge=Bridge-LAN caps-man-addresses=10.30.30.10 \ + caps-man-certificate-common-names=CAPsMAN-51B25E507D07 certificate=\ + CAP-18FD746E5814 discovery-interfaces=Bridge-LAN enabled=yes interfaces=\ + wlan1,wlan2 lock-to-caps-man=yes +/ip address +add address=10.30.30.86/24 interface=ether1 network=10.30.30.0 +add address=10.9.9.1/24 interface=br-awg network=10.9.9.0 +/ip dns +set allow-remote-requests=yes servers=10.30.30.33,109.107.177.79 +/ip firewall nat +add action=masquerade chain=srcnat src-address=10.9.9.0/24 +# in/out-interface matcher not possible when interface (ether1) is slave - use master instead (Bridge-LAN) +add action=dst-nat chain=dstnat dst-port=443 in-interface=ether1 protocol=udp \ + to-addresses=10.9.9.2 to-ports=55055 +# in/out-interface matcher not possible when interface (ether1) is slave - use master instead (Bridge-LAN) +add action=dst-nat chain=dstnat dst-port=9443 in-interface=ether1 protocol=\ + tcp to-addresses=10.9.9.2 to-ports=443 +/ip firewall service-port +set ftp disabled=yes +set tftp disabled=yes +set irc disabled=no +set sip disabled=yes +set pptp disabled=yes +set rtsp disabled=no +/ip hotspot walled-garden +add server=*1 +add server=*1 +/ip ipsec settings +set accounting=no +/ip ipsec policy +set 0 disabled=yes dst-address=0.0.0.0/0 src-address=0.0.0.0/0 +/ip ipsec profile +set [ find default=yes ] dpd-interval=2m dpd-maximum-failures=5 +/ip proxy +set cache-administrator="" max-cache-size=none port=80,443 src-address=\ + 0.0.0.0 +/ip route +add check-gateway=ping disabled=no distance=2 dst-address=0.0.0.0/0 gateway=\ + ether1 routing-table=main scope=30 target-scope=10 +/ip service +set ftp disabled=yes +set telnet disabled=yes +set ssh address=10.30.30.0/24 port=2222 +set winbox address=10.30.30.0/24 +set api address=10.30.30.0/24 disabled=yes +set api-ssl address=10.30.30.0/24 disabled=yes +set www-ssl port=18443 +set www address=10.30.30.0/24 disabled=yes port=18880 +/ip service webserver +set graphs-plain=no rest-plain=no webfig-plain=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=Bridge-LAN type=internal +add forced-ip=109.107.177.79 interface=*A type=external +add interface=lo type=internal +add interface=wlan2 type=internal +add interface=wlan1 type=internal +/ipv6 nd +set [ find default=yes ] advertise-dns=yes +/system clock +set time-zone-name=Europe/Amsterdam +/system clock manual +set time-zone=+03:00 +/system identity +set name=MikroGig +/system logging +add action=email disabled=yes regex="* added|changed|removed *" topics=\ + system,info +/system ntp client +set enabled=yes +/system ntp server +set enabled=yes manycast=yes multicast=yes use-local-clock=yes +/system ntp client servers +add address=10.30.30.81 +add address=10.30.30.10 +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 +add address=time.google.com disabled=yes +/system scheduler +add comment="Every Day Set Global Environment Variables" 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" interval=1d name=\ + AM-ScriptInstallUpdate on-event=\ + ":global ScriptInstallUpdate; \$ScriptInstallUpdate;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +/tool e-mail +set certificate-verification=no from=MikroVados port=587 \ + server=smtp.mail.me.com tls=starttls user=vadossio@icloud.com diff --git a/MikroGig/history/20260628/20260628074104-Diff-Config.rsc b/MikroGig/history/20260628/20260628074104-Diff-Config.rsc new file mode 100644 index 0000000..a19e99b --- /dev/null +++ b/MikroGig/history/20260628/20260628074104-Diff-Config.rsc @@ -0,0 +1,79 @@ +9d8 +< add name=br-awg +19,21d17 +< /interface veth +< add address=10.9.9.2/24 container-mac-address=00:00:00:00:00:00 dhcp=no \ +< gateway=10.9.9.1 gateway6="" name=veth-awg +2567,2625d2562 +< add dont-require-permissions=no 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\"\r\ +< \n#/tool/fetch url=\"https://ros.vados.ru/docker/awg2-webui-arm.tar\" dst-\ +< path=usb1/tmp/awg2-webui.tar\r\ +< \n\r\ +< \n:put \"1. Create veth interface\"\r\ +< \n/interface veth add name=veth-awg address=10.9.9.2/24 gateway=10.9.9.1\r\ +< \n\r\ +< \n:put \"2. Create bridge and add veth\"\r\ +< \n/interface bridge add name=br-awg\r\ +< \n/interface bridge port add bridge=br-awg interface=veth-awg\r\ +< \n\r\ +< \n:put \"3. Assign IP to bridge\"\r\ +< \n/ip address add address=10.9.9.1/24 interface=br-awg\r\ +< \n\r\ +< \n:put \"4. NAT: masquerade container traffic\"\r\ +< \n/ip firewall nat add chain=srcnat src-address=10.9.9.0/24 action=masquer\ +< ade\r\ +< \n\r\ +< \n:put \"5. Port forward: UDP 443 \? container (AmneziaWG QUIC mimicry)\"\ +< \r\ +< \n/ip firewall nat add chain=dstnat protocol=udp dst-port=443 \\\r\ +< \n action=dst-nat to-addresses=10.9.9.2 to-ports=55055 in-interface=eth\ +< er1\r\ +< \n\r\ +< \n:put \"6. Port forward: HTTPS 9443 \? container Web UI\"\r\ +< \n/ip firewall nat add chain=dstnat protocol=tcp dst-port=9443 \\\r\ +< \n action=dst-nat to-addresses=10.9.9.2 to-ports=443 in-interface=ether\ +< 1\r\ +< \n\r\ +< \n:put \"Create environment list..\"\r\ +< \n/container envs add list=awgui-env key=WEB_USER value=vados\r\ +< \n/container envs add list=awgui-env key=WEB_PASS value=zoomzoomrulezz\r\ +< \n/container envs add list=awgui-env key=SECRET_KEY value=RanDomSecret32Sy\ +< mboLs2325272931q\r\ +< \n/container envs add list=awgui-env key=AWG_ENDPOINT value=10.30.30.10:44\ +< 3\r\ +< \n\r\ +< \n:put \"Load and start container...\"\r\ +< \n/container add file=usb1/tmp/awg2-webui.tar \\\r\ +< \n interface=veth-awg \\\r\ +< \n root-dir=usb1/docker/awg2-webui \\\r\ +< \n envlist=awgui-env \\\r\ +< \n start-on-boot=yes \\\r\ +< \n logging=yes\r\ +< \n\r\ +< \n:put \"Start...\"\r\ +< \n/container start 0\r\ +< \n\r\ +< \n:put \"Verify...\"\r\ +< \n/container print\r\ +< \n\r\ +< \n:put \"Should show: status=running\"\r\ +< \n\r\ +< \n/log print where topics~\"container\"\r\ +< \n\r\ +< \n:put \"Should show: AWG started, nginx running\"\r\ +< \n" +2644d2580 +< add bridge=br-awg interface=veth-awg +2671d2606 +< add address=10.9.9.1/24 interface=br-awg network=10.9.9.0 +2674,2681d2608 +< /ip firewall nat +< add action=masquerade chain=srcnat src-address=10.9.9.0/24 +< # in/out-interface matcher not possible when interface (ether1) is slave - use master instead (Bridge-LAN) +< add action=dst-nat chain=dstnat dst-port=443 in-interface=ether1 protocol=udp \ +< to-addresses=10.9.9.2 to-ports=55055 +< # in/out-interface matcher not possible when interface (ether1) is slave - use master instead (Bridge-LAN) +< add action=dst-nat chain=dstnat dst-port=9443 in-interface=ether1 protocol=\ +< tcp to-addresses=10.9.9.2 to-ports=443 -- 2.52.0