From 903c0c563db2ab003764611920960fe66ed26539 Mon Sep 17 00:00:00 2001 From: Michael Gordeev Date: Wed, 4 Dec 2019 16:19:57 +0300 Subject: [PATCH] Added user authorization (not tested) --- FoxTube.Core/FoxTube.Core.csproj | 16 ++++++ FoxTube.Core/Helpers/UserControl.cs | 73 ++++++++++++++++++++++++ FoxTube.Core/Models/User.cs | 24 ++++++++ FoxTube/App.xaml.cs | 3 +- FoxTube/Controls/AccountManager.xaml | 14 ++--- FoxTube/Controls/AccountManager.xaml.cs | 36 ++++++------ Src/YouTube.API.dll | Bin 0 -> 46080 bytes 7 files changed, 139 insertions(+), 27 deletions(-) create mode 100644 FoxTube.Core/Helpers/UserControl.cs create mode 100644 FoxTube.Core/Models/User.cs create mode 100644 Src/YouTube.API.dll diff --git a/FoxTube.Core/FoxTube.Core.csproj b/FoxTube.Core/FoxTube.Core.csproj index f869b67..57f7302 100644 --- a/FoxTube.Core/FoxTube.Core.csproj +++ b/FoxTube.Core/FoxTube.Core.csproj @@ -136,6 +136,7 @@ + @@ -144,6 +145,7 @@ + @@ -151,6 +153,15 @@ 0.13.0 + + 1.42.0 + + + 1.42.0.1602 + + + 1.42.0.1758 + 2.6.1 @@ -175,6 +186,11 @@ Visual C++ 2015 Runtime for Universal Windows Platform Apps + + + ..\Src\YouTube.API.dll + + 14.0 diff --git a/FoxTube.Core/Helpers/UserControl.cs b/FoxTube.Core/Helpers/UserControl.cs new file mode 100644 index 0000000..1eecfee --- /dev/null +++ b/FoxTube.Core/Helpers/UserControl.cs @@ -0,0 +1,73 @@ +using Google.Apis.Auth.OAuth2; +using Google.Apis.Oauth2.v2; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Windows.Security.Authentication.Web; +using YouTube.Authorization; +using YouTube; +using System.Text.RegularExpressions; +using Windows.Security.Credentials; +using FoxTube.Core.Models; +using System.Threading; +using Google.Apis.YouTube.v3; + +namespace FoxTube +{ + public static class UserControl + { + static string[] Scopes { get; } = new string[] + { + Oauth2Service.Scope.UserinfoProfile, + Oauth2Service.Scope.UserinfoEmail, + YouTubeService.Scope.YoutubeForceSsl + }; + + static ClientSecrets ClientSecrets { get; } = new ClientSecrets + { + ClientId = "1096685398208-u95rcpkqb4e1ijfmb8jdq3jsg37l8igv.apps.googleusercontent.com", + ClientSecret = "IU5bbdjwvmx8ttJoXQ7e6JWd" + }; + + public static User CurrentUser { get; set; } + public static bool Authorized => CurrentUser != null; + + public static async Task AddUser() + { + Uri callbackUri = WebAuthenticationBroker.GetCurrentApplicationCallbackUri(); + Uri requestString = AuthorizationHelpers.FormQueryString(ClientSecrets, callbackUri, Scopes); + + WebAuthenticationResult result = await WebAuthenticationBroker.AuthenticateAsync(WebAuthenticationOptions.UseTitle, requestString, AuthorizationHelpers.Endpoint); + switch(result.ResponseStatus) + { + case WebAuthenticationStatus.Success: + UserCredential credential = await AuthorizationHelpers.ExchangeToken(ClientSecrets, new Regex(@"(?<=code=).?\w+").Match(result.ResponseData).Value); // TODO: Add credential assignment + CurrentUser = new User(credential); + return true; + case WebAuthenticationStatus.UserCancel: + break; + case WebAuthenticationStatus.ErrorHttp: + Metrics.AddEvent("Authorization failed (HTTP Error)", ("Response data", result.ResponseData), ("Error details", result.ResponseErrorDetail.ToString())); + break; + } + return false; + } + + public static async Task Initialize() + { + PasswordVault passwordVault = new PasswordVault(); + List credentials = passwordVault.FindAllByResource("foxtube").ToList(); + + if (credentials.Count == 0) + return; + + credentials[0].RetrievePassword(); + UserCredential credential = await AuthorizationHelpers.RestoreUser(ClientSecrets, credentials[0].Password); + CurrentUser = new User(credential); + } + + public static async Task Logout() => + await CurrentUser.Credential.RevokeTokenAsync(CancellationToken.None); + } +} diff --git a/FoxTube.Core/Models/User.cs b/FoxTube.Core/Models/User.cs new file mode 100644 index 0000000..fc117d9 --- /dev/null +++ b/FoxTube.Core/Models/User.cs @@ -0,0 +1,24 @@ +using Google.Apis.Auth.OAuth2; +using Google.Apis.YouTube.v3.Data; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace FoxTube.Core.Models +{ + public class User + { + public string Name { get; set; } + public string Email { get; set; } + internal string RefreshToken { get; set; } + public UserCredential Credential { get; set; } + public Channel Channel { get; set; } + + public User(UserCredential credential) + { + + } + } +} diff --git a/FoxTube/App.xaml.cs b/FoxTube/App.xaml.cs index 87ddf64..57cafe8 100644 --- a/FoxTube/App.xaml.cs +++ b/FoxTube/App.xaml.cs @@ -2,9 +2,7 @@ using FoxTube.Core.Helpers; using Windows.ApplicationModel; using Windows.ApplicationModel.Activation; -using Windows.UI; using Windows.UI.Popups; -using Windows.UI.ViewManagement; using Windows.UI.Xaml; namespace FoxTube @@ -21,6 +19,7 @@ namespace FoxTube protected override async void OnLaunched(LaunchActivatedEventArgs e) { + await UserControl.Initialize(); await StoreInterop.UpdateStoreState(); if (Settings.LastReviewedVersion != Metrics.CurrentVersion) Inbox.PushChangelog(); diff --git a/FoxTube/Controls/AccountManager.xaml b/FoxTube/Controls/AccountManager.xaml index 8e7dcbf..a194d6f 100644 --- a/FoxTube/Controls/AccountManager.xaml +++ b/FoxTube/Controls/AccountManager.xaml @@ -13,27 +13,27 @@ - + - + - - + - + - - + + diff --git a/FoxTube/Controls/AccountManager.xaml.cs b/FoxTube/Controls/AccountManager.xaml.cs index d6a9c1e..427c09c 100644 --- a/FoxTube/Controls/AccountManager.xaml.cs +++ b/FoxTube/Controls/AccountManager.xaml.cs @@ -1,27 +1,27 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Runtime.InteropServices.WindowsRuntime; -using Windows.Foundation; -using Windows.Foundation.Collections; -using Windows.UI.Xaml; -using Windows.UI.Xaml.Controls; -using Windows.UI.Xaml.Controls.Primitives; -using Windows.UI.Xaml.Data; -using Windows.UI.Xaml.Input; -using Windows.UI.Xaml.Media; -using Windows.UI.Xaml.Navigation; - -// The User Control item template is documented at https://go.microsoft.com/fwlink/?LinkId=234236 +using Windows.UI.Xaml.Controls; +using Windows.UI.Xaml.Media.Imaging; namespace FoxTube.Controls { public sealed partial class AccountManager : AppBarButton { - public AccountManager() - { + public AccountManager() => InitializeComponent(); + + async void Flyout_Opening(object sender, object e) + { + if (UserControl.Authorized) + return; + + (sender as Flyout).Hide(); + + if(await UserControl.AddUser()) + { + banner.Source = new BitmapImage(UserControl.CurrentUser.Channel.BrandingSettings.Image.BannerMobileLowImageUrl.ToUri()) { DecodePixelWidth = 300, DecodePixelHeight = 60 }; + avatar.ProfilePicture = new BitmapImage(UserControl.CurrentUser.Channel.Snippet.Thumbnails.Default__.Url.ToUri()) { DecodePixelHeight = 40, DecodePixelWidth = 40 }; + name.Text = UserControl.CurrentUser.Name; + email.Text = UserControl.CurrentUser.Email; + } } } } diff --git a/Src/YouTube.API.dll b/Src/YouTube.API.dll new file mode 100644 index 0000000000000000000000000000000000000000..bd8c3983932e9db13bc22c948de833afebd2457d GIT binary patch literal 46080 zcmeIbd0>>))jxioXZA@l$;@OS3(0_h1A&-8kVUpgSfcD<5d~pL1{h5;ab|*G2u@t6 zwyssGbt|Z~+Jd{SYYSE1s?}EeYOAd%)~c_q*80BMYHRWPIp^MIWJBAGKj$E9~;fOkVW}_Hq|7v z@N?hOM2i_YL{!g?81!RMK6tl3MYMcS-oIdkh}=2ZZqV~{=(<#NYYKG!_W_V6ZPj*z zlAjRK`np6i(FRIvI|PIqdM7?PKeNzXb%|(49D-~sk@ms8LS(=!q7yTOM&3dD`G;*Y z$w42%84F+-G|11aCx|v@oU57tBB`u5#N8LSQ#z(1fkNF#@>GJtOx37lv`QwZWV%XDR>>-rY*I;5CFiQ-8!EYm zlWH?rg^1#Ggigy<+KZ~~%s*Dq;Zj&WX1Hk!moZXp_HwL^nE}QSFicyY(8rrhI&H~q zS`8w|K4z*JhFTU95<-TwAJPvcc>?v=W z^F?S(#SRzOR&AywT^Sw=O^JgjC>img5NE>?^mcY!pCDj(EQTOppdKY52pH&Z#SkRN z2?%DDAqXT9HHslfj--z>AW+EZ%8dV+ZQA<~clI&k%`l?h8L!rc3Uh;E+Tw^ZuPq(| zl%Qo@AXH_7fHBNs2m;1%iy;UY=pI!>5HJvCiXjLXBQ1s?IW0iWe5VvT8kkF80b7zRFIR*zyX$n#kb47s6zEdC=AdYcwABHsWlD?Hl zoxM!u1q@_joYn?tJ2S#?p%*O_Jxxe$TF(Y4YwK&1lmp+}7ZJRqZ#`0HFH=e%>Sle+ z2*a;hpM!o4ef(2Xaw(>LV`OQE@z!e-cKuZZ5a(UE!d0+HSP&eXuOM z^DVp4#NR7XpJg|i_y@%*I?`^FxUVrGO3ZWu4Uny`L((uJ*xPre;3a)$Aa(XKrTQB2 z!F|n)Fk(~pwT&=&W91`%7=h_cB3!**Tg}Wk!rPm85$!FFBe1=BrTMjsT<+__<3SX7 zYNp4r7I}kqzrD0!yrE{l!d>qK{;KWwzmK$BsItp-(MV0cTf%X3Y+3S3thOR_WH9s1Gi(y0^Yx0 z;$?UyJQY?>-Gj`@tlpLQeTIo;hbAN9!F(^n{2DWVD$KuH%yE5_?-maFR4E*noP3jT zyctex@+HD4&T!V%_PG*O8S<3cE>~hihFn^^&XpLOAy2AZl4u0wtzDFuFX`OGsglk} zbVz!9V!NbuiOVD%mAFaL>cpLrmL?vR)R%ZhQfK02PMu@w5^n?Hc}e%9UG|eM^rIO+ z0qzM;1Mt>_>|tzLym?jDSUb@nV@)QJy@F$y&49?wBP$Zm2&2-a#;exO^JNp$^KtA2SB%8w>lq-1f1@hMv5!s^43hS6ghl;wOUbjD!%(#;IOc*(rH0Q;m{0 zdE3*AY_+Bc0>*5MA&9l2+rwMnXKcc@vb(MIBTw7E3uUlGpoF&%;V0|IRY;w^OewoD zzSwSNgy*myPBO#iK-tLnTu||O%tH0e$d@%v5HKLC7=mQ&=~MQ=-C29`wLRZP8EZYe zR`8O(Ymhp7nNs#3PS_r1sPzo?pg%2p<|}*Db}hTYA4&JZcv-7qzcN!0^o8Z40&UZc z5P(fB(A#%|;3a+76T&8@lud{)wuu?xli2{+^!2Pw3z(Jlz{TQ$K5cQ88TsgL+aI#ZEk1auKRKu$2&~|AihmR|~0WEfg+qm5j_r_NN1nmKP7^S@9n#|IXUfK1T zetX}kT$60}0;mz=y!POwsLbr;j3BZ&JzP>mRB4vza?k{>x4`8IN2RKEz;SsAEUn$i z(~xt{02|hT6JHAubOxN^(}BQ-vWyMRJ`}NRaGGI^?b?GLv4Jy!PIEh_hf4wjY;cCx zu}X)_?+9-aD>ib2*Mk|3a2|+<|eU$Lox=s`(U$WpxX>%pw}MU3{RWAoDp=J+c`a4QZm3mcNhkFgD$g|tMa?z zu+nSQzM3@_ixEh~E*Hx?! za^N96P#mwq+7|8>6&Oc$dIshF_;2>Ehe;Smhz^XSDHo!QnLG=EeTW0t$}oH!$3+56 zMJGlgSmH_0iE+SOVGjFv3Us&!AdZF8i{3`0*~4h2Gk!K0GVZnl(c_Nm<#C57Lr6sc zdECMN+JiXPK`(O#k2_8em!L6f+;KUKyK|taz!Sfd)%UV`N9_p#M|eA)ED0sXFU8cDn#07cJ3GVamW0_eVifa7p!1!{~-$9dv+aW&^d zAn2~0grRW(qn;y`m*ptuU_@bOP8IGjyz3peUsvHiTw0l_LU~ImOwK~NOlXLo#6Jj) zolu}cBg=M&zXt9YqNW8ZHhxa(j4cQ^VyciIgaeHS-Hdcg4xckQW-bHo27Gd2CIZLV z!WXe_Yr;OyX7*xi+DlbZ(=E>>@-Py=m<8*S55o2|1~|{^YJnbaOPtT{|2hkR_)dmE zy_NM&8g3Um5sMhp9E*qRt@l5BG=l;aw z5|TXWC;kaE{0-C_zl6c106|y46}}9J4nwZe^t<{nHV|tF8hgNHh6`o-w{aM92I6Qt zr-w_1T4BiLpy>)|Do9=l=m~!lFn$$-s{sVuQsSInF++xYi+A{OqS@^P!2WA)r>Z#H~P)aRW3YZv;3e@$$gZ$4Kd# zvFahub=V0XI+HhXjTrv5VZ=vm(4kx_U8@#?sSp@f;nx|5oj>GKd%%p}3{X4D@7RIq z**9{-b3`iWjNbyKLHFeUK!;;+-O3aO7k8`- z@Z=jPHq!4&9OlM-m(BR4=6Jmp=hvDO$Z&q8IaL|Xi<&dS;?!(ajw!n?{^}Rym0lNr zP3P5R$_>wydwEda3Dy&J{I@z!;$Q4O1Wp_aju-9x9+Ii~ykw|32NS}u%!GhPckPwC zbG(AVAzrx)YLXv{c5h;`WmOywgrB@QetJ`LWYCEPZ)?uc+VaOidD*tSr}MHN|Fh;~ zJ^mMP;O<1DR+v%4I*$J3!DIk=H8v~eh-6t0c6j39}qopv3;iB*>?e_+tYvn>=|Ku?ZHuKgxSj(yl3R} zaLF;&#Kq;X`^2=$>n8@bcbuy@@8D~8yex_T2xJ^n_~5ycx2dX1SKozDp{vAPSrS6l}*?T=KZm#PB@4P{xD&Oclr6qbxdy^?s>EEVYAyvPj5nToS!@&R4aF3+!N^EMjbcML!pd zf{x^~K!P4WmNFUq&oSS#0~=7WwDgPO>lctqvy_PTVyW{Awxf?rPAyGb0ZWUsAwT4Z zkRMrAZUz7IC>wr(^>JBI(EBUaCnPq@_Hp&4i5mv$8#IJOiwq$yv4eqXx{$rB1^)ZW zJ<0`kFi=Vuv;4<{u_natD!lO*8N3AG_auLfBylIq^gEKj0iO3-gL=y1>5`waI8~c< z^*|+4)pnj0v1@)ACDe+NWxK+!fIG%|o+=}vZ#al<#%Bm{>&FG;KJXsKryO`lJx`^f z*cSdRYd|m7=h3w`qGDM$u+to{Zd?KSsOyGW7A`~f;AKI$nPp*Qg2%7fdpXPAiAwbv z!(5@UZg4e;2f_HVb%X8Wb>m7-|@EvsGfXJpY?CTEgVzmCbJh*1@(b3DuR-+7$2&{yV4%jkkUP2Le`np`0y`L} zcxB9rSD6#_x^Xe<{nB-VrFJk->xPgQamoMAy1@l@Fi;jTW?A$zgoj=?bih0f4wkiV za3xqnIS27O;%_qQ^dxUZ{Uh-#@dwWTjwaq> zVvi=?X5tn$^BrI)dL|N_gA`$c&GS3P;#g5RJUN|9=ypwI+`|tX-uN`&yp?xxhe(s8 zDL7tv7ujm!&9dF$KY}|3&9mm->w(Y52R5tgQ%L#8hmQmP=5vgkKDio=w}t9}cf2+RddPv3Q% z$P1EyO|IM2zSe5?GPl6o&dK4DMyn6F2$cL8O1SZMZoH_G@^~okLXK*DmTev5k-DGc zqps~#9#U({wNRLIM(3a|J5iam3m)Qj-G~J3>csO$H{#Sxu19nFZUWbI@gY!7`1PT$ zabm*I#b-Fq_!^j_4xjl1q2hou2q#AR;HqAF>U5g$L91@$~QkvdoX8N419hgR5Xe@;_Mv zUUvT%=YGV5XEMen`91NEnezJW!Mx;Z6blym3;o{sUs-S^XrcZFDi%M*B0u5GS6aS1k|74=5cDuhQewa}| z_B%@;HPY{o!zg)a=p0!ZU#NMwJLiqh(S)?QMH6^&?hOV4fv^b#r8Be*bkfe{PS&Cr zWAM&mP;n;<7pZ1!%{HS5%_vgM@WgRB#T$161cS9b{$Sk0XmQ5S;<%Ru=CSfJZD?^k zk9o?_V%0gy(BgPL7ta`)Lmy#iX^w?KHA$VPy&Qyj!NFd3#0$9A+hECnk>F!)Ay-=x zC<)_9o<{;d->Q*N(ub+h8VM!xC|Y|EQzk|NX9P>k?VKJinPE)@G7?I{erQStSg$jV zIm;U?tzG0VjTbXo=65D1i@(d_C1CIiY4mDYyp)LvnkZvp2tcx&u?Eeagfw0O4z$Va ze1~p$DH>jy+i=YEm0S{o(i%XRn}+b*B#o2d?H>}Zf{>b#?!qv)_%6b=JZ1Gx+HTPy@VII5{3C@SF06$p+xsk~b!vt=mcE^VaV>mRs7a!s}aGqss!OtLclbDP zd9Ae8i^qPRSueO^TNv+3_}Kj!n4LzXXOG=awIzmglN%tA7z<1;(6BCk|8u|;pTnn^ zgxIIwN21@OMj`V$KL3t>9uGMzF4wSEhG7Eg`vK(4UWWRh{oBYo>OnidGnGS`MINtmzhaiNiS#ca|fq64*^^bn$X$aeAAtn&n>~GOC6*4VKK4G@w+=b z0S%2e0>}~nL?C)ttD0SXcS2Uzj0J8e*ZdfoX!ddjFL0b5E}3I>nbhnGAI}cl%&wdS zY{&N?B`;n)E{KED=`D!!xASX1uVi;Wj^wt~Gl( zgJ0Nkn)8$=ZFbA);E{)cnf{z%@(Wc}!_oTlwKKj-XX5snBeAdmZk@_q<;DVX0+4es zIAmup`Xx@Vv!8~X*MdA|vh475Fgc!^5Jddr!)GT#>1q|*)_lUDW((sMn%uy+8sjB1dInQh9K5EHDo+wWe8$5;P zfbp2c5Cn`LTMR+K_=&|3#4;0K0%$$yMBKxY{>ca2(pXntH=%w)!xUyYr~|NQJkc>b zh_)j=gb+QZHI;~UZA!Alnq@>C1>mh{rKi@Z_2Za%D_WY7_cG{IG4f9Ah;LwF{GjWN zRW&!~dGbK~-I%}+23$M3lCb0w%p>eVsRth~KImQI@c9^@a{XD0^lE&dn&#reRCa?ss@NnQTdpqHNVj<-2!gzwxTKKjIa#*lgRn=-bzCvUvXN528bNuvr_{zs5; z(kf$Txsxt57!L~nC;5#3*?8RUqkS;L=Tt&`po$zu)X z+z@OH2B@;?pU}A*Rt4ynCI2iB&`$(b2)s++MJ0Dt1jsY=-8?VN4Q@wW*ZbKLql|06 z%4`jm()6KS(0PaXxV@BmhMo%ile~R+y*_H_4W&++>DyH1q?KaBso#*nn z_Hk1CuZvjEb2UX(PCB=Y@teeZ=T$KNN-B`nSJ``#mmjX#QOlURqtX-R`AdIvB1)G}_D`W6kr?FO08OmeL6o+=_do zEvFmY?g`oQg}m6NTyr^}QNJ*}y5H{oYHx(}DSg>dE`gkcAj~ zLxkE1Y9emmR|<85PUd2ZBh&(+95f2G7SV%4K0!Gr)k@yyB|j{mh`X)zLS5iz zDj(ku;M%`l&6JO35FVN6^~xWX{}Yxp3-u?VhG^>k8qTX?3gw;%aJi7CUJ_~)EtR}2 zQo|TJRc!GLX{?+`<0wMh_L|Dg#S>{fb<0FE6<(T1jg+GEkhck&N~U_y{vx^tex8W0 zRVnIOp_*yC2WQ0{9v)O@;BD5YgST_%*$axz^b z)NV|U6KNscB-FKH^%D9ndZvh8EaAN6`0AJqZTJOt|4~{=_Xzbjq1MxVLScP@5QXiMgHYyIS%t(NqvR zqjVJ=kUT|QLyrq}zerwBPYU&l)OR!eEXS7HaG#KMs&co}v(nn3SW_m_-Si8gjuq-5 z`aKP!x!zmzhS5{b+cWTu4DP2Zf_wA)w7lRRJPebI;on_+!Tk>B4}o9leh9GF{bRs- z&rhM@UgQp=r(Mqif6Mhkp3|AK|2l7FI&Oaj@MDwVavQ_{5;#urjaM>~Kd~P~ZmgW) zcPbBN_ysj@fYTRb_*u~#sBxW_Ejico3UFoBeD_`Qy`>Bn+8B-^h9?WW zP+*0?)dIH)+$^v`;0%FR39K=>YyohC_KFX=7Yto{wnuU~7G5uH&y~O1^Ko~+?o&NN>@D?&yBeC8Zah4$Y3Qo4cCfMGM8Q}!|~ zW7YmST?75C<_&-$V-Mv2Lbn5!8xH|qXfhtKJp;Jf#`vW+=I;~yXM(>c_$WK`4JrFK zfu9rZ1c!~MdYR~$RwXq(orqDT9TUK%e zWV*}8N?rB92lm2b;ZMuqw}|{w@aLN=03EhZ#KYV>mzb+_BsYmBRJ(Y_CSS<^a}-^ArYlU(BCDNju-w@!oOZ{9{X0Q zC4&1UN^|9v2G+SGgozZh+$qnb(y^|+?mg?dI)UZDneI7kpm&u!gJ;Mw|;Y%ei6}V_Yj-;EG=SaG#O-sIA{c3@S&dN}~ zFYwYKE#KcSp%gUm0DjsdGx^fcmDA6sw2Ob-LW5k_EmY zbhD;bl`IAI15NF!Smi6H`!scP#RgE1DoV6e&=WfEF8^Appl3Bz9GLH}pcgZeUA_wX zqt1JJ=vwRt|E8%Gfi1o&`dm@8y?Q&SA;+lryu4;D4W*E#uCFNqHCj_oS6<+&rUp&D zSa~t1shVP+)zFEWVxQH}$(s7C_;O!}S~ZndvJ2E2O|31x-5f^knmVI&s&5!|YUAc4rYw1|}t)`xE6oGnEQ@<;}*B7Qg zY3jZ52SI(LsTV7L;u}q$Y3iMdr$ISJNIysoV`zv_YK)Dckfsh-z33Z5VNG%EW2jzJ zci4Z6yvdrn*M108lcwsb-tpB^i>9Vly$@=cP&-lIN4~MNT~TzQ;~%~{dO}Iknbn@c zIw~H?mi5!f;$Y!%^ogdX6;~A2)7|_qgS?53VTBEZ=Hq84C2GbNj;D)+>ZfD<+dLDf zX*B13%YAU@q{2p8q9}Tym7xr@OT|%iDb2{Cqsr$WCXgb}dB+=T_3TM!xilW!-ZH2SQsO1`VQfW~K zsA5IA*}Jo-N>kjovuLDHs)i;Srzx(XiH=v2;?EOlfuc~uKYYz}rKU=Y&MIuCTQiad zp3U|Spza?)JvxATLMT;xGd-*F#Li}VQB!PZGaXVCYVR$aLnFsp?YjWfWJQUcb7)b9 zGH4#H6iU@Fk51PVw{IS8Qj${Ne2Qy|>zhwoH8r++EwxZuQxmI;KwYdT)OT^=Np!oS zVA++03+a7Ll@?tO3J;+8QME55x2CxEh2$4%r_{cP$~DEcFQVa!k`^td$%;Y^P3BVS z(^M&5sx76j>vB7)cG#BEH#K!})t3&UJ zKePzcV~RrU2A(jV)Ok}&ZZBL;&uOZ;KRBV}&c|HBGga{tVPRntB`a(n|V3 zQ=ed7T1lU1is$)NM0L`e?qdSKDqKY#MM>?akYDFbEqSHz6e`zLOX-`Sh7FLMCDa?n zpYT@e6xxcROZ0~EtNcn(7io&~R@2@I*}T>Cuuwc}7&trlsZi&W(|Et|RC-gWD~Ajl z@=4)o^g)i~Y4kTOc^kctJbNSSJRk3J-Y;B3#X>1t*3uP|v-Pc|-I`)sPN%14Wb;m^ zmo>$C>*($ivw7=izos~EJvBFH^VZXHO>tg?s_=HjYI}snYKrqV(1??=c^jxvQ=HdE z3r^1Fwb7|UskXP%Mxm6K+Nn#Z^XYf=Sz$X}q|33ED5Vx%-ZdR$W*if}?h&uZ%L6>DiTy{IX(vIx{cP4W5{qqj80>tBri ztf{d>*5Z`oubP@PqzKgKn)(#ApGmI8Z1qmJv!Do6k*4mdT8q>3A)0!)stD9DO$D)I z>7>z`8tN|sHQp+Rye^umsfUo)MY9zpaTceObsqaAPOX|^zr<;crr0mtbb+RBDq!jo zP3?o7-Ly+nZp>0=(T$p_z$|qZ-KHsSdxGxN6t_J=KhhMpJxM>&6t_J|Picz%nWE=4 z#r{muE1Kdy>Y+C@#eLL6e^ivTZwq{B5dSV6gN`raQ+>0Tf1gJ)j`j_D6Q{oh4asBZ zmv;vWf7`(vh9+Gcv~au{5=oVtl{9GvPT7>as^zP}SNyF1A*pMO)WQ~iwX*+RNw(Ic z?M~MJq_n+R+N+@A^a)?#t8o@-(neTlqf)#o!E7a*hs92Xzasd4@CVwg>Qes9wKLZS z#UIQWEYIGyX%8PgcVO+gGK2Bo=d-m6cB6Ip{U$juH}_xTs+5u$T+9DC&PdE1qwpDx zPc1&<@TtS+IDG2yX~1U!J|5y#!w=}dn=QtN0eX>NiE%bXV3WWWflCFh78nuO4`|@r zwsJ@by+AyJzCgTYc?{tL4x@(ze+BSZ8f`NFEGfH9$}*fl-v*oli#YcVIxl}F;Hfq9 z=m1JB1bn<`1zl;Li!uGKF|8heCEznvA>$77B`LME zXf!wni|POy{f&Sx>Bd|+JL9&d^*{=7x*7hHub4NM53$`( z|0t`qJxb>fJz%_H{;B$Q^Ih`_oJQS1n`)-o^67)(X26fC0)QMv?@HvnYpliWeWlqL zJlp0mc{ga-9xl4T_Nn>f!eR7;)cCB_SSPOrDT6CJd!RD=Ovrn-#24nUX zTQ%0CrM7Dey6sDCe+`~(Uu~-|+F|EfuCPa-r-(j+hi|jTY@VvUfPbj|mHji}*p2Z4 zr^9Y=d-DzU?FecN9^>c`&*x)D$*tpFsFD^{NsFqaMcZUNa?Bev0y47@x1HEEoP`>f zJGPm3Ioch?21iJh!4Wc6T32OogzPaM$h+O#V_ZJ;a!0@IPxf7oi*0|ayxkEIombl0 zs`oiYAOfEOoLlu9#~rpu3x4Og()PE~w;W@IKUVmS#T+&NWhMjg-35_Q%q5obAGC7fuKC-0Mt< zwga}j^5>i}l>Lpf0q`~FSn2lgy{YQTi!yUteOd=Pxlh!W*TQMyaJyYH60pxmEPvDw-EaEu)R%z=`e# zrtue1sm^irWmgloaLTk^cD3$lIz+lbv>a=O)WXk{j8LF zQA#x#XBOS$ZZaOnT-0QIR(z|w#Ry_HYccMsYBUUZ~k1Y_7Ef9~zMB9flvOcu^1J9eE3jV3>w}Nl=7SV^cpF5^| zV#dO%X3sVXRh;ZG>~~PR_`J&GcwVF8+2nX$BXzBjy4FZt7aR8!Zu4w2 zcvdX8-{830Gs5JtFjmTrl~U!l8}onXX@uW+Y(8K0p=XadzM|Y~NL#iUJg03lKEh1C zO`hf&?AI1ld-s@ssXWH(#*_L=zy)*~;1XI7xSXPZr_dRIYbXxbE;5@%rbA>>!r3aE z?cn(6DM|r8OOF6PPepk?%xdL;FVk?qS8-dwM~A2p@DDT{@IPoS;Cs{x_!l}I@FVI3 z{Digwen#g5l5rWJ-S~E1i1JX^Fg#4E+tT7WXgfu&{6G&CV}e(Mg;Z?+#~Q7f%^p>5cs&j zHw3;Z&~UIeo4{cLj}h1;aJImRz&3&X0?!w?N8l|24+wl*;2Q$p6i80iZwRyr3<(@2 z@EC!!1kM(?USONRw7}g0_X~W&c?+~15G7!jBj*e`Imz&!%@3p^n3kia(t8eY~P z5;#oYEP+h|*9(jYObhH6xLe>Jf%^p>5=eRC6M^dmrUmX6xL@EQfuVen6u4esTHtPh z`vo2n7%DiEI{mW%AMo!6{F{H4?@XE)SP!@@u-nIa_6s~DkP1a};hFSEAT9WAf%^p> z5*R8vlg!|uB9Mw7~rWshs({1@13r z{f7jGDj1(7aJ|5^z}*7(3p^x{Dp@ilaF)RJ0@DI_3*0a8kU**u%>vg8+%0gwz(WFQ zsOS_pOW=BeX@R>1?iYASAXT$`9Zomy#cvS(kmeg}jhJzPaSirv|7N^vc(9VKGEcVs z!8XS6t~2bq-1RfpD9;WL-?6e&A>MfwhSV^e#1E(0SR>|Qhj|iiDlNdxn3XtT=`FS4 zJi8$|lB0tZ+0wVG1E z17$dD!@3yCz|Xu{d{x;<@YfX70{*OkwGGX00IUp70-WwV0dQBzOu(6?a{xcAI0^9I z{hWJjUJ%ZhxRLgwq;O9ec0Cb2|s&z^xx23rpg-@yGT5{!Yui67n zRWZYtq%HGHz6boQ(t7~?;s@2z`6Z7C|4G1k`K)J*z>P(WJ1ZHg7M(DJac=>`A6GFP zCLU9M%Po74$W)6}xuujI)@nrX0}|7LPobH> zrvjSDYXUwE5a)szEhe~g0Gsfh$HaZU7QkkV4+GqjfzQPo7=Ay*w;t!?H+&k9SPc9m zxlMR7powwT3jRVs6YpeJ0ACDfB5xJ&Wq>B~Rs&xSXd>@4;41-5>O2Ap-9pksjTIEUu*bgWJ|EAIrp2@t>Y7bnCfW~8&g z*#cMczivdmCIP3#{IiN{bKnl0x z08RQP-hCQ)JJ}EX8af~NwSXp0m@Wi<9iWLDiC+W$9YB+IL!W_@q>F*yOy2;03!sU6 zikAYvP3}2<5B1<~6QGH6sVjkhAJC*9@LN#)s{E^f-$}aw?~=QZzd&sU{Swf`Zv1+{ z7wATCeg$aKzoTXY`}6Mre+e}kn49+i{|#z3uw%ak_$#R2px*+TcuVm;;I9IjbP)9$ z^ctXv@8A3Y_#r?OHw*Uxe-qHe4*xE|w{R+l(`7)D-oaf;1AG4afWM2Ilm`6~(4;@% z9;Jb&fFA*WpB@7KXF!uaz>P`+&jpWQG;A=20=649i2jYhN6}{BqwyQUa2tWP0t^huPt_41U zt^>Z4evH<*jV;DL<6+~V@v&i=rRE58p1Ih(+}v&c()^A2q4|mVxjDktX#0lkD%-ub zU)maA9%IZqhrd98SPk~ARPw-xeb9PIgVvqOM zih!!a;?2Z4wDngO6tCGk26t<@j_l7uZ(8&*hpql8-fY}%D*TcFe}mYD*^bYEow&u~ z!kp*A$*2o+qzmh~8?&SbYqa&uWVx4Hr8WTb=ja}G-&>EUEiPe(^&Lq~Ld1GUWQ>gkLoG_@pwR<}h`oHIMp z(a{1m zU1#+~I$|k^cXme-!ehCnvm>ZSNhoVlai(T>#G+lP)@Wi&tS!1MdR9*~nPN3otqWp8 zH*K-nH~xsGx3g=hr7JaId?q3$)K7w#?u#P}sJ;Lf_H@peh~hxeQyOg(gEY|@>56TH zMVjn@F&brGa~<&Yc$(YO)wX^-ogCe^GSbl#T@r~UKsLw3g^|QI5cA^kO&!s?rtVm> zuBj)rxo$B-)-Y+^y0a6JZlD8tAMI+GfNbv52{eEF@gTZ8V(nZ%(5#tSh!YqyK^eo8 zRy<)q-K*n0%X>CN>$Xe~Z(*2VXt4T5Rac`b&y+3?bk5ec&5^E6(dF?oqg^v#*+gW= zRRa4!ErXNdiRg-CG{O1Dt9(w7kL1XvO;R~X=0&>@nyKhQ?dqfoes2C`B{Z2sP zOIoOPTN2Sx*Rq%vb~mo1E!?f^)_s8|>zX5}2ra>w-VkX!b48*fDGZ*UFgzlZw2)~t z+NdegW@*oksAQeAF4ji5j*_9*lvLlF6Soj%l_SX69r0weeRiaq+p;_X12U2;W9?Bj z0dcv^Y{Z0Q4KB(ussl1YJV!=QXEf3|Aopm!MoS`zWVAWnhUum&rR-P|Pim|^-X<~E z(k>p+2_h%nlW2=-QVavWg>6U5PE>P*7GO8?I0U&M9%-khb|r-26t_Y|RWOjlidvEb zc`_-j=t@R5Mi+PSgw+vEMU}di&KNK!4$fYHq1Y4I6djz?($yZ_x{OEa;Jk&g&gk-O-ABo3jc~)4VPFg{ zbz&^VgL803ODeKyFn3j~J+*l-dqHGFv|}(ElVz-ZMOP|zl)O_qkHW;z8mwhOY)e$d z=TWnpd(Z$3jll&u@;Z8so+n{AxCoDc!CV=2N6*sZ^5~gOJ?*ihsGA=_m>xA}HvaTk zTXL`gxl_;JTy4Wq$_sO`)JH~qBD{jTn*2AtgGvMF?^P|{s9fhyL zf3W=QcvlM3%26_~ICVukFk*X-mW@g2D23)@4v8nW4c4|)?Oz6E@GO3EtP5AsqN!vm z($yYGv|DQiwIq4JwK%bAGj?z%!{UYCY_szcWMsTwkXmmS##9hB6;Gr<&jOoh;$zj*p7VlP#wPtZ?LWYoCCz~W}*`*1UyXl;b z?V0#tc27sD2RJLX@PuB3Et>xd>Y8-k`3e)+%#JX)di)Xt@y<1fo@j&A7Lv?-cc6z}3q?9nng8nOkBFe#hS zluSlDH*{=U9!urQ59;=8iRF<596c9*@iKaLJaJ|=!!l}qti3(jmF3G6lEk8xWm&b( zMYC5%6GzYFWr!zll#FzIp%muXjj>I7+053Ud@6;PMLQx}1xsf0Een<;u-$G;Wi#eQ za|pzGHU~3vq_ZiJh-}O9EHzwRq$?*wk3-?7VjE)okeMx}_stT2G8_;C*x<*yl8V~G zJ9ui1YypU!qe8F+Q{3W>cy`3D2`L9VZ)+s-1YwR$oH8R}vnMjLIA=KeNkLmzr@hLu zbCPY5?kGPBs4Y9jjYi-Rjm<@WEbWOVwrRSxXG2ncO$KSb&56j_tZX6A)Wk;=65NPn zRRWuZ&0>{oy~K2dp^g(VU7gK4G7(r1?b-x1cTT_tRpm8pkgZU3MIr_(8S)`P0%rh> zXWU?|A9!j%@}Wb7_(7j!_@FqJ>R6;hRn7Z9u}yY@S%rG7Ra_ibKuWhe_t|LihBIIZ zp5k(|JFyU;3dI!*GrLKt3Ei$Ymoyivc2h@(wOH5T0k5tdw5|$kkXXbiKT=q93pN!A zCU~aI5M0$_xLaoO$CQb*e#>|kIvfRrIu4cf_R=F>C zuPZY1JCXy%cmhCZ;Bk$xerk-hakQ(ZFp|5>Ye!I17mg@eH%AiPb$kj`*BtN6;&uro<*Wl@L~p8_G3_lr&6X%(I@qgq)~fZulGq!#IL{kYyupgfQ^ytQ{4htFRu)DvZ-z98qNs z{&F0l<_|dyLNj$@q>H%)ZAs}&jp{Mn9E)I2n@q)YE*>()t(&CoBAm%Y5B!k z?=8P%@~vsZ@@KY$c5pJY4q>O%9ZjTS(PUjl8h?db^+OVyTHcbv*}TRbt(3G$o(<4E zenc2(GOzCXn3?s<)SINiqhW4GWRt~L0iif6NJ<5BWDp*oi>0m@R?ATtIL^llG5k7s~ei8 zcY=6?v;r}+gU)mhKP$scn|eAT2}A`NOwZxmb<|j5D)O2yt+x!;_jY-Ik{mtw*yab+#h)VBno-|3zS!T zjY{ljT;^nrH)9!3s0l*t2l3d8qv8+Knjqn&!=1DnTVl)XKtA{u|wC1o|^yvg2yjW<9bznd6 z_?|^FIWmW5C^4cZk-)x6>g55(ICrCkbGO0S>MREj+e$)A(Ku_d=5Q;Yd2_njyRo}V zVZSK5!wj9`*H0NrjL1FtQ`HaPunZsnNtPhpOf65uI&p>otObWjNYGSRu!T|HDOtF6 zR$y64s}eZg%TT$aL7p7+AkfDV7A1?hMW_lbir;QpIBejfFlONyOp=B1Ezw1omvUED zo~Oj=g5dofPLy(oJg;#)%=B!1^t{fz#R_pPbnsE51Ui_*!m8Vp~ zAWtYkebgs~^jslcw(#6FKt0}AIq~jwbIwwSth6e+VFk|r@hDGoI=cb!jj&D8(9i##Jxp@O~M@o!3r#lWhk!!P|M&qD_FRd$GW9d-gs)T zv;v=OmC2iceAWmH@)~gmLf0~7HLUBw9jqSQpNitX#1eeNH4dpL(iHAe;dBk$Ws>6GNfP*WFfWo!@) z?YL_d!_BP}bWX=j4=NarJmv^h&QvGpPH0$;8(D0lCqNd;Nzh4!Csj|15ksz^#AW$(cYJ%2X0fRY8hladConY7OUhA<#Q< z5fWeT1l|SvaNwbwZo_Xl@mK?h@QX~;u;O8@1{-5)2wH<4!xzI}4Ow!jVNEVIp}QsGmb$4YPGRE@iFi)@ih>_m`q=yT;Oq#+^Ppc}Xf%+7I89q#1v z(BVcb0Y8GOdA??o>oDhW4Jq8e8x1WXVBA!mp}47P4amxKjXY1{#;vCbPLP$+wGevo zD>x?b%*XD>{gW?PDVn82vNIM9n~nN7$W&)C#eQSPb^=?3I958HI2dv!m$pIFaxSY{qeoR2qJfvU5JW@hl$p5H zeG;0+4w(U|UREg&8xWw~yd0iG?raE#KBzzSpm&g6vCO%IQtvGTZoQ;q_8$Fae zx3eeMm$`GiWev~oTd;;42TS9aJS3FM)mp*5nv^zVa!R7Ol+W5!pYuG8(63}_qvmZM zSRyYw_fko=jvUYAwMzeTACP}Jtl&6dAK^e5{^w}n(M0o(Y-=1|Rz4m7r7h0ZOpaBU z?5J|!lyOYXJWrl3Xy)mFD!Fd8yX8K*b%Lq&n zev81RE`p%HzsV7zK>r*U%c|7p3#8wIZYde)U&sRKmsm}pe|nM2^rx>b@kfl3K>A`M z(3qElyYUBRkP{?VyZ{!$DhaFwJC6%r%OZhBr%FTuzq=ZSIsk_H@>%9q z$xL72(y2(@TI6z}(x*)y#N7CY5-@$W#q`3$l0ZLK1XuWM?vOdu=kxbRK)G#jhR^Lr zv)rcB$N6q6d>)auLE1qkYOuQi@b>_GHf_>gF{Qs;nbcn{ChZk#@7Gzv zx*sJyZd71%OLHHT%zD+_dJ%ch$?gc$+e5hLFAjZ_g|tIO3dB3t#@d49wzP_l3weMA`di$HIG@jFWxj}3n21V- zbfPOCodJzLA2(iye=dp&m4TT&86lctdkNtU^{S^ZXxM##T+pkWhbG+0wxplqW(E4M zbdee8|2AZqx>3yBgV6wM?@&hH;VURIjB0k{IOf{ChR;QIuE zk!gx-wdxx#388ALHeiD#TLhilHm$dsJYK8G`6yjr5f~g9Ulek?I^kRq2vWZISXU0q zgUA}q-P>QTnSt~RRe}Bn)m1NuA3z5Br@&dz26bo?v^<7hDG0%A9zic)ay+2B`32q0 zFJ!v;1%G<4`09X5&2>4R-HX_*^5v5)(Epsj|AoMcz=}dtF_h~W2Iff4WMU{Rg^bM^ z#Mgk}|Cjvzui%&Z+EqDUV1*b4{t?BiyctbZn8kt!o1l^9Vc7W7mm1z;sUjn)n5;?} zp=b}cU&P@$bgci#QZk$>$f1owar8f$q>%oXihxMdM#JurRkkYhRlkLML$;uetqYnt z6^lx#STI%b6v(3fx8U}MQ7-S7cVD_cw>R$3M)v(RF1I_dK2XnL5=dVp{ocd#K%fu* z(*1dAWCVHw>3wQ$=}~h_k2SaS_|p&S>G2|&J9y>m1BV;mhcc|I!sT!W+7V;CDsm+n zoo@*cw>C6D(UeUtqJqv0^OGfZvu%(l-XuH!7*XeE%#r3#0Dz z9{l&GzpIEl0_i)l6{hzo?mn~*revH1o%>}D`?kOj(F(5I%kj%iNZ;sI?MvT?hH@o0 zVj}mlaR2*CxH&7l#}eM7h4+ZyhobwtJO{YlS?N10={vOa9Wsio1uwnNhvA;3Fr+PS zruX@C{RcA5s~{{NYw6slb?!smf&Pz6T~2o(oyHo7|H_@fcos)^{Zl2Mvgmn+Cu9cB z!$S7}=HkG4JpJ=($`kqn8Mi&)Pd~xC2cHCG`U0MpWuC(%EHg2SKc`B)fO@iV_-p~` zx#YRPd46lTdrsHzT&9NSurfu}@Px?Hl;D8CtEG{xIV@j`N=1&t#Ir1{4F%vxt z`%E8Z9XB>en8T{v&-MmCe{S5trS6lBzjrnM?(849|D*BhS6}UH_@ldi-{QCLz3tBT z-FMGicdq{>FaPlqe7TYLn|6}{f63C$Ogpv?H227D^Z)F*Vfm42tljtj&ZCxFJ;B7u zZM}(bTQ4Hq)_VxI^%}x$y?e;F5Iz(1T8Eh3X~ zc9YE+sKRHd-5E%~!Qd^^X>z&`+_4^nu8g&d>`s6BL*{>MI^8y>e=`XodT??#JjNmyFnGz5d(MGf(-iETY;OEuFyYyQ0z(*t7!)(8VlaZiQcOr# ztHxsY7ibsJ&DeqjIxxFn4M#_FMz>_77~Lx9c09_!HhI>A0X_%eiR}%6Z(-MlUU`dW zxb#~x-KF2c1YwxM*{3-BxNfc#^JsROz6jHhUd7TEsRzXL$5QNL7wX1-=~I;RPv&wA zu`(E}k0TbuEaA^$bef=mFyazbG5%6 z152@{sCwj4El{7axY6P-V81PJ@m6Iio*}h#`T?=|0k#=kmMMny(rRdPhKUu|V$N3u zK9oEeyBUnzg4IJ>fFNQw^O9h5Bc8$>PXHkX#SE$#j9{?T;g(Qf_?80NJNRcfrz_b^0vaV5L#Za5hzH{TL# z&S3KeQ!V6N#$LpqGj?NzAv_Y`xMR6lx(Z*k#SQ<=g-Tps!i5wAa=b7R;g5or*Dac} z9G3&+`#odzb?)g~#@FF5t}gc#XKv~1Z*j{PJn(zb^Q?Pc{OwV~ADwaGl6;iEEg?h$ z{yrl9;diUxkCX_312cn8v`S?!=G0l&3o@+QL0^3x@EuwHnBe+W^iTd>t0r!xOv4>0~^ObOPZSb{~xB}SK{L5U3|h{`AP?bjeBj=W^V252;r^~ z-!Gg#9FxKDP?W#%hFg@=hxep5j+;C@G;>CQciO@w%^}=%Ke1=y#%O}C)=wY4Wcm2k z;i2wCd?PN4Cn28bn%dbN-88iwUp<(r`BP)Kp1g5fylY&0v=bBA)baHV4Z}kzbj0-G z{DrAl+i>U)g{CdRmAZJl;Gmk?acNO5zoXTf3AVnQ#W!8$D;J?Qb%BdJcKYxw{6+2I zp;$YbqO^wOhxPN%wBwE}Z&i2P(dzr384=;{%PJ$r|8E;H?YN9vrX7bqkiU>X^Z;Vu zgGYYy|GH20?6m)nbsrB`D%&5sj*Nv%ct~G?&u35J^L@@yKRnEX_#BJRB~Rn?1I4#s zk%j1VS!;9a`41GG-Z}cUlWtnybL}xL)ACqH^VBu?n!x5rv?DYxo@kFox2(ZePU1=a z79*~EuhFw_Ya8|>(ZrXK@eOC7v}`8UR8AN2BsJDk<~% zNGLhdJp;Apn(^V^o5xoY=HksN zzfa{`etgY{e?~~D<;dlW0!ifO+@BGNyX}pz6@P~l?_sT%;e$2dmmosMy7a|Y(>AaV zaKeN0dAyZXH*dI(ZD^5xsg3-BH;d}13AcM%z@H`Mta_SZHNU>+`qgC$zHGriUsS#x z&qLVY>K3m=`9$XBIhSj|3-bERhwCeXJbQ-UHYbqst)Q%>N3W+2ufzFeIrc{Iyn)yG zd`FW@a*MmsR<>B(CnZc9-2Yt9V$J8P1uSjZ$&|eA|AKa9>r;MJw{hF?%AVhwXRZBL ze!-cB+@Ru%9;)1t-pTrg`;YzD1mE!mgig7u#FxvyxD5OC|I(lR>bKdsr~Y5szW>*( If9Zk$1rEc!*8l(j literal 0 HcmV?d00001