From c11a04dffcb099b199ff2d316c1b67230fa8f2b4 Mon Sep 17 00:00:00 2001 From: Andrew McNab Date: Fri, 30 Sep 2005 21:03:39 +0000 Subject: [PATCH] Add SiteCast ping to htcp --- org.gridsite.core/CHANGES | 2 + org.gridsite.core/doc/config.html | 5 +- org.gridsite.core/doc/delegation-1.wsdl | 91 ++++++++++++++++++ org.gridsite.core/doc/htcp.1 | 39 +++++++- org.gridsite.core/src/Makefile | 20 ++-- org.gridsite.core/src/gridsite.spec | 1 + org.gridsite.core/src/htcp | Bin 31631 -> 41943 bytes org.gridsite.core/src/htcp.c | 159 +++++++++++++++++++++++++++++++- 8 files changed, 296 insertions(+), 21 deletions(-) create mode 100644 org.gridsite.core/doc/delegation-1.wsdl diff --git a/org.gridsite.core/CHANGES b/org.gridsite.core/CHANGES index beef484..ba03db6 100644 --- a/org.gridsite.core/CHANGES +++ b/org.gridsite.core/CHANGES @@ -1,3 +1,5 @@ +* Fri Sep 30 2005 Andrew McNab +- Add SiteCast ping (NOP) support to htcp * Thu Sep 29 2005 Andrew McNab - Add SiteCast support to mod_gridsite (file location discovery via UDP multicast of HTCP messages.) diff --git a/org.gridsite.core/doc/config.html b/org.gridsite.core/doc/config.html index 2edeb27..8c3811c 100644 --- a/org.gridsite.core/doc/config.html +++ b/org.gridsite.core/doc/config.html @@ -36,9 +36,8 @@ GridSite also includes some commands and man pages in /usr/bin and You must also install the CA root certificates of the CA's used by the users you wish to talk to. These should be installed in /etc/grid-security/certificates as files like 01621954.0, and RPMs and tar -files for many common European and North American CAs are available from - -https://datagrid.in2p3.fr/distribution/datagrid/security/ +files for many common European and North American CAs are available via the +EU Grid PMA.

