From 5c8b83d0f729b45c4564b67e0cf8ad2511fb5197 Mon Sep 17 00:00:00 2001 From: Roger Zander Date: Wed, 24 Oct 2018 17:27:44 +0200 Subject: [PATCH] New: Upgrade to Redis 5.0 and .NETCore 2.1 --- examples/Windows Inventory/inventory.ps1 | 2 +- source/JainDBTest/JainDBTest.csproj | 4 +- source/JainDBTest/UnitTest1.cs | 2 +- source/jaindb/Controllers/HomeController.cs | 2 +- source/jaindb/Dockerfile | 6 +- source/jaindb/JainDB_Logo.ico | Bin 0 -> 116201 bytes source/jaindb/jaindb.cs | 545 ++++---------------- source/jaindb/jaindb.csproj | 21 +- 8 files changed, 111 insertions(+), 471 deletions(-) create mode 100644 source/jaindb/JainDB_Logo.ico diff --git a/examples/Windows Inventory/inventory.ps1 b/examples/Windows Inventory/inventory.ps1 index 3af1604..818dd2d 100644 --- a/examples/Windows Inventory/inventory.ps1 +++ b/examples/Windows Inventory/inventory.ps1 @@ -196,7 +196,7 @@ getinv -Name "Printer" -WMIClass "Win32_Printer" -Properties @("DeviceID","Capab $user = Get-LocalUser | Select-Object Description, Enabled, UserMayChangePassword, PasswordRequired, Name, @{N = '@PasswordLastSet'; E = {[System.DateTime](($_.PasswordLastSet).ToUniversalTime())}}, @{N = 'id'; E = {$_.SID}} | Sort-Object -Property Name $object | Add-Member -MemberType NoteProperty -Name "LocalUsers" -Value ($user) -$locAdmin = Get-LocalGroupMember -SID S-1-5-32-544 | Select-Object @{N = 'Name'; E = {$_.Name.Replace($($env:Computername) + "\", "")}}, PrincipalSource, ObjectClass | Sort-Object -Property Name +$locAdmin = Get-LocalGroupMember -SID S-1-5-32-544 | Select-Object @{N = 'Name'; E = {$_.Name.Replace($($env:Computername) + "\", "")}}, ObjectClass, @{Name = 'id'; Expression = {$_.SID.Value}} | Sort-Object -Property Name $object | Add-Member -MemberType NoteProperty -Name "LocalAdmins" -Value ($locAdmin) $locGroup = Get-LocalGroup | Select-Object Description, Name, PrincipalSource, ObjectClass, @{N = 'id'; E = {$_.SID}} | Sort-Object -Property Name diff --git a/source/JainDBTest/JainDBTest.csproj b/source/JainDBTest/JainDBTest.csproj index 800ff74..b08b5c9 100644 --- a/source/JainDBTest/JainDBTest.csproj +++ b/source/JainDBTest/JainDBTest.csproj @@ -1,7 +1,7 @@ - netcoreapp2.0 + netcoreapp2.1 false @@ -11,7 +11,7 @@ - + diff --git a/source/JainDBTest/UnitTest1.cs b/source/JainDBTest/UnitTest1.cs index 4356a9f..59e4832 100644 --- a/source/JainDBTest/UnitTest1.cs +++ b/source/JainDBTest/UnitTest1.cs @@ -72,7 +72,7 @@ public void Test_Query() { Console.WriteLine("Query data..."); jDB.UseFileStore = true; - int i = jDB.Query("obj1", "", "").Count(); + int i = jDB.QueryAsync("obj1", "", "", "").Result.Count(); Assert.IsTrue(i > 0); } diff --git a/source/jaindb/Controllers/HomeController.cs b/source/jaindb/Controllers/HomeController.cs index f7f0ea8..a46c9b1 100644 --- a/source/jaindb/Controllers/HomeController.cs +++ b/source/jaindb/Controllers/HomeController.cs @@ -291,7 +291,7 @@ public JArray Query() //string sUri = Microsoft.AspNetCore.Http.Extensions.UriHelper.GetDisplayUrl(Request); var query = QueryHelpers.ParseQuery(sQuery); - return jDB.Query(string.Join(";", query.Where(t => string.IsNullOrEmpty(t.Value)).Select(t => t.Key).ToList()), query.FirstOrDefault(t => t.Key.ToLower() == "$select").Value, query.FirstOrDefault(t => t.Key.ToLower() == "$exclude").Value, query.FirstOrDefault(t => t.Key.ToLower() == "$where").Value); + return jDB.QueryAsync(string.Join(";", query.Where(t => string.IsNullOrEmpty(t.Value)).Select(t => t.Key).ToList()), query.FirstOrDefault(t => t.Key.ToLower() == "$select").Value, query.FirstOrDefault(t => t.Key.ToLower() == "$exclude").Value, query.FirstOrDefault(t => t.Key.ToLower() == "$where").Value).Result; } return null; } diff --git a/source/jaindb/Dockerfile b/source/jaindb/Dockerfile index b3c8eef..feec308 100644 --- a/source/jaindb/Dockerfile +++ b/source/jaindb/Dockerfile @@ -1,4 +1,4 @@ -FROM microsoft/aspnetcore +FROM microsoft/dotnet:2.1-aspnetcore-runtime ARG source WORKDIR /app EXPOSE 5000:5000/tcp @@ -13,7 +13,7 @@ RUN rm -f -r /app/wwwroot/bin #RUN apt-get install -y make gcc wget #RUN apt-get install -y --no-install-recommends ca-certificates #RUN mkdir -p /app/wwwroot/redis -#RUN wget -O redis.tar.gz http://download.redis.io/releases/redis-4.0.11.tar.gz +#RUN wget -O redis.tar.gz http://download.redis.io/releases/redis-5.0.0.tar.gz #RUN tar -xzf redis.tar.gz -C /app/wwwroot/redis --strip-components=1 #RUN grep -q '^#define CONFIG_DEFAULT_PROTECTED_MODE 1$' /app/wwwroot/redis/src/server.h @@ -31,7 +31,7 @@ RUN rm -f -r /app/wwwroot/bin #RUN cp /app/wwwroot/redis/src/redis-cli /app/wwwroot/bin #RUN cp /app/wwwroot/redis/src/redis-sentinel /app/wwwroot/bin #RUN cp /app/wwwroot/redis/src/redis-server /app/wwwroot/bin -#RUN rm -r /app/wwwroot/redis +##RUN rm -r /app/wwwroot/redis ENV localURL "http://localhost" diff --git a/source/jaindb/JainDB_Logo.ico b/source/jaindb/JainDB_Logo.ico new file mode 100644 index 0000000000000000000000000000000000000000..9d4197676dbcf1c26c7287dffea20142b79e0030 GIT binary patch literal 116201 zcmXtf19&7|v~9<>Gch`x*!IMp*qAt(c#?^2+s4GUZQHi(+yA@o_V-nHS9et%?0vS@ zIsgC|021)u3k*O4D6<3rBtYMNeE#>G8Ws$21Nu!!=>MOK004N7pesrK_q+rZ01$2f z1B5Ed|3HGrhX>t?Bqb@P{NKO-9k9@#4;_b+UjWd~LPaE#)DjtiR#!GZx zXcJiXC@gdV>%Ejp9;0hxK&1X?HGyGx}) zA-ZpYk+F%C<5-I85A$WN%qiV4*rZrgd-TkM0Am2k6nWfM+B95)yKGkTQPT9Ie5|V_`)&haSNoCn-sek`XJ*3PzFCR9yp=4$b>%QV?PYpc#p@>dQaGIZ>nP zyAzOWq=Ez*mUIFeAcTE1qSCR5qe7^@rJS60^kqm?fTrSArY{hzE-_bKQ$)Zt7xyP- zvYI9edYY<~jr;0WF^Ro{m`};GinPw{YM3+_K)<-uNl!Al>Z_nZs(pO`pVhrpRt=41*(si%6@gA3cVzHYFE2+MP58OOt~)IfA%0FI1W2&7^!7{aj0Yw#>GW zSRM&5h8SZR=hb!|B>Izzi~FS6fB^M)SlxYf3?Tfyf(o*fpSpYN%J@>N))*o;9`tW1 z^LnOf3gCVPsjfUWy1JvLYpNc&o*c=s5*!aN9)3#AW1Yu`oU~7)OOPH>(4z}DfH&*P zXj}s9h!87T~&rZ~3G7_~{eg=PG#tI=-8jH3%u94 zL9J5j8kw`Cb-;tIa%=1xqSu<8ottFL zO-dRnpuBCd+^f&a1k4aHg@t0STs??@zBEs9zGA<}E z1QZ**^k!eER=097tS#)5QVWm#WN6$Hxmg~zKIp#7Ecez zn(tN+J&J;c%qz+s4foDCdEk?VNvoSd&pCc@@k63n4X?JHqAPu@G^W%P&ds2cZmnF^Z4su4GV8M_ffr=DX6FMgEodc@&Cv~!`vb%ll7aAG)(PE{fMAg@ zSIGUfUx_hgI_LbU5TV>D#?k04^;HMqnW$C7rviuMKiV#D_Yzc4db^CW^v`>1iWa*fIUs36=& zeb~X1sDQ8MK}A7)qHaV%BJZkrcxIMQRAZZ^lsuE9C5pq8>mmIw=Z{(BfPt;X=jV$Z%W+mo&e&eClhK5hk8=cu zL(eE(!fFD9v2Rh3FCB1xH_S>(tNNCZ+eUzV4VHG_*O5H%3&BsgligYtpUW|SU00Zw zAvoWt1+I43Zp?_ManAij9;a89=h+>mFK1_H6!yB=uZP=e7&Cnv6y1owGU>XDO}VL) zs~G!oLQ)?dGz?(k(u$#7_v4C(RfLD1ET!yGG7ma?OM!{&CC!0jci~%NzM+xx4?)i= z(bUOPWeI#TaFgGta_C9z1W~aG&t9|ey>}*#Tb2d4vSV1Eg2XYHl)Z!d(S(X2;iR+H zclN%FvlBK2j7)QcN56G9k?ax#JP2>|Z_7l{ z>~+r_;C*F$aD4^>-JjSgUzeh-;anB)AT*8^Y?v8VTB0lr^Q&2I%SqG6g1udvDsZ|z zBqp`TGTW(!c^4ktb#dN!0?-}Qq(t;6j}KCdc%T;lx(z0wf81qXh3IHsA#GEwk^5IV z)MSs{C49Ou-yF7kJq`p}GcaD-6jid$ICh~?%MGiDD-3J0 zM9nddP4#o${oI$GCEVIsucT(nE;ZAzzq#lo7{}#wbTjv`$a(Q~tp8!K=1?#QmYlfR z)Qv*hGRy_bNSx|j=Xu!thz=#urhI#Rc~NDO?1eRijDrw!ph-`&Xrrg=c#;(BPDcg6 zcAq{z<{gZ@&4euYwqc_( zq3!;QX)8$iLn%5m&$ShOjdN8+^#tiBxyO17xzR{;s98sAf3rl7!JKXlfP-DPa1?^0*2HfWQ_QKCh3U3BK3Lz0A5d}`x z190OrIbxxPe-07#xLpkIW}>vo#KwyOTh)qe@|M}XI(AF%F)1ldH!|JGpvloM^Qj2* zms{bT)jhj5^*%G)>T+&=yFg|*Or2l9k4ZNQEk%BXX9LK2jZ($*{eiq%7Xw3ALw7V*I6;RzlINQ zcO=Ck*i_z`Qr-9xVwQY&YfL(Cjlr*(tjC#Ry@CgeCepaOHH&Y6C=rj4oRQb-Uqae< zj6Dan1!(}NcYAJIeZ}>77EnePOa|_VaBnTjk&KeoPgfYazvxH^uNOthERT#OR1{h+ z$r>9vz*$Jxm2z@7Wc#QNwvNnTMp4J0m=dFrRb}d=eQHKy3=jcF|FaY`aUcxxWNR)^ zf09kI?q(`>;D-A0{?Mfe{*hg+k%?1`X9;7XF?KM6K_ZtL1j6M||0$%V zWdV(l_7xoSP3H^dLoJ8}5w4J-uM>G|u<%`>cB+j;KOIs;cEr-Pl_m)Yc}d^Od=WR4 zWqxMl%E1;Z5*OhP0DFfEEt^a9E=mC9v^u+$`Dpg}r1|sxw}D#?d18O7xflpX_^zTG z1O5i?)&5Whz9w`X?vdk{*_k&Lyq$`+PP7cZa8orW!fGaDG$QpRi)+=XvI6>>#k^$O zZPWIM4TotC{(acsRm&zzx|?14fmawSjy{fXCByloZa+r9ad(v{oTz&kul^&l7)=hK zD#kW|KC%BQnx0Y~1pefeuy#9vZRRsGT2S$r3AOj1lR$VvaD!v*EMA6t?KSWVPE_yV+Z3O-U^b95nbJgE z2?oQkQXk97RZ2xbcCZE0_xBMShs~a%tDdLq7}iqD;}B&Q8`TbbF%YawtV#bOqM$GX z4B!LwX^!z8*2LC%K5iU-2%0~|Q8*82m34gQ3}|`pFP3uE)iu*i4H8Aud)ui)|D7f^ z5|n%SbmrLV@=&cQW}wY7Y5H@d-%hmMR2m-ufQ1`t1auh+$96Wq-R8`2Ybb!C&gwO2 zM*VbDhg{%vJwp{%gtQpD=3fX@bbkKxyVtBngi3p&i;T(9m;i3fANk;A(oO~CV5$yG z^uwJofAll=zGk);a+87}8Ij1B0_x5kKb8oP!x6-8oNcK0VR1VyW^(9)!&>^elyiX%5A=KD+g<=Ej6Yn58q z?T?{^Gftve&VAbpq_d`)*IG|}U-MJ9^*DD%yJ=!b66J2H;K?GEZ-0}2?3Gl+LJkmr z(;D7Xm3;C#fnQyTzvD1a)_!YySOv&pnp;+^R7iDuvXQkO4D^qtx~={QQauxU7j1%h zUUO5bnXMY(6?KnXT3VSI>mxedE@;5F-cyixBNmeEeR6kgOMXpFA81tsu)A7>% zwPXb$2}>}+gvRTGW5qMjMo(%_Mr*5h^xXTQ4&A4l4CVdYA7ySP+KEH|8gUp7&e^1g z{_G+i+S#2R^YphN4NJ7(y$)Jvi?PbSeS*@GeV!vNpb;9vKwX8(@>tM)`QbvZlMU|( z{&cHQ-!l@Hq6x`fg75BqDwSG^Oy`rYU<`kw3}~ROGH7VHpV|Az=<3=|#A+$whB(@; zV7E`NF@*G@j@3bKd3{|P#v0}N$5PS_qV#Xweel0Ia+oX=Vn=W6FGAKCyh#yndr~PB zqOb#RqmqgLveo$QHs0&U>)dq z69rGb`-s*#Frw?iKVCK+{^0ehrg2!y@R)EUq4#R7Ry@;KYafN>h_WbQ_$sgZz>)e+ zSh=1vD^*onASTt-HzGgU*z3G+Z~tDB5Ur9xA~|VKH1@39OYX6t%FBz5G?drLk*iN4FKC905)(rK z@T(=BYAE>)(K~u4HU2)6|~;pi0S5$B+&&5{vCo4=#!E+F@3KD46QIpN_w__aU{ zxqIKNk*AJOG9y7atQpHBA?+2Z4Irl{j*Jjcq>0z#A~@t5!{xiunW_o&>(PWy(>UJJ zKeOalB%a-0;h&-T=8?DdOYn!Vtbu*(rZzYP-P2VmYMX+L9#J0T@pjHMq#h5``hy5+ zx07gr1k$$Ajh6bEFrex|8}GGS+I}pwq@A#P+hiy++94Fe5Dp**#;24PlM-Jg9G~L1 zB7g+I*QVB5qp6iEFjwn*G+DSZOQWfD@Om0-@J-Fh-+Qw&@aL(fqEOV#mx48Ljmaik z)gwxpo0RwVVxzUE9#_I;+7IhZ4RpndajC{%B z?6;T)Fko51aE}7o`TsbZTYAZ^mn*;SPXewq)t4}+uM{DFI}B>;$*P7iV08DY1XPT~ zQ!)VvqEOG_y!3O@60WTPRmX?e=v*HUYe3>5;MP;wrM=SAiFFZVz;@yza;vc{gy{t+ zK!pJ_@Fg8jIH=XaJ1+B$qV}->6m7I|&w9$Ge%6Vi#TKEn@HoKXP(&hx_WsGlcgqA7 zfyxoJS9zAh>?)n#F##NE9*CfNQB(WUTq9O@Cde&kv?wtfgoN{V{Yz*Hg*%yrlpTES zHw!`&0{>)7G`FC2lL0RaHY?leOf=@VE{HM&lrHnZf@?StXg~r$iZ1^`p?-F##9Y8e z>KL~|lQe3Q0!v14#o35zr;hVBbY6DxMh7EKz!q2BY2XCDnAGG@{sraJ<|39p@qN0s zYQkVI>c>Grg%>g}x!kS@&%@X{27Q81@@0uFLn+vBU(4&||uOG1U zF_<*l7Z=Mp+ZoX|u+z#YhppUKNh z^Kh-qgHo*_HTCl+h-C}d@e(kd5zs8SI2xbLEHNNg+mGNNAK)511s?HVtKDqWaRIyOdIw#%LTAyp)QWbsxR}NF z6fs-EPJcVg!fNK6z(}%^kr%6ucSnG^Y4P#SFLPt^Ek2N4VTCpLIRHalb!g4vwRn|4 zF}5|SnhduE`PERaG4vxCBNY$Zp-w*@EB*vdU~F!ZQQJ=ej`SJ`$&mmULw}uxcI*h2 z;8dJhm+*e`z|pQzf{;YpJhsfVe{Ixj%U5{{h6;uAUDp^;&Jv@i(yD zLNwjlWhJ=v<|RK_`|!A48gSPt3T&?tq-LU0uY*(WoYX5Zy8ii+FqDS1We7$<&qz{9 z#cXi80|`5z+mZZi8ZfqHntC#eC93l+0$(WXM%m2jUgzx#ygmAMN>fuQ@=SFw=yO!^ z76IKGnml%p4xO(^li1+Og3UBKa09kVKJBK(}g)0+d2+hoN>x0nEEZ_)$44?FhhN?R>rqu^!jrVcZ^0GH~2Y+U&r5rWrrs$pIkz5wHYfIJsm} zkLC^-J48F7VR%(S+tgzj1!bz!{M!EY<01pX_?(UqTz<&VT z{o0cL{TF|oVy9ZU6;$`zaGi_6=6+{VFF5IU%M<|V0V{*Th9$0#Z_f*BBcu-Puj9CP zE+*0slM2B%M>{Q11kZyzjL^4HE{!m#K~75mH1ts)g)bt;%XIHB3QF{i$I{G`ed(@T z2&j(7Em8bX-e2X?(ai+vb)A_xSe79vqZY2)%k{Ct%~lPAN#ybEQ(5>uuT1l}tnkh^ zcF<+gxcrXhzsV?6rS(RMlU<}e;&xO1IIE<7O5!P0C}*PyRjk6rP`s$?s*W%L{1Zee z0fM3pU77wLZcEftb%|@Xni%7g(IKwtSZBsYkTmKC`WeL^);G?To`CCgT{Kg}N|y_u z_r)I9Y8m~6MC7?ij3t+Xwss6`j=%pU(b~Mz=&a^vKt|`n(AYq>-!v^9&VhK~hy__R zaIf8V0n{XBk#FGcK0Q602A*2dvBNc;uFab~5nG4TejIp*H_Pfefo#F@u?0K+&(ty>Vi{NAe*(NQ7*CC zCqI*@jT)|8Z(d1&!C>xi7ZL=MFwjr^H2i1lXG2%?)v$@GUUY3TbhRh z+l`NY&L!`=$xE_L7vksJQr5{k8T{XlB)o1vs)A5s*#~{DC>h{Xa z^+y{0$kMt&!Q#{Uv{=mEr~KofHb@Tnxrx2`=xnudM~Aw55#Zf>YrRN|m}jhx^dO*5P6o8I|e2zHPOUdYrap`4g9Bd&lF?2|f9yH?FkQ5lO+JfVgIA z;ysy34=;p1UZv2z#Pm*@1v~!){`eamKVx}b7~al7kR((o?IMP@yiB_{R|a+y?_O{Y ztjM2&#RT{8{m-?Pz}^emj5=i3MfoTbr=Z!xWBmf-(6_}h?}73E6%wPh@Ky)mI@ zYiXknO#~kv+#JK{_CVVgOSx^rwYfU5x#iNK4dH_v?is}{?%otr?pYB8PJOU^FHHO+ zR0$*yi%$IgFpr-%Uzhr?3;AH5+Il#>$$)?e{rFsK*{10(Q2p7i(=EN)P$`rYuG+Di+ zT|)%5Y(GGz@}wbMvC~_eHev- z)
F!^p}!Oss$A;>7@uy6Sob4~hTM|h27F?yI7RhI~1f8!{Cn$Yut_|Bn$;5s6PQxDvQGg6V|bv>j~cs z4>WPj1b=?17ub8HY*EoD#oG(elBDw-%ka-8M$Mh%)CAQziL>W6o}JXdgNiEi z1U*n98TZDDTb&adDbKs-_)T+!1Z(B=NR&Q7CplqOU-Ev+z~k)Ua;r1TIQpk;R&v?L z$8U}C&cB|={;eKKJ!25w^1r$3#NwB(P3YoeX2wVKG-42ItU&j-z{sr^ZepP!>W@2A?H0K;Eb*fl#{NR#I}ev z#%QrFWc25n*soRW13VmW&(~tQfnhAMQbJw}OFn)`$P!R1l!pOUA4|8Q5?CV{`GaN|YnSe*3)g z(J#opTaOTesV1ieYzHHD+h*=53JFz&95GVlNQI_A?t;TYP$PtgJwTqh{3AmX;-y-< zxuBjZrh3CugT}XE*h66j4>)k`D~ga8@}IW?uV>q$jA!D^QZ^`#7F2$D+SBgK6lHc} zrCOlJrF$je0wY7G#y}6$$rDPZ-bL2~6Qk@rD{IEh9T~+D)K$h!#`tF|aDK$-&Yr52 zCv%SA&<9#g$?Yl4-|<8&EGduUUvwcnKp7+{g^B!V5K^)bD+b)=%q-i2pg={5gX3Jw zCY5~uK7~BkBqchW1un?MJ8+T3#W{v{o9XJ%H{D*@c@lVicOJMZ5^{w3n0fOgP@&@# z>|nk9BN^{~02wuE)6`hY4|4Ouqsj>}#n4S%LQxU}L9Xtgq-6WX#?VyNch0RAYf6ZTy;)-C@WD^j02=ZxSRi1Cz0L8KZoUL!D!{Km_v|(G2 z0kVjkI2#F#psq$2b^?K63eFyaBFc(5pg4rYc}DDFs%NO=zmTk*V} zuAs_W&bhM9fYoso*IPG)0`RXbnu8J3N@x$$3tS= z4HgoXuX`=MPRC^GeSAO|DpkLsR@?NoGqGb|hR|@r zE845J(b@s(J~WN$DKB2sGHy$ucag*Xlder6fXQ0i2X#Alz%4WxWvHM6i1ZgE8^lx3 zbeL)-irJsn!H$`Gkp!ts}+` z%D4WD^#Am_6j~bnXWv(34U6=CZ2o@eFMLxXVJ}RM1aj<50MtcF9Plb_-=TbwB*u07 z(rq7IFc#HYB?R8|8p{%^$*ei#lcz%O*;BF$eS4|(2DfCh|C;h4&*ww$#sFj;S9d=} z&!*twCLQBf5QwQHdnqcduHj90i$$Agk1WZR-+wB=a2XGEGQL}JB;I~oOujLL6t2+U z{|0Sf@EiY4!i^}konzU>v}Us?H_Q;smu9n>iCJ2Q>IeMNENTe7K7^tq-emo>IV(b@ zdP2-sTSp7cS7Bu%>kvhT5|4Rs^p`@hIeol|NUg{B_I#APJ z>TCbYFAcpX$NUIm3x#DQ7F84o*`_~pjlE3`Mus;@qb`U1i!3paXH|#gj{J%N2wJp zVSt~YlOzB*B=#f7L;o7-VV{?fyS;V`k7%o z1ry`_`WWrwYxWj}ylp;k6s}cI5cfepr~?W{=Y>H=#o+gW5y;yiM&SGWr!&z14HH)m z@Sj>l0K1_K+o9Q*=AFR!8ooPh0kb!MdrRgp0dvmcgh1ldP-G?x5%L3%Km$=LYCHU6 zvn=RewW2;4asKpy{8h05u1^mZ-REfl_S-|0<%rkre94vrC$(jh}#s>zo~=R67z2H#_k)2w(POvU2~Y4187VS zOl&UWY;*pW=Akg=s{XIvX1QlZTT=pj=)~f%LDJymPq()ctPt<)=BY9XD3G&YVidTz zJV`(bwd7+$xPkA%BH$|dp>iOcE$JM0=Vd`tfsLEI8sX16>)INZD5Z*m*ueCk?|-2| zewL|3r0%7&48L^$?z_O{>IDvwd=3d*-Q}cW8btXz!7Vy^Of63QvBhh_{bwg0{3!bE zr*G2)+n#Ew`5 zJ~Ki?ZevmiOC6pn^WSNRjc3J|dAh%?PM4YScw7pZaj`Iax_*PnoSu_&^#iH)??53* z<$LG@#3RE3q7py!01CyNku27zf$Ul@t=60izO8*$!qz?LsZQodIeuHKfAZuyME@+N zivF`}!EpgPbO5m_RQ>f_)R@;=E)p+*dx z4A+%Oz`6;ve9T>nPbx^cVtSY3s@of5gy?TRE;2I_%R9fL3i-fxBa81NA>5+Zp#(i| znDNMVv3nNF^$30Id38+nDU)6p`Y)#;*QLZ#;<5}5Cp(LJ$W=*#7-NgaVI%E$sPhNE zb|4u)NZ-JI<=DJ;zHXgExrtH`v@|mDOEtioyiJ?SkK6mt3)4^7u+bRrve?`gyD`sn zo;Gr}twp<+tCls5L81vM?=BPVcW@-1);VIo4S8)r&fYe6L((!I$pWu;KCh$y3pzEm z)|M-^5E1}GWZ+g|4_6oX3AyJ%B}bt`aPkxdN54b7=dCNRcII5Inu=foVqAaG?3ews z5T^lCi4*{ZYlmIIMhaB<+44B(7X;@+#dd=DSY6iUqz_&-LQ+0HK6PxsO>5Szr|Bo; zlngY2MGa4OQPQmn)*N&;SEm2x4Xt_o#}Wd50m!*K(tGZDWC{ENskGxs1z5a)(H{L81>U4ri6kVRro|%du|6phx61NeQ?K z%Gx9x3zRQgg?p14uD?Dih~sL-t9Xw^qj5cGK7=UYRI_sXD8*L@@{;-syt=wM(ljUm z&-W+lyvYBFw08XT>fVlF?n-9RCkGPXEi2I8H&s>D>ZwIMbXlav(_Rx=FxUzzF@UEy z(=+S66x0u$v@Iby^6xfImwwkhpRa@@=fx`^+hD=mRf;3e@JI9AL%X>>X$xXg4R3Q> zzSBgUu}t^jyKt;AENF;~)%-|)dCTl9ix1~t9hd&k1a8Om#*;+YBQ0cdNV`ypn)rcl zC75rlGF;1*w|>}dT-*fu>r6cis}kA%B75FU2Ltng5CgctvoWL9Hz_$as({|v*fF@w zUucE4yT#q=gnR%$Wb}0=xjdKt^;B+y4&&r`0JX3up1H;EP%iY+4s^Y9qcuzB_^Hqs zGAX=xXpGygr7FaAkJrk9rB;FF&e&J~DCE`=irf`I3gly>^R%3L%W)=VZM-ODW*U$A zQ54Z)i+1a5X@0P?<)uMpChdtVDeuOHi0=HNFeeRxc16kFDj3vbpxB-Zf`{F^!;gMv zqORsm6!mtb$vyGy3Ql7(C}N?OH<#1Qf7l8Yat7m=hdcZn~3(%Qz(PGtY48$R#;{ zR#~4O8m7mqH2?v)c*tR<&&~A{smRJ`2= z>}%oAs$j;P5wi_x^s;$K0Hr6ugU3|#II>kbxa+AN=1BictL6=QYWVYHbgGrd`~|U) zf)nZyy4mc0sfX$`5(I&zN&oJI)0Ge8TQ#yb2p~-z3kKEj2iaKSgC~3z;csr0Nby5w}8JyNI&MXB~VOTVfF9$)yC zv=|Y$^*3xl7}I0+Rr7?vrrCSzwT389GE8ef+XdfjqoVSyf6*x?JNMP0;iYFL-#K}2 z{8r2%`u|1wRD2R`0A?VLZEO zW9#uk(ZINH-E0(M%(6;r9i_}0seku{mT3IxG`lY`2@QC@zd7s8YS8;E z_DgC0@)+W%oql;C*EP0uj~qGk`~LL&+ij%nAuz^I%RT1!HCkuV9E6%f2K!q2c{d ze$+H=`qAv6q8yhMp{-6a&8T})=U!rE+`;4nRwiO|o44EjvI$2A0zgb0*H%vUK`2&V zP=<2!qYMPf;lO^E$ZN~RrYxLlSW<4yWj>BW<|2ZJIx))4mqkMTzq{Z#0HhFJOa(d{ z6UO;dc$auHqG~^8JSvIq>EUix;0Bs05RmX1Vr}}XRr)dOYB>PVoWKggF7uJ33XwOGVTJ|*GkPdz%W{i# zi4r~3m}U#rkJhbj>vup$F35GEVk?+v4fD$C_4_{W^JP1dP@^6kBl%7RRq!?F8{9N7 zT(PH)TB)UF{AI&%TF1eBT10>V_<;DsuKfY$b`=saKjcVwflO^Pz1^Zg^{0XmYJo%( z;h_qiY2)P*I|1_YkM=#1OO>Y?ZC5N^+GhJ3#`Z6t(^$SLziw=B4;MvSJyHZ&HReHA_70aBpwXKdwZ!Ez%$b?+^@xKTBg38DC6ttXYw?@boGs-GiAKes%%B zRy~xq(WdwWSxwX!Wjr~~)31%+MBf#}?cFw0@DHliN9bUBcLz@O_B13@qQwGeBn&nW z7P6Jhk}_E=hdBi@jg<$p-^{96XqAmcx(XlxFB@`O0rGhD@tlL?cIJ5!v$&e{752$?jr@`?qG95YqPDGRN4;xypGX z%;(HUp-J^nN6H6xsDc4X#k-j~LG57~lq5ekIoH6c7uO*jV#1xq#vH%>taJet3dA&O2ZcR59`{z@~){s3H zl^h;-Yiuh;5&cZ(3MH1wrP6yeaeA1!tp%V6;Q1$N!m!BN{`aDDxUKk4%apt_dp3vQl zmJS_d$uRU#hBFi{&}c)j)p=sX&#zg_*KBOmhKyaj-HvKt?O9R2P=*Y!O%2ULj@5i zll5&M;QG9D1NE6;Gr{q%r9QWp0w)PgUO4gFSxBU7u`#e!)H7D<){dd}6Xjp;d0iK= z?&=UF{kbt+4>HDr$NMIf>@Ru&GJg$q_-bm!zJ47qCD@C*2oYmr7Bl_eVEjT`KLsbQV!ueUJVcep{#y>a{JhMrixn$ltr zVhTE==5$s--fK{esTAAYS+T@KI1Y*P+kt099Uy!`DDT?YlTP5=dP@5c*-vZMJBH`AYj5$hPc?*Dg>L-c)L}UIgbTyPVs*eshZ+R zdX}mVy4a9~s;1c=8X7FhY#6y&BaGe2KBBz*9zMoh4<@0)VDaG1%egF^RU#vI$R6@mI|A zNby{*D+)$j&aoU)m1K2bK+f=DswiK7>dh)~(+%yk)$>hK(T2cXTTkKy6}0gH0KN`hj@OO+ zP&fg(&1?|Uvg#2z$(z3S>yKw6KB&W=Q*=L{_ZEYQHP!OcwKPtu9s~cl*ywZ8LH;h= zzW@dKnpY3?-~Tl*SHS6YRYWjy<6_}t8ADAEi#c@xhkn>fWxICv{r&zB&4aniSzLDi zna>QCxtEaYF=BC^yp=o(pz0}tthO`}b)Fjsn?z9%yq|m^U7G=|3fc-!s%>4Hb2pX0 zn#xlETAsXl7nj$$4pMp4Q$TkQmey)ilajfDDH?wH)U zt4&zFrfNYMGed=3#r;;BKY?xHJ?v@iV*W2YFzbkO3xlFB8mW*aPvdndyznhAU%Szs z>OaZxs|fNl%qrO#URDjOJKDXcH6h23rnwT(W~C}i?#?ICGU1WAKUKlg$~C`8!E(@j zq@MFd4moR-PLz)8^Ob6thWg+;iFSF;WQU`|JsX8Yg6@oJUH%=Qd(O!NJ--h zVUd%Z0B?Qu<6RFW;@AB5)ZnyM%tzD&z1tcx4aFL3A1VmWs7lk+Kx{YpEd3Ja4-N_w zDyqNx=3)inaRmIuxq-d;aZiuPj`C8EA5S``(Ke3`jy7%Or2vQ7Xkm|*(P7aC%r6v;~mIsdK;|ge-T$uikuMSe*3a==? z6rpdtHmmE>`kO@C)N+y?pjyMJxb9K)3fBj`@i}jZNRiqU!k8<%;RpJ5SKObPc-sFH&y0=>cEd5kSlfB zUK!lM8|^;C&0I=my2yKBSa;H^r!!E$ax#Ikz5P5{I;5xj>f@sy7$_p7!_}y4jf!lI z$|h-mVi7|X5_T~3S&C7%(DoD|TaDpLA_;8Wv#zad?SF3>|O@&;Zmw#>X z7kHbJgl`}dDVm%KrfT}$`n%7@OMqleR)6b!X|5$eT+*0%9)XFK)K^0thmIj6lqy~0 z=*hrZz;5V7hc52tmxvZOf%M7xi-__5msYu~Q3Ca*{a zmbksr-+zzu4_i$glD`Z7s(N2()5f4ISBn5AXXaWjbB%A{fOaj7&b1lSGXhs9MwTQwInVOYZ}0ypBLSuHS^R)r-yhCkY!iiW zxME{!tBZetCI2}w2)QC$5fX=BU3t<{x9VuB zHUE9#1O6lDYznK-{}P!T*yrRWYa~aznax;-#7{jj{$cDl!2OTV_N`qNv5yjRm<^4> z=|z*3ig$IcugDuzcCXY+_#At$x}@Jz7_fXR2R}YHF}Kb~N8*#-Yoo_Mn4rR%auoV2`q6!O#UV0m8pZ#oT&6I{Uk>J#%a?WWxjm$ZYik;;Kf zBA{aqv%XQKfM)sTaHpS+7OJ%cw{lEzU%ypLMujF4RHLkvId1;W$UrsV6dOwdiDM;# zOc#xWYHdy029f5mXan3L_!f`dj(PI?Y*q1fw!ME%&o{K;Cr!MMHmQQF^{>~puD>gR zpgn)SW=~?lVCJ~fz87Hf|L$4U8kNe5zE&Pb!oFhEIB3%~;Q;&mASTqigG?3J*!u=D z)WDRnj{LR#vOP`le%P&H`?iBuQs(41Vfy1%xF-T3&KnGl_`ofT+#(MlJ`LnYZQ`5Y z*%gQsoAv`5Iypc<<|8RCrCB`r&%2oFeNw`F7r_YStejYy&}A!+jONn|`y>R;008?r zYwa86R9@pvC5U~HLYNUFuw(r}4h@ZyIEmnS0yEi~HgHd%hPVbSgk(yrDzC7@UY8+iUSgAT0Jf(rI&A z73PqbHU_zZ&G$JN|F}jTm~Q3jb5bG9Zr_k*VfYufqnFM%Pzh7vy`z}FlrZp{^g|nE zS>ZDsVvc^AgU3h<@yV>?Qqc6zKxS??SD$#l=9pF>90kQbO{U2K1TQV`kof@Zd4P|y z51J%2Dl54%DT$gyL8xB|B$Eo+LTLR9i4vs<{*foINQtg@x`;^viNY< zS!EHqQx5Wck1x5UyTIdeU8qWCQd(P5z^ZOx^VF`t^A zhj)B`qBq5wlzRP^#nwCxYI;WeNCWfsy4CRVeKl+9MstOb+1J7y9r0gX=3%6bW$R0{ zuFV-kDVVG#G0V|l|2H~#=YsT+gxvsDXW#wNb7tiK;{pt6$kg^_zoT{!`XC1GV+|gx z>*1A~gCKj>1va0aGnxAqUe9%82}Qdj@~p>pTc{Wvh9%e)!MxnCMVHri{kFBce};hX z?e;f@nj**vqlq&ECUHVmyt^^GrLu|ll8FGD*@ z7J`-XJ(^T(*=K}3P_}e)`-E^{R-U?{g+(H{;Bly8YrGbGjAWN~w#DCE*0;H>N^WB5 zAQm^k?9&GizP-SmNS8{D)Oz636o#0l4vTaFi#He76W+E2%!t1)9V1kg#>aOw%7iu9a~mQ0dzocY=c}?K;m=iGW41ZbybOE>Qz8|QK-2HyimN_GnmfZix zO9Xa1J+oE(q8X>ij~WOI+j!S-nHJBjfLso1a#1?QSDX5n3d~-nmDMF@JZ*Zt<&*o- ztxHC2opL;uBc!``jh2`1?g@Q+5UI?(kI}!6W&m8~TTi|FqVYV)I8WFnh;mbF(!71~ zyDInhtJ-VEepuSnmY#T|L~KJX7nQZW4jaR8s?}2*mgWS%U_fW&d6jbw?n6UYCD+7W z4}3CU`%p~H+V+Ty26puAAWBa1QrK`pNQ!f*?*$54gKK=0E3O5yvaejvo~3-=v?jKz zJtRf@5L3#R&emik%HG19jDt~pxyfn6YkZ3&4FvAk>?=)3=^7eIJVUOSDVIQFckS4N zqlxcAkZ+%blx}UmAL8nRE@W$r6QfBNrW8%lJ(FJ>UwVyC!It(7v2sJn3&liVRV?QI=Th?ZV>$#Di*4x2*ti8~; zJ~K%6qOx)b@?SLLp(#=lTYnb))uI-Hl84E17X=9Qm+5W>EbquY2=4Ue==z zd)AAEUH+tJqf;xpx@>2CN2-13oAmq)!lR~6liH1UGcA|ssJlLk{6w_}z?FR1V%}Me zD@q%bd|@ovre{tQn_zyv*QN)|QGKY6QcQ6u+ z_AB#bADG1jFI0|?)Y`7-95vFqQ_Ntus%BO7!>2~J{_prtcI%|Bd2k&4m<*ZV$b=Bs z@-PuQz*NYhVQ>DRt~d12f?eMBmz7S1<~dje$FvSs=MG9qRXz=4?DSrDxk%x3S-t43 zk6UV))=;NN22(pKXl>@&*coua%jV5(N^$qt4>#r4wd}1g5Pez_;g;}PquhM3t^Z=R zq;CCU$)dOgS7O{L_DCBdHH2Shqx-B56LOy*_VMV;0JiHP&ls5{)w|U%pQ}u&aTMm#uwm)4c%))6KEU0>Eg5^JQ5qV(d{7>t5P}%LOsR-%Y;H{Go5yvl+AcI zI4IJE{48@U3rA7!?ygY9UXaYN*^|k8@A0gwIa(RvPd7K6ur+W@5ecl_vAc-cnac!WtwS&N$&d4ii3KV~U*bLZI)rEyD|EckQgY<)_?BF3^N%!q?O@){e^|boxk;>8JFj<@eUItr zO++G#>t5>S4YfuzAtnh2$UPsp=1{1fC0u2rM00;sy}d+@jB3GQ_fA?g?=4Y6TV_YB zPl9lkNAk69#D0%c3c)e!k_VFJ#cwwH!qw;Wgz^EHEE3v>F?waTro<=8YME8^`1W~uGZ;iT0r|bWKgKN^@1D5 zpkG$tJ#yEgJ@O)n2i;GXYF#lld)--*Y~8QEKgn(NAwgc8Q*OHYW7R{F-RP~4ANx`( zRl9OCkcTm1jRkh&>*jSbqk9h=Ke5Yq=tY+PqWwM%#UoK-NX3NW`lsUe4icJgk^9@m z^mr-PzbnhXUQm|i^ln3-@7DSuedbk*^*;1S?mSAz5nW-4o+DPH6E0KTyW>ET>EmquTOvzK}&*(vXl2kdZ37yHsK(;{hwO&Fx6vIPX zo>?WoGFIW|-Q>gg`e<~799!nWmDiWs~#v-e{*zK*-$}>Y*d>1G4 zfpwZ@l}*S?TFIhPwT(}NZe1w6vR;otkLDTW`Qni;2h0cM6T+4^t+>=Gk(RM6jQI*L zb4X_a$FeovnQaaneo~jxE>H$ zH_!2$smcqAB>~si&t5?&kQ;oWG(x<%gx*BH3CqBxQ`WK%xoW7-b7Amqng~M86Jy#J zXDirSUf%J+DLvA$)6&z}vf&`>;l}RZ>&ot{j-&{D>22d>dm7GCZhd0yDt*N6{8Jnd?5}3O%=tkx}QtSqFw^gqI)AG(_+;jCP=RG`=AV*W0b1dZVno zu%l?Z0@*wtmq(H(*qA6hx8p_HooV-=^Jq&P9kV16JQlUvO68AkQ^c=Z=N4JAoL{0p z+{a4ODdST;KZjX@f7+%83*sdB<@jALU(vjwe7|5@k+kHFG|pR&0ctk;4T8!S(_LqJ zYr2MD2%B};jFkO>U0!W*X->AOSkA@%Zv9Vpn}k1qyyYad%f9r3uL*d|2<|sMdX8;w zBW|ngR#?2mL_R;aF`%3O;?X?gk$cj5OWm2sJgZ}LKUSRm?7r!xrPrVfn-I$_RVBQ$ zrD)%N3Wg?iXU{d^T_KKmZsE6E4tw;weW<71(cu^3EgnzFF0t0J<-MOY|ND{FP36aG zLK25c$V@N3=;&E@(S1WEZTM~Eu_E#o)IddGFhxVCPfg$T-i!CFM&*a2t;F1&PTi0S zOgM}?D5vLURi$(BeowRO=)1-R%dRi`Qp%iAC%~4*P}2Rz+s39;&LFNgUG&tEgyRG0 zizzIfKP)8UH#`22`cj+5;ipU<%D^7Lb#+-!oWg;^|s(=eNWDD+~-b~P$Ltd8+B^Exo==ZH^C#dkLn?n zGuFPN#KK!zr_LbByO>ef`(xZ;!$*zFxJSRpD;wYDj*+ZmG~y=9Rn{@3@={hfH1ByU zvm}K^vCdnivh5-vrf=yh=}zEp88Of#)whR`^Y0rTl1dEQ7W}~Lk(rs8>D$`RC%sh7 z8hM)X(iwYG@2kpgyJNEZ6SI7|m`$uwAPcq1Ca+R7I%K0z=ZF&Kv;@XIs54G6({YD+ zcT$yR?-8e$GMAON@s?>fuvfYV-0Z42UvJ8lDOnLyDCOw7nn9;#y-jLr%RXW0<<>lDztWc9%Aoo#qBs$w=d2TMmcjS&YQA$DU~ZU+*1D0GZDK*Y3Z*| z_n&3kLC7lG8(myhkX@daqx!ljLhHopoht_e6JlOb9V+d9ERISug#_dDby2U$o7=5~=gd>O&ekIgf;tR^%yM=9BF{_j@?BnOg!vTba_| zJv08iWC=B|N~Fe+P_jVH`xEOqUq?m?S(9S+VD@^= zfy^crMqi<*dHUY>&MU?q#%n^vLdHJ!@P?1)?m07kViP%wpT`hyT*qHr%xzJhWM$9Q z^hU&JC@{fwmFX~}<@_|;TUPEd#Rezmd&{Au@U?VWp*|7^cO^L+4L#_0dosG^X-mxq z-y2$5%grAiM7y1NH>$LPN%OPKM~^rgo!~4lZgE7R`CtQDePcQk!jR*PM zLh{cU)dGW>MwGPL2h<{83=oJ5}aq3 zvJFZ--nq?^znI?zE2qXIux?oe*O3clhwV3{-iT=a@YXz^hOe%>=YfxQ!r|3z($qcr zH{XZ}zV)3?1hM`XT??C5vD-=-ld zzOPE+O7M$@W~%G>(8{4A51FW=&)cPa&$_S*<3kZ-{M@Wueu)+Rc7?&)ZPXB9S3CT8 zcf>8q%&$FtL4PMC4mtO!b@TSPMRx@aEoSunjWek=;J&2K29IiNby?m95`n9L4 za7Nt^FR@&{PF8*;Gw<9hN|BBtGKG@HP7)cdA z>MEg^uQo`lD?)Qp#v@pEi}_2!%R3#&g6?RxVDtUW&lc6Go+GFVTKI9f-&rC5E{Mr# z*O#l7JKUMJTw%@WR(Rt?nbsBbCCFN>Wt(d*8AnGB_p&}?CKdH)57)IL_bVz3- zuT`^3l=`8g+iJcj-?V?8FA^`kvJ`vo#!kF8^8$X0UOTtJJx0$)>_oQTAd6xj*teA% zeRGZKIw!;UT5gt-6-HMv>em(?JY=}JSG%iKYhJiSTGhtJdJ&^dw97?08g8*_wcmUZ z&-T%`{HUZQeb>j^ksCCOT21$p6|4y5(n|>DOcGmq$R|9u8ezTD;JlrVy$aoY>j){q zO>q&2ESIF=KZv>3OLGZv@m9Yom0X!2eE@TU72_;&SY(SX-h2ItO~%%o)vpr_I&S$M ze0l1I636ja$&E{14tZUExA9WgUd|OL#5EJTQXa3oc|HV`WGp7MUUPulSEy?1ZqI14 zO?Nlf>OL!8Ro;<$A`TL&1lo!BV;5#h#+1~sOKMm3$~RtTZ*c9EU}M}Y$kO05b)>Z4A@AmcU*wP2n>-66H&n4OzN zK4c3s-SP{|v=jZrO;4*MT>q{}EUhZTBuVbbIk#2eYj`+1iVnOPRVTAM7yj8@Z18UK zvQk_9vu{~-)hy?C~aVxf7yxgQ|-s6 z5_x68!aYq)(o$~x_Z5!|mb-px9Wd@Ij>TIte=gp8#N02|koCR&AwC)|vTe_FG-<4C zed{pi&NMKu?f9U!WP{Sl&#U%S7Kamx{b`rKXgrF$d5kQUdc}?wzRjDF4W_0Ig!Izr zg*2D55&4d&W;ZH@sz;))Z$CR1>b>U6J(e9Wq++xnN2T`0;Jfm*X%UNL9=zg4aLr%K zof8gXRzd_%YiHv-8E;g}jd=#jC7M=6t;;xdR?%pi>}oN0=K0tuke;0|{G^_TmB&J} zWJAsU-7Qkeo!efv9|{;iTk)O@Xlqv!QGSqkoGnNFLD2bYX+l4)l51#TOKD zaRung1%~ase_ozld~=nyRdUc}b5t~10iC)Lt8u_$6Qys#b#Xs`;j_tBeT!D6v$$IA z+R@Q@YroP7`|LhKj&-8!GeH}j6(SdQmE1XEuPYRubFIh{-xta(7&@{oqBvc$(&TR2 zjsp@7+@_s}5m$MX+ecCRWXUl3(nUL+(Z(0HZs2K16n@%|efHdJr@*F)_JgNuiue0$ zw0LaLt!)%G)YE@VJ$7RubM!H}`Xjn`*f=4 zzHMUePh~J?)>2>8Guy76s-SeH^rG_VFFV8r*Sq5-_?86BFI~@6M*IB5mbEo8@(Wxw zutJ$KiisuNcOZEo&0DEdv=tQ_?`_|BGNF(+D4P*QekX2(zh$N-qLDjJt^R=C3iRIg zu=5*j`ok}OlDa0kaQWID#fDd+Yhtn_qUiNhMzp~mLCbXWfW(zJSHqiC9fEY(mwaS) z_f(~qI?=FEEHyv>d3nD72FaB>D7B?xvA^DN8+_)B2`yk=sKIIuYK>Z)P@Co3`VSX>(uMjgSFZ;-#)lR zuBOzH7q5|OY@2_@KQVE;cIh_TGq=smA099H!0XHQz`@zl@&js(^|Ljsi$joi9Wq&) z5yh$dYP&`&b66<%zbyZ>k>jKX;vS+nz7TeC-7YtC%g^&*nIeY3)89<)Kn zgP=DwrjPHC1&S+XW;-sE)%SGQr6GSArMEOzrd;cFvBk$!A?{|gT7PyWF$RRx4YJ1) z6bt>oL^UbL8@|0#8`@_}yM|&_O{pR`nRHDcH{~7mX0ttBh#R??u5l%DLQmNYH@@!b(0}0*R*x%u!<6l>cHo`#X`^NQA4SF4t?f2DQmuBc&D}vm z_z)kR^J<-E*KTWOE%EN-xKmfWF-c!v)@Q-!VcX+FJGbl2z z=`zgKruh|q&kkD1+uA=hH8cshe13^`&0VLLG$Ux?m}N=bW~UYL2_HV0AlSMzXbo74 z8c!6Q+fK$PzDcy0B4pd5BJ#V!SdL3;`2reP&E7QEEtp4rlK#R=SM4u+Wjg+!mCJ%d zk_T(@r8m0S*FU2=P%Xx`;={W51ZfRZ#~e-NGMj3TL;CuwT)aA{*V1(?P-nj;bDKI&LaRn#>q9Qw$7+JQ{*x$r``l+nVTRtw^N07Z zuBGPJJmCE{huh5SU`=^QjaM&uOfIUhE>YLBsF-D$X|demG7)4e#jQtbytsg)WJ0Fb zZ_%q|7hwu8dlW(|Ld|wC_E=V2zW3=dWOXUFqflU1w9YV;9!U|g+*Y&L({91CCffct zH|Ym2}5T-m!@u6Y|g5so@->EiM-sEh5^Z9|>lt=@Nesh4^vRA-hH?L6Qon=`;! z%$yOWCAw@_=DC}$^!qF+lRNeAgwsaQ$Ucfg3lK6An?4@UExyp$w|HgyS$*?rTSSL{ zL}1zd6c;I8$@wC$Vs)pZ6b;`o!Ll3&d&B0pWqXJALSPtiRZStm9DVEvGEo= z=GpF{>!uC(_Z7MfgF#H{dlyp1#$$&$IVIw5Na8+zkZ-E9I;#<&DVEyW*5D6&>x1Y< zC2lv%82a43dvEnADd20?pD%iyZ6nEh@-=P0shm*BX4!=7W!>tk_suykKBy{US#sFW z){C;i-cwu8V2={Mv!u}DyqS|gE8T5NR9es}|KZAuY_W75XH^b&rfsuA2bcEgDEhI# zr!JOuv0iE7=!!}$ICS2-J;|pyS@=P_l)^sqJsobc^3CTHM+mS>V-0F|uWIc$;o2W6 z*uAd2-gbGK``K$^jLC}*#a0v)mo+YKNbtC@HIx(4T!XPWb&l6{VWuZe$G=vo?9 z<|J+Jb+}Y&{ivJ$U71U?C`0v*x3EmT zc~#PRY0sy2Dv!X>@G>^N^ehXy`qO2T9gb@^Gua@>)NGHpZK@4k><4=A;r3FxeopdA zv$IO(ajbUqLQT1Lx2#b7x4S~UEm@V(YcKdEyvz+8=H3&`W=vlxXn6Tt+r=vJCg0~Y zAKD)s4ZfU~dR_0XgYv>${Obe!OV?%(?u{Nj`Dr26c6aitM{+rja~$qJT56QxggJ_- z6sI!W=&UumzRNNkXyIJ(ngPVqH9=F6cpFXifqp9o^ zTbYH$K>IMmhnf*rWKijQ#>68wRdFHP+}}i$Z&ol7yh$e~U+irC(Qls9lU>_;?6gA! z)b{Nz-gNg+xylrj0u5(F7W@F(gi|U@MuMEf?+MBl~zb`a5)~Jb!Y9#k;_qDe1dR~P| z?I#ykl7B+!?X#^VR543$@Hjtr@)yrO<4f%e3ZF`7rR&%^v^1kvC?~sCn!H}}`c~PA zeMg4A1bUpTZ$4VGaef({-+3fPI;>RWq{YJ{`y96&nYW8_5qa^IG?nm&G4{*>FgP zBl^W-yqjaI{fR_HypzA7HxpHVcPgr@%l9ORDdyzhcffLmpkMX# zb&Fo^cg&a5ess?A%e$wMn&wvT8_ypz3*k%muv&F2tk02cXJ%a-F7)OhT}~J z;w=aSy)p5OfE5o19M*j-U+a|kuFG>{_S55v_e`umjBZw`bu#E{c3HDi+RTd}f7dvp ziHCF9j%N?B%RSA>>(zpn96o2M*iUYrcF<0)WbCE?L0XyNGQC0 zTPHm%m$7O1a%FaTRlbRJD@L4AwA9*>Dg9%^l>l94eI@hQ?$^EAqnV$=Iq@@|}MM$cY7|<_OkMa|_bj-p>{eI(%teVpj4M#T+ zLg^XQxU6p=p5eVgNQ=W^dsyhwxjU>virPLYMJ_;NW{>WJLxp4VmXjF-`+r75I} z_T{?rJKefJvTr*JZ`@*uQmaojCVN|IZa!I=bCXOaU%%9!Tj#p}Yo9Ix@2G(@_ot)k z9|>R$ZEQwWuLKZEBz_>czD4k`aT)m5WU$70YgA?l$q!tUB@WygQQllhq0;-2UVh zdqf})+dE`r3ylo^wxGpgBdqFtjYeUACUyP}Ws3a=4jT<{H0B%^On%K&}BEB^>Pr)p-3%&A5972m zs`3N|m9Kf|Ryz6CF;HB3Pg7rig#Fp-=8uVxNWGWFc?(PE`&4X>-0Re(iYc)LXIWo7 zsFdW=6Vem0WW8r{vGmacr%PwAAEskS{M?Jt63NU9jCaG8q#ds)(c_WOv+HwRhHfo_0Xth<3`CEoko|>)4=u9pwlsTu> zEZFHjSn#Q}bNMs>w!9mQ8Wu!|a5^*Ivzm{yHES%Db~dC-j&XaC+8!*^R}fLxeH~BO zE~hzyPV+Z=($l_rL|=J*)I}I^Q|eWp3WP2 zE3K#H%)dqJ;PMp<$Rm&poe4cf^FPH6pNW2OOuCzKL1{ko?&rmPO?k^`>@Ue9FoSU} z@e2dDxGnCGbuk@EU%N8RO74CjYq^n!xms`Az8uZ<_x}`ihw+qyJ?ChM`-36ETP=Q5I|NEg z^_{b&39oRbfC1Tov>-nOgXdC3_hrbIs4|X5!;#Nt(tX}LUGCuQeqH7&@yOItd$Bd; z9i3xh3HOcZlZPKKv>o^?(}f<4t6c8 zv-WqQ)}-Nms=HgzZK@d=impk9iF;4p)#<&m{ec$sptwkf3QquMNJvS+%JQLyJH9At zw(U5wd6jRn%CofyreekgKAkn}_m&NA6&t)~UpvT7mYH>+`ik|s`ADgvvdxx(yF+%k zhMQaKc#=!-ngs4`VQjPOck2lWsXOD?iu|xV&GkX{gL`f|PWP)#?$|_D@gAef%Bs6` znOFQFYW1ZIkkA4`LbkNa=I|eW*?YN?yg!)ENXhe|!WRh{)MmBQ?H>xY7tt~IVplhf zhWlzA_loO6KqtF}UAfmMn%4&MdsSKXNTit2$1BF#?p<#fsLM}L+r_5^`)1VTiHr_f zHFTfashqKEIpJwUUuZZe**9e92UKWytd=%CpH+IjH-9K2)1WEfrDNkUyN>cZhBua4crCzlB;e(#rs!Yy-#mAF_oXWWmBZCn89k`Q)D(qUwyqTN=mu4({ zqFNs?--LGi<+Cw2ZW_f3cd&0fl8e5_-TaH$0yMV;B}^_}-A^nlP99vsZ{?9OTN%G1B2nD{`#*+)j9et&+83mj(^! zhooKGj_BlhcV5BlzZkdHmn^jV+A?-(HLeX3ku9mPsxExq;Bi||VvEZo%>!QET`Y)A zON7HNB{V){^$TO@5?@XCM2Vb`!>Pl@>s;L^-4MYV$hw@vQ{Pj9R)Xwqfk(u~ynf0K z4ks$I(_BgmFPuT>nT8#|&7{+))$xeFdoPT^wd0P_qPRt#yyIa(&&lAqmJ8esOa*FRr(D}9o#l7x# z$#f3A0{@$6W6YBkw}n|P4nGn*sJnrt;`Ftun5JOK`d!-gS-N6EBD5A%IzxO_x&n9K?3jWGo2{F4 zI?nQLz1#Hm9&4DA^nBBpOezFD&3y&E^ox(mirr2oY7Nu+a^J02KQ^#yZD)_Tc20=> zEB^ye?=PYD8)Ys{YBA!uTICCeP$5>YG+u$!wLUEl_nyQDWW;|$#Jhj&ukVcqbzg-G zJ)y{o9-nVakIPy{k4=@M$0P{Rqhr|V(UA=FC?q+3Pbh-E=LLcu6-q{riQ}awyxKzh zvz6?tK1ts9<^+B3Ye#xQ!A5#~&PsY*hB`eaX%UpoO^=SA4>T$0QDHzc?_@H znruwG$S5XGU=$t031zVX-T6S1ew0`?3Q6??%`y7$9O!;Qg;6G8Q#JQbrin=uXNIzj zQ4!4d(NSz|W93Y!_rx}!!lA6ldGDZJQJ4hrUFaBI@cjPY`9sGoWI{zP*oKZ|%|;>V zhsW9;4jBH{57`&MHUgKqTx7iG@Y$=_FiFyT&=KJkZfkVd%c#YE|UeVD! z;wU83ZJ>wwDSE^)5>C|xV;~flrN6wl;t1u$^8cIeaoMXDpdwk_P~kMKKh+nr9Un-# zc1*mu#dzNUe_=+)3LQg5Fm?Yqy2Lio$c@v)#EEjFBUl~-kNW)E#x;?*#c=0glNC4h zmbuJotr@S+|MeA0NhAj2d`>_LT7_xw4U6vXwg9vv@g2jhTcysiI<*S=~`j-F7e3os9c|6G6kYwZXn zOGbs$f`t%)z^1D+VB!})hxz{d=HC=!t|yed5EaP=sR)R?o{JI{$>R4_F8ST-*;M6` zfo#Iog^m+GPI$GQmsp4Yq(4H5y&xt|xb~0ZPQ)dWrG-$uQ;67pOoAv2$XV%Qvf&h% z{l5Cpbm6k}=2YVVo<%Sbps(-1{y~gbpa1K>o)8LJbQJsR-`CgE zw3~=8k{$~(79X+Ay(PAs=t#CdPdAJl_6Wi&$NL!*!x!<7w3XC&M$!yoVi(5Xa#kyk z*X^Ia_LkZ+p`#XL{`vkO?)@l7U@bHuwvkY@l>!w;9sK9%62~0VrSX2n#EV`+g+X0N zVe`ZH#QKvD!Wz{(_6a3}wd!ZenPP5)!QL3jbc^t62T_IdkN&~F>*Cz|f^rD<_aGlq z`lDm{{IQ8r{@4^{e_Z-9e@vq6{+?$DgP!N`2|*Y@Um1Yj#{eD0WPpz4UPf$OZ6iZM2xgCDxz^Du%H+hjvZEe~jl)!oE?!51S$n z33&eOAIL!Qv+NJx+gz!rh$hl$~T2s+MU*sDb1vX_IF{rf+B z!B%oiijpxpa(*K4ouS#aajG#u4tqxKP{Jz=o06^UnllGv_DSZ z;Y>7s3=!p5CXPem0sqaBI8xH%#G2V-g;TQe7Pxw*ZLPxSZ zo=ju%|FNlX%yk$UNu37F+sfWzn=u{-y3YlmpD3NKu27$l$#karKjVDI4z`ySME zWljH_Yy@PhBU6?8E6@6?PjZ0Gh4i=#qb`8?_SU`i;p0M|S4>Iu_WCZHZV2c{tFVED*xqOFB5ME1qEhMf%oNMM%E~(#Q5+!v=wjFpY zthbwqWr2*%3Ulz~%-B(kf{l6kOteAfC(Y}S^vJ%dV`FPNHcgqhZDwL85_*ycI%|V!pz351$Uhp&Mh4%NNB51$r7{bWEu31ER zBTZ#Sd1Eg0`!W^1NwzIw{`I+(Ih)^tO#6rwU69iPCzb{C-@O;Z6HAINCPDJW#CPU;kIy%w?JeE=-QH$m8Tj1Q%3!Ai9Rt@LOrp%K ziQoOoeSfyf(;3@FoDXq4zg_MCS-NIMx-h4(X&T?P6CFu^hSWPSf2PGBbWn7pcscNx zQEamK_xMLFa3Mt(WzBjrS_Zf~4^eL;O1a zDH7k|4{PLBQuu>D>ZkC>=WnC~y`>81jND-zIEjwsErzwE7{)2+kI0Kr5sbx{xP^JJ zKDc936s-UY{_+0!9oJdS|9HONkAJKHN&LYVGT*AM~&@&k^8NAkWSke?rktGHiyf5h{}XF5ucUwkDC1F^Mw9*l(!uuy4pg$1gsN z%hdg4+i_o(@-|l z`OkJDUC{ZET>sJG3C zzRhLAD@!r(Ssj>>E?|L85}#52$K|bC4E`ojpnLi#nV}-bH31V^d3;Uoi}C+sGgLWYE_P4dC!p_+;d|Xzd1%a53qFXT_oE`|4~CB6 zY8@|sx+^YAOJzp=ZzyFr=#goN@8dEw6+z|z8#4)9A{a}i^ZN%pHUYhv`3I@;=kh(0 zp^Q*ysXATzr+SW#;)o*IM(}UI#0mvW_5RO3gLUOcxhjmR7loYX*;`^apI9!TXooN= zlDUOs{eTC9fB1|%c)XsVSD8Zgr2GtarMu7Hb z{hL1l#GZ%%->;q(-Ef*-@L&4ow>(Z4c$OLP)w!?bbD8Ti{1$jv9ih--)??tSEtLeH zBhm{)fc>908WqJhqn%~EAAaTv_7N)Bb8LWp4rzJsUwr_3gEK!vYw|mwm$HDhW|sc$ zu;xb0KQXxs@I?|t6MY+Jz~{&Bh-ERL(Q!i65D#}270GZ8?8WCm=XVWs05BFQdVhRp zX1steAt%>uy8naBs{%CoXHSFrBRYchC@yQ|v^vY_XifJVm#r@cwzlmkBx4EiqdCkS z+6rvC>Q9eHu-}V=FSZjZj0$+o?D;i1g7H1bF=MvL@iu`UD-9-=5A-!NV>9u4n5(rA z*JcWS&|Kr?OuoYR3t?{y@kq2i6W^WjK8$P#a9uaKtm*y_za^~;nA|QT%@^R6u%1#~ zg>ios_}*1ag4k7b9RF3AlUKpca}{hDR{_tfz&ox&%;#0$>0du#)VCq7s|6h=*aCYj z_|THH%m2{cPy`0{zZ%3gfiKX!G1@>EWCoy1a}{Dff1nHU_*2m709~Q4bi=7h%9cOS z4W}G{wQA|)zQm=eYlBW_)?*jsjbgAdey7L5=dag7$MI@mlO?o3{t=u=1Dhnr4)glO zn9gW27E}J8rfTB*Gu`70w=hFoLjI3!o&8w;N8g1#4~$vo@iu`B0sQixs4LOVyx4u5 z?!+s^%29wWPz~Z@pG-Rr#_CAjiHTq61+w~>oC|v^T2vVACE)q6-yn`3BL5@mDk(u8 zrh5r?;-kG4Zew~$LWvy}E`8~(ud%cw+6%P6hCXYV4R|0U#9h7lM{%5T+>&k=I-1WN z*2ghBJtj$FJM`7u`Nv#o6USU6Ap%&tPq4xCQ$M(TQULpSxi(?3r9a z#ta8pGaTk@IK)6^us(^qkj91|;eYBu8? z^r(gSf=wiSSBd2k{~#tw1bl3pNY{r1Msw}k+5R5vM87vapARIXd1CMBjb-NOTmZIwole$^M4%LJjd2c3^Xywf_e?l0AByKKQ#b z0KZQ9H~sN?Ou9nM3*;z}7=Ldz*S@NwTwss70etA6-@ktA&q?hdK1VV@EHI7McpGrp z1{Q$9PpscllsyC8o=~znu)CX$(*=JhEf{lC%VI=b9Fn#O)+SHzZ&yUeaAUl9B11)L4lJDw7^#5Z2&i@OOC?-9deI~>$oFA|IT&|!m-3{wcJIOKj zT|MX4x3epod;k^2@dB5%N_f2f*c7FGFn37nEqaaX5*HYA=vW@z@wWWT6=YCW@VmEzu?+e}nh}yT zr|j1ma3ao&vH3zb0DL|l=G{?POTXiH*ioi_dh)){J8Z_p&`hNPc%3{7n7Z0<1$iv(aD z+XHg12aLaLkZ-dg_s_pSp>R+9guy4A0`kSsWP{B&8+cVV*lDsMb|xF*y|aO*!BzYj zyz7FA<#vJjwi0})zuON_tPkiwxG_nxmM{-}U>^+SlmDA0k^Xmd;kz-qj6jzq8`f-~ zD*$|6Wym(%qC@F6O6BSKJG zL^v*4V8#zVQ#l-$Byg1Y4lY@6Ti@#)>oHLj^u2`}eoFo&)n2d}DZ&0K8g$@4>Dxo} z-Jn6CLTOKf|L3$e0Fw2H<~&IXe^?Jk!G~0tR9XMj_n)hekO8jjy?MF@*aVKF*f^F% zY#eJnHimu_@cWG)h?_`QFbriT!kBmjW5^DlCN9`l{QKf+!B*|A{XLd-cA&E5s$CJk0(A@GY0`1I-Jzp2_m>KWuemB7UFXo0VR z7WlG{eL!B-!lp=TLH>woWld7$&h$O#19M68fC#3J-g0N!nacSS-{I4wcyUSmhXL;r zOf<;0zjc1k0r%3^|fA_vAhz6kBs*6J=DuKJ$Y=$mHMtssDp* z!Jc$38cL$Ss`j6%!{l;t$wI`r{s18*4Jw3UPe%Jr;g=V~76&Op|fbAg3Iz@ua5JT67PZ6GV&I`x`be<&tg?$?6IKJf^ovQwjdtfn) zAuSY=bu~6w9(*enT?L=%s}SGn26h=OT!x0iRPPa=f!{?K$vT8n-kI+GUwsZT9UI_y z4tRX;OnnS}I{@=L4wuAZ2mE0{f8{>9U!^_U_X%05Z1|ML+AuD>Vcb;%kC|aT8XE@{ z_%um%QsraQ)s_%*PS4!`)FY@D)MNRtCYA^KRSnQ-`2&7L*%9Qpsbt6Ty(Z-#*cH0K z&ny%b!L}Wrzv|zsD?pS^yshLKkb^(S8<38VgVq&XB?+VS0qC%E?w&z z_I;JdSwR2k4*P%)B=tAbl}UUaM*SIl0>;*KY>Mg{Qo1Qz;zHa^eKObY`bw?I2sz7k zK;N}Z-G5|5xRiyNggm`pvO&zXUO)4D7{A+qFLh5hhGFc!0y*}(7{Q5jKn6Gdq5s1W z!{BH99Ab3F;zaNTn<>C=Z3FBT-%dMLX2N^&ccF+;Lh&vUVm(kHw2zS*KD zwgVF{$OJa|3ATOU;~^B^{M4}NOXtIUkAeA4Vx5>7!|%oo(Z(FjI<{wl*aedOA7T)r zCf4~^?qRJr!A4X6hkL|a?Us@a1cark?ci2vvyHkA!cxDm`%*cs-0d}rg$bVG7MgGHQNXx+- z>6<)9u+g-&Am1(ili2)|W#f|gPJuj2)E|B~&Ovtk7Na<^KG-BtVw@S#U+F&V_b7?q zVKcN>fM4?O_fvxyBe0{I6KOz8zy=`w%laRDs^}gJQCBiyPQZBR#>KNu8w)#L&;QmdHjddA^cml^ z8OB5-@PQxpGK6BQF`o@g!oqnV6Oy#e{lM3WG92;jkO=1Y9a36S0T;5m@h6{ND1rO z(@E_fg}v;Q@c_g!!9GYb*TqE4U{4Piu|C5jNt!_%2s^|Dee?anWv^g|yyfCxBR&NA z;&&76&_o*G|IP{d5y~c(Gu=JJ9sIKW2V`G8822L+`vG(~ImEjBum6Gmc?s~j?|DEB zZT3{{hCQA%*nDQ`FG%F&^I^?cDb`!MXVyATu;~j!%v|Gicz^$#3J3l~zbu#G5_xSW zVo#KhvGFX1Q}x0B;xk+VSHZ;ggA6n? zbJ|NloJ1QwOLtnm)j0jxT%qlg@JFC$^WOi;Z@@l95`T33!aLvd-l_CC5Kk7%cY)Y1 za4rEkx?1A?a&vT5O ziVtC9dH&*0?4+Ok!)N|p{E5}TBun#yZub-#&N&1BFYp1QhIp=VUH?R0Kb9AU05W_t z>Mh^@ovvx(`|vx-spkB|G4L1vi9I&=`+oxex!3CJ8!-R>;{UUq zH_P|_;{X4<{`WVm|0MYTEQjI`Kl?lXf9R6m{@_o}f3Wk&LXO>S;LGC=K0^=>z?lZQ zGP)pks0(5V?}9ymc-HkLh~+ovEq5KuA2_jrf7kzsz42%6e>eV+w^S4r#SZyO=^H@C zn}yAgxE}z&VkAvJ*b0*%7uH@lOK41&1oGm1$Q|+g-+n?9_YPn0|H^;AHZFcA{$T&J z1&_R+kxbaxxwfl*8m&jrGUK7|JEh_ z#s3fEkEEJ~{+DV@|AS3h@|)-X#JNtL3jX2q;4BEx1#`rVkAc7X|38aACZ3P9{V$xR zkWjLV3UbD8{Js2tBL2jCx*}}4CMW2M>0mGMnz$C=5=5GRr3e1ne`enQLk|D7r0oB2 zE&}*}2y%jNh9KH$TRCb8*CsR=ON_dB8S|!x}ewl=C3uezo)y$W$1`NK75Gx{kLpCBh4_HR9uGI z4`XV&@~3}lC-&f&Al1F1xRfw^mS0FF>JN!#C zaYEtdc_7=*;_Nv}EG~Pw04a=T`u;EeGvg2EYE!~_8(WBd2z$J%vzeFQ_yjTLJCY?A z7iV#z(4{@S5AFHTrQvKO7!)k#epuNW$c5bn_Aun zA5gF(sP|Q$q=Ec0Q`&qc(ucg1fA@bsGw}cQ4ypY=E=${nsQ&@n^U$%}a7GUYH`wxL zu^*giEX@2J$dI>))dOs=OvHMsdpL*Vm-0XTo&Ph>e>nH_oBkgYx9~9$7dRVI8GLrT zr^4g6JPSo6jnPThav-Ho{?+_%fAOCgfAG!thW{A0NQRS`cwur}mcBUX;AfeuVdnWh zdCY(wVYL5^2m7r2zvBM-{}boR&;F6(|1kf)$Din{6EW{5e5l~OPcpE{L2NkXZ#`R) zh<{&|`|R-l8~^dM(2u=0cl?QS8TNN!R7g0pQj%B*gI22IO>dINzHA=B*{{_lkZx_Mhnc6;9g*IYjpNmb*~SivRzI z&wt1Tz5t&rqzH0`A|XfnG@R3M7VggxKe)6dIrt3uYJ9qEHRKtr#w80^fBnC7>A!w+ z7V?mtBjhc2A-><6zd{jm$bZYBH9Lf7{?)JH|CJX2<`2iI^i_FKK~6?#kPY0?(QL(# z(+A?e{*~*4i0`Bi(KZP7qBodW{ujVUw!zr~Oygz!x8V=Dqd6ddo)#{N_aZitCmGIb zXn}mPD9A7NeU7aOc?JR3S${W{mpF>D9dfm{K#sB(aQ@E`IF~>R@)iAb4wPTVA26N4 z{~z-Gu!5~u5bQgvaA~S8m?SY5um`%}bJn;(Y~Lct|1a2A={A=8Z5;RU>wg3Oge>Ky zkYD)%Hi7F6GA%X&Gk>?KbtyC_nZGz z{PCGe%8)nvH1s#*Rww^1ci^1yo5TE{UD;%Vz<1ta6S=$zIU0+m>)Y|?fAODP{1+ix zjS9|PTML-KnjH?~d>!f#Ucyc2R%&w~FDb6T=CfZVVeWCfau<^IKgdi+6d7ayCilX63UKMr6%MpLyD z^QU7IIL-Ttw@6Lw-#>j%$X}@rJi-Cw!8lAb4T=%`rY||re5QtX<%JvK1ImU9Lq=bvu*mP-@~U%&WG`O9@YlT6gZE<`6g4f_Z$A$ z6iJftAK;JQ3VFJ|>1}>j8{zyRlIQRgZ;xmw)Uc*@s3x9>q|Mk0| zlVXQ;;m0#hrj}{H;g8SXAW1U*1N3{?G_@Jef&7{Fqas;SepqWJ&417}|8D<}f6s@g zu`^LF^p)=&JNx8k+VG$B4xDpC34CA+$Ofd&4E=^bHhBpx=e6pi45 zlp55EsHIrk6%nmcT9@Kh*OqZXTdj4gwraJuKijI1naKuW2@JvAuqYP$eO3*1s|&Lw zfxwq$zW=>%-sHVElgUg-G-~Jfd-L9X?{4SZbI(2Z+;h+E@p=?KU&` zcIJCs`ufzDAMzJ{&KZIGd^TTF`&Zg_b2e7Lp4(@G*Y4u7Ch>%>-)pjOQu~K$hUdfE z-_f^0+Gj8P!drNGq;5jmvvgXwT#*}DJN10_4ZobZ{Sb2bszYw-4z^6bC$+!-n|1z6 zv~GCP`5(=Jqb8@q9eeJDe+SNU?@lh$eP3?d$^PM*aLt(8(dT`KHb$po`A($n(5sLa z|BUY7BWWL!O`g8OHR0M(DQyN))jQog*5S@_xC(@ZN0Pzo%Xwi_OEt*(!9QGV7j{F z-#stnE2+t7PS{JdfxNdj+HmR_>FU+fZ>;HjV8DUj(h;qnaAHsM`>LdiuH_tPlRZhK zZq%Ag>K|Txn1lU5PazLvcqZsR@QdY+P0cT!*=Ow$Svle-NqDF!_`LO%(|cW~oe6Hg zh&b3Gk3|>!se$e}4;p)<^$=a|{nr?2C;JYPNuyQC&Fyx5SeVD4+nwtUuUijd{m8!pcJ=nXnx zudu)5rJ#T4k=fRzr}0@QEdwXJl5kR;`|p+wQx^0z-~N^kyOwE;`N?)IRjy#`t7q<$ zof^KrhlG3su4Rs}BkMVxT<6dEoc+aB_y?QS{QR7(>2P0hjlIMy`g_Do1FV(oMNjfO zWX0lrE#u2dk4=-E{(U|030qQ4&!gU}obAZG@=VpW#f8zp@za^xUlm=k-@1rD zcXOm>NGww6ibec(E(dl8MT869=4yznD1D6cBWH6?ygWa&H|o&SJfiCkIUW2Y_YZIe zfA~DQ_JE)FH197f9k#0{V4s-Ueznrx7Ofk7USHO2_jZrH^_8;*hwH`;4pj{p9IhEM zIJ*9r!L3^sW$h!VFYBr09jSI67_Q2BKdGOnGJ&x7dyS74(YcJ9|1!@H6S$0zrHl>#iPR3L zqfaeDzCOD3<=K61GlV@)4*Nz2w*-!yN4fW6%a>vfV;t*=hwX3an1DxKfU(X1-tuvk#a#2K&M6+j!pyzi6cYHb&PS*4P}F(s`|)+=v~2BWv@GYP^z9 zp{2~)S!-``htE^i7HH*`mKSI4lb^h=L;Lk649#oDk7u73`}yIWPA7+oNm8A4osG2K#FGef}qzju8glun#m4sm?{7%iSc# z8up{lZGGXm|MK`Ri6~oM_--NoJjNr3FN~}laxeV14xay6Z}AA|OXwk9!*;JOx@x~W zS~eVg20Dqs!nXTM0{Oqic7uUkTP}98evaq88i|0s9DMk2#dIOStuC<$b}D7l9c6yEwK*8S=^?SO_v#ZoP=G3XOw$H0iI&Q zB}Dd=Hvw3Tt6eE|hAoP_O`$;Iv@0`YxI?kU!vSZLjSn6XF6Vm_VUIhuWuq}nbq(KT zex30F)$me9{{Ur`2lB`@;m~CvVNZ8uvKv@phec-q7=8;72pGK5Afv>9xlU* zfFESIV`1e6z7qyNO``6~Vx^cslXxpIu7s7Ti?#3qvI;9RTuQmXzuHlW=NTSqjilZ-`JH283?VruJ&KQG|!_7qdNLeYvP5D$}qjY6mk?GEn!vK89HEzvFTEjdCWT+H{U zbl-~=dW9%Zx^9!}vGqW@Uhd6mFtu?ML1 z;Q(PqgG78025(w_EN&uPp6IV_ZppDa`?JI&>_zMEZWxWIyF>?Uq#T@xx_incfI!zo zc6@M)a0k&;)&NZWz)buXmI(61(I7BKE^@3=BTx*CftqSR5A_rbQu~Q|LMVx!Z0|T! z@@2pfUDQSJTlF`AZ!@?&ToyTQ@Q~$?a|rgs&-JO;C(jG{iWhNS)zVPakfrQ@T*}!d zOR;wo`;iG3dxHLfbHl6466?W{HHVMjoa;Foi z_trh^y}C8J{@6l%KhDSho3P#Qe$S4y4Y6&&es_P)g*ptJvaisZ=3E)eUT}@A_x>lf zZoR74W!_-rknz-Ao+Frr-AXXH%=T^e#7$yfWR`x=?OtHd3kv$Fu%v<8Y(pc?)5zb7 z{r&uwSLgSN-X44WV}m|tYZh>`2iV%qIjkqKH}hERJ2Kj*)0w}cF}mS|f{?EOf5B$> zvIzTf-^aJ)b(|g6V}AqeH99cpwWqGX*Reb3&1no(4dfcsK%ae@`aT`>TDkIn7^)pJ zmbUvOB@AAB8#KKMpXrU^>S2xKvrJ8Id73^y0lS#XQ>Lr1wi34nUU4UTo^Rzm#TDSU zn|&6x4?{JhF5r9yOSZg#vQ{S5!|MpbcP@$qW^{?y=I3WxsN<@nv=vtDb_QMF&3IUpR+;^5$QPBZzO&HF4}2la(NoP zaQ=tCztpkS}kE84miw)1lf@7k&E)jUR9hUcEjoV^UG5vScwd(PU1qg~d3dPm}JMvFs5o zoqC72c=KnbOQU_Go=JN@Le(QqXAC$2xc7ACYx}=Q{S<=^5}MoG`CYW1{WH#T#W!ft zJ;wL8j%Z-UP_0bu9;rXtj*a2@@Z9aj_#!XMf1sUjYjM-vL;gJamPS9^y*dBnTpH#~ z-T&a1jFI}Z&JRT@&d6M7cuyRBAt!JeSu@c=TIy*?`;<&uHFS~TJ#iQ#P88o#{esKv zHu}X)A)ib5*S(qk!R2}q+&q~ozWH6WcE~n;dB_}V&2hR42mOTx`%coX6mdh9MaS#& zRG%%gwS4ds?=XLd4(}1)gUbu1n7?t(&_!BW^PTfUOE?3z9DW1NEejb7E@4bMI)c>2qpNVxU+|v3uJFPaL|kb2O#OTD9I6?afIqT!k|UJd{<6Jjx3kS@ zIq&@Y<~qQ8P73-8;l0)_#s&DKuka~k)`QJy^!0Z>enVQH zOVU4fzfT$7i~hJ`oI!R)&U)Pt$DjYU$Qv4Z*6^PF!}$IXN+kJ1&MpD2O5zxKw!SYJ zo{=ZLxp%!*FWh@Ykvp49m|+ETkxOG_w;d}>kV^q zXyo=p9G~-p;4;Ud)a69p8T}l1bGEj;e5M@J@sB)``GldrCh{=Atm7-%dtY8H`{DCkYS{i3d}Rr9uUX-`gJ;o)XF;E{$bZ((WeS(=Waeb}tuDL+ntzq| zMxHLqwXcr^%3biVSwVk3X{|CX`y^(|w7xtK{fM+F+RW>GF|z(>Da$aXcG8P3E_~A; zSu=SQ=W-W^>&Ch9yYGSy4`u%NT#EX11bqc3o8fPMsIq_4}VV~aF--Q>C4*H93 z$UblT4)Eh~;%;jzpuE!*L}0c5@Tku_7CoJIN&@-h8}vq;1|#9#P$ zWZkhP;hIs%&qkO?-`M=}8A&oS^Qc_t!Jr6rxiPTRYPrHk8DZ{yNysq9H&P*sA zO&>f9ng%98SN7G+6VIS+2Sx+ak*g<+pwC4R@3(uq7OxDsiLM0(nev|iV5IOA*%zN>OYeW3Tt zpyeLyqZ8*J4tlVq@#4h%hyG`kW$OEZ*YQzw1FI(e_{SV@G(KkThJK<+?Wcz&@ofJP znwitO`Ld+G&0Gszorw?Bj5@tHz;AAt=3tLpS)_K*LhLPm&04&d`+C-Nw{!2jY6om* z&A%2qz-8Es-GLqUSDZZ5X>`wB>L#3PgBAG;nEU5+ z1rIXdMA8{#62`F}pOe>Ie~1$s*tysbKGT&SN!F$_uz?c3lATT1Ek2Cx+cfswrIgWH z8W(UbeEFc3*DlOQZgTJZo8k`F40pF~UX)fo(c^=@_^Ocx?0X_>)9-86rx(x7>krLm zpXATNl@80&h>aDTD< zvosRA8~DN%M);9)tA{1Y0_L){FgD~fsB`Du@G3_O_Qk))rsX7j9`}fsG_O5q2>3V; zym+yT>GWG(&r;>j2(>yf2r3)&ux;XEA3(Gd~?eSC-ka4H@spPwDmYNq55E} zbVS*U@m;M>tUC;&4>Kl+{-3+F`T5zoA#eU|z_`l{S5nxQdpA1HN#^&F+EG8&^6%=7 zo%4M5Jg9xS(y@kpVTO5JSLu5Cp7N$(OZ%S0b(I6(%Y_M91s+(1jCOx?!-<37iO7LQ zp59eBTs>kYa(oK+o7YX+kM_WaI(@V%rwW_Cr1?`%;Y^o~JqDxb4{yckZ3{S% zd@)q*J~XoS2xKEyU01NeBif|Q;T7lW3Yp6FV0h((Jl1pGHl*=7_C(hmnclBMP2p`C z&C~9^O7H357vG@$Bf-6-J7{8#79Sbw8Uq}08h{7Xu--N!Qh%i6MfCULQ02&}=u`6W z3n}ykDn7LM{(|SUJiEHX?>5BS{^73T^yPc@6S2QQ>@#%!MwAm?H7*xf&a_Tp5p%kQ zk@{ok6N66h*eYn{Ms&u7@QF8=Pp0L6w0*y7e`w#RZ>+Gs$660(j^@4F zJ^c26qH8)eR6A}|aJf~=f{_(tuTMwT{SN*L#RR|YT~U9=#Kq|U8|l~RK(w}&^Mh7! zQy;7xd{OfYr{&T1=$7q?bDASHBX9gl!|%0A_%#?>UOu~s^CH#xLG>_q>+6dX<2AnK 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 literal 0 HcmV?d00001 diff --git a/source/jaindb/jaindb.cs b/source/jaindb/jaindb.cs index 1c28988..e774814 100644 --- a/source/jaindb/jaindb.cs +++ b/source/jaindb/jaindb.cs @@ -53,6 +53,7 @@ public enum hashType { MD5, SHA2_256 } //Implemented Hash types public static bool UseCosmosDB; public static bool UseRedis; public static bool UseFileStore; + public static string FilePath = "wwwroot"; public static bool UseRethinkDB; internal static string databaseId; @@ -106,7 +107,7 @@ public static string LookupID(string name, string value) try { //Check in MemoryCache - if (_cache.TryGetValue("ID-" + name + value, out sResult)) + if (_cache.TryGetValue("ID-" + name.ToLower() + value.ToLower(), out sResult)) { return sResult; } @@ -120,7 +121,7 @@ public static string LookupID(string name, string value) if (!string.IsNullOrEmpty(sResult)) { var cacheEntryOptions = new MemoryCacheEntryOptions().SetSlidingExpiration(TimeSpan.FromSeconds(60)); //cache ID for 1min - _cache.Set("ID-" + name + value, sResult, cacheEntryOptions); + _cache.Set("ID-" + name.ToLower() + value.ToLower(), sResult, cacheEntryOptions); } return sResult; @@ -129,13 +130,13 @@ public static string LookupID(string name, string value) if (UseFileStore || UseCosmosDB) { - sResult = File.ReadAllText("wwwroot\\" + "_Key" + "\\" + name.TrimStart('#', '@') + "\\" + value + ".json"); + sResult = File.ReadAllText(Path.Combine(FilePath, "_Key" + "\\" + name.TrimStart('#', '@') + "\\" + value + ".json")); //Cache result in Memory if (!string.IsNullOrEmpty(sResult)) { var cacheEntryOptions = new MemoryCacheEntryOptions().SetSlidingExpiration(TimeSpan.FromSeconds(300)); //cache ID for 5min - _cache.Set("ID-" + name + value, sResult, cacheEntryOptions); + _cache.Set("ID-" + name.ToLower() + value.ToLower(), sResult, cacheEntryOptions); } return sResult; @@ -171,7 +172,7 @@ public static void WriteHash(ref JToken oRoot, ref JObject oStatic, string Colle { ((JObject)oClass).Add("##hash", sHash); if (!UseCosmosDB) - WriteHashAsync(sHash, oRoot.ToString(Formatting.None), Collection); + WriteHashAsync(sHash, oRoot.ToString(Formatting.None), Collection).ConfigureAwait(false); else { //No need to save hashes when using CosmosDB @@ -391,8 +392,9 @@ public static bool WriteHash(string Hash, string Data, string Collection) Hash = Hash.Replace(sChar.ToString(), ""); } - if (!Directory.Exists("wwwroot\\" + Collection)) - Directory.CreateDirectory("wwwroot\\" + Collection); + string sCol = Path.Combine(FilePath, Collection); + if (!Directory.Exists(sCol)) + Directory.CreateDirectory(sCol); switch (Collection.ToLower()) { @@ -403,8 +405,8 @@ public static bool WriteHash(string Hash, string Data, string Collection) string sID = jObj["#id"].ToString(); - if (!Directory.Exists("wwwroot\\" + "_Key")) - Directory.CreateDirectory("wwwroot\\" + "_Key"); + if (!Directory.Exists(Path.Combine(FilePath, "_Key"))) + Directory.CreateDirectory(Path.Combine(FilePath, "_Key")); //Store KeyNames foreach (JProperty oSub in jObj.Properties()) @@ -419,7 +421,7 @@ public static bool WriteHash(string Hash, string Data, string Collection) { if (oSubSub.ToString() != sID) { - string sDir = "wwwroot\\" + "_Key" + "\\" + oSub.Name.ToLower().TrimStart('#'); + string sDir = Path.Combine(FilePath, "_Key" + "\\" + oSub.Name.ToLower().TrimStart('#')); //Remove invalid Characters in Path foreach (var sChar in Path.GetInvalidPathChars()) @@ -445,7 +447,7 @@ public static bool WriteHash(string Hash, string Data, string Collection) { try { - string sDir = "wwwroot\\" + "_Key" + "\\" + oSub.Name.ToLower().TrimStart('#'); + string sDir = Path.Combine(FilePath, "_Key" + "\\" + oSub.Name.ToLower().TrimStart('#')); //Remove invalid Characters in Path foreach (var sChar in Path.GetInvalidPathChars()) @@ -467,23 +469,23 @@ public static bool WriteHash(string Hash, string Data, string Collection) lock (locker) //only one write operation { - File.WriteAllText("wwwroot\\" + Collection + "\\" + Hash + ".json", Data); + File.WriteAllText(Path.Combine(FilePath, Collection + "\\" + Hash + ".json"), Data); } break; case "chain": lock (locker) //only one write operation { - File.WriteAllText("wwwroot\\" + Collection + "\\" + Hash + ".json", Data); + File.WriteAllText(Path.Combine(FilePath, Collection + "\\" + Hash + ".json"), Data); } break; default: - if (!File.Exists("wwwroot\\" + Collection + "\\" + Hash + ".json")) //We do not have to create the same hash file twice... + if (!File.Exists(Path.Combine(FilePath, Collection + "\\" + Hash + ".json"))) //We do not have to create the same hash file twice... { lock (locker) //only one write operation { - File.WriteAllText("wwwroot\\" + Collection + "\\" + Hash + ".json", Data); + File.WriteAllText(Path.Combine(FilePath, Collection + "\\" + Hash + ".json"), Data); } } break; @@ -494,14 +496,14 @@ public static bool WriteHash(string Hash, string Data, string Collection) } catch (Exception ex) { - if (!Directory.Exists("wwwroot\\" + Collection)) - Directory.CreateDirectory("wwwroot\\" + Collection); + if (!Directory.Exists(Path.Combine(FilePath, Collection))) + Directory.CreateDirectory(Path.Combine(FilePath, Collection)); - if (!File.Exists("wwwroot\\" + Collection + "\\" + Hash + ".json")) //We do not have to create the same hash file twice... + if (!File.Exists(Path.Combine(FilePath, Collection + "\\" + Hash + ".json"))) //We do not have to create the same hash file twice... { lock (locker) //only one write operation { - File.WriteAllText("wwwroot\\" + Collection + "\\" + Hash + ".json", Data); + File.WriteAllText(Path.Combine(FilePath, Collection + "\\" + Hash + ".json"), Data); } } @@ -556,10 +558,10 @@ public static string ReadHash(string Hash, string Collection) case "_full": return cache0.StringGet(Hash); - case "chain": + case "_chain": return cache3.StringGet(Hash); - case "assets": + case "_assets": return cache4.StringGet(Hash); default: @@ -624,11 +626,11 @@ public static string ReadHash(string Hash, string Collection) Hash = Hash.Replace(sChar.ToString(), ""); } - sResult = File.ReadAllText("wwwroot\\" + Coll2 + "\\" + Hash + ".json"); + sResult = File.ReadAllText(Path.Combine(FilePath, Coll2 + "\\" + Hash + ".json")); #if DEBUG //Check if hashes are valid... - if (Collection.ToLower() != "_full" && Collection.ToLower() != "chain") + if (Collection.ToLower() != "_full" && Collection.ToLower() != "_chain" && Collection.ToLower() != "_assets") { var jData = JObject.Parse(sResult); /*if (jData["#id"] != null) @@ -701,7 +703,7 @@ public static string ReadHash(string Hash, string Collection) public static Blockchain GetChain(string DeviceID) { Blockchain oChain; - string sData = ReadHash(DeviceID, "Chain"); + string sData = ReadHash(DeviceID, "_Chain"); if (string.IsNullOrEmpty(sData)) { oChain = new Blockchain("", "root", 0); @@ -887,9 +889,9 @@ public static string UploadFull(string JSON, string DeviceID, string blockType = } if (!UseCosmosDB) - WriteHashAsync(DeviceID, JsonConvert.SerializeObject(oChain), "Chain"); + WriteHashAsync(DeviceID, JsonConvert.SerializeObject(oChain), "_Chain").ConfigureAwait(false); else - WriteHashAsync(DeviceID, JsonConvert.SerializeObject(oChain), "Chain").Wait(); + WriteHashAsync(DeviceID, JsonConvert.SerializeObject(oChain), "_Chain").Wait(); //Add missing attributes @@ -917,9 +919,9 @@ public static string UploadFull(string JSON, string DeviceID, string blockType = if (!UseCosmosDB) { if (blockType == BlockType) - WriteHashAsync(DeviceID, jTemp.ToString(Formatting.None), "_Full"); + WriteHashAsync(DeviceID, jTemp.ToString(Formatting.None), "_Full").ConfigureAwait(false); else - WriteHashAsync(DeviceID + "_" + blockType, jTemp.ToString(Formatting.None), "_Full"); + WriteHashAsync(DeviceID + "_" + blockType, jTemp.ToString(Formatting.None), "_Full").ConfigureAwait(false); } else { @@ -937,9 +939,9 @@ public static string UploadFull(string JSON, string DeviceID, string blockType = if (!UseCosmosDB) { if (blockType == BlockType) - WriteHashAsync(sResult, oStatic.ToString(Newtonsoft.Json.Formatting.None), "Assets"); + WriteHashAsync(sResult, oStatic.ToString(Newtonsoft.Json.Formatting.None), "_Assets").ConfigureAwait(false); else - WriteHashAsync(sResult + "_" + blockType, oStatic.ToString(Newtonsoft.Json.Formatting.None), "Assets"); + WriteHashAsync(sResult + "_" + blockType, oStatic.ToString(Newtonsoft.Json.Formatting.None), "_Assets").ConfigureAwait(false); } else { @@ -1220,7 +1222,7 @@ public static JObject GetHistory(string DeviceID, string blockType = "") try { - string sChain = ReadHash(DeviceID, "Chain"); + string sChain = ReadHash(DeviceID, "_Chain"); var oChain = JsonConvert.DeserializeObject(sChain); foreach (block oBlock in oChain.Chain.Where(t => t.blocktype == blockType)) { @@ -1244,6 +1246,58 @@ public static JObject GetHistory(string DeviceID, string blockType = "") return jResult; } + public static JArray GetJHistory(string DeviceID, string blockType = "") + { + string sResult = ""; + + try + { + //Check in MemoryCache + if (_cache.TryGetValue("HIST - " + DeviceID + " - " + blockType, out sResult)) + { + //Try to get value from Memory + if (!string.IsNullOrEmpty(sResult)) + { + return JArray.Parse(sResult); + } + } + } + catch { } + + JArray jResult = new JArray(); + + if (string.IsNullOrEmpty(blockType)) + blockType = BlockType; + + try + { + string sChain = ReadHash(DeviceID, "_Chain"); + var oChain = JsonConvert.DeserializeObject(sChain); + foreach (block oBlock in oChain.Chain.Where(t => t.blocktype == blockType)) + { + try + { + JObject jTemp = new JObject(); + jTemp.Add("index", oBlock.index); + jTemp.Add("timestamp", new DateTime(oBlock.timestamp).ToUniversalTime()); + jTemp.Add("type", ""); + jResult.Add(jTemp); + } + catch { } + } + + //Cache result in Memory + if (!string.IsNullOrEmpty(sResult)) + { + var cacheEntryOptions = new MemoryCacheEntryOptions().SetSlidingExpiration(TimeSpan.FromSeconds(60)); //cache ID for 60s + _cache.Set("HIST - " + DeviceID + " - " + blockType, jResult.ToString(Formatting.None), cacheEntryOptions); + } + } + catch { } + + return jResult; + } + public static JObject GetRaw(string RawID, string path = "") { JObject jResult = new JObject(); @@ -1488,7 +1542,7 @@ public static List Search(string searchkey, string KeyID = "#id") return lNames.Union(lNames).ToList(); } - public static JArray Query(string paths, string select, string exclude, string where = "") + public static async Task QueryAsync(string paths, string select, string exclude, string where) { paths = System.Net.WebUtility.UrlDecode(paths); select = System.Net.WebUtility.UrlDecode(select); @@ -1515,7 +1569,7 @@ public static JArray Query(string paths, string select, string exclude, string w DateTime dStart = DateTime.Now; //JObject lRes = new JObject(); JArray aRes = new JArray(); - List lLatestHash = GetAllChainsAsync().Result; + List lLatestHash = await GetAllChainsAsync(); foreach (string sHash in lLatestHash) { bool foundData = false; @@ -1858,7 +1912,7 @@ public static JArray QueryAll(string paths, string select, string exclude) if (UseFileStore) { - foreach (var oFile in new DirectoryInfo("wwwroot/Assets").GetFiles("*.json")) + foreach (var oFile in new DirectoryInfo(Path.Combine(FilePath, "_Assets")).GetFiles("*.json")) { bool foundData = false; JObject jObj = GetRaw(File.ReadAllText(oFile.FullName), paths); @@ -2031,7 +2085,7 @@ public static JArray GetChanges(TimeSpan age, int changeType = -1) { Change oRes = new Change(); oRes.id = sID; - var jObj = JObject.Parse(ReadHash(sID, "Chain")); + var jObj = JObject.Parse(ReadHash(sID, "_Chain")); oRes.lastChange = new DateTime(jObj["Chain"].Last["timestamp"].Value()); if(DateTime.Now.Subtract(oRes.lastChange) > age) { @@ -2101,7 +2155,7 @@ public static async Task> GetAllChainsAsync() if (UseFileStore) { - foreach (var oFile in new DirectoryInfo("wwwroot/Chain").GetFiles("*.json")) + foreach (var oFile in new DirectoryInfo(Path.Combine(FilePath, "_Chain")).GetFiles("*.json")) { lResult.Add(System.IO.Path.GetFileNameWithoutExtension(oFile.Name)); } @@ -2343,7 +2397,7 @@ public static bool Export(string URL, string RemoveObjects) { try { - var jObj = JObject.Parse(ReadHash(sID, "Chain")); + var jObj = JObject.Parse(ReadHash(sID, "_Chain")); foreach (var sBlock in jObj.SelectTokens("Chain[*].data")) { try @@ -2351,7 +2405,7 @@ public static bool Export(string URL, string RemoveObjects) string sBlockID = sBlock.Value(); if (!string.IsNullOrEmpty(sBlockID)) { - var jBlock = GetRaw(ReadHash(sBlockID, "Assets")); + var jBlock = GetRaw(ReadHash(sBlockID, "_Assets")); jBlock.Remove("#id"); //old Version of jainDB //jBlock.Remove("_date"); jBlock.Remove("_index"); @@ -2425,421 +2479,6 @@ public static string UploadToREST(string URL, string content) return ""; } - /* - public static string GetFull(Blockchain oChain, int Index = -1) - { - try - { - block lBlock = null; - if (Index == -1) - { - lBlock = oChain.GetLastBlock(); - } - else - { - lBlock = oChain.GetBlock(Index); - } - - int index = lBlock.index; - DateTime dInvDate = new DateTime(lBlock.timestamp); - string InvHash = lBlock.data; - string sData = ReadHash(InvHash, "Assets"); - if (!string.IsNullOrEmpty(sData)) - { - JObject oInv = JObject.Parse(sData); - oInv.Add(new JProperty("_index", index)); - oInv.Add(new JProperty("_inventoryDate", dInvDate)); - oInv.Add(new JProperty("_hash", InvHash)); - JSort(oInv); - - //Load hashed values - foreach (JProperty oTok in oInv.Descendants().Where(t => t.Type == JTokenType.Property && ((JProperty)t).Name.StartsWith("##hash")).ToList()) - { - string sH = oTok.Value.ToString(); - string sRoot = oTok.Path.Split('.')[0].Split('[')[0]; - string sObj = ReadHash(sH, sRoot); - if (!string.IsNullOrEmpty(sObj)) - { - var jStatic = JObject.Parse(sObj); - oTok.Parent.Merge(jStatic); - oTok.Remove(); - } - } - - return oInv.ToString(); - } - } - catch { } - - return ""; - } - public static string ByteArrayToString(byte[] input) - { - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < input.Length; i++) - { - sb.Append(input[i].ToString("X2")); - } - return sb.ToString(); - } - - public static byte[] StringToByteArray(String hex) - { - int NumberChars = hex.Length; - byte[] bytes = new byte[NumberChars / 2]; - for (int i = 0; i < NumberChars; i += 2) - bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16); - return bytes; - } - public static string LookupID(string query) - { - try - { - if (UseRedis) - { - var cache1 = RedisConnectorHelper.Connection.GetDatabase(1); - return cache1.StringGet(query.TrimStart('?').Split('&')[0].Replace("=", "/")); - } - } - catch { } - - return ""; - } - - public static async Task> FindLatestPathAsync(string path) - { - List lResult = new List(); - try - { - if (UseRedis) - { - var tFind = await FindHashOnContentAsync(path); - - tFind.AsParallel().ForAll(t => - { - foreach (string sName in FindLatestRawWithHash(new List() { t }, "#Name")) - { - lResult.Add(sName); - } - }); - } - } - catch { } - - return lResult; - } - - public static string sTopic2Json(string Topic = "*") - { - var cache = RedisConnectorHelper.Connection.GetDatabase(); - var server = RedisConnectorHelper.Connection.GetServer("localhost", 6379); - - List lRes = new List(); - var keys = server.Keys(pattern: Topic); - //var sResult = cache.StringGet("BIOS/*"); - foreach (var key in keys) - { - key.ToString(); - var sVal = cache.StringGet(key); - lRes.Add(key.ToString() + ";" + sVal); - } - - return Topic2Json(lRes.ToArray()); - try - { - string[] aFile = File.ReadAllLines("wwwroot\\test.json"); - return Topic2Json(aFile); - } - catch { } - - return ""; - } - - public static string Topic2Json(string Topic) - { - return Topic2Json(Topic.Split(new char[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries)); - } - public static string Topic2Json(string[] Topic) - { - try - { - List lSource = Topic.OrderBy(t => t, new TopicComparer()).ToList(); - - JObject ORes = new JObject(); - foreach (string sLine in lSource) - { - string sTopic = sLine.Split(';')[0]; - string sValue = sLine.Split(';')[1]; - bool bMerge = true; - - string sPath = ""; - - //Convert Topic to JPath - sPath = Topic2JPath(sTopic); - - - JObject O = new JObject(); - string sFullPath = sPath; - foreach (string sItem in sPath.Split('.').Reverse()) - { - bool bArray = false; - if (sItem.EndsWith("]")) - bArray = true; - - if (O.Count == 0) - { - if (!bArray) - { - O.Add(sItem, sValue); - } - else - { - JArray A = new JArray(); - A.Add(sValue); - O.Add(sItem.Split('[')[0], A); - //JObject OA = new JObject(); - //OA.Add(A); - //O = OA; - } - sFullPath = sFullPath.Substring(0, (sFullPath.Length - sItem.Length)).TrimEnd('.'); - } - else - { - if (!bArray) - { - if (O.SelectToken(sFullPath) == null) - { - JObject N = new JObject(); - N.Add(sItem, O); - O = N; - sFullPath = sFullPath.Substring(0, (sFullPath.Length - sItem.Length)).TrimEnd('.'); - } - } - else - { - if (ORes.SelectToken(sFullPath) == null) - { - JArray A = new JArray(); - A.Add(O); - - JObject N = new JObject(); - N.Add(sItem.Split('[')[0], A); - - O = N; - sFullPath = sFullPath.Substring(0, (sFullPath.Length - sItem.Length)).TrimEnd('.'); - } - else - { - var oT = ORes.SelectToken(sFullPath); - ((JObject)oT).Add(O.First); - bMerge = false; - } - } - } - - } - - if (bMerge) - { - ORes.Merge(O); - } - else - { - bMerge = true; - } - } - - string sJ = ORes.ToString(); - return sJ; - } - catch - { } - - return ""; - } - - public static List Json2Topic(JObject JSON, string ID = "") - { - if (!string.IsNullOrEmpty(ID)) - { - if (!ID.EndsWith("/")) - ID += "/"; - } - List sResult = new List(); - - foreach (var x in JSON) - { - try - { - string name = x.Key; - if (x.Value.Count() > 0) - { - if (x.Value.Type != JTokenType.Array) - { - if (x.Value.Type == JTokenType.Object) - sResult.AddRange(Json2Topic(x.Value as JObject, ID)); - else - { - foreach (var oVal in x.Value) - { - if (!string.IsNullOrEmpty(oVal.ToString())) - { - sResult.Add(ID + JSON.Path + "/" + name + ";" + oVal.ToString()); - } - } - } - } - else - { - int i = 0; - foreach (var oVal in x.Value) - { - if (oVal.Type == JTokenType.Object) - { - sResult.AddRange(Json2Topic(oVal as JObject, ID)); - } - else - { - if (!string.IsNullOrEmpty(oVal.ToString())) - { - sResult.Add(ID + JSON.Path + "/" + name + "/[" + i + "];" + oVal.ToString()); - i++; - } - } - } - } - } - else - { - if (!string.IsNullOrEmpty(JSON.Path)) - { - if (!string.IsNullOrEmpty(x.Value.ToString())) - { - sResult.Add(ID + JSON.Path.Replace("[", "/[") + "/" + name + ";" + x.Value.ToString()); - } - } - } - } - catch - { } - } - - var cache = RedisConnectorHelper.Connection.GetDatabase(); - foreach (string s in sResult) - { - cache.StringSet(s.Split(';')[0], s.Split(';')[1]); - } - - return sResult; - } - - public static List GetLatestRawAsync() - { - List lResult = new List(); - try - { - if (UseRedis) - { - var cache4 = RedisConnectorHelper.Connection.GetDatabase(4); - foreach (string sRawID in GetLatestBlocksAsync().Result) - { - Task.Run(() => - { - cache4.StringGet(sRawID); - }); - } - } - } - catch { } - - return lResult; - } - */ - - /*public static bool WriteHash(string Hash, string Data, string Collection) -{ -try -{ - if (!Directory.Exists("wwwroot\\" + Collection)) - Directory.CreateDirectory("wwwroot\\" + Collection); - - if (!File.Exists("wwwroot\\" + Collection + "\\" + Hash + ".json")) //We do not have to create the same hash file twice... - { - lock (locker) //only one write operation - { - File.WriteAllText("wwwroot\\" + Collection + "\\" + Hash + ".json", Data); - } - } - - return true; -} -catch { } - -return false; -}*/ - - /*public static void WriteJ(JObject JSON) - { - //IList keys = JSON.Properties().Select(p => p.Name).ToList(); - //keys.ToString(); - - foreach (var x in JSON) - { - try - { - string name = x.Key; - if (x.Value.Count() > 0) - { - if (x.Value.Type != JTokenType.Array) - { - if(x.Value.Type == JTokenType.Object) - WriteJ(x.Value as JObject); - else - { - foreach(var oVal in x.Value) - { - if (!string.IsNullOrEmpty(oVal.ToString())) - { - File.AppendAllText("wwwroot\\test.json", JSON.Path + "/" + name + ";" + oVal.ToString() + "" + Environment.NewLine); - } - } - } - } - else - { - int i = 0; - foreach (var oVal in x.Value) - { - if (oVal.Type == JTokenType.Object) - { - WriteJ(oVal as JObject); - } - else - { - if (!string.IsNullOrEmpty(oVal.ToString())) - { - File.AppendAllText("wwwroot\\test.json", JSON.Path + "/" + name + "/[" + i + "];" + oVal.ToString() + Environment.NewLine); - i++; - } - } - } - } - } - else - { - if (!string.IsNullOrEmpty(JSON.Path)) - { - if (!string.IsNullOrEmpty(x.Value.ToString())) - { - File.AppendAllText("wwwroot\\test.json", JSON.Path.Replace("[", "/[") + "/" + name + ";" + x.Value.ToString() + Environment.NewLine); - } - } - } - } - catch - { } - } - }*/ public static void JSort(JObject jObj, bool deep = false) { diff --git a/source/jaindb/jaindb.csproj b/source/jaindb/jaindb.csproj index 3f8ee68..abe4b7f 100644 --- a/source/jaindb/jaindb.csproj +++ b/source/jaindb/jaindb.csproj @@ -1,7 +1,7 @@ - netcoreapp2.0 + netcoreapp2.1 ..\docker-compose.dcproj jaindb false @@ -12,11 +12,12 @@ https://github.com/rzander/jaindb https://github.com/rzander/jaindb blockchain json - 1.0.0 - 1.0.0.75 - 1.0.0.75 - jaindb.Program + 1.0.1 + 1.0.1.76 + 1.0.1.76 + fcfd6c0a-e53c-46cb-8a9d-b786c0579861 + JainDB_Logo.ico @@ -55,21 +56,21 @@ - - + + - + - + - +