This location also has VOMS server certificate RPMs which install into diff --git a/org.gridsite.core/doc/delegation-1.wsdl b/org.gridsite.core/doc/delegation-1.wsdl new file mode 100644 index 0000000..35c46ef --- /dev/null +++ b/org.gridsite.core/doc/delegation-1.wsdl @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Starts the delegation procedure by asking for a certificate + signing request from the server. The server answers with a + certificate signing request which includes the public key + for the new delegated credentials. Uses PEM encoding. + + + + + + + + + Finishes the delegation procedure by sending the signed + proxy certificate to the server. Uses PEM encoding. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/org.gridsite.core/doc/htcp.1 b/org.gridsite.core/doc/htcp.1 index eff665e..f5ce2ac 100644 --- a/org.gridsite.core/doc/htcp.1 +++ b/org.gridsite.core/doc/htcp.1 @@ -1,16 +1,23 @@ .TH htcp 1 "September 2005" htcp "HTCP Manual" .SH NAME -.B htcp, htrm, htls, htll, htmkdir, htmv -\- get, put, delete, list or move HTTP/HTTPS files or directories +.B htcp, htmv, htrm, htls, htll, htmkdir, htping +\- file transfers and queries via HTTP/HTTPS/SiteCast .SH SYNOPSIS -.B htcp [options] -.I Source-URL[s] [Destination URL] +.B htcp, htmv +[\fIoptions\fR] \fISource-URL\fR[\fIs\fR] \fIDestination-URL\fR +.br +.B htrm, htls, htll, htmkir +[\fIoptions\fR] \fITarget-URL\fR[\fIs\fR] +.br +.B htping +[\fIoptions\fR] .SH DESCRIPTION .B htcp is a client to fetch files or directory listings from remote servers using HTTP or HTTPS, or to put or delete files or directories onto remote servers using HTTPS. htcp is similar to scp(1), but uses HTTP/HTTPS rather than ssh -as its transfer protocol. +as its transfer protocol. htcp can also use the HTCP protocol to query +GridSite via SiteCast. When talking to an HTTPS server, htcp can run "anonymously", with a standard X.509 user certificate and key, or with a GSI Proxy. This makes @@ -42,22 +49,33 @@ Instead of copying files, output lists of files located in the URL-directories given on the command line. Calling the program as htls has the same effect. .IP "--long-list" +.br Instead of copying files, output long listings of files located in the URL-directories given on the command line. If available, the size in bytes and modification time of each file is given. Calling the program as htll has the same effect. .IP "--mkdir" +.br Instead of copying files, attempt to create a directory on a remote server with HTTP PUT. The server must support the convention that PUT to a URL with a trailing slash means create a directory. No file body is sent. Calling the program as htmkdir has the same effect. .IP "--move" +.br Move/rename files on a single remote server, given the two, absolute URLs of the remote file names. Server must support HTTP/WebDAV MOVE. Calling the program as htmv has the same effect. +.IP "--ping" +.br +Query specified multicast groups with the HTCP NOP code. SiteCast enabled +servers will respond immediately with a NOP reply, and all of the responses +will be listed, with the round trip time in milliseconds. Any waiting times +specified in the --groups option will be ignored. Calling the program as +htping has the same effect. + .IP "--anon" .br Do not attempt to use X.509 user certificates or GSI proxies to authenticate @@ -97,6 +115,17 @@ the GRID_AUTH_ONETIME single-use passcode obtained via HTTPS. The --grid-http option will be ignored for directory operations or HTTP URLs. If a redirected transfer isn't possible, a normal HTTPS data transfer will be attempted. +.IP "--groups " +IP multicast groups to use for SiteCast queries. IP Groups is a comma +separated list of groups, in the format: +nnn.nnn.nnn.nnn[:port[:ttl[:seconds]]] The IP number must be +specified. If the UDP port isn't given, the default 777 will be used. +The IP time-to-live, ttl, controls how many networks the multicast packets may +pass through - the default, 1, limits packets to the local network. Multiple +groups may be specified, separated by commas. +If multiple groups are specified, then seconds is the time to wait before +making the next multicast - 1 second is the default. + .SH FILES .IP /tmp/x509up_uID Default GSI Proxy file for Unix UID equal to ID. diff --git a/org.gridsite.core/src/Makefile b/org.gridsite.core/src/Makefile index 5eeac2e..1ac72cf 100644 --- a/org.gridsite.core/src/Makefile +++ b/org.gridsite.core/src/Makefile @@ -151,18 +151,18 @@ libgridsite_globus.a: libgridsite.a endif gsexec: gsexec.c gsexec.h - gcc -g -DVERSION=\"$(PATCH_VERSION)\" -I/usr/include/httpd \ - -I/usr/include/apr-0 \ + gcc -g -DVERSION=\"$(PATCH_VERSION)\" $(MYCFLAGS) \ -o gsexec gsexec.c urlencode: urlencode.c libgridsite.a gcc -g -DVERSION=\"$(PATCH_VERSION)\" $(MYCFLAGS) \ -o urlencode urlencode.c -L. \ - -I/usr/kerberos/include -lgridsite + -I/usr/kerberos/include -lgridsite -htcp: htcp.c - gcc -g -DVERSION=\"$(PATCH_VERSION)\" -I. -o htcp htcp.c \ - `curl-config --cflags` `curl-config --libs` +htcp: htcp.c libgridsite.a + gcc -g -DVERSION=\"$(PATCH_VERSION)\" $(MYCFLAGS) \ + -o htcp htcp.c -L. \ + `curl-config --cflags` `curl-config --libs` -lgridsite mod_gridsite.so: mod_gridsite.c mod_ssl-private.h libgridsite.a gcc -g $(MYCFLAGS) -shared -Wl,-soname=gridsite_module \ @@ -295,7 +295,7 @@ install: apidoc cp -f ../CHANGES ../README ../INSTALL ../LICENSE ../VERSION \ $(prefix)/share/doc/gridsite-$(PATCH_VERSION) cp -f ../doc/*.html ../doc/*.conf ../doc/*.1 ../doc/*.8 ../doc/*.sh \ - $(prefix)/share/doc/gridsite-$(VERSION) + ../doc/*.wsdl $(prefix)/share/doc/gridsite-$(VERSION) cp -f ../doc/*.1 $(prefix)/share/man/man1 cp -f ../doc/*.8 $(prefix)/share/man/man8 gzip -f $(prefix)/share/man/man1/*.1 @@ -308,6 +308,7 @@ install: apidoc ln -sf htcp $(prefix)/bin/htrm ln -sf htcp $(prefix)/bin/htmkdir ln -sf htcp $(prefix)/bin/htmv + ln -sf htcp $(prefix)/bin/htping cp -f gsexec $(prefix)/sbin cp -f mod_gridsite.so $(prefix)/lib/httpd/modules @@ -330,7 +331,7 @@ dist: Doxyfile doxygen.css doxyheader.html \ ../gridsite-$(PATCH_VERSION)/src cp -f ../doc/*.html ../doc/*.1 ../doc/*.8 ../doc/*.conf ../doc/*.sh \ - ../gridsite-$(PATCH_VERSION)/doc + ../doc/*.wsdl ../gridsite-$(PATCH_VERSION)/doc cp -f ../interface/*.h \ ../gridsite-$(PATCH_VERSION)/interface cd .. ; tar zcvf gridsite-$(PATCH_VERSION).src.tar.gz \ @@ -345,13 +346,14 @@ htcp-bin: htcp cp -f ../doc/README.htcp-bin ../htcp-bin-$(PATCH_VERSION) cp -f htcp ../htcp-bin-$(PATCH_VERSION)/bin cp -f ../doc/htcp.1 ../doc/htrm.1 ../doc/htls.1 ../doc/htll.1 \ - ../doc/htmkdir.1 ../doc/htmv.1 \ + ../doc/htmkdir.1 ../doc/htmv.1 ../doc/htping.1 \ ../htcp-bin-$(PATCH_VERSION)/man/man1 ln -sf htcp ../htcp-bin-$(PATCH_VERSION)/bin/htls ln -sf htcp ../htcp-bin-$(PATCH_VERSION)/bin/htll ln -sf htcp ../htcp-bin-$(PATCH_VERSION)/bin/htrm ln -sf htcp ../htcp-bin-$(PATCH_VERSION)/bin/htmkdir ln -sf htcp ../htcp-bin-$(PATCH_VERSION)/bin/htmv + ln -sf htcp ../htcp-bin-$(PATCH_VERSION)/bin/htping cd ../htcp-bin-$(VERSION) ; tar zcvf ../htcp-$(VERSION).bin.tar.gz . rm -Rf ../htcp-bin-$(PATCH_VERSION) diff --git a/org.gridsite.core/src/gridsite.spec b/org.gridsite.core/src/gridsite.spec index 44bafe3..53d751f 100644 --- a/org.gridsite.core/src/gridsite.spec +++ b/org.gridsite.core/src/gridsite.spec @@ -90,6 +90,7 @@ rm -f %(echo ${MYPREFIX:-/usr})/share/doc/gridsite %attr(-, root, root) %(echo ${MYPREFIX:-/usr})/bin/htrm %attr(-, root, root) %(echo ${MYPREFIX:-/usr})/bin/htmkdir %attr(-, root, root) %(echo ${MYPREFIX:-/usr})/bin/htmv +%attr(-, root, root) %(echo ${MYPREFIX:-/usr})/bin/htping %attr(-, root, root) %(echo ${MYPREFIX:-/usr})/share/man/man1/htcp.1.gz %attr(-, root, root) %(echo ${MYPREFIX:-/usr})/share/man/man1/htrm.1.gz %attr(-, root, root) %(echo ${MYPREFIX:-/usr})/share/man/man1/htls.1.gz diff --git a/org.gridsite.core/src/htcp b/org.gridsite.core/src/htcp index 96935f758f70651237d962801326b8d0a62644f5..42bba47b097c130258f3b181e0d3938065b09278 100644 GIT binary patch literal 41943 zcmd^odwf*Ywf{b6CdnZT$pj*TL>Yl7RG5T^6+vwfM!4Av4JYlFT>{9+qcA z)HDW&wY{{0)z;qjs=c+nS|8OGtTouumiA&xf5nzs+I!N_Hd@-^uiWPM{qDWb%$X3p z?fw1!_&d+^^ZCVrGeeC=!#! zMBEF3V%)6lpg}l+XNCd>8qNX)>Jh| zrZqRUW;O;>@!$-V7Fi3h6$@`#A=6S02FhKDa2>+c2(u9;A@I6D-Q@2z_`MckB*Ik) z7a~kYxC8-NNFKO}YJ@6;xdd=g*HaKiAe10XM&Nasx`{FR9>l#If%065@P34u2!GEd zb*l-7;Z7U!8m?}z{m_>MbQuD5OB?f|o_LK`H~D)JeyJW==xxH9qfKpzv}T7^KH?9lK= z4VP+xX)Xm~zoPIoGK zM?ym>&2Jp&nEpu(H){GfH0;Pv|KgQFApHWwTc^AhZB+*Es{$XN3*N8uU#-3!({<4l=>BMWeL8srM>3cLhUc*m;p7MemKK1s1 zu~+kdRMXS1c)fr-^9yTwM?d!f?|1pbTExE!eJFn^5#u$zBX3l46XGMdli$ZQf3|^y zHy}7y01xli>F>ca<2w;}Ir^ns<*xicsQKLj8q)74vAzPlqn)nLB%9ep&lKSWx7{>v z>Eii8{)|p7pAGTDJ{YcxQXz>B{yF`Z%O#(8*aEYv^>0g z-t|jDVMUs1Zc3%Yb%{i@H6p?{t`9GZHUbk(&Tp$4mr zK{A?(H?PmeM`TtrnDibZr}L>P(kh=NP|wHri&Q!i&!k0TG#$+}MRNH(=bq8mj+0uSj1CI>>atV=hwL=`zmW9fKnGkCN{(nt|iIi*tdb*&Ag zi9o3kk4fSUk-ANyH64$oL{n=t9mZoFX_zBu5*t{Sx|GhYp}skuVm=Y55QeEwZ%RbN zYf#F1fl${>p5devAVoG14{|lcVQx{Ej>9z;-waDenp(q|R1_W%hBoW9ZCmP^TA8RB z#0`l|IwcyC(I{v(!uIg3XrwM(2N3pGDS$%Z7cNJ<%VlVF)Xzb%11CK{}$KdU8-Z41&BJTkq zp5pydA)e-af)LN}K1qn@d9M`WCEhW3JHR^{zgKxjr*MdO4D=53jzP{5-Z2O|%6pX% z$9RWt9OoU4@Cn{$pdaBK13sH~bT+4Wze}2s5tth=dmrrc3l5knj@19FF!r zE#X?i0m6?;csXH|RP^qV@M^*+k?7qn;X4S=CY+G)U4&~0-zDJ);YEaNB^)DMOSneD z&4gDIu9k3u@Lhxh5>69lfqMNC-bgq>SV(v?;f;j*{{}?mcEZ~UpOElQ!uJzCBH{Z9 z?;(6Z!VeJMOZaIC?;-ps;YTI>5aBNp-Xr0?gr6q7UBdeaKTkLz;YSG{AbgjEA18c> zaIJ*DNcafh8VNr|_!!}82|rEv1mS>$pCN1$_DlGA!u^DWgkK_jhH(FXvit`Kqdh?R zCHyL3i|`Q%A0q50d_cm736CNCw1kfk4iJ7+!bb^nYSz0)!p8_#6W%W2&+UA`<A zXH_R3^I>`dUSsJ40uTPSQ@$9H14r6k^@9&HpbTxNmUo8AJ3`NNhJ2-84*5GmHb^@| zc1^8)5)y&BBh=q^$~%y;2Qt<`rr-Wg6cK39ctZWJ34zGY&@*iZe6_JdC@o3mppYPG zJ7Z)@5!X{g+ltz1& zMvB(ocA&V{{=#3CaPygYXN|WbbgDCSYWr-F9xv}B(#7&#oHqLk@dQD;LZ=2q-%lld zMcc+%qIB1Gl_5|A32*EjW_l&N8GEB*-^;lfCAv5^?1(DcE~OPnw9X}wuhzc6k;no& z7;f#{frMp$Sz|+|+P0h$X)TIdhOM z{TnK>EA)JA>~=(V!deGPYwaGDFKmCQ5|b46-Hwdqkf5iARZR44MeZ_v+?D>mA=6JE zD*bg%`Zibkh1vAuUHQMQiqtXRl|PJ1pBb&n1h?wE^X%*2l8zjs>c!ew@I3ool@xy3 zwy|8K*UE=6B3&nGVom4(NfuUQK*k<~II#weUzo)s;XtB1HbqAS^CQN@8g;}4Dx$BN zygo@@P9A@KSGhOvpLXZ*D}|SmRfzN*aDuMT-ddSqg*_czrgG)?b%wrJDbQYCD{3&U z9)}DRP3&%+_oo!6zOlKeTXfV`S0JMKvsYeO4}DZXxCt&1Vv+Oht8Vj>=S2 zS18Q$^aiQyXT(%}?7CvUIU$kapl%vbUQZN^5kM-lCv-|-Pj!{It0$}N z8dH?+oKXm8*m6RoSD|fkMWWfL8kl9;vz19YLZ{odoEDi2@~Aq!CL;O@pz8sY?=%F8 zy)1R#Uu$2YQY!U`t(T#0RYt`#n6sl^FnCBEmY7+LG0$g`t84M3LhxP4e9>Ij_#0+H}cd$JNh zi&whymo)U2lcKc!CwSWN@@gax;;FQqtBq|h%Unu#zDNKLSrlueik~IgzC-bEKTz8K zX-O;vDc#uxKxQyYY#py?2c9M4SR!YiKfELDBk6obrhZV}3@ABs1}|;@CW@#n;;Nrt zP<-<2{5dJD?0xqt8GH9QGWkr7xMVvpq^EsBD=pV3{8ye?mt)Nj?1T`GHOnd1PCjuosZvR|CdDfouN2z2QICGIQZV$A$wM0E% z0g<+ueHiEXlz|2^C#9cEitWu=D)cs)1jrAvbt>4|)-bWB|#MVEFym`hzG zQ~PC~P@+}hQDVt)YwvSx*Ev)=Zhu7K^M*>ZdqSLmsUGO0ZK0HMEU!iuPVZ^kA5i(o zF-l(VY1@;u2>HFIZTqtLuFxT;>$L4Z^(&?6!Hd&V+V=5mMC|)0U~HDOgKfW>#iJ3J ziV21NRR@E91|_p?-N^QYPOGw0oKn9Zzj3)E^;98k2`~bs! zw$L09V8nNb_X&>d9M}zH4#iv^P@u$>oqDeU2iD?rITq{JqvAzcJw2gUDYabzd0}h# z7o6HIQIO>Ci@gPPz<+9O$G=WN)u#qBuiAx*ZAa);N&2d;N49;jOKq-!mu-7hgz{te zz{|FKfA6T;J@B&aA7=4+uJd9Rt*en5WZCv(*@)OXZrAC^;vLu7;9yW54BTw{yH2G* z*Ng^rtn?@=_LrPLJpMaJNDPg2dt%!`#VFqeUQkF*eYf=g7cPL|71WgH0&`?eaDjg5 z0{tAVDHk|R7tj)(>jI`Dp{xuKIr=`W4L_)kPlAv`*74gFo1yA>jgv#(2-&tLXYqN4 zD$Am^pF4&+bIQ?W%=(e5j-SZl9YcNJ!8mn%f@*;OFY34tIqKo-kv>O`b6NtYjxSXL zyLjHH__-!)Vf4q)gs>D5~j{l2eF;&M`?KrhyA{#oZv*wzP_Q!uQFn}2angr+8 z89GtZc4mC(?p3nCK5_7bw`;{2TJ+J`k=N9a46N*bS3rC$>ruN-`f-Be!c(y!GRU6^H_C(eA2I1l1vwozMYI&>KJ zLI$4p2#dxAg09fhwf18H6b6fgPI=aj zb+yMNv*FnJk#oz^_6cD1vG!J=57^$)uN~k_qXf9pueHa4Y&&6KCAPHvuhd+70+_kd z6r~^SmAX8kEOWJtD!uP0eIFB{Pf+NNBba3yNAxi4?|V3SqOjwjr)xQqcYcEI{NH3U z+kSLpO>s)OiQLNN1gPTwf0MjW-!x^c^s>A z5k9v7I!4w2pWXXw`U1vxr@_}*?!fwUOyU4bfBJ)g%$Kn7X2>jM^A_g)QGh_BsdF#(w%%Y#)YZMT-g~qE90#iuEA$H zJSST$%&N(|)_(g}x*Vr^_amk&bQUY}kAo8y(MosT2d>%dz>dPCF>X?iRhcpBF+YbD zSw7#D%ChVSoJ{)N%=*dfV#xuI6EnlrBlZRQij*A3uy&3CL9a)Z+LC2N-ae*;W2na} z^cFh7FFRM9MaiZd>3BuOLe0x)ZoBEEEGO)e)x?(+yUrD-r@Yc}q$6~uGjv85@p9*} ziVJVg2^QN?Ip7>`_n;XtMRV z6TXd%vC?J@R1eIsm#A!Ig;@k?I|pH62G{e5CEmjCv*#dwxrhXa-=@m89K^| z6^8lHMWg_ABP*PM?-`qG{IkfJk$=W9ya-$kq(RbjalP2JB)yy9ej z!-HJZ;_CrPd~yJ(PoiJlJQ<^e9rC?D*5(y%X$LY#?Nd2{hhW*`Mco-Xx1I&2D%4QP z7MQA-L&vFt4;?2}f$2V#-P>WNyVu6V|wav6U}*Xenf|S0YbIa6;+M4i{LQ-pX+T35TUq&f|6InA*8} z5iHdRHjia`u|qi_eVy|dNY8X0i_?>x$6;&$R0dPhBb~?V(x}he8hi?vxgmS&7U{G$ zpeaYtn;%CPCL7;lsNbC{6dpZ2_CJrKwjZJ5yG}V|Mv36&oqxA~<^Q(@Le=XV+^Z%!d&Uf)N}0(%2)~2!B=i(+H0< zm!aL}JzWtQDdUyWcvD?0vXV>NVz`D8<&7%Cjs-KlnAhgA%cm`sw3p;)yWkz7wD*6h zytdD7)}>m4Qt5rnwI#8C)upP;mFltcm+Cz$O14xb?_DY-X&0 zm37$uMK%Vrf~fVf!a46eRnR~&Kj_Nj({Ka=LT zIWD%|fk@Nj) z)=y<_3aPeY58LB&NbO%E6y`kKA@(`7Qtd@QRBJz|w1XT5G7sq$*TeQps+75x33A&# zJ?-ap*vpg{bXe|!JY@e`OA5~`ZT~7PuH2R`T)OMCS(t8Iy8A)ESO9<#M7riYMZs{* zVr_RtYwJnFcgi;xIu?|9+g_GZz853^dosEMa)1#A)kFKJwC6NA7#)&eAZ>bMbJ+IX zrF7ZHP40m!`KD}a9`RrNiL~wcGoVX7L8V{`qJ0eHsm;;C*FDNJ@lu!l<~gkLwZh(M znz-qA;%by=%N3pNQ-LUGMq6Jy@a!Z!ceYQ)??AeJZ)_=LdrC3vQ!Pscl~af6AKMO; z*V;c*Tsmb(tcohPmH(c?ep99Gn<|^p|EjRvk~#p|!IjFs;SE{dt`O!ey`MP)fYsoz z-KxzKI?)w9efx*Q&YTS;!K);gi~deY&=ES@w&kqgSeKJ-O!6$E@Lu3OQ0GAAEdIxK z@=?7Qvi>OZ3+P$ij#DStx}EHo%Vl`O4F3<>UMw0;dDR*CbWJ`xFz{mH4X^dQn)!Ka zm)&B2U&-CJ#WFJEWx;${Ct*#*;Gw5|64JmLqPHDGo}Tt4z!P*g4$HH+1^S6nGYfm3 z6cXF{caR@?X_h6H_%SxU+Gi7WAW&=jlmdF%o3o5^tqey}D#KXzDV=>vI8UMDBtpIz z-qm)sv!u1Lk0T$o&174$lvwfOithkA1I~uH_*>dOLp~ihG9w=2o9i92jD251k7;I6 z?7I6t4U{K~VyXM?1In8z?P+JpVF1ya!~>884w9q#-a8et&_SZd6}{|RL0RM=IUDPp zsgM?tR05l+-b)m67?IS6L=IEPVj`&;i9F4Z(&r#KukHPgLXODz zb*ZV;-e(lHq_q7BKEZ>pq)zO2{~Zw4x7w-j-d&RFB{dS>S0bcv+*B8lB0^@TTXpK-Z5TB=Kx9%U=G5i+0!CIkCfe69ffVqlTa^1Cn< zMR=7WJaj}Gfk}w{cVf3XSV|2nYMEUuv+>s2-A?4J|3swL)$F#H35s-8XS>Anea(qT zr$D}Z`OAE<*ZxT6taY6EI~cs{VlbxGp5icItq~dYhQ65pCy@TY4U>`#>>0aRlYDke{kkA;l6&I7_IuGw;f> zs3x9kWWAqdBB@3XrgQSao4!yz6sEB+LqQ6#81Qp=`eXdOXKu*ad?oYQlgsDL?aF#mnj*sj;?3iG8e3Ea!r<7Y`eY;L9qqerN=mT zo(AA4AR}kUjb;TqW8Se^?#z0UJF`lXX9=-bFK3lks9g1|G96-7aFb!A5_>6Ez_W+* z3pn+ROOdjGx50Vj&Svj5&{!TXy&S28^Pt`0_)c078vynXomtJ~e;H5+OMzt**F9*|@eX$LhCYUE~-(ih@#)btvC?7!^cuF2Wm> zGRj&sSL%2hjjjACb{ZP7PYlvU;4g!9v6S4noFNM-?aA>gx`zYafoch74I-_>cI(m^71{durRFMJx$B~TUp zj_=5FWGngue;TZS7f{`me~vwme}d`xOL>J-6iRtXmM&*Qh{l59Z7Pa9RNV~qV+Vs9 z94>E4&~y(DlKe-f2TRUjj=N20J7aEL!fK3{QX?ugzIx%}P};?O51C&%h&kTN&JA&8 zwf(H>z_Z(Q{q{VS2Kz*OAuPMG*KbdDBAkwZ4QQWktHH)uF@y%PnmMg@H-kHsp)fkz5qH7pcGCP`H(Nl;vEiVzTD%`s3hoz6kl01cq!K zhmv5S!#}BuJXR0%R#2oHo%5#11nTdeBS!s zDO;)BB5m8L*?6e~Tm1T;`x&^{0*J(U*5{?XSX}URU`fP>HOo!dVL5BV^u52A72D!? z6#IU1TqlufUKTV_TBjAU}8lDNeEL z>Ri#U6Qb3_Bt#}{vkS5Q#y3it4)4J*-3gh^$t*Q05?A#)g`~z7C{~<~a=DIaj;b_A zoxF~zypG?GH$1#+@zIbU+aQp(?Ev<6f8C?}z}YW$KH|yOo{`m~)Mr+Ejnbz7bG7${ z*UqE8wk;Sv``WgQ!N+55TPo!4ymG3qp`z0>2Qn4-Z-4vSgZYZV)Z5Q)p}l`1g_ zvnDmy`m@ih8qK65&<$at!;TMCKdzpH<3y!*)xgKhLmRRN_3t8iNe`xHjxpWU(B> z_NC~c>We((m)Z^AcTP4H_UIwADa-SpqdJ=#Wj3|ifBLe`s)-u#Khc}+d1YvC;wu_! zC$lr!t0=JCIbs{sbw>6=WpZ5VU5lmx|1Eo_B0>}Xv6@2X)iJ*Pa|TW*df&pB!yPAI zBD+tj+`1_Th1A6gai2W4X?wT&An5INBJ5d-WuwvMZf09sQl_z)@S9}!32Gjl}*>jNBIpESYE#9Wh_6o9pWP7$N7sv*_0#G|9tR_dG%|l z(H%eO`13&dS38&XPkHx-gYTMGf4}2@Cx74es!y)M%Wv_)(_wf47;0W$C$A}Izy=w1 zB?kHm_6(FicT?Nn1~PxKmmz1r;5NjNtvaWTXSmFdx2B`5>1j)%t&Qo} zoIqtnELy(2wyLrLj{&g+$K0fw;;nN6Vp+64+O$3z3FL5LrcZ7Tjz<;O$)_}L}RipLWy*)Rnvpj zS*)l`Ri^M0n08%&0`R=4U@#bHsB3CQ`O@(KPw)vuqA46VB#R$N#WTtJXrKWEGD$3* zPNe2kRf&0#NFXp|cEyrse!@U&JRN9=XIdl0A~{r^`Q*hoS}2`hHj?%RCJaojj7%wZ z$`wdPBTYC?DNU(a7M6ntSp{%pQ9KEIreuPt0jYZjiR?*u^9L0-u^_Z0v^*ph;2b6t zxlwXjkSjjfh^05L42h-j^(x-UuXe?9v9xYoG@!(YCt)6lQ5gw3|L0Uj)PHe9R=

0Pf9Qc$DG$!MjL;%>j791#*lq_=8(i$rk)WQlpE(+XJ;25Y3&TMEwUb(Ag zcTFrAZMas{Yp=%HO7QAy1y0*aPixUGcCEOvu2tL+T?4_BVqPMN`zCQ?rWK)C%*!;w zrxW6497uIjd_8nh&kSD1TRI03zK!rY!uJqB> zHbm3)G3nzVOS&w|Q_N79R51_jg3GnU(@~^Pu8$^DflP|!lOxY%)(rYOY$-FZ3Q8VtnJ^$E(IYHDd}u9Iw2^@+*TrxdRNcO0vgl6h9ij04HK zbPWEJj@7jWQmI&=4rONHl69@A21F#1@pQaC-W)8xH5vt3RPqNm%Bd^ch7B8njc7Mg zP3dSbo@}f_jfFO%X&5jSES|g)rIt-%Wok;XfDYF1l(70JPCHu{-6ZPk5)eW}WTA0< zS}Kjh!de?~Dkz>>)}ipCH9if=ni@8Vy4HBBU}n>pxoC;6kBS8J2^?xfO2|{oaL$?- zhFwMN_S4zIX1v@Be^?B>|Uk?6&<8vo_-EYs7y^@-D0m)=c?~$ zE&`KV!lD{JK_ZB31`2 zu8CumqI=^y5V;2fs<~n*h4_2Z$nydNli8BA#y3cHipu)){}XJ_FlynMndhfMOiIR{ z^Gn(Ov2=eAJ-On96zJZlx3Kw=TjXfDA0rF(I1AnwOM>a}Q;pJ6(PWBufrgE*#7ULO3; z!l_+|5*xv<9CsWsD9=*f{4uY^=jFJM#CJ{xbN-AwZ%aF z-IsHQFcVC8z-!GwxE^5{!Wx7m!bcGvMEESiR}o%8_z}We2=5@^OFU~N!UTjF2-hPl zLs)~5MEEGeg9x8R_$tCH2tPu23*j9Ea~IMhOhA}{a6Q5@gf$3BgpVRTi11m2uOhsH z@FRq`5Z*yBam58z(+OyOVGtqjRbF`iq?S zW3PIx{}|@P3h_rg(xZhS&FoT~Wf{f1Cz%T4bkgQEb@l7S-+;tW4@Zbb!srW9;wF4f z5{ZZF;|YwvL_HiIkQ%L|#M=s#o$s0faQs)EbJ!`Oj+a>)PEk!yl~mA%Q-~2>E_yG^zIC%E}WDIek_w0l}cc+ z9F=i>uDIp5E_Wx(pmlMQ$tYK{|8&JIjpF>>#%L{PGn7k&VFhImez}X2H(@W@6xAaz zU)F>7bW4Lp2-b_LSiB`#)l%PDx2CFoJ&vP|S798Q8SLJT7?w8`!%@6}EE-xBW|5bp(tIS+=A|suDP@8JFK^xg zl};um!g3?>vcMUw;qcv=x(EdH&gJqF;7k^C&7Tw|5?xP%`^Q6LBxsWK9V`&QMg@LM zgH5e*5_htTK`f?pEWWo(N4H|MvW|#d8WC@3Ah??!A1c9BA&?eP@1MoUJ*9}$x&vMw z>I?KgF%94phJTn(MpC5xlt!*;UZ+*>{pTF2zBNsvCy6QWmcbLe$^?bvgUKuAqa=8R z(iMIk@d+FQy&)nh%4QZNHiTO-{S{@ih9kn9D2UPVWmhVkmpGnLT=xDNN;lCW0y+|9 zSKTZTK8b+9DN#0CCGr!+X-C=B3QS7G-l3YK$;%SiB@83W7YG*YcrSJp>sa zDhLh>H#FCwLKgZAegq1+hVfKmoUY#dWlXt_@w;MV9vA3{a#w`RWVDVLBO}&i8Z@U1HNGN?m*itKIv~+04D>Yp zMLJ@VqDRAtIf4>uoQ|5DOCxzzX#CV+;g-7OIyAbX@DgJ!G-pn%9u|h-alUh5z(`2U zEa(x#+}5}#oS+psTdQ`4D7;Lgt{E1NH=~_yXh4%K3MU)8q)}_YE|Wx)-JEFw&#A^I zr6m>&3$IVtt!a*C6HGG>%9tC5!M>>TP>3iD8tbJ93y1OK>UE-UhH@_LO~jRK?IiNXb1>N|#oGp#(}8bmi356EP9iDEPr z<#-;>df=naFn73r;nRiG5;00~!dLjUqHt?;BjTP@3KT`*lobAXr9N*_I02U~d`bDB zh3a)VcocZ)t|M3+=2F7rxQHDK#E5-v>`b(k*$Oz~k7Gey;FZjDefKOmh{T0|Mj~Q3 z?Glz}Ls5D?j)Siko{fxVtpoyH2r!#u#h>UEh3~`YlZn>XH4Cfs1}9DFLRT8LBBFE= zw-ijIL4(i`LHSebg#RisDqRI8+x<5cHK3(ggGCGBUqlqL6eAk(z)WNT#|sll=^*}_ z<^oyZ{Rn>jh2O!in(Yby$aO`sF&Wuhf&x+OSY0a2v)4z)l?uH@tk+^>;!BdiDx&Zs z?^cmMXsa&VP=_U#O|aPTi|KHMHF&L%O00`Sl3_Tvf7q%bEdFRZQT!$)U|vKfc=~(s z&+kGSbD}9xOecze@llELN|Ze1z4+IUOOz$d44UWZ??;xt8PdlI&zwPcmTb{AV<3v} zpg1NFQXC-euLYuztTa-1i$7L`Qp;>R@0NK$na|*%9p#bAEE0uhFrbx3uCKN#i#S?| zt{2v2)3v#EeXuT9b-wV+ij~#wcBHe zV7qNqt9HABh@{MVy2?6GB(t6&ETxKxlS#r}M_98|yD*g)-4zLI_HSff)d~qarGzz? zU-X+3XAy^$>=t3&a8TNNHZd&{vru7bh^d#DMGCVB5~zwjOIRypmpidGi@Q=-D-~|J zDEtlvrRf$i)w8oG-J&W?yK*H|n3ix0yv1{`<9I;CuvjTP_bH4eNq98B=YH*f5`(2N zPlrPIiP#jyaK`g7g()XygG|(^Fk>W*#5|xd6~r{9B3O9ybSYGTsK%Bw8b0CKqiXOZ z;_kt^itu!+>M~jQ=3@~=6iqEG#Eeq@WeEAV9E4T0bC^7szo^Tr`aLmBwp>NKyd-7a zF7W;c>hR(ZB%o~;7TQ6or$Ew|V(DFYO71UGOVi<&)EZH8FWr$9M8kX`jcE7>~it6j? zV^NKKL?I`^-ZXXAT6(S^N*>t^bm6U7$dGd*7%*Db5|%zyW2_u?IZ9xx)UGvZ8=+Nt zfK}9#;_7Eg7^{^tXUl2arVzfOhJ-LaMCRs1f0101Hf~qR%Zs==ZQP-tF%Vj=QyX_G zL`4yoIWe2TnBMrX!Uc-th7mhrBdk!9idwNoE!VD%yHuu=i#X}q)Dq7?vO0yETEw+a zxu$KbQK)KaT`rXy^$Ihqh@B??jEE6YnAu1s*S(FX%I+Gdk>ljTM;v{rspA@~%nF~; zs49oisHg?Y5D|tvnjfAqDoFJ;C6rT$LQlz{2`T>E*t#M)F5}4CSm&@&l}8xODyetW zjbzlKgtSI=5^DXo0_j?#_7l>*s$Ud--pNEw#g%0P#@+CzwDfpGce1GNdUsVANtLrV zt2VSoRE{uGD#p@ngD|j7pz@V6Wt2>QCDVE(Q@N69gNhq7DohDCQZRF31vEMZm7_4Kp5kkwF_g1!NPI|eanN`FIaH_X9LDoIWq0GE|8s}u~jOh z+jl`L{@BbKL@U^T0jh!~EguaJ{wOmsyVIlLrfpKI-HFk6XP=u#^KZu}mOB-T*oD|R z7e+fr2%hfew_Ui7;Js(S$J71HHsNh(j^j_Yc+tYswaU4)V&pBh7zgASnbLpv<17J| zF+lUp(QF#JtufLv-;|c=_FecK;|_9s;_2SIO_XzS#=xqGUaK*FCz+Aa=7%LS?pzyv z6`GlBEc#T*e2Voc#o9j_y7;|{Nsms)&?n}0ce62x(M?=bF#aIz*4=#JY_iu-?7|f; zC<0PUDaj>EaTk_wK;N&*c84m>+p08YwkqAdLp^!Ak8K^@7z^tPqB>UNkL*4@-AA{M zZjGNW^2k;(mP=GrinC;8e9@3dMeQ;9+`?)r=rMKmq3)2lOe)j%&&MN|K*v1(T9EU}BuhWLkB?_=V=IQ&Um^Pjgx=PO2cd^Bm%vNFqWE}hqAeF zWE95DCaa>S`{`{J92Nm)$-UiQ{N}cCsHiI1+xovAx~)H@1yae;AA{ zDkp5lEhcBI-tH5KViPNuukvD!AttMXbJ898#aIjC`~ z$+@Su`_K#9E-3}Ui;v@72ZV9qaHLuok4ZIqyI*}_8!_lJ9L#|iwu$jS_`KKpJZu** za(`~i_&1rxxD>gLV4j@0PvFdbV7wT?rR674x%5nTEEkoHbJv=UgQn~n=orhsAe}=x z!~s+H4Hv71{D7%Dhl_RR;2``L%Mq91Bq$eSxm<1JEnpizF=hQ?4m{fhF31H&Vff(XF!&2^@CPS4O%MC2=93U(xh!s+yIO9H@yH6v?Ej=h zs1wG8o{V%n>tghG4&W1GZ$}dhRpW1_h24wm#w8wuQDiXl%d#ADQej-|QGJ3ghXeY? zrRy4-qwAw^u<;&Uwa0CUCnK%V4LOvoKu&z~I4(Fy@61ue#;F0BLlmh9QeOeh^DcV( zrCNqNs1OHOJx;zZVNCE)ZnJwiMlBoyj@^$Al;A< zdvuF+aZ`$G*NXkaS`feP6!vR4qMBj{yi0Wo4tNL6>;S8gl#8ob#PV@?Ples0Ls6^x zkVibK*;_IWZCDzAfzJ4hhoxax&!Rsj_2B7#2=!0ztq9|@p4(7rPxqOvS|Oj43h{KG z-m1Ey$33b?J+!q#j#WPIQG=DeTgPFaSs4H9;gAqqw~7(mQ{ntp)~(@UBKK4b_a=*R zuh*as`GsT&Yk2>D2Cc51gt#zLUHjA-7Y<3uOPsaEsuXIwT8` zC3z86M)6h1E_$y9(-pE`+^cyf#zi;cr3tyTDGUeO%{K*tBPIaejLIsNUMeQ(Kb%;wn+uzUL70DCuwajd z!AdUE&1KC*JXvEoedJpP5?<<}Ey<1GOiTQ{3nRvX3x5$&- z-_biuo*{RZJVWg)dGdFbbV}~Vm<1lSDWlT4c9vX-Y|bRbU;WEcc?(P>oY%`8B#+*3 zDYyy%5!i=-;xI<%flFTRSU^SX`0<$cTdp+{^WM#f#~O@-+24j9oL4K!L!u+(tln+0{gbyod z%JiL6cxr8Z>JYXQjn_gv)zA?g1N)y$0 z+%U$l&Ebw34JeQMvMhJ*Lua`U+M_l;BAJu%%-8cpPdm(|=;=J>8O?lzwvMr$HNxyJ zA3^Il(3le1!htG;kGtOalDNzHgcOBh?D%eeIX~-&BFp*NJW)QUMJb`BjmNbdB{YZw zjVYm}jnAt%$kD(?(=nztwAy|*ekFyV0BjOrO8Js8R)gMRB3N<+x>Z1mmv4l##`BhR zt!{rQn(vd+rBGkcP{6XnC^P(MK+23tYt(R$H4f_l=4j!u<_w=Zd@*KDo=I5C9X|Xz zjG(=EwA5oksTeM!vDImeB9`=M9Y$o6&c`C zi$|3iBaIqFTu_je-2^y#RvvgEK%F0B$AH)cj&(6Da5KF~)1k;rHcm6uV1;I?!Hcy7 z8oXpUB&Bp{=7+;VdZ1a$z{(e4ARNX= zk!V9u9{;<=?8-kIWfJG+W%-)0Ef~i}D%1(A0`M)Zy%B zY6||XW>O%FOe@|+im2!8>)@qJa>#?@)wss_~@yft9$ zw?4XW-@aY=V+(bocUi}FTibgSBhWhZx9x+N+pQe~)=2BuX64AEPwujw^!#zFb+u=Y z@$egSR!1_?9gc)iP$sSMIKr5ML73!#)oo4~d347v&pKI*@$mC0Yc6t#SW(X~^AW??5w)H?xyqd6_e8gOE_pa=J#o_7 zZLRzbsHXibg3_An%dHntu)F`=GwNaUin9Gy6#FgKDznmW&E0QaKGIBHfa%=N3}fF@ zFd6c+QFIbnD6HC$q)+JBwx_slmQWMo>(94N5 z@|3t8NB2ZlN36xc?bf1<_0%p{VZSy0g;j8bl{;Vwuz5oMdn&nl$4VIX4TQPojFI5H zq&#VUXf!?SwE=Uo|Hu1Q6aR$uqnTo zEw;-_(Xz@~SI}VQbUKN(%erLWzV0NvbdeQ+bo&OR@&ES5epvrk=H>nrL`9)NZZ0lc z8QpKzlv#^GJl>oOtue*l-Z0-c3RS~;hd-yy4+3uum{ZCiw0XrSGkAfu+Vk~oW~Jfz zj(B)KBA>LTGGgjz&mXp04|_gA_wro1t-+f5TGaY;(CXjdxxo5%wEra8!-?O(ZU4T{ zq#(x=`{C03M}hPIt%UxqhTYbp*S1@a?B4e)>yp3mKdf)ny;XraN-uWc{T?9 zo<9`^16#=>py{|{(dXjP1fn%YFaY8#4Yv~BbK3s39@yOk!+j+fSQYfQ1_NOreMZo?ENI;ngs_fIxY+t}@N;-TC+0lJ>ga^a zr1!dXw%8TN^~;N0X=@QTSbHVnfZ85Xw{ z-0Qv1Qy=u-5)`*FjTsD>D2`{8HyD7)%r}C8+k<|m3|!v2*(JvZgZ>RH<1EdeOIZJj zF`&bL5oJk%&2NGM&j$?ft%hdEw=w8N0V&&1KC}ROU=_K}WxK{;jYoh5WnnxghC{_y z>v+!Xe&I^LF6dnz^nW<$TOPDl1pUi{zUE-UV}c{z{nlwX3A@Q+LpK7S9BsnKbP>GB!tdd7`@|`HtfoHM4dcZh^`(r!OXu9u z#=1aV>n8c`fXJ>n=*1Skx5f=f73%<-wFTy`Ud(X4qv3vuW6-`)_EnPX`yhJC?(81rPs5VB2;wcgm(g=U<^?0_wzeLOzy$B})gyyAlH(^Rxv7+EQlO?W z`1(gM5?zyN#M^>(t&R9ZK!b_Q8o9#nz)h_Uap$>i&6*_Mn$wh+H9!Q#M9ZT9zO3{Q zTyjAh^l(j4j&FU+Q5P^_%3c8FH4(pPZYAAh&`m}}K*p#aOakOJ9lvO1BptsUS_Ha> z5S)148LmRWv|7^fo1)_ofT!UyEyyJe>-`51fR=Rp2I(ctC;07=bCH(ULIi$4#3+6{ zv=?uKZN{^cFYye^5%|3kpZU#EIp%CNcy{LtP}C!Ug-Qdu<)C9*>hR+oO{8c>aOn7r z(`L}|+b71ed=4GgVJIuam)}^OMkID!vX{d*0gOZE!?#F$(@82oQ<9G1BM4bK^rOB~ z=(z?+eL4Jg0Cwp3P1mDnn6X-(y&SsxG#&MF0CWdHcNOQe`eIpm?M5I!(w)IcGG}hf z=>({(em;d~($RK2K;bOt93DVr=^n+iL+8b}PTmgyF2oP4YMyjJz?IUb0TbfkUJMaOU3W`XVy3G_u8hHoJ_{LX^z zEa+%D=OPU+uC+0L%Eyz%tvld{cy=y_?)yNHpL3Cp{g`t(k3Rvzd1l-VcPf7#O!u2X zboYX8k1L_`_~$`%qp@XkWW2*Imlsb7a?&vNH+U=X2XqR3IdR-KaGn|U><%odu_4iy z6IU^a?$!H+m_#M%%hAh}L3DPf5Ena4K1r~R;5d~cse>jmYI-*=+i)S$Ssz!yL z06fPl%$MyD>$poCadIEN45Opz%f;G3W$$cP31DC%acn;ota4^MsbG}@+d~DbQnTGt zu<~N(+enY}bk&Zjc%@i=W2sgP9ikp8m{tM*EX*%KRXye1qkA6KM+K{z!Fs3Q3wRT( zUkb*4J}%ZH1-n~R@^ePbk__-z^`L(}c`YI7#|?_F7zA^4ox42Zk{p01k8o8K2?qwn zUpfe$AU&d5>)S+&p){n(yyEQvO5o^|v#Iw1cJyh0nqyK&pN1GILX6k?FvMuF81NXt z^gIJS&I;U@yYgEH_i9MqD0)G<1f|maSi`o!#e;w_VJr<4?GCinR76|CjhSo zKei_$O`rG*U}rAL_^$)z^$-H{|DJ|flL`Nvu(oFz_C+`cEeF5rWjyqu=`X{b^rdK0 zocSC1j|1$?DG5&l%xj^hzaFqNr)2!ifSoxN;Ra35a*+Re!2Cv>JVuIQv02A6AI9&} zaHFRGjD{V3e?`L_50U;Az^uPY-xrFbfPF6bH=2I6=J&3K*|IP_|0pl7YY+&J1MIY4 zlz#$XXI@VDeVU#v7wP8$cGeOYAJXylI=__~{<5ZzIq@3atl=%1o;#tuxGhfpj{2-S>Kzp)7$G>0Ge;lyWKa3P3#HV!pF*8+Cdf5@MIX36Pq*q*fN^uN~Ww*s!v^^x{!)A5c!Jp|ZUA0WS{bo`?_{kJsy zg4V|y8g}&m8^G@JzYCau&4FqvL;F1(V~$r{?bAhi4C0h8sNs+3{AvI@_VdGjOLhF! zIzFsnfFgj9((pZaram8FdflJ1edBTS&YBv__a#m5*!!D+d9b@9?+B(*rDc=DI&{;59A^i(VcEQ;p|Arn8|OL4 zRnGk-SG|koUdtVF@BG}?4&85sg%{j*)4ZjN=VK==8ObDSY8Eb8eEt0J3=}NyU3VNq zww~|7<`JZm^(}cx!VOrE*&rHZ4h?SFMrf#Yy&HpXB(c7Mg@|O`rZ7Lcb)=WCak$<< zz}MAS@Nu11pf={*ykhBiYqJ{xL!{ziy|xv`3x#Xy!}Y0*%u+y>#0Gq@=T6{$>(F^a zFL(Mu_61jWlDxM`^MucxQGi3TQtmW(aSiJ`EpjQ%jhFAUxe+O7Bpr8SnKU!!QTt0Xa zmIB@Efx$~|`B~nb!uO+th*}4`lG{C04RzU3sxchK%9a*ZzN(m0Q389-aU8vr!{`*! z6C^u)pWBot>AA1ghNZcNaM3}p>u_BZr`@$g!+FZ*Cntz<7gWC3rAl*d9Z}wi?_BB= zo7{;S>YH&0Sq?Lu9t;n_H`lF=IdnDAlp@}M?aEM0nwno3Ed@Bz*G?H{2Rp9$r50`X!+-n!u>50M$ZP zc-GoH$8svs;A1)9L#c}%Ep@Td7G2t6_f11vY^lXApY#3y=Q8t70Nd{O z``+ZtInQ&R^PJ~A=Q-E+a_$c;ywT(F2y=ObPY^o%NxwB6{=%4wnJxmNNYsjP@C$)r z-1N<$fjfpUT>+dQ&IeZoCRJPf7Tqwwbc`c!I(Z?jfIAQpf{y7$xr&##HxLJR90^Xt zF<-`e;T;A2QN+;&5J$&&=0R5rxE6WS1pw2TxZB>bBLL63O!~U^wv~15k;(0Co#}PK zR6ICUrA5{PY{gBtER$&|2OZ_Eful}_!_~o!f#X%7JoyVYVjA2~xN5jcxQTEh;Vy>@ z!11bu8xJ>y0Io@JW8q5RhQM6{$Lmt%N!>Dd860J#Jd@#qa96+$zE}oxNu2^HGKjz8 zUx0YlZ<(emJqP`Tz)>&ex)_1c8n_6)1vf&&6SPlnln#&6K6P3J$9gMhLlkMI@^Z`JS~4PUC^u!eu9;Wsrr0Y1~u)`fdg!*hXK@sP^D z619}l^cR7S^#7#cR!wipp4R;50sr)yev9R-fM!kte#Qmgtn+Ks>AwP>7wek*pVII( z8oplBH*5MC8vd$|_nCMNx9Id6HT~B#JVwKJfS&S!9C^N*p?>1djR7B!KH?IKmS& z|F<;!XU&hc&-fQLTn|GcyjR0l>G%ckt6wtuyg;<;c-lAV`!)O)e8NjK9MbWXI)BQ^ z`0wg?wkN`i2bJem;Djz8?I)zuzoY4+fcLq|-=q0Gq2oWL;TPbOf2mGy%6lL1&${Y! z1mb6)e5$^OieoyxkuM4!y#4`>{MHiCR{(ym4jX-Z9_gQV@&BmicN^%Key^sd7vK;2 zx8`)Ron6~B5uSI)Eprwvm@9}1N1IaX!>OnpPuMO*BART8Cp%o2w$3(Ngj?W=@J$PE zyFb1l#5|= zd$g%Doe<%0Ye&2@oU)seb~sE?QzAl!`WY zBt){UGZI}VS`rA@Eu;iObgZ`9Iy51PqKZ!{)!fwC!t@cS3?4Z^hQwM5)0EOoTAJJA zDW;A<2Lb{#wUb>q$t1`CoLl0k8_{IPQI895h1w%+o#AvUis}lZ=*_w!I-1%#nW!Da zEs3<95-rJS6g2BlQ80vPq{(gqh{{qafP(lpEnINJ-0)PW(S+ECdoVh<{}jwIVV+ClWrSOZ$gFgDOD*9;5)$|K6*3rkn8laDXbv%9ap0)Hbh)<%A zfxMnR2GHsBF`&+%55caXUm?V7`e?xO=noTOK79;`3+ZE!ZJ>_{$5Q&3P&CrVWMTz< z45s(c$ABB5j{!JFAA@l_eN0di^aDcJ^e+`+9sRLFY@m-pcr$$r&|B%FGkuUgCM=(% zj|t08`j}`uLLZZj-Sknxd+1LUVp-2ST_-GO7gXgO=T8@que~BNSNcJbsj=|Oddbw3 zA{1k=|5yfBO*vu4^&gUO6=7Cc|2_#<6Xq1E{}~C7CLAFAgoLU27$g?`J0(1xFeDTG znjm|H_@G-&x z3Da_74&ibMZzX(^u#oVBgijMb`F~jcPZCCZMfoMXldwhjkc1y0TuykOgm)9JCj5+q z_Ye*cegg26-`?Hxi>~7*8`cfvN8P(X3#mRB;E za4hcaIs-|%_u1aep~gF3du6Ajf&Fg2_FLdC?6QF|+t!G#eg1#>65!11pQYDZ-u-%d z;MD?>cwT#@=RIZfpFM)8**khI7?9$Q1^sRcAfBN(oQCAWzr{|MJNH#pz(!HzA6MFdqdB5?JI4F zU4vN#NmeP6uG5}$8RGh8Q+J}D6QfM}*=?ay4Y6A_>Gg)xn=a^o7i3_!sw8%&j!q~U zy7#5m_uloa6zyc!zLEy#Ca8;f}q<% zCo*E-r;>hj*ShJVZ2M-FAyAowKi0xbuY3l&bgC8me$K@y(Z#WiTB5gA3?y3ZlBl%7 zdG3F-L>Abg;#SSdBrIo)mNRssYvT!FYf;=XoPlbT4nZH=lat{Q#jyY9ieOb)>^oY9 zj~N-ty1%U=w}qZ>i2VrBy{N5yWev^6BYxTkh@I(D_8nA z6(wxPoqpH3(qCuN|JjxPpR?)5xbna49O+T1w3!=~z%Z-cyUx7!iZtXHRWDXehvhlH z{);xd(ynz?!d?Y~?OInY>?TPQYr|}mWZzL_K*kEcjkedK@n6j1kuZ~}icQoJ+w&u; zW34(Ots(~M$*Y9COdfY;lf;I)^O&pfQnJy)UI8Q67TVn)GaT*w0z)_~7KU?qZ|F-k z0{zjoVzv^D22I=n19!N z#uMjNRC{w&-c^#SS7Dx~HAu}b*>d7pg!@9Wi|XrMimnN;=Nh}JjdkD*A=cuGLvF zYi2u53?wZl=~mU@m~6+#Mrq0xLkVjwb-tt^U#N8c<{5Q%SzqWF)q+|kP_w3IfHZNZp@oaIRA+WyMSvngekJoYQ8hr>y%@$lZGv z#tTeHNQIVpRF$=7F8X}vyj-NOg?bPZo0sGB-|tC2Fqvi)ltnAsIv0~s-MV9nM}NIU z!Q4@$<*^E_+Y%*&RJuzW6tz;5X#U3D!YI?r$GpAEEF@ZG#Zr`Hdbu-OaY7@z)@Wcj z%d#OS>9tBy7C~BO>@uCjRu$3zZKy!T(*ClsEjsoIDu{Y0^_F!%EJ>|x-Ro#ogZ1qE z4xD9^w{JdI*>>lI`qH1yR-Yepdab^Rs#yK+V!E$u=j%v(R)$j)ovW(Onep?vjO)&k zaW8Vw89$>-y-uP`y^YiwTdWdOS+E9ERoA$%pI# z>s2hOk)7k9dOCVa>PmKws>{e$(#tBUPquP4xW4Wcs2LUF+?pZe+0$(UkYzmTj8>WT zgpLD&NLZ$`E>FC&t-oMJZ#*u_y15&-<)ua>43C0(f}-8`sm&epPL+h$q! zcThxKBd+#&lj4)tKL15ZD@Wf0O2+=3My66PXI!!$IH#rcsRVzArG4)YXPY>C8*OPf zY6*q{m8(Bl`_y9Ay53f7WJF#A)%;}yH5{9ut@LS=0wq1_1eGH4YWpFDV+E&2smkqx z7B>x5;VU50{oVo0b4pbOWzxr`os5r_eka?LzVj~27a$WGE%P|0(gXFq(h?c0F;zWU zN+|33%3SI%%GBj@Oc<(FGEIpk=dJzUV870((oyFh6h3dNG@~!X1&$hljysJgvz*JT znS~iW9p`bCkDNZ_jh>FPRf~{6dOA*f7QZcYzzm&^vn-32lNU2mI?grOh}aD%U~Iaq z2gezo#iJ9Lgaw6DX)qXPP%_6URNYNq=#(nUDc8^_$4yR1l8dRo4@hemf`B%K8ItrsCXGc0W|Mh# z@Ye6O9bmf89-0#Z%=iw_A8Tai#4eLQ5OZ0;zshvwa!~L0z<{+l+mtw%*y~yGe660o z(0)qow4)HHYuFcz+PPanl7DGzIn)9BX>cxcsrp1Fz2BK&O1WQ>?$>>&<7`xNNVT~p zUXBw{5z3C;6EDYEkWJ&Bcsb6LEI!Y4MrF~u8L3H@<9M|vfs0?lKxs;zRn!seSmv66#J-;F2#iV0wP1vTZFz|VfG zP2i+7fs>rADHAwF6VMW#Z33T@>CoxQ#_)&|0w%62eoz~?A^ADm_%{_BWo!8r^@PbG zZ-(qR4`%Us6?JbGt?k@YRAV+Gw!vLdv$J?pQ9*+-ZG5a52maS>d~>c&6DN#eoYfPU z3jT^B$*4!7_XLQS?YX6m4QnW*PP9AzNhfmF}Ro!dJ2R^=+h>mtot*vmqq*I znuQD0ohHsM)r+P6a_kbJGJPQnyE*AB`hCs|LfkAttmPxY84#3htVL?~6^#O3rW(Rf@#zq@f!Mb#{ z3g+jqEX(HyQdySsM8D4Dq?_4EGP_uEKyX}ohzeqlYM@BTaRjD7$$YBRmMkOkE@es> zh6>iDy(YmgdY7F+$tE7^`L2pZ9V}*bJCjv8Q7>6dd_}SAU3O~XcY6-?giiN{PU|9G zW`cEGbP_9zSgdPjAiXVg8hZL6%4c!}O@s3d=YL;Nlqo$zD(s|ly^_{shy60@0mG>7 z17X|97^^-jQ=ggYG^lK4chRE z!yM#kN{9PqW96ko)V#vUM-5~w8k{sax&Lt#(wA+07%6%~2wzjy{XSylD2}B&R@85k zc-e8CZFWL)SZxIClf=g}NPT=(=-7tZZJ}dZs<0Ovp#ski~9^JR!lcWm|h(V2Qm6wkBhW?UV^#XUEj$@rz)I9gM*& z?FEK%tbLsc2JC4jSYp?jU@_ah%3z{hX@b|;X!qQmej1orA!pM@X{!#PiHEQi|Jpmc zbsb?}zD(h<-+(RnqiEzuoadEFO%ywUBd@|5T!uGV?Leguv+c0=J>rblJ8k}Hvh^Kt zUe;`+g!5tF{m&?sZl2MsD`74w;bo|ReSBH$)4CFNn<_2q{yg<_Zfp5a3q(e$^2%6w zlUyvalFPc~^r*r4v)0JGX?{$ta@pn6mPy(l3SIz(Cj&s1KUXSC~5EkvpGR{E-; zu`laVeOD!hL_EY(nMJIXa6 zz>Mf`!d`o9(&zlvsw%8k*?A08DbR;)i=MvoZrN$xz8ssq+@;hznAUsH(|BB4Ebys0M(%^jjS29a=m{O9QF0w*mYD~g8Cl9fo3sa=i&YfT zZ0nv+wt6%6pySKNxK|Meoq=Dy`{+`$-L2kty}5`+ zhC2t`8kG!PBwRmM@5?Ues}y(42yoY}3*&A;POt_g7>@+b#H{3bdFD1S4>}VJx8vK4 zjbbcwF@v5#@7$v3eEwfO|X&bP8L zxG^v(lRoG?sW5$Vn-a{h!Lk9Gm!Z&RdINKj4+F?&mB?mtuZvZd|B4(^uHz3nHyi#9 zhO27>&TPB&$=X(Vp!YfGd|stOFVnU0sN8s=IdBx_#tXxGwLOojd5#G5_tMbvR$J}I zs0nPm92GWZQb&72$NC%5OZUnQ3Ej+H=Z%Na_1`bYX;uGfBZh3<%kWDixAI=8A)nM3 z=4I!o`OI_N^mV%QmaE)R>{&-evCCiG9EX^xv|8G0ua ztVYuOGH*+>lHBIY0r*Hy=up?jawu2k3Apz4PK=0-U&m_x37x-GDZxe zQ+ERnI%kv!q}2BH8-+O!cZi+Wkl5c< zJ)_XoJvO#_Db4j!J8YI}s94<1#1ix&MbHyE)3x!8;9Qq$ZLIAqqVQ-+66(yP&)`3{ zfkAa|^5LIRbpgG&+j4?)Q9M^8A8Nrv4)}lB@xOe`KXJdA_-t$ZAd`7<>399s^M|B@ z{SAw6a~@N2cWtyh=`pfkrPvcR z9tFymMX`hf4*=y)m-ThCl<4e5e-Z&83k;Go`2M>Tvd|zgx{7{|qo6D@NUps4rzxaG zBn!+gss9p%EGCj=m&js;EFqF=kjPUgH+T*y>;4TwN-Q*L(f@N9aePf#_m2_UCe;d? z+)wmNQpcM@oaRXbtanOu9pT}ojgGWG2lj{QS81=n4yrakC$@kZR<<|d2w3ee?|Mnr zjy>{RcG3TD)cQ-(VA7+yUXrsPyP{Vs4Kc&CU2*`-8msE6# zdgkRY2wZ0LiY~r>mP;?$jTxQjdWp>jGl6HM&aBueB*!fx=czZOf-tAjO-r_-hcwmu znyNcXRjMqV&1#?(H5L0nldK#}!Zyl5%!>K;tGuosOu}|Ja4AWKXp+%c5)_jnvq>r8 zeOTkjQuyr=Do|(-Re=Jmkl%)VefD|uDzie?hAYXloIF*FdFQY!B^1jkdcAX+H!My6 zDm#;zkcH6~k_9^diQI(4+N#0nS81`Qte2rO7wq$Ke-hhbW!w4kNG}E$J@DUk3V^SG zjLbX~jTz6y_(y8F(`!g>mRcmw5@O?8F13E8a@9*MJ46F?lW`(>O|F3cK?NLfRsruo zm0|x5J<&B#YaTDXHK&C0p|@ZIoB=9q-<)!;R*Sx#(2;?aM*cx0f6-e5Ah}-GNSZ5{ zRUQ|gD^5zClMj+(CFhDO%f@wWJkq=gn*~Sswiq^w@Jtpu&i=<>mWp!`o@|j(*8Evg z$M>_cRXxXSXvEq5pZS$N{~bFg<@Mf2A@&DKE*=DW;Iqo-d<3G&d#Q?M@i+Nq@n`unP{QZ>2mF~sy}qu~&;%d%YrXl}gIj&|CU7e+fl!+oCfAH!WT;kEz4A-V$26U{UB=ypv#3-IQ#L zFfK4BZN~y81vxBiO4*Ya#UpJkZP5ras%e_UylYklYVHWsEDhYWXsO0cu30&`W_=DP zX+XIYl%gTDNGx75M}%&xm9wSmn%Nmh()(94~a$bH7efZ*RX7< zSk$yS8c<@ylc*ku5lco}t`*JNxbVgujO$u~7bfk=9lEBk6*o6^iW{RVkv=KrB$Dvg zi<{G(aP4AFx)r9C5Vtnl;+FUth}JBg$1R0#!R>?F4|f3WAlxCi!*EC7j=~*-I}W!H zuW6dnl+>hVmf#h}wst!{Q`AhUZ-;dU%ym7iE)}Smin^?sjtC9Z)K87fAl?}b#9IPQ0lcKj zKy!N=+F*)0ZHd~=F==NYOS-hmw@^`-R7p!R-hpcGh}%)5Pp*k3Q-O4f<&;&7MD&+T zVsbFVi;j{Jt5z8>8JC#qYKu}F(4Cj#l3*ZaHzz1}s;#4~y-Bi7H79DPOe|Rm?s(HO zCG)J483&S0b}R~_SW{;pm5K$LP-Ye`+0>b8K|~@Mx8u$6_F&19XcS~o$sgP(r#ASt zYu5%_(RNd9b~G4Iw$`E5KpRmT6_^T^)GkMT zgb)!~XuSTKvhn69GcBFbP!xQ2p;K5&oqHSaLpjNZV-IsAo%)0@Vr6rErVbqr}0^Zb+EqL^C@^D ziivod6BHX@BW}F1{_60uC85RPhQ+rw-XZGjjzrx$Ak&F(8UsZxer{;-QWyBq&>i4a z7i?{huS}=vU?Au`lXfr>?HG(lZArA`MK13O|zK$#~MqogS zrqD6A%b#|H@KSfoR<#O$jaZaU*@0G!Re>fufLF2sO+nAwIaxK-0A62Ccc8(Qln60L zhXZ44Qe$c3?4g@n?GDX0P}>=&`L^L>8m)o4iAJ(C52t^f~LIe_H#+00{F9HTvVUKUw>HubMZg`P~b=DlNa) zz+IL3_})qZ{8Qa{mm2=FANN}<;3mw|vAZkZyXN;nc=`B)4Fpc%Yr(GyK3;p4-v*i9 z+~sgH z;ckZe7~Cqj``{jedlc?TxEJBR4`&K=7!y@-)7-f;1GV`4%*4R7;Pl|Mz|{KsE9<9D z{Yap8F(zO0(L^ME^3%+N@;O-l|0DkJB94Z3Xgul@F?ewu z@0@)LFQS|4zY$RJ1+cB3!VY6mOl^~?FusJ)zOt!#wRjvxiJ1gGS`Z1Ni%W^$0}+Xb zo8t)#@1g{@4M+`!DY09Da+qrzDx+lInG){+N;a*PA92WWZ@^pq_)6Fo1+qz z5Uojk69dC2zOR4)zuYhkPjRV~HuBOjVi@cnd6SBh7ZFA~kRNtPt?#s()``ymmfvMy zaXl!YRt1}c;49Bm2aF{!S6an~R+5KfT(DWx#o`^&x{l_~rj>QgYw#V3cpYXCx#>i6 zJQ=M^C7Xpe)plPrYzyxjn~<9?j30lIKi!#XYwd(7Hpeh8^;rH?3?IP=WYPGvh42>n zIiJW!B5hvELY-13DDczs7pQbHF%gyw`ckPUD;i@D}c3KNO0 zA;E)VpfM7(N%|fZ2w_`#Y5s$F={%tzC6XV-zBDQNpyrqTU4nEqd z1XqPXT0~tnosm1s5UF(sy!`&|A%|-2w3+rPVha3c$X;o?u<&76 zKms3DSsM|fi>DPO)`mNq@Qsh+>F<%@I6;h#FTPUY{KWC2A;nkCmP8g2D7z^B$gL7l zDiP7ITk;zY3cYK5pGA{C3aw~OML3RNwA(eHUg9ENd3WCMq zmi8vtb7856j}AkwVt#rA-!_Sa6LwM*R(P~*IXpimQdp_+Rt~=sUr!N*!*oQcj97~q zPc*Dzs&q`bD@NvVp^m6>MaWEs>xgO@u`=DFIgQZx(OJACuh!^*M5i$1Yx+?-V!Wb9 zUxS&S66zuyRhvsAd5zZiNyXugrsQh$Q=;$^&kYz(z2oYO!>DC^grzXxnJ+QZp+`)3 zI^&{ntXAXd%6QWCwcx!*2KKx z@EW^mWqUN6V6x|B8FOPX>KAPp3K4}t&n;4fn~L$p8+4+DQ$2S}#C+tYbDHJ(vdn2= zaTqoinIa13cxVLPaZ42jLf)V;jVLhMlPH|0rCw1SPIvMvG$6Xs6Op{{5hdsXPJqP_ z8ht5khPk6L6h2c(EfJ#>CrS&yUKH+(u0z~6lmbOjI3b87qi|jS{o>H^$ ztz$GBArPGsvtBmpaegp*1*yskD=}kI^K4OGvbsn%-jeoTORb5bSW_y@uLqUHmB{`g zwj)uJpjh5l^qRLxtYKp!Wp ziFZqiQX*unSd%*>rkohr&j~BIQKG7dV(($qsotZS2=*RUz3M$i6OoizPf=M1ie%PP zg*8=WT}u*nbi$ghtaB1Ey7v>-j33Lq>J<_-o)Xq9eiP6;ZaQ(;W$F;tjSosK%pj&i zVs28H*~BzU%zTBJ4+)fcO&8WO*`beX$l|UP)^degiuQsDnB5^J`L-6>9jYF?m4Z=^ z9pMf%NZ%(+bpR2=CWi1mpfHvsiC|~O_n@v7iNTWD*P{^SM68cuu=G8oFjd5?m5F*4 zrdrZS%qJCQG%;0cON!Pgm0HJqFPZp7mG4cG^sFx zlF2WA$!~NCt7vPn{DxD}Hoxk9MX_u@Mce%(^-43XN63#4U!jK(7Fr|KQ@90puH~Et z--n7eb%Z6G8Jj}X>R2=5xPb9>_u%UE~Zn$bE}fyD(Z;Zv3Nvy8Wf_ms3jph zw~@JbTzQe)p71PI$*YREQQ=vlpz0#MW#L(>5TlE@YHn)9GF5n%DO{jP?%%No@GMuT z@kO23u8`Xpp4(NXwMCq)t?!7ZAz7osO)BD2T5fN6?og=uBC~7a`Iy2?M;qqoTX@hY zL(S(_Vihk+Ju8ZA_C=l*DrZ3fQPjH<&RZznu46=M|{nc9?0rR7qlRXSfK(`qGCm6EAl#Z{Lt zp@bb2%sXy0GlDx1k{KDz`?O^CDJD2dGBT5m z@2DzSsbc+>VqHEAy7(^@V-K@q=vH!@*V&lFur|*6J;!8K?P{;uOZFOyRkgdI2uLwE zB$q73Jyo)7zffgcp-S^hRhrYAlkBWQWV)q9#k{GUDgO$ zQ3fnklKqF4tXxU*qpV<35(6fxT@|MlC_INWXD#QC^W}Wq$XT7Q@c;D4K7ZFdF`Nrq zDL;39(L9R0GJ8tT$Kj1K3%z;DEcE6p)AB0A(aJQSR;B^9kj36SWtMoe%H(?5bM`XZ zGs`P`0}Av7Dby(08_e|T?qHN^yEDDIKNzL^1A{0ZB?m2!Nl+z*bEWRdTf2L1^Ganh zhe@Af(MNI7hY`!GnvXR?R;B?Qy;lT{RjL`pF!|u7+xtXg&!soI3)Z zydgkt6nOqoX`k6IP$D!5&n_<~ooMEx(8U>`R1CiW8wj4W_Z2+5y&gu9!KN?Da;VYi zpYzLMz|v~FskJ@2CfY7MpEmVay*8eVbVk?aPze^-#J5*-@+v>#nM1~^)v#xXA|tP5 zzwxN%c~3QZqQ|rhE2t0yG*-)-51u_<%I)2?^rC~1eHmmQ?J?id^n72k@mvg+1%3|0 z!*9g~2~9V0@cw_fs(!`Dk&z zU60&fZCZoyjP^Z%Qu}tD-lWxav5)HV?K-td^-7ocR2O?-(?~gZjq$0`YxkyV>?I3N zz{g=3TsMgexY@)xqg((D5tneY$>ZLA@(90&I^;+7&=RmPSyL}O87UY{k#*@&Gs#^W z#PEN^-mOP&R(UG?vJRMzn^Y<-hL&=*nt~OR7hzQ?r6Xl;U8zBPr0gOqHSa`qbRBMT z$VIF07;HPUH$bc!+=prI5!QzR?x|L*Ei8Qa?bR%Pg{o3R);Lm*zEyrRV2zXm#)Wcz zg28_i?K-J9Ry@P~EG#Ags0J7hcNKYu6Lsu=Rz@7z00ZTVq8IE3ydNr;N-h;+^hae2 zc5sR6@et;RTMKq-7_8(1a29JH@nnsSk&*?pObK3Ko=!Au<~KD4t=&k{+J~zaX1*21YYW#v-|jE!MFG z3x+YagiNqBpCFh~smT?E#MkAA5jXTwMAd<&YyzY$NAjUcE+(%Cg6jHGCF*6R3x-vA zDm}9iQCW}`!V7TN^gOT%pytBZ3qkAxhr5^-xS5X7bda6Nsx?y$j?_#wI7&;P!Hb4K zzm?Gc=%7anjF@Xmg}AsH<+>z4Pg9IBIk~SdC9yceRlPVOEAD@WmZ17jfv82?@k`1N z(6=@>*R@TXan)ozrVwkg)z|3jxW7NWuC24VJ&h^4Bt92Vw-UFF^P=mx*b}(fC--@g z6O1boZ3)VU=sAT)2sUZ2PefA);@%7h+Tse!k09$H*#HUYxwoJw6Kf-iPJIShh0zUr zIX(BiD?IBWd#p*;%pKO@Us@-vEmrCSD`B6s-bFBJEq~#p^#|)))*IIR7qUN(AD-FR zVvWJye2{Fm9>=e}(_33%ea8Cu4(k%@TDZXSy1i*@)|MUCOl$ia*3;JJ?MRTcc4WXf zn%t7I`l8k^tv$C}E!({lD*CL=JFG29E7@pm_KvIYUQxN-y4^Zp_4OgrJE5F0@OA)F zMAmXhJ;usdd##V}*|TSRCgW`yzTG;q!`j@Z7=hN%-?0Z`Znm~$tV-)0Z%yUlr?y*9 z`Tn@cy4ttX^Vl0R8zbrH79(L4lu2t0-q5CC5GI+ic6rBE9^SIux7xbjSB(U9k#uSc zl0;Ct+XpAyjuPIUOJr?%V`lL0%^(Ps;_nIX*kRV>WMgZ_dsU@(e1!$!-mvyqJ?mbu zF7b{VYGwJ&hf3MFGk68<5rk_vwimi*SF*~$Iw_7P{UTO0RD%3lLm1u3ZF4?nZR}xk<-wHsw zJsDX!AH1;_weu_QW#uV|ib8|jyP#rubgy@Ig|z_0W4yD)ioT!75coeO3tWyQ(9oW2GGMJ& ztu0$5JsRlkNH(?N`Mp-3chNA3gm?Q%U*|V`!ymKuppGu!V5RN_^L{l3t_@bH-~S+c zRp5EC=h%z>XwFcp+k3+W1@pRmjX_L`*~jwYviUyHoA^bD9Iv7TSHb^|b-9VVsmr%6 zSnm5%VKA_XJOY}I3&H^xk2Vmk^aKMS&eCwD#xI{3UTZ+pF^Gn1q4V)E<>AugVdQK0 zpgi6=$j6k2i;7RU%Dcc7$JsuowI*!?;tXF-;;+PP!Ni5S{Gaf31k10{W#m-%4$K2g z{DQ86Pxv43H3!RY3yM2Xn09Y4;JpQbVg6tMD)zn+4BQzkH~cxpyVaHVM}y^Sg8_Bc z3A5ldt`g&!;Xj}Gr@-d*V8B=BLEw+UQnGCg`dKFK(aZZnJZJzGa=l@!(>kc@rXw>IOm21ld(DyL2S;)L@=_x z6A6`%?WkaDXF8~#3d#fU1^!?%+D>{ENVMA`D9=;~%C%Tfo~#f|#$~C3(O9@8$*sR& zvmM7OJQz{ls%9_(lctWgW@Hct6Z`}vHC=4#4)ae*$LCU>g#fcL(P3JU zOB!?!;(9nfn?m{c9Lp&X@c9*Uk(L)1B76pgQGEU+iFOu4*yKw*-BLI{v%)Z+TR954 zdW7Bi0u;?~+)W}KpL;0

ZnPM;}QArg2$1J|{Dsbh^AOpP@@5Oj-FH4xiImfw`_Z zdj_a1-vq*j&IX+gx_c;uzQ~X6AK(lf&xbmUc0Z2=(HCiWZGkg%JjbQ_+T7Vbj5l-- zXgX#!9dy${x19ylm!aDMnEXh$AJ4h(59N4})X;qzVbZbg_+U5BkaDrh(mmm#I|RBz z=b`(ei;mxJKXRUYp9URu%zWjURG6i<97LCB5#kGoHqA!ClySq zP|xWpSe2Uno`O||=J`6Gv~<;vsrVv#vcFQWOeWYbDVU{D{f~mtx!__yqhR(}>byAx zD+@N~Kluc9^Kr3%Q1RH0#>IX>!Puw8#rCdXY7S?9WntEe5NyBneey)4+<9R>J@T+U zs(5X9YV53hD^ce!o!!Y21_i%ycE98L~5*{Iz!5@wJJUrNasaOra!37V_pYN0$)b+*u zw*r>WKf-7JyWpGp_uwSD#{s+Lc?$3;Y%Ri*`Mu15zF58k2%9-|CFJ=TU^CAp%zn!} z7s~U%ey`Ks4jl4C<0Gss);kR^pbKc!=0lUw;`=gFG=iL>kE=HL1?uKg^-IpxyWq>&j zDSeC(R|Dpu9|r!Yrf&pa>gR3^vt<+R)G*gLgf|1`d;~$jrQ!j=W(`P~2N3Zx=iz-$ z!(RoC={XPN)eJ}YfQI>u3*nz?n9m6kJ^|QWKN-N>%3dtfmx?lUsVpBrSns=;ivXK- z66Fb~E;oCA-c$|WEYo8xrQx?Ud^cdT&SUxnV0ZpoG(E=-%C}p?=6t=U0Gs|RfcE)y z9ew8|q zMt`pXc9-WUV1CMjWIUhocL=!8@B2TDz-8pGC{Qrx%Pj9Wz()V&&_B;$FjoM9d4S#d zeGIVKn_zjPn%>mMTEKkq-pJFV<8K2m=C?<~2#NrI__VX@SUu64v{QDvr3DZChQo80 z-nuxvaKVzLIBhQ+o|gyRJU1Jaz)OHwN8+^ztiN%fTR4n6YkWzisXZ)Lqv57Bj<4e? zH5^HIbgVb5L$}P!aW=q>i{~r~g$=}POgP6?E_EeWy=i^+{ZRJ}sq8t1Sd8LSyxFsJ zN6zIvcY$a;bM21Sw-DW^a7!W_TWe0^bI0(+zs@yoOjsU+h*fp6X}vu4E{vzi@$3h0 z`m3Af&{ZlP*1H}0T)pO0S{0{WbEA0gzzwS-3*8v11231MQ2MEQd4iyu0(F8d znn4(OK%pBaPgHaxkgRi1UFKDW%GafEb!w&SltOt+GN+02opU+(C`7jqXWw8C%Q|ow zgnVg-dJ5+$Q=jJOX2wk$tuA$jqdQXBi#k!#jaSD!<}g#-$5H0c^=OfaWRyB^$#&M0 zRXhvQok-fUI&jjB)<-_l$obOWxeo#5A(8osaJy308lKx8RCDS8OgFVWn=*%BpW_l< zRj#@vnj=CXvBSt)SNa%Ba~P(ZL{@nI*`#v&P;35N2bB(1UDl+{Q${%>GAEg;yR+_m P*>~V+SvP9Xai{+mi+7a| diff --git a/org.gridsite.core/src/htcp.c b/org.gridsite.core/src/htcp.c index 3421af5..5b8e281 100644 --- a/org.gridsite.core/src/htcp.c +++ b/org.gridsite.core/src/htcp.c @@ -47,11 +47,17 @@ #include #include #include +#include +#include +#include +#include #include #include #include #include +#include "gridsite.h" + /* deal with older versions of libcurl and curl.h */ #ifndef CURLOPT_WRITEDATA @@ -73,6 +79,9 @@ #define HTCP_LONGLIST 5 #define HTCP_MKDIR 6 #define HTCP_MOVE 7 +#define HTCP_PING 8 + +#define HTCP_SITECAST_GROUPS 32 struct grst_stream_data { char *source; char *destination; @@ -87,7 +96,9 @@ struct grst_stream_data { char *source; int noverify; int anonymous; int gridhttp; - int verbose; } ; + int verbose; + int timeout; + char *groups; } ; struct grst_index_blob { char *text; size_t used; @@ -108,6 +119,10 @@ struct grst_header_data { int retcode; int modified_set; struct grst_stream_data *common_data; } ; +struct grst_sitecast_group { unsigned char quad1; unsigned char quad2; + unsigned char quad3; unsigned char quad4; + int port; int timewait; int ttl; }; + size_t headers_callback(void *ptr, size_t size, size_t nmemb, void *p) /* Find the values of the return code, Content-Length, Last-Modified and Location headers */ @@ -567,6 +582,125 @@ int do_mkdirs(char *sources[], struct grst_stream_data *common_data) return anyerror; } +int do_ping(struct grst_stream_data *common_data_ptr) +{ + int request_length, response_length, i, ret, s, igroup; + struct sockaddr_in srv, from; + socklen_t fromlen; +#define MAXBUF 8192 + char *request, response[MAXBUF], *p; + GRSThtcpMessage msg; + struct timeval start_timeval, wait_timeval, response_timeval; + struct grst_sitecast_group sitecast_groups[HTCP_SITECAST_GROUPS]; + fd_set readsckts; + + /* parse common_data_ptr->groups */ + + p = common_data_ptr->groups; + igroup = -1; + + for (igroup=-1; igroup+1 < HTCP_SITECAST_GROUPS; ++igroup) + { + sitecast_groups[igroup+1].port = GRST_HTCP_PORT; + sitecast_groups[igroup+1].timewait = 1; + sitecast_groups[igroup+1].ttl = 1; + + ret = sscanf(p, "%d.%d.%d.%d:%d:%d:%d", + &(sitecast_groups[igroup+1].quad1), + &(sitecast_groups[igroup+1].quad2), + &(sitecast_groups[igroup+1].quad3), + &(sitecast_groups[igroup+1].quad4), + &(sitecast_groups[igroup+1].port), + &(sitecast_groups[igroup+1].timewait), + &(sitecast_groups[igroup+1].ttl)); + + if (ret == 0) break; /* end of list ? */ + + if (ret < 4) + { + fprintf(stderr, "Failed to parse multicast group " + "parameter %s\n", p); + return CURLE_FAILED_INIT; + } + + ++igroup; + + if ((p = index(p, ',')) == NULL) break; + ++p; + } + + if (igroup == -1) + { + fprintf(stderr, "Failed to parse multicast group parameter %s\n", p); + return CURLE_FAILED_INIT; + } + + if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) + { + fprintf(stderr, "Failed to open UDP socket\n"); + return CURLE_FAILED_INIT; + } + + /* loop through multicast groups and send off the NOP pings */ + + gettimeofday(&start_timeval, NULL); + + for (i=0; i <= igroup; ++i) + { + bzero(&srv, sizeof(srv)); + srv.sin_family = AF_INET; + srv.sin_port = htons(sitecast_groups[i].port); + srv.sin_addr.s_addr = htonl(sitecast_groups[i].quad1*0x1000000 + + sitecast_groups[i].quad2*0x10000 + + sitecast_groups[i].quad3*0x100 + + sitecast_groups[i].quad4); + + GRSThtcpNOPrequestMake(&request, &request_length, + (int) (start_timeval.tv_usec + i)); + + sendto(s, request, request_length, 0, (struct sockaddr *) &srv, + sizeof(srv)); + free(request); + } + + /* reusing wait_timeval is a Linux-specific feature of select() */ + wait_timeval.tv_sec = common_data_ptr->timeout + ? common_data_ptr->timeout : 60; + wait_timeval.tv_usec = 0; + + while ((wait_timeval.tv_sec > 0) || (wait_timeval.tv_usec > 0)) + { + FD_ZERO(&readsckts); + FD_SET(s, &readsckts); + + ret = select(s + 1, &readsckts, NULL, NULL, &wait_timeval); + gettimeofday(&response_timeval, NULL); + + if (ret > 0) + { + response_length = recvfrom(s, response, MAXBUF, + 0, &from, &fromlen); + + if ((GRSThtcpMessageParse(&msg, response, response_length) + == GRST_RET_OK) && + (msg.opcode == 0) && (msg.rr == 1) && + (msg.trans_id >= (int) start_timeval.tv_usec) && + (msg.trans_id <= (int) (start_timeval.tv_usec + igroup))) + { + printf("%s:%d %.3fms\n", + inet_ntoa(from.sin_addr), + ntohs(from.sin_port), + (((long) 1000000 * response_timeval.tv_sec) + + ((long) response_timeval.tv_usec) - + ((long) 1000000 * start_timeval.tv_sec) - + ((long) start_timeval.tv_usec)) / 1000.0); + } + } + } + + return GRST_RET_OK; +} + size_t rawindex_callback(void *ptr, size_t size, size_t nmemb, void *data) { if ( ((struct grst_index_blob *) data)->used + size * nmemb >= @@ -1043,6 +1177,7 @@ int main(int argc, char *argv[]) int c, i, option_index, anyerror; struct stat statbuf; struct grst_stream_data common_data; + struct grst_sitecast_group sitecast_groups[HTCP_SITECAST_GROUPS]; struct passwd *userpasswd; struct option long_options[] = { {"verbose", 0, 0, 'v'}, {"cert", 1, 0, 0}, @@ -1056,9 +1191,9 @@ int main(int argc, char *argv[]) {"anon", 0, 0, 0}, {"grid-http", 0, 0, 0}, {"move", 0, 0, 0}, -// {"streams", 1, 0, 0}, -// {"blocksize", 1, 0, 0}, -// {"recursive", 0, 0, 0}, + {"ping", 0, 0, 0}, + {"groups", 1, 0, 0}, + {"timeout", 1, 0, 0}, {0, 0, 0, 0} }; #if (LIBCURL_VERSION_NUM < 0x070908) @@ -1083,6 +1218,9 @@ int main(int argc, char *argv[]) common_data.anonymous = 0; common_data.gridhttp = 0; + common_data.groups = NULL; + common_data.timeout = 0; + while (1) { option_index = 0; @@ -1103,6 +1241,9 @@ int main(int argc, char *argv[]) else if (option_index == 9) common_data.anonymous = 1; else if (option_index ==10) common_data.gridhttp = 1; else if (option_index ==11) common_data.method = HTCP_MOVE; + else if (option_index ==12) common_data.method = HTCP_PING; + else if (option_index ==13) common_data.groups = optarg; + else if (option_index ==14) common_data.timeout = atoi(optarg); } else if (c == 'v') ++(common_data.verbose); } @@ -1185,6 +1326,16 @@ int main(int argc, char *argv[]) else if (strcmp(executable,"htrm")==0) common_data.method=HTCP_DELETE; else if (strcmp(executable,"htmkdir")==0) common_data.method=HTCP_MKDIR; else if (strcmp(executable,"htmv")==0) common_data.method=HTCP_MOVE; + else if (strcmp(executable,"htping")==0) common_data.method=HTCP_PING; + } + + if (common_data.method == HTCP_PING) + { + if (common_data.groups != NULL) return do_ping(&common_data); + + fprintf(stderr, "Must specify at least one multicast group\n\n"); + printsyntax(argv[0]); + return CURLE_FAILED_INIT; } if ((common_data.method == HTCP_DELETE) || -- 1.8.2.3