From 0e08fa0d286e34494c2fbc7320f82c964b0dcf7a Mon Sep 17 00:00:00 2001 From: Luke Murphy Date: Sat, 6 Feb 2021 18:56:33 +0100 Subject: [PATCH 01/19] Pypi: bump minor version for crypto changes --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 6b3bd5005..2a7d568f6 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = epicyon -version = 1.2.0 +version = 1.3.0 author = Bob Mottram author_email = bob@freedombone.net maintainer = Bob Mottram From d76d0083e832bdfd43f6a8a3430184861e74e976 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Mon, 8 Feb 2021 13:22:18 +0000 Subject: [PATCH 02/19] lynx screenshot --- img/screenshot_lynx.jpg | Bin 45946 -> 29434 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/img/screenshot_lynx.jpg b/img/screenshot_lynx.jpg index 47ef21ce410377bff15ad166f28c284d4447cc50..2790a45937f20ab687ab4e7b1360423cecc13eec 100644 GIT binary patch literal 29434 zcmce-bwC`;(ls<0AN5$2Rumsq)kAY2?p*b9~PuJVPF9a@C+&fTp*1Hp1r`s{u%sF zOe9EWf(KZ|&+EU=au8)TDlT?T4t7ofP?Lj;Q;360h(myilUs<3Lx_tTM8o9&QO}(Z zcLnqc<`1iPreOZ0dqD{-=n>o<1mwg0=^Y|?iTF1f|4;f32LtaPsi*P%;XN3UG>WJ49(nsb_Vo)3e;V;D@_7_ADLExI zEj=T%u&B7Cw5+_MvbL_ip|PpCrS;wW58XYzAN%^p#wRAHre|j7mRDBS*1v9SZf$=* zJUTu({c(1FaTnKJoPU)+0{gePu)(-s;o;%nk?!Jxf%O3oIBa+XYEDEPNp&Q1cU&5- z5M(^5#DdpdD74%f2ly5qpHK5&voL<k1u<93)Iw}9^T06((GBN>+XLZw)c4}m6IW$8R4qp z=PsMamaVSj0&r0fQsuI$+MNrwm==h%&aI=e3>}Y-c#XhI30wf2Bb|V@Lv!Qg+^ftw zR920i05MWE28l63gf0yaq%HlUkF~>liIJ0WQgM@RZ%>T~N# zLq|;+#uTVVXc_a7Rk9Ch6K}il4~kz_7aC^oXN%+L)WnRMkK0gyW&4;-g>l(&W!p4; zC1WX6KHu=e%IbRzr9$N+vLDfsO!MV)EW6oYM|pr9l@NK@($#g-T($36oWw9Q4v!wV zzq5OMe36+k6T^nI)WJb}J;t zwn5?LgEh-F<3R%!|B; z6Z&~lK^D#!^zZ6Zo%8i$%bBx%N5sSGsz?IlzT`}Xi}!ha>n-qJi&L#pEc)IgPwLaV z$dXmG4@NQTg?M8n$Dm{M;{Re}9I!E0vg37)N7S$`klG`n*oazD>5@W$#XVGj({z1ExSpK6KK zoEv;SM48DiMaIp}o%il#!`IXv)ni?l##Q7ZqMf+M6}sB5EF1$?t7U z)o5Eywh*(hTqc+-&Z^q=;n5ojGI^V!uZ~{ZHMG5o`;4Co48Ks!yH6HFIRX`DDRKAu zfxh-sZIGxkjti0Hq^8Ijp`P=Nl0FgC8*V1v#4lbY$H7)LH3#atvBt~VzlNo1mzkwZ zXWQ4uQsC$3o(bJPoh_7h#?tqge=fm=O#3phe!%}D&BW*BA_hK2w0bhzdGV>}`H$>u z7Ew&I`Y4Q4_ttNLsnS)ysI6N-7j5%|Ni(J2772)%^4GOAUxH1no}f0BLdHg9b7IzX4_dV*6y#?bm5?|$7V=BnYKm-Aa#;$#INhW z^N4Zo@3-gok7;Zh{}Q6Ud_-);=pOTAV3_*`Uqf*B^GQi6)@weuC;_CcZoQ6)qUNx^ zC@L618|$v!@yseq5$_+0_oEbo{m^f+gM_G&2AFyFuDaSa*iB65nsE$25SVjAn)BCi z(0LC7 z^uSqgaLJ9{G{hQ8Yu5pR>xVS6KakFDI!k0}_#2V}!@`c3W=EXy2+?@_b4LHlKikG! zx2f0Pl+0oaf>PLWCiqT&pwJN5V^t==^dcQz+fCFIv^C>MJ(VEp0yNbF=T3vlBs!Zl zDt1fszQzxc7GM&m5f4df%zaE)l%Wk(*TVsJ43$mpKWzy-J?!z`l%uFgvI3>tI4>gJR5c7ds;lkgur%j zp+0R}v&xe6W56nNx}h`k^BhrNH>;Bc417$kpZj#)SSq!r>{RFQ`_gZN0~zqsnhJR3 zKKEgElhm~{#vG$F=m`$*@y>?sGIq$9cfZ*nics-;QkFTzMwiGYoT#o5Z9fA`ov>zQ zuBvKaNz1?w%f-dLpv{o}!l<^*(BD6O(5dGkEo~FL=lPg}kE5>#o!%@*^LuqmZ?lLM z{P2&~Qe31&M1s3#pDK;oCqMd9+3B(Kz1}!S-B7N9TPg zxrSm9DL&m*QTY(zbcMSko+55(46zI;3P!Zaizi#gsp(xI1O?$`aQbG}eP*Jzb`h+IRP5cAZ`O?WD2 zsx--PL6$m!n20QQfobFL+L3E<&E4pEo;_JmkLWqJa7c#ZtAc z4u1vVKu})#mv6bHGWxHZ4IN8TQqqCPCJhFq4b6PhC``t0DYR|@tUXrys_sJEI(E~g zlA-b#sm&)nPPy68plX|`?6j9Y&gc(Z>GA|Ev|C(?SL1{xv0VjwsFSi+*F~nhoRMd% zo($yHtZ6s5zA?LZ?8`+ndlS$(%*rC{dymbPHMF>9RBm;=#18VJKau4Hg&|RBgv|&= zbGOXnqk1TqO=0Uh7S$L>)`pZUdRlV z%Sv0qAu*P%D*jsvIEZ(2J{Z2qK?w4gW}29)jdUqYt}uIniR2ei(wOz_2j@_ecMnF) zD({<=l}~7%vPT+<(GUx)k0ncjqQXH>t)|*{uj=#%NcG=1>_gXUVOHl~;wS1Yui81g z<_whY{a|Rw`!>zYw>XdKp84r!K_sXs4n^F5TI};Jpz83ku*S=r- z{XKWUzR{)sk0GXMc`I>SO7@`mX^PK_w*b11C_bfj(acMG_OAs~>sXa?I7O8Rm~QSd zo}r>u9CdvBIBp!=5E*+^wP)|1d$@WmmV9rBuzTlngclRht0pH_lxe-bmez!EKbTp1zhf%P+{-h9sLP)A@|Js)ZpUrR^Et4*^y)S!vfRx zop{&Nz>ee2`DZpub@44t`9;F*Kr`m5&-=@fAhN*<@e8XsrEf3|Vuv8L8a$%wOb#$1=i{Lra?Y9!OPOvTe-OFy2JauoS&5xBgpf3O&E~~|gXV&)rNBc*dQFi7 zB#)q0mK=OcY}brx*(`-F89ePsrot%TS4W$*^=TQ*O!%C9*M;^D@~ZsHq(QAit-Oo_ z)44k5QhzQ+wK6x>P{(PWwzi-%NoUs#Hrv(+oR7qC&a_SCB0BZk6UFBhJ^Q8ugW3fO zUK2LEl_I(CK0Ef=#GzP5W7j|Pw_I0G8X48zJT^8R$W*=03a>k-0DWIZv3gW$r=g3;IT(B0-4_l(ZwY0BW78t_vjptDHTT3v?f0bYA8OP>DQ5f1!jjmKj|yb)6eRSxg&8viB+deHO{>phK%RYg}cY&EzQB z&x;*(;45~eO8xbVGzQbvmuT@Zj0_wXnGN&RW$-2N z)MbGG$stfmtGj^d?}J}aQh!=d8W-@Qs+EfF3xD&4j=2P_&SwdF= z2FK>l(>PgK&eyQBS82(-u31(gTCL6x)D>I^?D;p{oY>i4Zloxw_Ya~W-|VTD9hIK* z7bTpZO9+_1{ZZvNmBbKRv!s^+8<3I4K%-|BEGsTlYLSEcfP1aC#jQ90n2d;nBSRB6kJgH*V&$D)Z*z{di+8E*2&G26$h>My{Z7eFht=bU z=@)aU%fO8-BBHIwQ{BEl=^OBMVymZU&F7pfu+#bR6ZLYo$yw;SvGf3mGe&~2ZOB+H zyX9k8Y+*4;N|o%uhN8*`b0^o5#qGYc*329Dz(+aax2p{rHzO9}QyXY)dwEA^V`ZYa z*$uTFxw7~q+LRU5A?h0DRVS`#zJI{KS3#l7{sD~AKgL;(q`88 zF7aw7$R5V?VY!NmGM>6Y^8X~M{*Dm|Kb#2l`9V}u1UV=z;mbbC;oe1ggv_@`L|!&= z)a}u0o7y~lbCWV=FZ8Aik~*=iIS;`}BF4euoI%%l#G+MZFZm#&odo;k16xMdGeoU9 ztww|JQ{6DDgI7A*X70J?r}&h!+Snn&%VMjcy?=_#J8`D!6Rbn~b;u3)!fKBFp#{pOhr%Au;& zwJLg>KWnwYp5o(&1cSZ(Qf_#f=ma(AxCo?vF68!u2S%og5I1ZeK`QiM!QH4x`ucQR zw@#|%#~YmvRUVg>7f*%_;*Ok=%^N>7C)S3ouRZB0Y>uT(!fsse@e8}E{4h239r1b< zaW(~^_2jftyPAKf(wzJe7<LR@l219GkZxZ3ivM*L%DTa~M zyhXlwU~nRi^0-VjOu4o(WqdG-s*o4S`-=)>S6ayJ?3t7bgfVdQ=(%!vXG?#$O5}@T zv+wgwer5!QDvGu)a*Qv_6ehpp?^Z8G^9nzU$LYo z^O#8ES!g>Yxdkj|sZ^TJJ@Yhox1u7xJfyqs>t9`ZxvJSTe+ye$>>kqXwY+Jz;}oW$`7&8ijR!-dsJtu1z7pPT(X$NyinNHb^UE)aJ; z#d$za|RZ{V{m%oQM5P=!p}-2#+Tr9N$+())jq-fyhsd4U!Bc9phsLu<3JA;wob z5uyi1Rqzx~T>>2Y{(m_rZ)btq71&o0X=zh64OLl)k_@;G53Zr-J6Sq=!gBzCvx}F9 zhMW`?xFk%4v<|?6OX(N@F2HPV>FFk^rl$1Q!t{ThPp9tI*a2Xi?atPJ%Ks||!^+yz z5?s)x0#Oo{ZXVzk1dJI-^Z9tW-O=eFjb~|RZUxdWK>EH1=pabX-0>~`pugSGwm<2+ zoecobLrX&%jO}hqgUa@Aw8h_OOFIu|5W@pv=&YPwK>M(If6!KU^piW<+36AJ+s|?r zA-c7TwkCL{0}nDl4uAm4fEqvrSOAXzd%y|YK4AmTE+EGf&;aF9ztJc8sjmiVS%6yh zfF-E$5O4*Y0rQ{wz+E4J7_j`aZ9Q$cxqh->(4_zX;q&e75j_APB>}*7!tL#4!R_sJ zA-IRK003`Y{;BVp4*&uuAV1-sJi0spxEBrpjUWEWvq%Ggre^>^H0x&WVg9ooIPeqJ z2HYMxECT=xeE`561prjTKiUnH-N}KXX#mgy+e&E&05ZVsA_iN~w$X3m2DgF!*>3+8 z^Jn}0+_$-hhK_-bdJh8~0~_le4j}_EAwE9g0~$Iq1_4fCA%0FiUQro6MNvsDDPBHh zdlfB16H6;g5d|kNCo@kyb4$~o`!-nE*o1h5Y{bNDrs90!rvK}5`wrZ~Da3AH;v|d;BE;&p&0ie{LUO!C`@%<29l@VTr$C{*9!R zfnzxc{x{~|E-@f%SqY#Wd_#mftGg3G*d0}qPdEg?>&m}UGUWrMY;3h7ZYa?T0NwXjomOd$`@tY=S;F?2v@I+V5am3KGp(1sZ)r|N! z{rKZQyo91e<^${uVM{uch`Ss8eSOuJk_w4IpeXe=FS&s`2zV#|u|BO8VNe)BC zc1+1W+Mu^a0)(8`g8>3cj}wnUEYH#2QVH;0 zue0i~3)a=h3{%oiZB3p4Fnq_xJuL1Z!7xweZKgFcg8{ycf_SB1@J8jdG1%5r0|3II zMkoE;uM5YVTX)TlaO9jt0Qx>oU|mzfd*>uetCAt#{Si0dL6+0poeGjFMx4tTh$Z!y zEE(|yB>;)V)}6zCkDWYy-hvW1C1XxUy_1h{E(Y~2`+|32kxS-5IDa+(=-2#)*2Dnl zvnkJ=3xFY?!UQ;|qC_hqJYvhD=LLQWU^wl~YVyHUoGN?^blBAtvfam@0MH%|fZ2PO zS(xIg9AHX+>eU$Hoo)lxJuy6Qd~n%i~mY%Fb1dhdYc?&Svs7F-+WGK_Vp`Z zKDCslZj057U}$r-ax30wiO|J6)ARH#;5=ZkvfdJJ6bw29;7I&T!k_5}(A}|M=)eIz z_>KW+W^eY^tSI)#tfn!LIMtB-AWL6)c1{!W^g7x zCVn+{^@E;F=R~DvK!!GloJcJ8d-c82Pd)dECS!vvE+%5YTq+Fl)5Z=C2(6l@h|PerMUxGm854QHWq-We77GJ_qEf&M&cO z^Nmfpo}H%RjO?!;s0uS=H1DB%RsY5pfShE>730*L?8ni~2;W()0jo3{v)Qj)0C{ZA zR{K){fp5e-pOrqXgALBEYIEv~+Tx&!RsWR+3>~3RLt4^!_ttWmpgp309cp&JtIi-4 zhRc9^v8MbNDloiQLg?1jN!Sv94W9o2La2U`#$!;z&GVtrY-7LO*X9$xp*7c5{(k^} zGT=8W~tseP-Bl6vt{ObaHI;JYEe|(bla>!${a3*E4(aMnNsit>h@UQXYWq}7h zS6s6p^3gUDtd+k8fkst2guk8o(su70wt05w4EEOi#vW+EPt2v`Wm?I-oC_028x0CMTV0l)j;``v;^{awHRqr22anTq>U6e`9Rh7dy=_}_p4prIkDjAUyo>if?74&DCQL``g|toq4c)qcm@-00XdKA2|5 zybAsu1VEdI&<9|e?2(^W)ChSd(Ik@8Pe%O?4Le@UO~{vs?ewv7?l&0l-eR=PD{J_& zKhpB$T9rLLiQZX*uJmro{L4a;`!c>MXLDm{oPY3PH(xqxb^b4L0GM~VufN%vsIqs+ zxez2s;2TQ(8VJyZO^S+385)*+2~K+wNxOzbYu@vGZGxx^itvZFIGikn?|q z0eN7b^Z9K0ZEg&R2w8Fy4A#6rt*Pdf_zjw02qN<3H!_gjUZ2!_Jhd^rA!Gq#C{sM* zZKmn<8}#TFugI8%l?KfxW<#6bjAme#Lxh!-Xdr`7tN-E@uu1n7;fN|7Tixk**#(X+ z6fcv?@-}me078`sr2@Z>jL&^-EhD#C`Pfdc>Ef}2pB7?JK7zEgIjcFv_5zJ2q zdGE^^cEkWTxCWdI?z0_NWx3UphtvOa6>YcQWK)(Rj|A<=rwGTcrw_i2@)ze z9=ewhB);WICQ&FJVzB3i$o86hLiASPE6LxYX#IiVgv7p0fk4Nw?NNVPlWR&Jc%`QN zws$GW{r#)0#m;*jH>=Og3RqQGy4tpM{_T*BzmzucEak+ zf}L)GJlIlLERsOgKCZ|7M2;VXRRuk3KHu;4EELqzmMbNbD(~a|C=gC2`}SRVpS!Tm zX8932y_R<}nN*T6RH3{gCoEE%n86nbD=cz`NSm0QF@cUH2C7OkVs97x3By^QPg!hb z#xqNmps{$WQbxtKxe+%DsGFCo=aDwtedS@6Bwz6kmJN%9w)m48fb;b-+MGbj(~KZ? zhE9X!aaebC!c_Q3=GxeRo~}8VKFuBrB625x3iEI3Du(Z&E)4#FUZ1I{XKG&;*uSMFi3FSo$N;CkpKao4+iVE+q(8NvCp-)UnE zppkKs#rb18SU442269wjHc-V`V`c?X`ekg|Z?r7ItG=RQjFHX>>s1vUK64vaZVmbv zDcqeEt+C2@$8on2UW)&|dX{ku$Pd(@qBO$3$J`%?eczwuM8)6oRaLS8z4_7`ap>)< z_icx(Zil4{JV$9lyK5tVR8Ei*bX zze&j{QGgcT0(Mfa-REPT5`qTFW4lz?W*#IKh>1*Z#bC!>Rb;GVWD`qsPn*eAeb$j3 zkV%Rev5da}&Rf7$oNC=Cto(Ad0+@P6WHl*)-LUJP=K7oWsYN6P9nU46&Yzq>J;g5N zTqjfHH!cnAaQNa8k<84sS?ZV4>~&_8S(|4%Y%jmz7#7&tBOb(dyn!cgr!_Dp;kafK zl7+Ca&4S4ZD;UD~R+KM)+>>mKa+?@*w?e}k{19KlZj8u!gh8uJjyT3`Eq@RM$#d|< zL+{TzqauZ2?noJSZ0B^8HJ4P=9Yv*=JYd*3*{s&m+w|$?XAV8DCsO#bCOS$#W*h)~$Aal574Hz` z<*>AHin>GzEJ}6)rRnAyr3hy>-qsIvy)+hT+Qoj82Z!V=5(=9r<-xj16+Te+MaaNb z?WWeJ*wgUKm))%x!EgZw%CoG)3n~`1a8jx#S&1H&Z;7_=ZTR`;8k9gZv{G1Hg_Xc9 zFxgopbMrbrL4gvv`g`zWrUc*QS)BPRU}HyuVcK}e78$o%vNZDZmypJMQT89W4bDj= z-U5-g060=JVuJ%T3@jonEI3sEQDCsKV5z7%I3?B1;Bau&@Z8-07^T5w4RM&8%y4RC zDXj2$@FS~$52UD0m5-dLk(FP80u{w~*lT+}BKPu7XV0_jEjD&Hs&)u`1lA2#xh45PA^q`^;0fvc^C--a{k?6l|rmue&o{914>UNv}3g z$g-D(952LOMVT10Ul@(8qy@0sdoPUbmkhCA7>|ZZJ!HheP_DqrmW_yrl%!@yk_(r7 zA<1sA8UdE)$ttu9iHI~9E)Q2kCrX$V)Z6R9PmSaj8Fpr0I~isLk+JwkOdmA%WZ2JC zjHu3;!ap$bVr7{lwPION^V8r4;I0>br=-Vo#Z%3U;%^{~{K23eS>&JiIQ|JEm&Y_Y(8f?5Gk^^yYr+83w zWsAuF9(RmXEat){?bFTXWKF?}^|~KHyE57sJI_{WXDt7R3pWb?)$4c1Y(n|pUR~uT zyvp_(uMp*>e9(LHSmroB-t<$vrf+&~#Ov=TJvC^^GTjdf1EqCJk5D$Z)Q3<-lRIL| zbSGTmEsu>!C_0{fKcRf4)sg6az>|l&Th@1Ks>Bv!aT8-`sY4&8H=?QV!iuqo zO$4!&S#k5i)V-A}=tC!%^BmxM-QBa(cM}xwnd!R%^K*70c{e>#H^b$UqES15moRtJ zaQ6rf`8h>_y-WUEK$)5Gb>_^FF{V8xgqW6I$EvcMuAAp$o9<#C+h8KJic8x9YLRK< zXfFJH$bp&h%h!apL@1ZGjlzo|lqK&i{Y)vJl||y-uKK3Gke0lUzRVeBu)KzFDs%I` z&TJ(cC@~Mz+QNn~X(Ow@&U#bV|+cSY5^Ip9VeT zLcoWH)>3mWvdB0+`!J^I9#ekDU5qE|R6i>oPF-4Da$N1d8t*g~KJ1w8EefbMl-+3HM~3uiZysokx6pN)gb*ZHG^1FE42p>#q*v--%^K*&svM-elvF;eH$Q zPiK3I*Je&WNc%NEG1R!+U8!69F;lTwLW;M$swX^M>Sq;{IFwU+(LsWDQ6?(PduEm4 z(D^=`^mAM&mjQ~mfYcoKUJl#JEuY0@*6M@|IO_2@7 zN?hY56OsE?pFY#)@(VvoRXLCs-7epYZ@geU$5C0x`H~UQ{F{tMp?cjtcR9mAjo9rN z9$JzkP444`hGN?WY@f}3E!(2kiUtA6gV*{BF~{D_j;h@Q2D1L@gn=qvf^Ly*I$-2? zh(L`LJKF+c)6B5;XGnqBtFD%W3LjZaVkV0SmBQ^$)qGxk*`Lg;x7bRE6({cb+{2!d zp!KGNV16{&R47rI4N@h`o`F1mm{~^{i>1ABm-c=x5ErZPeOH-3^4H(HPdlUPB`?q5 z)MvJm7HAb-(tOWJ5f7ukuVArYF{7ou%iRrfdg!6(Nw33@``P_VQ1{lz-sd>luJQR# z+fHsD13q6vucdFO``AQ0G~&YfjS^RT!Q`a=;eU2QNOxR#@A&y#Cwj^!(b*aUY`sdM zEHW-ipUyN*uRw4`co(-*DdQNh z*rNVHo*}J>?^Js3Awiq2-!!bFj-u|KOXAt5TAL|Jr!iK;@7qpJ85r~%6Em}%_D`@r z72c&ShN*8;EyWmB!`z$V#W8Wpn*{AOFcoF7$#BUuIb>1GyPTNlS5|VrvX0$l;b;8n zIFvZ%bXo*0vqn6eqdzm#nXx5wMqOS_zKfdW;3v+7dC#KLJ*(L2yA{3bvPLYJBtr9N zn)WUM$}=U^;!l-DM_1-7qD*(z$FwU1R<2y4*&c*4+HJR4HPS@RPc-T9UOabGwNQy% zqMgyG6|P+y+%yb4k*b<_>ZVE|Tq4U(>~oU@!2_ZWpiOD(qC3AS` zT{;|@Iq98ZU9p>ZmRK68{RFNIpRs%yZmZ|zyJ?518|32E*C$p15S$rxS(TBG&YvS1 z4ZRrU`^E|1TB7Y0CPtAcxhoanSu}E*!fc;#h?hZ=U53jH$`myWXy)*8^N?-|45Ty+ zv7=vCX<6=BmOLS+-H{!6Q4_JHSF&dnljzcOLRv)MoO6Y+XcvHz!yX+|4V^^g`PjQK zIY+(-C2`fLr+spTimeoooV0s`c6NXL`S^D0Xn?X^YWKVrV=W7dL0t9hEs$9Kz(e>6 zs@q3n@k93b^D|>?pE)67VYY94`;s#q$dj4g>hj%qHNAMfcsD553Xe~><}+(PS(!w7 zlp3~5=MZl`2oaUL=4<657kKY7SzSjSgF4kq?u^03lJ<6;9^Yx}L@a-i6|WihNd|Qv zb>D-~6CwS?sHkE^E&7T|YoS5|z0t<$FWZnG#im`KKal(&yb6eWM6!@#H_K8e98_$3 znmwAGkv>mW5wADPn7_vqdvqY$&ne5~u2ig1$!QFI8S>gm&fCi5g-5j*R+5aPjSx{R zAH6Ajnzn*=7jcCXzq|(5!?J_GV%%$H?7Mm`$rb!)a+faTz%NUlyPxvl|8WrD-~psR zzAFK2YFJJzDoJx34zn&AE-Cd8HTOg~35^0;Za0tM*WdsAO^+xJ>xnYy{LPO!mYTB| z-v|!-lZjs^>xk~UvNr#s&?6!)r`Q+<<;2l`{sE9SBpCMSkyC|o2|YqNhtbAZBpM$K zxBsg3{#0&~rQO?9rHTC7nXayTvGz};b<*JO>i?RPI#dAd4D2^J- zCq2$m?y}jFYelQdRZ3YaBDP(6lh(U$x*31T#a9dG=&?8c$Ruk6>{)hEy-$^G!}tJ? zgtL`TKd-jp=|g8H#6<`ipE;3*%)IYXt=y&ccNR2v7xNA zQ{nw$nw9N2F7*5P4x8Pdo-;iRN0fNi7yR}^GL0NZD0_7g)w_PgnrO7L?ns&Lqh|yf z{Hnp`R&Q;nqN}920Q=lS{I$b{WTW|1qD27#6R9`3ZkAqO);-icomO0mOW$&~x_OeP z9DDH(+lVHlpRO-PRYZ%kr?JmfwY(klr{>%pE*WU7-1E5Oi@CGyMN<%~KV>&=(Dh6o zz}qhwO%q%cXUAn;>%k3UTocoK=h1clPB6zMp!)5crTDNrTw6#c!eq9U2Yo-OWDs`RkQ($h?p8M(qi*;MkR*Feh+1z%l zT3U)4{kTn$wok4MNx^oCR;QKhlvYbbt?-Je%!MCo%OE6WcUj8l@`(KCo$z51tCi1M zFM}ZG$M}lzqAGrw2m&P}8L49bGNi)j?eeUkvZGI5jrF;hR*CTNuD!K9C=1Za-=*dw zVMc4yIF(~gy3Ho6R5ZVcJi)*t+~hu0v44oz2%yS4Ps+UMN)QpFB3{H~F^%|`_?2Pn ztxH%oGR2uGb3g9&qj$BdMQ9zeGU(*w2UdI+TWWqhG}R~v;RZX89yLj{3T73std3GW zcCe@vwro9@oNi@Z#+YmcGE?pwX+-*N7Jb+X-uyc;r7?y1tU&9Q=ywn6&7+t`l#?~b*-vas2?Ae=15ZrhMCK9 z?d8qqpwrQT9)agW zbJ11b5YAiGm~fCP1^EA~N+IM)gPZ=B6Fgt8%gXnLS8y_ zaG$MXDmu2Lbor!J!X(&s-79%@-*Nor>rJZo#zp22CKme5w$JxPjJ%GGcgS&a(VNDb zZqzdLVz}d8NIUV6zK`cNSw}DjJ!lSa4(p&;6A1beg?;j9epv5_cTY`+QuXrL)Qs@* zp7gUD}MUO%DP`sQ60b)B<>u4mc@v1TNRl3`vF_(QDjp(p&K$^{mmloMtfJBY?h zPBZS8qAdWQI1>gt8~nIg^v0&_Q4BIk7x-Z;%eCjmCYa#faUmDKdR8}t1|}E& z1EG0$3r&tk0{01tjMqCNohuaY1g^{Ll?MskA=}xlb}gYd>(BG08u<}WeQh55sB=I4 zOp+n^EQ~EZpJ@+^Z8<|rvHDYmE5U+Z@*uei8bS}DuWK`?nwQT`gg{YqnrdgD#b$(t zdSi@_Pu%KHsG#UzmR*v!Hs>nn~!L zBH%Q{`ey%G3nt~67;$}&I{)$OviN(HNUnmGgVQb2(${h6uH^y9Znr=e9_{vS3zq#K zL&Ap3XOG^DmmU$%F3GEyOqL?&$M)ATLSl%%6Q>S$y?lPJ$9o{1{E_r4hE*@~vF-dTPnn*WxUV{HNjW$^P3|GIA9iN|{6 zvQ3WPhFq#HU!N;+Kk%r24@M-o zGzinUl<7CMBbMGp1jh@l{b4iaH*GCDk>#!om8?2DLQ$1%{(MZOs#VK04N5((46kP$ z-OMJ$#`<&42NC$wd_>*vMgDpAh1IP(uj@Zt2)ZE^a^7O-1hEW9F3 zzi{fdkL-f3M3Kf&waROH>NhjhsiD{GalSVl)Hta&K#oL5vr8T3?F>#HzdC&CHS&S_ z*nh;GtW3zJnlZv1hLP>~VrV#^emT0-!HCqfrZ3SaRZN0_*qTVxTCz>l|2{#iSLf^} z)*vdtwuD4uP|2RTxFwpjiDQfZ2wnnO2P^!chCeU{k71+u)!W@70;CA5nn`wfFXCeOH}mU5vydY&_;? z7nHQGTF`N(ZUMRzs;?~fB6CsOhmIGoqRj)i>(oNfR&-Md5yA$QnHH6FHSs54T%1zT zR?r}sRh+m%BXgc($@qN0c=VO?Fm36R0o9jZ4ec^kN^yn8xI!m;&|Gk8s6N_e=0=+^ zyzKAHE7?x5zE(h;^76rq9c`UZxR;kO!CC_MoyOw%S8XjxheE_B{ZEo*&vNqXY|)ST zIa-4@(|^<*xF6zvF%mL`iWbqkt_7nX>)<~Bs^6A5B<)_HNFkyy;}FiFsq|JOBTV!G zAp$$=8n;kPk&7qT3h9;WjTZ1~T_o2sjY|nq+++D%>DD;$E~JChFGLm)pE2S*6OTlX z4Jbhy^23#UM83LqzkurlyJJ7y?21=O)PpG(o&JdQk2>o&c1{G64^+Pzbtc?@P*zJT zyUio`0-oR=hg8p2wS=C@lR;gz395{j3C@CByU(rW>h7fj@?N@w^!Ez3I6}9m%>zhM zX0_qlP}}KBnxBLzews4np6H)j&5fSx6m`>XfZZQoqetBgnG4u}k3;CHC72Ri_;jE( zT0A}|52u*^$#%>IA>hl4taB7Sz@QyPBhkxea%T+-{dl^Ut%r%*AQ#tnk2cX0&N@|r z>3fVs4BVI6un4v84{*saDVh{4*J2U|5-W9x-f!SFt3^UF_!KqV2BP$L+|}3uODbPZ z8QNIaoQEkV8U~4A=LT1z=`F)(Th?oQaTK6#wu|RT7uQH%OVj33CT=uf`*M7at*J#I z>(mtKKAvGVx1_Js)40M1760Yr`5P7>kx^NV_QcUO=Bo#Hm>-i2MXetk0HaUPU?OQGs zG67YBaZ{!%50-cbkqx$|gDOhUKm?!=UOaoc;tIM146sHKP$kvwP7$hKoYY5`RA2*D zZ~%>iH71kOK@~n1gNhJVKZxWQ#AJ7i?oA(E@^g@54XU(`KZNRifbP(xWzZ!cy@1N+ zLm>dDnAid;;x+ftaX^?fATCfv2sMPRU<=fugEG^ApGhDA!MnQz{|^q~fr4-CZ;bqI zDfo0y`Hr-ttY!$S288v#e|Yp3-OE0D*8>0sY;6c#yZvJTD25i%LAB5>6sMr_;de&< zYxyM;k>CJO|VaYrDzQLY4ywMK>W2uUam;;uN5I;N$aJbe`ym!f4vg<8!gZWi7Dn^ zd2(lpUJReGulqMr<)izLvq^qkBb^^Ve(i5WP|eF$B_fSqOX;U_ep4NYS4}G22>)Fm zDY=vx&wrz9I+fr21?Sf_(j|6~S6lx^QS%ixTl(*6oVTj(>HCeMT>YxzZ$w}MoNM&5 z{YGjmJ_cd>U5%HxQ+z>Izma;|Sbwwnb&T|v0q7{dYaEFG@}<#lY|@J_+Ti@!CLL56 zn%(}JC_okF--~`{@c+So04^~xWB;+lbhoGc$DMPHe| z;g&NPY#VGdMe!pr&-$xkb9AqE(vJiKZzh&cGY`Vvl`)?!?Vqq|P2Y@gxoo`|nje%= zpKy3W$iMziOv&j)gQ=nh$^6JyR)5c)svAX^!2NPVwMmAbHUs%DuT`*A+SndTE^8)V zx&FAwrs}D&Js0QdOrE&~*ju*rI=*zI%%$va?G;al=~WC=H<=!VQH?(rz;bNze3(Z`vL&gC)leh+GCo!^e^>}6i9GLlij3p# zzpBhv3}sVBhUzc*m2;0{S2#9Tbu5#F($wfNLz$y|#!l31{bv1Y!qj9-YF6f5wKYn% z3zsBGQrn{1_OQ!oSk^-`Xwu69-erOupM_PAlo~4P=T=~szcX6^;iVyqBUPHqY`T2Xx z>AQwlW!B14OYILb|6gNY9T!#8x4*#B0t>RFgn%@PDBX>8cOxk&Al)F#QU)cxh)Z|Z z5>k>vvR%eGGQXfx>@i z3-@~$-Rtz^`=tx_0-xcQaR!7AcnoL`RzRSIdq$$kP|2Hf>xOR4I`?m^HA)r+$-b1v zephS36Bs=YZQ(IxvqjTp&sQcsaSiDQ_`>*2V)GBd&faZZDsg$oRqFuy<2iCS{V|8F zS`Wy;Pg6%k+MmD^Q;~lABYq*I8*E(NEGeW^aF*a1W#UuYCCbFH+R?Cy58MNGNVirl z>#X}9s*5-=piW99Jw!%(wExO`p}8}^WHp10QP8b7#VJuMxh*jS>D%6aPO+&=X_G zw9#kT9Ovh=47b+FYz#c@bPlhTp%!}tCJ^_laQlWAX9Ah!F|5h*RP)d0eKZYdmO_AT zJjXBVH8$!|AeF*|XKl|~;XH{RU44J`vvuqhj!lvn7GrkrV1=jbS$FAcSIEG+?z08; zcgZG{VELUwyq^Upqs#azs7a5&7rC!!_9WfHgJFYPx^rds4852!AWmGgck-}P8+@J^4 zO{OC29TQijBerTv-S(MrOkdYgd-4yFlz>4J$^8lYASlDZjRsT6_{$I zfX9@=KJK`P`j)Px`F(QNw6u(G%J8Dt`f4&v=(9o^8_iPIx+qO4xE_1J^C?=Px-!r3 zZYwL?dyhV=!i5QCKJv`|fOxj@XYlCyso#2Uyc|IPer)EeiKA%Wbj1RZ=oG|<=gXOp zO4b-dMw@3*^Qdg&6Xj(^P*^PCdlG5x3Oa;+ug`mdc6JqbtaL2*!mZ~{ zeEI1)PVvf4>rTT-Bz#|S!ib;{5Ur0(t2mq#Zh-x+r=WQSV>h;DQdVaJcAoMqOEoL! zufkI#scD!SHe(Zc=0=TEN^OWC=swE=qvPvYWlv@)ZF`Tjz7f60r%dXn3S`F;J1-fc z+cHW^2a`M+4ORMRCu_ZmEGw5`JfM+#V0}m?K&F$hX;xSy_{p{A97AP$@cum{JGcsk?cpyM#pIfZKZ7|OhQaZo-fGXooY(^P2@rQPBLF4 z|Bp_lh3uMF7E%AmBf8OETpYiZ$pYA1qSsExr^5b*OM~YWz3MSA5Y%|B{5l}-#cu%H zh>eYxX@x)_8f}^q6C5Hlz0J~r3WnXaK#-l(+^9?i}h{*d}&c;V8kC z4g9MVz0Y1c&Wl#;XCdeZ6xA3=m))MW`VITfvHDz9-^VB3q zr{9b=Xx{6P|HH}Y3ABDb<>n+ILDSHX@aQt}EaGt6e?IO($2)u*{996m{O7Cut88YU zG^#s!!jSj`=j>jQJc^!Iy%~7{OKVABI6UP#WLF~GrX|Jv{;GkAO-)Ez$}MH#M>t5U zpZT33XtFqCVH8b5fJb|bfCRdcTDr}x2_gh-IKt=lK!)RHUb7t9Mfc1)T70i3(Op99 zKeInWOEHof$~&5@lPYfRt{ai(rdeUvaJ#L~c<#_a-JI|c9plo0XDf^(mCxqW32#yJ zUTYTGVea*{Z!+1@cy?iA7*7Y}suZ7Pr59^Y1E>|ilMcfzk-SXW1dF6k9z5wK7)MfjY!kY12Oe636EvkS{Z_2md2ZZAiKX z%nD|Ux;i(GnAePhS_>VM&LO|}Dt*-NLo?DVmYC1p;rN>$-N7$H)Wt1I_unT#RVv+| zaF+Lzb|Py>4^2xq*p1q@58?Z-^C+wy(0r?HsWIJF4QDgW<1-Nid_Rz&&FKnOa%%~# zP77G)SgbUL^9gCk{X{Pa;kQZ})i>$&S_LL3><6ACdVaN-uY{fX^upgbiHvU3HF<4B zr`@NZ^jIPdJ~{XMIHq)uW6RnT^On^j#>j%t2nhCHH}nx22>;0^q%IutQ(?xWQ<5y!qZD2VZJ> zc5a^4a^aW!#XEj=jL_g~XjZ{pIw;8hvKhewU z2(i*MRGGD3_1C85ekZ3eKBLMWN3VUTf+pLxZ!?#yw&H}}wFHOBc8@73qyYcdVEx~M z4V&upDrv3QXOF}jgC~k!GlZ|*c3H(gkMlM<#!inPQj&=0k(v5s%?_Z3Tuuj=+6G+u zpNC4hVL>O_Yc&pwD1^iUPWBeJRJnv^JztVc{h(R(cfT`Dx}4>?n)8zj>s$^{PwFkv zOcBN7Kc?uF`!7Rvf6fAiIQ$D@Flgjt@7#>!?%y~w6YMn;=f3syY9I}PSgFJD|DmhA z$2VTG&Y#$cYPlG#fCnsEom^k-g5k%=lKX`-HDh?}@?Q)yCqgb>D%P+uo&NSzVxi1EL(~SxIH+z~lu~qcu?675`gOk%khX zfSGp&o`oa4lVjAdmax@P?T%gC>~>7ZGFY#q*#$j>m?;^xeyd|GeHQhyg5f0958ru= zsH3%+rZkc@vrRW*GMWUde=Yl{X48Eq6UBTHdW)hr_d<#yxSm3_>XkPNc=nR4glrC zHL-t&`=P<7|BfG=#eKXyk^A${t@(XKU|uYf9q!!lAN!+Ux`KFSH_Cf=y+y6;@4g64 zkI~{7^T?cj=!P8nq_Rv$qqs}|LFbcNb&Q~gm8v^(^EbdIsDJ4Bdnbd=7tDH05#o+M zLTa{5oC{eBT~58pqnP(x(o*v3N1{W8{)u8u6+ZM<4fw!E6gsCo>@o{5fV%KdsB(pzfw6o7ARUYfJ zC;HsaU{5_B`(ekH^j>rm-4aR=kFG?Czch{?cNXvMMdDg%oeBAbL;_4nA*x0!)fb6i zt?pH_REDiMULntJ0U42{0R3`Y^N)biPC4ECKayTxUIQYss+E9l+yYV0CWXk)SudBR@d{vko{&t2YCAHW@qf(*N?b8=uciiz;@TCiUve!J-L}qyIY#IFmX&q1; z^DMzgV&ugoU(B2K5s1ty&#@vS)<}!ia@hz3Le&jeH{8meQooMh^!SAd>bv>CemzG_ z@f|$=)>v~e(8VdZHA+ILR({cT6og+ei3HLbj{Emz8M1{b9)Jp;O|Sx(0>}XEQSxLe zB;lLd-M)lJzt-Jq@e)eoC~yQo^?3-zP7@LpR({(om@!pe5x)ORZvCJkwK>cq-mcD( z?L)AEMD(hzQn;F#CDjO&!I3+|5U&_ceOqT*yuDh0iAC+eFA&-*qo_Mdq>zQ88i8P6 zjq3{=&3G(;`+@+vT9HCF8ms16N4bC2Xmm;m*1eE|1_J_2DL9HDr$> ziOi@S{1JXQL^jw*pHX5mEIiWmuJ9(GPt*(o3cijDtbRZ?1#nEZ2P^MR8-0i0?5B6g zUXZ{9sM9Z#y!pO2kzL0)KHs^Nmut7I48h1Q*j6KIQ_Q9J%vscyQv>MwBx!xD8?S;x zUq-@>hspr%`4kUH?Z;;?m3sAnaPoplJqfyJd4B`+BST|M5$p!>dtPV!biCl!M~Z;n?$-~Zi8V{Xh{xWajbdG> zz;{dErCMQbREw~nhut&4&R0R$vxR?2{ef7g@_$xku)aF5vL8J=R-Y))Ypn98ZXc`r zk@H^rf4U#gt=o3CS9SIQDg0bn~w zI>FJhOV!pzpNo-7-4pu4;049bO%VmCd)bO_P81E8w-caa_-U{0OYp)* z*Df??D<|x&Kod8cYDNFAE&$!r)uRcC#f=9!7_c%p7 zVaL0$LIHS{pWKs35*KRi+g5CZd%6PPDt~%riIR3dSUDZgI@UsbiYg|?y>~v^;9^se z)K7|z;Y_6VINZo2WZ$;Z>_*term5i=EFzz*mLU>a7KE1cquCLQ<2o^PNuj4BDn%XK z0l7bi^|aC@C&IX*;g_7qr=uR~L@8I#gk^5oKRjCEVSw(yuPyHIt#lj+xFZ;Wy8pG|wVOo1e__86V!XkCWBrJToX^9H5<9`WEgZxA=*4DwZ^| z1i8H^x{L9&v>C_C4PYgJVp6M!zQwNly>tHjf2mfSjey9-ia+77#Z3P|WxZ&}@h^(B z&6)&AHx+2>+=q@Nk_pc`CT~3fwY^gyb_>6XP@3!NvcFxSfl^oA>vUCvzeDWm?C6bF zGyKM?Uc1b^CG+Hqi%0B|d7jvcfy-_3w(Do?%QWw5Y6z%VP(2xDFMQ6>n;bbFTGt&< z^Yoj%9-t~eFA+C4N%(ZaKXWqqo_{eru26Z7x<(7%shQ;xBcK++ZJ*42Xb55UDv-Sh z^JwgPFjMTCIay(DS`)K{qld4%F})@~g$dvyR~6p?;W8*KY@s|pY=`Hu=OQ9hs_2aU zWHOz4ZM;4!gsA%J7_rdBcv?$%Krb*tdW`^Hl+$w|j)e!eks?$$iWir1^77w1*XWl& z^9S7sp${^f|Gi_gv;TU_z)+nxt#e-4$4!Qlr38 zWG%IRdr>kwxb(cI@yV8T^_wtJ3y2|EV84kR!05%rP<52U#Q~o3yDc1i!Z$~;&c1SW zo9Cl7!^;Hu3%bXkARZ^VDiYM$CR<$7wzKFG$DVGQmdkOpGLtH}34mi1crii>^ipNE z{#rGsk~Xh@b@}C2v1!?lO@d#sbg;F}<3*AjQ0Asc>J%{i4pNb;rIc=U`elf1;*vxv=HD16NzN>Q*ER0X4M<;K9(8QWaP^uSQJNkad89LI$Rrq?G`Pr>Uh~@lEvC z=QqUuSHOeS`E@eB7DVGHY8>qiNjL8{XC{)QYcXOQ(0L%l6gf0dnMUGgZcgxG(9!F%_D<%U`w09Ntqu zZ40?nD3M6b^^I4Oi?`9;6;6-oLQCufkvG<#el)8mRSZu3kN}NseWPUHlpi}tAR&L{ zKx!#3qxrQlk#*pRzrtk<{DOV<4f zS)y0fm%3~bT#H_r8rssYEX{Wpzp=he{uu?NK_O?~FdFJublKseKGUKhJ+!bH+{D5L z>vJ;C!?;c~`GcUk@RcehVIu!ccCXIcNQ5|J;Y_zoJ!2QYPDC!N*|vR9xU80bYILR; z4ma-Skr_ zQ(ck)p9n{Im;z67{rQY$!ln1uVqAGq-B<{KD}l<@@){iI%JcmnX(BfPM3{0*gnP<`-+XJWt)x2h(mj!F3Cto;wZjc zp4e)$l;dkrI13$LF^Om)>Sg$(Ye?&Vr~5@YjSD3-kW@^VnXnzBBmiy&Z~q(nVTAsL z$-7GenMwZQd=e0$0$aEs-=l_L?6Su{$W*RUd?R0Na&Tz75<``Mu+l6~@2k73(l%I*+BmAHBmEtSImgy~h#Z!6;>!;Z(263t8^_ z=QjtULmY5O^e9>0&cm8-d${>+1Zfp&N^Bm~%f)h z!NXo@)E?~0Bzo_G9THKwSyaD1G_GX8XYu`!T zt}I>+xhekh9AVh^Hu5CWP~K4NnEy{`?AO;iT&1kssR92%D}$^{nJGE;2=CZE7aFPG z0J`#;NW{%Yae6BE%mj4yaw>^eO3IlywLp!AIAQ^Jo>*zeu4l$H_^M>E&izuSJz}yN zEPp-;WN3ub683ZIk~Q^X{=@{aq&BB3OA#QHz6$G^sB0~0P8|`3 z&q4SacqNuy-KjM>P2Ph_tjq)9ZG0wT>dV)`%F+Kpl=6Cp1mDhbRJWpK$wMT?ICjO} z6^BD`_MIA)0k~%`9~tppoII<0yFAW*EJqPTLc$23-I%W))bV|sIt7rq1%Z^}d0zG_ zCYq9>#RuBUXn2Ey#%KJbm?L+{TiZWjO{CrVd8qWx2OjwQG3?%`gnd^}-+g{|9H;Hi zBDP5mi6ZZ=kN!_&MAlysKK1QQ3ANkG6CgY_aMO!&KDG7sSI7ygeS`ZM_c{XgouGFH z?y9BCqF$F7{quYEu21?&Zr`OLE<{QiJ53SBvGPel7QQ;Pqw^FkNa>B^Qn{=Lki5*; z896CN_hkjs$}b^8*9hM%K3I}%S)w%K>Euw)2U8#f2fs|xK^{Za>VXycMp z1T4Yv)fY zIDwN}s=2!HGIEIjSuKmg01gEm7(ha_6MpW#@dX}Ct{L+1H>^D_hqG8 z%610(Ocdq`*fIx6??#O$H0?%91g~bVb!?LC>?MYN$y@@yrCKR7frahoR7uQ1Lj6Zm zKEK;5sD;L&H8x35pG5+^m5mIOv>Nd|v()wTo?A~b@g6W8MO(4}tVIVIj2&GvgMAU_ zPqUg&O!G8$AuJio8NHu7&qh*d%$YV*zw#ctpi^>r;xo55uaF=Jn-V(r#Zdifc?S$po357C;%IUSR~lgUUF#tgv9j27evzaN0y`Ec#47& zQ_+S;&1z?@pEJ8r@NkNYe$l=l(Gu!bts5#`Z)S@Qi~9v5q3cCV zq9eZn%N^Uc=S4JT~|MN z0D7jk!5`FS#E+6~6^E-Zkr_n8R?AE+aU#jGxR?Xs3cH_NGxmga;pgp^klHeIe9_Es zOs?7s`H7a1Nio7~;mN@edNHv9P-Za@+s`J&jn9Yjd}4>$%&-ftav3{Xr;04o7vh=C zwPI|9;2Md?8iG(VCED1{^Xr?Tr3;dY>W3x_dL_c}E<*Pyk(W=2g&OB3d-(=1&>EGL z^X-Yalye3L7uQT}HG;N!NBlm|r%G9Ik2CWy4MX?7H!M9~kZBLdM15d$AmZOFo@^_i z+ewX$n2e2nBtFH>vOGndw4V`GyeXF_{O!i`ZFwj5mjU39!1>*iV%_hwFS+Xs`EC_=VNr5IqS;&Jo-x}qf zhB~E;i=Bjq{@5G1BbFy?_Xr3;YU&EwlTTdWZy34V47=gBZ!ooXmKo6AX$s}<2`{0G zU!KMNK6EG^PXE*bCt-W; z_)5EnmR9364On`gTBPYw0{O65FuTWBA4=ZCo8ISD2dNSpA~T|{y^Ao03fw=0*ERfo{2%`{YXZ%!5MSGR@Rd9Uq&_auukasBHvPAYjE4E__8 z#!IgX(8THn&7z#prsQ*;z>OQ*v}Rz;ZZc#|^w#$g6;86sb~T;c)ft1FD;(=&rh}P+ znE>DL!ibQkAVNQ;qo*UJ5Nqzb_?JuS2QiaRf|gJF?#IO`{BZFe(CyckDyUV!x?b=5 z4Z!|S$~J+*%B}+^LX3eGec+IXMM5>mIxRU!k>_gx<;or%Zh36@!W`qTn_eQ= z`>p6LkD$rzh$G?$kEC%pHs=4)%6D%D#^_`1qv-YeotTo7~Poh^^I))oPnG4CD(X8V6vz``(Z z)OY3?9D+F73D`y7-CVF7tV*j@=&5(6jco0M1GUu@P+?ZflFO<6-YW7pKznJORHz-7pLJ+>KQS;2hn0c-Hp9IQ pI7Vn7lTdEQfgWI4qKqT;j>2uQ(^6yCP#WItP|0vifoc1_@ITDH073u& literal 45946 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 Date: Mon, 8 Feb 2021 14:13:49 +0000 Subject: [PATCH 03/19] Litepub social context --- context.py | 30 +++++++++++++++++++++++++++++- pyjsonld.py | 8 ++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/context.py b/context.py index 1a0a3ad17..11d9b727d 100644 --- a/context.py +++ b/context.py @@ -13,7 +13,8 @@ validContexts = ( "https://w3id.org/security/v1", "*/apschema/v1.9", "*/apschema/v1.21", - "*/litepub-0.1.jsonld" + "*/litepub-0.1.jsonld", + "https://litepub.social/litepub/context.jsonld" ) @@ -129,6 +130,33 @@ def getApschemaV1_21() -> {}: } +def getLitepubSocial() -> {}: + # https://litepub.social/litepub/context.jsonld + return { + '@context': [ + 'https://www.w3.org/ns/activitystreams', + 'https://w3id.org/security/v1', + { + 'Emoji': 'toot:Emoji', + 'Hashtag': 'as:Hashtag', + 'PropertyValue': 'schema:PropertyValue', + 'atomUri': 'ostatus:atomUri', + 'conversation': { + '@id': 'ostatus:conversation', + '@type': '@id' + }, + 'manuallyApprovesFollowers': 'as:manuallyApprovesFollowers', + 'ostatus': 'http://ostatus.org#', + 'schema': 'http://schema.org', + 'sensitive': 'as:sensitive', + 'toot': 'http://joinmastodon.org/ns#', + 'totalItems': 'as:totalItems', + 'value': 'schema:value' + } + ] + } + + def getLitepubV0_1() -> {}: # https://domain/schemas/litepub-0.1.jsonld return { diff --git a/pyjsonld.py b/pyjsonld.py index 0ccffe504..36b44d0bb 100644 --- a/pyjsonld.py +++ b/pyjsonld.py @@ -40,6 +40,7 @@ from numbers import Integral, Real from context import getApschemaV1_9 from context import getApschemaV1_21 from context import getLitepubV0_1 +from context import getLitepubSocial from context import getV1Schema from context import getV1SecuritySchema from context import getActivitystreamsSchema @@ -420,6 +421,13 @@ def load_document(url): 'document': getLitepubV0_1() } return doc + elif url == 'https://litepub.social/litepub/context.jsonld': + doc = { + 'contextUrl': None, + 'documentUrl': url, + 'document': getLitepubSocial() + } + return doc return None except JsonLdError as e: raise e From ae0b1505f902454978ba8f819ff48e33ecb9f2eb Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Mon, 8 Feb 2021 14:48:37 +0000 Subject: [PATCH 04/19] Join activity has the same meaning as Follow --- acceptreject.py | 3 ++- follow.py | 6 ++++-- inbox.py | 7 ++++--- posts.py | 3 ++- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/acceptreject.py b/acceptreject.py index 712d9ef7a..d3f4c50cd 100644 --- a/acceptreject.py +++ b/acceptreject.py @@ -77,7 +77,8 @@ def _acceptFollow(baseDir: str, domain: str, messageJson: {}, if not messageJson['object'].get('type'): return if not messageJson['object']['type'] == 'Follow': - return + if not messageJson['object']['type'] == 'Join': + return if debug: print('DEBUG: receiving Follow activity') if not messageJson['object'].get('actor'): diff --git a/follow.py b/follow.py index 085c7aecb..3411a7d45 100644 --- a/follow.py +++ b/follow.py @@ -598,7 +598,8 @@ def receiveFollowRequest(session, baseDir: str, httpPrefix: str, """Receives a follow request within the POST section of HTTPServer """ if not messageJson['type'].startswith('Follow'): - return False + if not messageJson['type'].startswith('Join'): + return False print('Receiving follow request') if not messageJson.get('actor'): if debug: @@ -1162,7 +1163,8 @@ def outboxUndoFollow(baseDir: str, messageJson: {}, debug: bool) -> None: if not messageJson['object'].get('type'): return if not messageJson['object']['type'] == 'Follow': - return + if not messageJson['object']['type'] == 'Join': + return if not messageJson['object'].get('object'): return if not messageJson['object'].get('actor'): diff --git a/inbox.py b/inbox.py index c213e0d22..d4b66c59e 100644 --- a/inbox.py +++ b/inbox.py @@ -275,7 +275,7 @@ def inboxMessageHasParams(messageJson: {}) -> bool: # param + ' ' + str(messageJson)) return False if not messageJson.get('to'): - allowedWithoutToParam = ['Like', 'Follow', 'Request', + allowedWithoutToParam = ['Like', 'Follow', 'Join', 'Request', 'Accept', 'Capability', 'Undo'] if messageJson['type'] not in allowedWithoutToParam: return False @@ -297,7 +297,7 @@ def inboxPermittedMessage(domain: str, messageJson: {}, if not urlPermitted(actor, federationList): return False - alwaysAllowedTypes = ('Follow', 'Like', 'Delete', 'Announce') + alwaysAllowedTypes = ('Follow', 'Join', 'Like', 'Delete', 'Announce') if messageJson['type'] not in alwaysAllowedTypes: if not messageJson.get('object'): return True @@ -693,7 +693,8 @@ def _receiveUndo(session, baseDir: str, httpPrefix: str, print('DEBUG: ' + messageJson['type'] + ' object within object is not a string') return False - if messageJson['object']['type'] == 'Follow': + if messageJson['object']['type'] == 'Follow' or \ + messageJson['object']['type'] == 'Join': return _receiveUndoFollow(session, baseDir, httpPrefix, port, messageJson, federationList, debug) diff --git a/posts.py b/posts.py index a0920284c..d44257bd2 100644 --- a/posts.py +++ b/posts.py @@ -2414,7 +2414,8 @@ def sendToNamedAddresses(session, baseDir: str, print('DEBUG: ' + 'no "to" field when sending to named addresses') if postJsonObject['object'].get('type'): - if postJsonObject['object']['type'] == 'Follow': + if postJsonObject['object']['type'] == 'Follow' or \ + postJsonObject['object']['type'] == 'Join': if isinstance(postJsonObject['object']['object'], str): if debug: print('DEBUG: "to" field assigned to Follow') From 30ac93d5787acb861ec6c5b808dbb728d51ff562 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Mon, 8 Feb 2021 15:06:26 +0000 Subject: [PATCH 05/19] Additional type checks on incoming json --- inbox.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/inbox.py b/inbox.py index d4b66c59e..09e073547 100644 --- a/inbox.py +++ b/inbox.py @@ -105,6 +105,8 @@ def storeHashTags(baseDir: str, nickname: str, postJsonObject: {}) -> None: for tag in postJsonObject['object']['tag']: if not tag.get('type'): continue + if not isinstance(tag['type'], str): + continue if tag['type'] != 'Hashtag': continue if not tag.get('name'): @@ -274,6 +276,28 @@ def inboxMessageHasParams(messageJson: {}) -> bool: # print('inboxMessageHasParams: ' + # param + ' ' + str(messageJson)) return False + + # actor should be a string + if not isinstance(messageJson['actor'], str): + print('WARN: actor should be a string, but is actually: ' + + str(messageJson['actor'])) + return False + + # type should be a string + if not isinstance(messageJson['type'], str): + print('WARN: type from ' + str(messageJson['actor']) + + ' should be a string, but is actually: ' + + str(messageJson['type'])) + return False + + # object should be a dict or a string + if not isinstance(messageJson['object'], dict): + if not isinstance(messageJson['object'], str): + print('WARN: object from ' + str(messageJson['actor']) + + ' should be a dict or string, but is actually: ' + + str(messageJson['object'])) + return False + if not messageJson.get('to'): allowedWithoutToParam = ['Like', 'Follow', 'Join', 'Request', 'Accept', 'Capability', 'Undo'] From 43775447ccaf318a11ee1b1319170e00098f56fc Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Mon, 8 Feb 2021 17:23:17 +0000 Subject: [PATCH 06/19] Remove Events timeline, at least until it becomes clearer how the implememnattion will work --- webapp_create_post.py | 12 ++++++------ webapp_timeline.py | 22 +++++++++++----------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/webapp_create_post.py b/webapp_create_post.py index 6387f9c5a..60e269ba2 100644 --- a/webapp_create_post.py +++ b/webapp_create_post.py @@ -136,12 +136,12 @@ def _htmlNewPostDropDown(scopeIcon: str, scopeDescription: str, 'icons/scope_reminder.png"/>' + \ translate['Reminder'] + '
' + \ translate['Scheduled note to yourself'] + '\n' - dropDownContent += \ - '

  • ' + \ - translate['Event'] + '
    ' + \ - translate['Create an event'] + '
  • \n' + # dropDownContent += \ + # '
  • ' + \ + # translate['Event'] + '
    ' + \ + # translate['Create an event'] + '
  • \n' dropDownContent += \ '
  • ' - - eventsButtonStr = \ - '' +# +# eventsButtonStr = \ +# '' instanceTitle = \ getConfigParam(baseDir, 'instanceTitle') @@ -400,8 +400,8 @@ def htmlTimeline(cssCache: {}, defaultTimeline: str, translate['Bookmarks'] menuShares = \ htmlHideFromScreenReader('🤝') + ' ' + sharesStr - menuEvents = \ - htmlHideFromScreenReader('🎫') + ' ' + translate['Events'] +# menuEvents = \ +# htmlHideFromScreenReader('🎫') + ' ' + translate['Events'] menuBlogs = \ htmlHideFromScreenReader('📝') + ' ' + translate['Blogs'] menuNewswire = \ @@ -426,7 +426,7 @@ def htmlTimeline(cssCache: {}, defaultTimeline: str, menuBookmarks: usersPath + '/tlbookmarks#timeline', menuShares: usersPath + '/tlshares#timeline', menuBlogs: usersPath + '/tlblogs#timeline', - menuEvents: usersPath + '/tlevents#timeline', + # menuEvents: usersPath + '/tlevents#timeline', menuNewswire: '#newswire', menuLinks: '#links' } From 41806db01e8931e581e2eedce9c290aa901f8cff Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Mon, 8 Feb 2021 21:20:01 +0000 Subject: [PATCH 07/19] No quotes in hashtags --- categories.py | 2 +- webapp_hashtagswarm.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/categories.py b/categories.py index fc13a7c76..2d490dfc0 100644 --- a/categories.py +++ b/categories.py @@ -114,7 +114,7 @@ def _validHashtagCategory(category: str) -> bool: if not category: return False - invalidChars = (',', ' ', '<', ';', '\\') + invalidChars = (',', ' ', '<', ';', '\\', '"') for ch in invalidChars: if ch in category: return False diff --git a/webapp_hashtagswarm.py b/webapp_hashtagswarm.py index ebb9aa2cc..e898ba563 100644 --- a/webapp_hashtagswarm.py +++ b/webapp_hashtagswarm.py @@ -154,6 +154,8 @@ def htmlHashTagSwarm(baseDir: str, actor: str, translate: {}) -> str: if len(hashTagName) > maxTagLength: # NoIncrediblyLongAndBoringHashtagsShownHere continue + if '"' in hashTagName or "'" in hashTagName: + continue if '#' + hashTagName + '\n' in blockedStr: continue with open(tagsFilename, 'r') as fp: From 11fd50419d7356ef0e1560d42e937d0eec76b565 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Mon, 8 Feb 2021 21:27:46 +0000 Subject: [PATCH 08/19] Invalid characters in hashtags --- categories.py | 2 +- webapp_hashtagswarm.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/categories.py b/categories.py index 2d490dfc0..0bf36fda3 100644 --- a/categories.py +++ b/categories.py @@ -114,7 +114,7 @@ def _validHashtagCategory(category: str) -> bool: if not category: return False - invalidChars = (',', ' ', '<', ';', '\\', '"') + invalidChars = (',', ' ', '<', ';', '\\', '"', '&') for ch in invalidChars: if ch in category: return False diff --git a/webapp_hashtagswarm.py b/webapp_hashtagswarm.py index e898ba563..70484096d 100644 --- a/webapp_hashtagswarm.py +++ b/webapp_hashtagswarm.py @@ -154,7 +154,7 @@ def htmlHashTagSwarm(baseDir: str, actor: str, translate: {}) -> str: if len(hashTagName) > maxTagLength: # NoIncrediblyLongAndBoringHashtagsShownHere continue - if '"' in hashTagName or "'" in hashTagName: + if '&' in hashTagName or '"' in hashTagName or "'" in hashTagName: continue if '#' + hashTagName + '\n' in blockedStr: continue From 74a8e3950936b0ec7beaf4467c853386784e39a8 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Mon, 8 Feb 2021 21:31:18 +0000 Subject: [PATCH 09/19] Check for invalid hashtag --- content.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/content.py b/content.py index ff5c4aa97..18491f24c 100644 --- a/content.py +++ b/content.py @@ -382,7 +382,8 @@ def validHashTag(hashtag: str) -> bool: 'abcdefghijklmnopqrstuvwxyz' + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ') if set(hashtag).issubset(validChars): - return True + if '&' not in hashtag: + return True return False From d7bb14d8a9eeca912da777d14216900955237d68 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Mon, 8 Feb 2021 22:17:56 +0000 Subject: [PATCH 10/19] Copyleft symbol --- emoji/copyleft.png | Bin 0 -> 2492 bytes emoji/default_emoji.json | 1 + 2 files changed, 1 insertion(+) create mode 100644 emoji/copyleft.png diff --git a/emoji/copyleft.png b/emoji/copyleft.png new file mode 100644 index 0000000000000000000000000000000000000000..e13f7a2662c66d1014b07b79468f43f1831f547b GIT binary patch literal 2492 zcmV;t2}AaYP) zaB^>EX>4U6ba`-PAZ2)IW&i+q+U=KHvKuK3hTmC5mVhLL#ByjZRoTHTKR%k2@rPMADYeGT%Q-RB(V?rO@2XudLP z2b&&hu<$_0WxtkD4Ub}MyB1WBH8vpcvBd+mQ6UDE87NVwN|VO!8#TC?c(9>oZUD7p zF2;o$SdpYvX|hR?E)H&uM-YLSZ(nHJE!$orhviOK$^>P!VgF4H|1|k0HO$#oh$#9U zEBM7Lui?fbw_2>JGDGgEbzfibLrmw`6k`tW)&o5u#xI zQh*S_w!#?_5Qv#XDN{6NBSIVDwrT5_@CrIcDZR2ZtzRk6N8a_}KXI`qhg9e$LfHl$BWOivcj>q$dhXV} zmtF^jftzBap(76)KFX*Qxi)2{sWVTTKFh2(wTtSP`UlkLqQ;xlT4y&kNQ2o!1+D8u z7c&s!L?CX901}!Pv*?r(FLH}nEQ~{;3@3H537r-(AWVZ;C*ABm$o&?#p#EFj_*dk@ zLib;g3qbdk+fS(V^*z@%vGWwJm`1_r!~F0X*z5XItN*rtD|#z>D|#z>D|#z>EBYTO zn)va6Ke6ElD?goy{oMEX>4Tx0C=38kg-a`P!xv0M$sZF zg3>`8GPo98%;ZuPf>0sW3Rb7|rp00sLy}T_1K&g+rh_2(2EKrTv;U0_4rcMf;r}?C z^Z)l=aNO7=#o`_$X_@=I&fs!z!B&KP7rjdJ)f^MtiH6ZSN+_dXDJ#L zMmkreCd*4`9EiPHnORn`b=&V9i66vnl%$dPNxT~+k!8EvOX8{d@wB&D9i3Av}hNMlCo%`{VTBP!(Nb13SIbeo#I?J6%&%1|B? zdD(i36;8OC00002VoOIv06!e-4i5kT010qNS#tmY3ljhU3ljkVnw%H_000McNliru z1y*ro3?^VgjzW; zaBLbx6y2C*FM3f|Ul?o!y$FI{v`rA~jV(q5lM?eEBq|ytLt(MaO_Djmpxrf^|5o1F zi|y)u=RD{9&hK}Bw>;<7J-_oj=llEJbDrn>JkQA^T+649wZvJ?Qpzcv8y#ni6eIN0 zM;~ealU7cWL)_wyPrt=sl9cBXN-+se`T|k=DH0S%4`wm#qymG<^l{o*937Y3WVDv~6?$I+FMZ8$%zCjfRuj>b7^JKdZxjHXP3Dm*OAiyO%13t$dxP zNDS;|t_$0{(*7QUn#d?Mk=gueMm7C|wr}4hz^U+ErhqEysbiS~qzVC2mwERDgHz2e ze&p)B`DxCxhh`QAgw!Cr&|z)w)EM1uE9Wil;RgG7(g>+W+e^ce+ufn5HEreB8FTVl zJw_L@S(}^G`Oc(v(53b$M*=v~YrJEC)S;0Rg)g;hW>DJu&+&U;@4ipeRKkcdugyzN zRW>MD;E)4s?P-L8I#9689dd)V*F4Uddls(Xu_F z+f~2h-M~gA7;1ZfzO0$zRsoVqPUeE9q%z#B3GOm3bCHkPKq-($DNr~st2{It6nmr4ZcXe9QT;Ic%x3Hdu@vb-?Q>9it zuVEM~Af@HW#kX`JHe?HLZNd4JMerVf7kK7X8vbb;{@oS;5wf1a~=N9^$kI7 zpTl0;BFJ5Jajq);Xi-EKimAQ5K5(8D8PVr1^uwQ^b%+G*YYPQ=nQMYXT8Lx?g62Z1@ww!38JpN^%-v40+?C|}he5>*`2;y> z-!SNH_b|vMP>hERqci>6Ac3M=D7}f9&~Q$P2fz|a7c;IrY!no{WlW~q`xzDmO*Xiu zV{GLHQ$G?W9pj#`TFKb}VE$H@aZgyer}SdHZz-$H?>|v(fYaok(hHXKT*OYUSfI>s zkoDOnJ%gu(H}Dxh^N$jn_Oe|tfg#eu;S=X8SjRfnQtMECmf&>SN(%!yEiKdta0cvo zf?O6~nt~T!a#^102v?r674RCfAp0ZX3wX8?=du5%#CabUf&Ki}Y?<6_ZL%nn+bty&+o5m{Ldy2Lw zFErgFx(QB%sFvmA%Gdm`@xxqRipvWLD|2md6{XQ$8~hh?^W*Z&Tp(Eh0000 Date: Tue, 9 Feb 2021 10:12:17 +0000 Subject: [PATCH 11/19] Open replied to post in separate tab --- webapp_create_post.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/webapp_create_post.py b/webapp_create_post.py index 60e269ba2..0a682af4d 100644 --- a/webapp_create_post.py +++ b/webapp_create_post.py @@ -198,7 +198,9 @@ def htmlNewPost(cssCache: {}, mediaInstance: bool, translate: {}, newPostText = \ '

    ' + \ translate['Write your reply to'] + \ - ' ' + \ + ' ' + \ translate['this post'] + '

    \n' replyStr = '\n' From 00af89ff3e8f4d3b729b9be41cf0325c21015f45 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Tue, 9 Feb 2021 13:16:53 +0000 Subject: [PATCH 12/19] More hashtag checks --- categories.py | 2 +- content.py | 2 +- webapp_hashtagswarm.py | 5 ++++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/categories.py b/categories.py index 0bf36fda3..a99241b73 100644 --- a/categories.py +++ b/categories.py @@ -114,7 +114,7 @@ def _validHashtagCategory(category: str) -> bool: if not category: return False - invalidChars = (',', ' ', '<', ';', '\\', '"', '&') + invalidChars = (',', ' ', '<', ';', '\\', '"', '&', '#') for ch in invalidChars: if ch in category: return False diff --git a/content.py b/content.py index 18491f24c..dfbabd338 100644 --- a/content.py +++ b/content.py @@ -382,7 +382,7 @@ def validHashTag(hashtag: str) -> bool: 'abcdefghijklmnopqrstuvwxyz' + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ') if set(hashtag).issubset(validChars): - if '&' not in hashtag: + if '&' not in hashtag and '#' not in hashtag: return True return False diff --git a/webapp_hashtagswarm.py b/webapp_hashtagswarm.py index 70484096d..19f08661f 100644 --- a/webapp_hashtagswarm.py +++ b/webapp_hashtagswarm.py @@ -154,7 +154,10 @@ def htmlHashTagSwarm(baseDir: str, actor: str, translate: {}) -> str: if len(hashTagName) > maxTagLength: # NoIncrediblyLongAndBoringHashtagsShownHere continue - if '&' in hashTagName or '"' in hashTagName or "'" in hashTagName: + if '#' in hashTagName or \ + '&' in hashTagName or \ + '"' in hashTagName or \ + "'" in hashTagName: continue if '#' + hashTagName + '\n' in blockedStr: continue From 7de4e24f2e1b2c56c62c2c22f3f85fda265db9c4 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Tue, 9 Feb 2021 13:28:42 +0000 Subject: [PATCH 13/19] Latin-script special characters are valid --- content.py | 11 ++++++++--- tests.py | 14 ++++++++++++++ 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/content.py b/content.py index dfbabd338..84fffeac9 100644 --- a/content.py +++ b/content.py @@ -380,10 +380,15 @@ def validHashTag(hashtag: str) -> bool: # TODO: this may need to be an international character set validChars = set('0123456789' + 'abcdefghijklmnopqrstuvwxyz' + - 'ABCDEFGHIJKLMNOPQRSTUVWXYZ') + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + + '¡¿ÄäÀàÁáÂâÃãÅåǍǎĄąĂăÆæĀā' + + 'ÇçĆćĈĉČčĎđĐďðÈèÉéÊêËëĚěĘęĖėĒē' + + 'ĜĝĢģĞğĤĥÌìÍíÎîÏïıĪīĮįĴĵĶķ' + + 'ĹĺĻļŁłĽľĿŀÑñŃńŇňŅņÖöÒòÓóÔôÕõŐőØøŒœ' + + 'ŔŕŘřẞߌśŜŝŞşŠšȘșŤťŢţÞþȚțÜüÙùÚúÛûŰűŨũŲųŮůŪū' + + 'ŴŵÝýŸÿŶŷŹźŽžŻż') if set(hashtag).issubset(validChars): - if '&' not in hashtag and '#' not in hashtag: - return True + return True return False diff --git a/tests.py b/tests.py index 83ecabca7..f5549bfbd 100644 --- a/tests.py +++ b/tests.py @@ -76,6 +76,7 @@ from inbox import jsonPostAllowsComments from inbox import validInbox from inbox import validInboxFilenames from categories import guessHashtagCategory +from content import validHashTag from content import htmlReplaceEmailQuote from content import htmlReplaceQuoteMarks from content import dangerousCSS @@ -3088,9 +3089,22 @@ def testPrepareHtmlPostNickname(): assert result == expectedHtml +def testValidHashTag(): + print('testValidHashTag') + assert validHashTag('ThisIsValid') + assert validHashTag('ThisIsValid12345') + assert validHashTag('ThisIsVälid') + assert not validHashTag('ThisIsNotValid!') + assert not validHashTag('#ThisIsAlsoNotValid') + assert not validHashTag('ThisIsAlso&NotValid') + assert not validHashTag('ThisIsAlsoNotValid"') + assert not validHashTag('This=IsAlsoNotValid"') + + def runAllTests(): print('Running tests...') testFunctions() + testValidHashTag() testPrepareHtmlPostNickname() testDomainHandling() testMastoApi() From 890c8856a84c289b8670c2e93226a71e21c3a8db Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Tue, 9 Feb 2021 14:41:32 +0000 Subject: [PATCH 14/19] More thorough validation of hashtags and nicknames --- content.py | 4 ++- tests.py | 3 +++ utils.py | 77 ++++++++++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 75 insertions(+), 9 deletions(-) diff --git a/content.py b/content.py index 84fffeac9..1d8334412 100644 --- a/content.py +++ b/content.py @@ -10,6 +10,7 @@ import os import email.parser import urllib.parse from shutil import copyfile +from utils import isValidLanguage from utils import getImageExtensions from utils import loadJson from utils import fileLastModified @@ -377,7 +378,6 @@ def validHashTag(hashtag: str) -> bool: # long hashtags are not valid if len(hashtag) >= 32: return False - # TODO: this may need to be an international character set validChars = set('0123456789' + 'abcdefghijklmnopqrstuvwxyz' + 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + @@ -389,6 +389,8 @@ def validHashTag(hashtag: str) -> bool: 'ŴŵÝýŸÿŶŷŹźŽžŻż') if set(hashtag).issubset(validChars): return True + if isValidLanguage(hashtag): + return True return False diff --git a/tests.py b/tests.py index f5549bfbd..284b13678 100644 --- a/tests.py +++ b/tests.py @@ -3094,10 +3094,13 @@ def testValidHashTag(): assert validHashTag('ThisIsValid') assert validHashTag('ThisIsValid12345') assert validHashTag('ThisIsVälid') + assert validHashTag('यहमान्यहै') assert not validHashTag('ThisIsNotValid!') assert not validHashTag('#ThisIsAlsoNotValid') + assert not validHashTag('#यहमान्यहै') assert not validHashTag('ThisIsAlso&NotValid') assert not validHashTag('ThisIsAlsoNotValid"') + assert not validHashTag('This Is Also Not Valid"') assert not validHashTag('This=IsAlsoNotValid"') diff --git a/utils.py b/utils.py index f024a2b98..328278e27 100644 --- a/utils.py +++ b/utils.py @@ -1163,14 +1163,58 @@ def deletePost(baseDir: str, httpPrefix: str, os.remove(postFilename) -def validNickname(domain: str, nickname: str) -> bool: - forbiddenChars = ('.', ' ', '/', '?', ':', ';', '@', '#') - for c in forbiddenChars: - if c in nickname: - return False - # this should only apply for the shared inbox - if nickname == domain: - return False +def isValidLanguage(text: str) -> bool: + """Returns true if the given text contains a valid + natural language string + """ + naturalLanguages = { + "Latin": [65, 866], + "Cyrillic": [1024, 1274], + "Greek": [880, 1280], + "isArmenian": [1328, 1424], + "isHebrew": [1424, 1536], + "Arabic": [1536, 1792], + "Syriac": [1792, 1872], + "Thaan": [1920, 1984], + "Devanagari": [2304, 2432], + "Bengali": [2432, 2560], + "Gurmukhi": [2560, 2688], + "Gujarati": [2688, 2816], + "Oriya": [2816, 2944], + "Tamil": [2944, 3072], + "Telugu": [3072, 3200], + "Kannada": [3200, 3328], + "Malayalam": [3328, 3456], + "Sinhala": [3456, 3584], + "Thai": [3584, 3712], + "Lao": [3712, 3840], + "Tibetan": [3840, 4096], + "Myanmar": [4096, 4256], + "Georgian": [4256, 4352], + "HangulJamo": [4352, 4608], + "Cherokee": [5024, 5120], + "UCAS": [5120, 5760], + "Ogham": [5760, 5792], + "Runic": [5792, 5888], + "Khmer": [6016, 6144], + "Mongolian": [6144, 6320] + } + for langName, langRange in naturalLanguages.items(): + okLang = True + for ch in text: + if ch.isdigit(): + continue + if ord(ch) not in range(langRange[0], langRange[1]): + okLang = False + break + if okLang: + return True + return False + + +def _isReservedName(nickname: str) -> bool: + """Is the given nickname reserved for some special function? + """ reservedNames = ('inbox', 'dm', 'outbox', 'following', 'public', 'followers', 'category', 'channel', 'calendar', @@ -1184,6 +1228,23 @@ def validNickname(domain: str, nickname: str) -> bool: 'updates', 'repeat', 'announce', 'shares', 'fonts', 'icons', 'avatars') if nickname in reservedNames: + return True + return False + + +def validNickname(domain: str, nickname: str) -> bool: + """Is the given nickname valid? + """ + if not isValidLanguage(nickname): + return False + forbiddenChars = ('.', ' ', '/', '?', ':', ';', '@', '#') + for c in forbiddenChars: + if c in nickname: + return False + # this should only apply for the shared inbox + if nickname == domain: + return False + if _isReservedName(nickname): return False return True From 345ded45c7a3aadf7cf7340cfcc0071bf39e7f48 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Tue, 9 Feb 2021 16:21:59 +0000 Subject: [PATCH 15/19] Honk style users path --- inbox.py | 2 +- utils.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/inbox.py b/inbox.py index 09e073547..26d9e5268 100644 --- a/inbox.py +++ b/inbox.py @@ -756,7 +756,7 @@ def _personReceiveUpdate(baseDir: str, ' ' + str(personJson)) domainFull = getFullDomain(domain, port) updateDomainFull = getFullDomain(updateDomain, updatePort) - usersPaths = ('users', 'profile', 'channel', 'accounts') + usersPaths = ('users', 'profile', 'channel', 'accounts', 'u') usersStrFound = False for usersStr in usersPaths: actor = updateDomainFull + '/' + usersStr + '/' + updateNickname diff --git a/utils.py b/utils.py index 328278e27..dae7af786 100644 --- a/utils.py +++ b/utils.py @@ -67,7 +67,7 @@ def getLockedAccount(actorJson: {}) -> bool: def hasUsersPath(pathStr: str) -> bool: """Whether there is a /users/ path (or equivalent) in the given string """ - usersList = ('users', 'accounts', 'channel', 'profile') + usersList = ('users', 'accounts', 'channel', 'profile', 'u') for usersStr in usersList: if '/' + usersStr + '/' in pathStr: return True @@ -1224,7 +1224,7 @@ def _isReservedName(nickname: str) -> bool: 'activity', 'undo', 'pinned', 'reply', 'replies', 'question', 'like', 'likes', 'users', 'statuses', 'tags', - 'accounts', 'channels', 'profile', + 'accounts', 'channels', 'profile', 'u', 'updates', 'repeat', 'announce', 'shares', 'fonts', 'icons', 'avatars') if nickname in reservedNames: From f97d7f4c6a685930b1e82f6311812d1fb65f466c Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Tue, 9 Feb 2021 17:00:35 +0000 Subject: [PATCH 16/19] Honk style users path --- epicyon.py | 10 ++++++++++ follow.py | 5 +++++ manualapprove.py | 3 +++ utils.py | 10 ++++++++++ 4 files changed, 28 insertions(+) diff --git a/epicyon.py b/epicyon.py index b776e0bbd..1a741302c 100644 --- a/epicyon.py +++ b/epicyon.py @@ -1379,6 +1379,10 @@ if args.actor: nickname = args.actor.split('/accounts/')[1] nickname = nickname.replace('\n', '').replace('\r', '') domain = args.actor.split('/accounts/')[0] + elif '/u/' in args.actor: + nickname = args.actor.split('/u/')[1] + nickname = nickname.replace('\n', '').replace('\r', '') + domain = args.actor.split('/u/')[0] else: # format: @nick@domain if '@' not in args.actor: @@ -1445,6 +1449,7 @@ if args.actor: personUrl = personUrl.replace('/accounts/', '/actor/') personUrl = personUrl.replace('/channel/', '/actor/') personUrl = personUrl.replace('/profile/', '/actor/') + personUrl = personUrl.replace('/u/', '/actor/') if not personUrl: # try single user instance personUrl = httpPrefix + '://' + domain @@ -1508,6 +1513,10 @@ if args.followers: nickname = args.followers.split('/accounts/')[1] nickname = nickname.replace('\n', '').replace('\r', '') domain = args.followers.split('/accounts/')[0] + elif '/u/' in args.followers: + nickname = args.followers.split('/u/')[1] + nickname = nickname.replace('\n', '').replace('\r', '') + domain = args.followers.split('/u/')[0] else: # format: @nick@domain if '@' not in args.followers: @@ -1572,6 +1581,7 @@ if args.followers: personUrl = personUrl.replace('/accounts/', '/actor/') personUrl = personUrl.replace('/channel/', '/actor/') personUrl = personUrl.replace('/profile/', '/actor/') + personUrl = personUrl.replace('/u/', '/actor/') if not personUrl: # try single user instance personUrl = httpPrefix + '://' + domain diff --git a/follow.py b/follow.py index 3411a7d45..30522720a 100644 --- a/follow.py +++ b/follow.py @@ -204,6 +204,9 @@ def isFollowerOfPerson(baseDir: str, nickname: str, domain: str, elif '://' + followerDomain + \ '/accounts/' + followerNickname in followersStr: alreadyFollowing = True + elif '://' + followerDomain + \ + '/u/' + followerNickname in followersStr: + alreadyFollowing = True return alreadyFollowing @@ -542,6 +545,8 @@ def _storeFollowRequest(baseDir: str, alreadyFollowing = True elif '://' + domainFull + '/accounts/' + nickname in followersStr: alreadyFollowing = True + elif '://' + domainFull + '/u/' + nickname in followersStr: + alreadyFollowing = True if alreadyFollowing: if debug: diff --git a/manualapprove.py b/manualapprove.py index 86315a797..9d2944d8d 100644 --- a/manualapprove.py +++ b/manualapprove.py @@ -120,6 +120,9 @@ def manualApproveFollowRequest(session, baseDir: str, elif reqPrefix + '/accounts/' + reqNick in approveFollowsStr: exists = True approveHandleFull = reqPrefix + '/accounts/' + reqNick + elif reqPrefix + '/u/' + reqNick in approveFollowsStr: + exists = True + approveHandleFull = reqPrefix + '/u/' + reqNick if not exists: print('Manual follow accept: ' + approveHandleFull + ' not in requests file "' + diff --git a/utils.py b/utils.py index dae7af786..0f3d811cf 100644 --- a/utils.py +++ b/utils.py @@ -656,6 +656,12 @@ def getNicknameFromActor(actor: str) -> str: return nickStr else: return nickStr.split('/')[0] + elif '/u/' in actor: + nickStr = actor.split('/u/')[1].replace('@', '') + if '/' not in nickStr: + return nickStr + else: + return nickStr.split('/')[0] elif '/@' in actor: # https://domain/@nick nickStr = actor.split('/@')[1] @@ -696,6 +702,10 @@ def getDomainFromActor(actor: str) -> (str, int): domain = actor.split('/users/')[0] for prefix in prefixes: domain = domain.replace(prefix, '') + elif '/u/' in actor: + domain = actor.split('/u/')[0] + for prefix in prefixes: + domain = domain.replace(prefix, '') elif '/@' in actor: domain = actor.split('/@')[0] for prefix in prefixes: From 530f573763538805077c6f1d14f46024dae7044b Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Tue, 9 Feb 2021 19:02:10 +0000 Subject: [PATCH 17/19] Use the followed actor so that we don't need to be concerned about what the users path is --- daemon.py | 6 +++--- follow.py | 5 +++-- tests.py | 4 +++- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/daemon.py b/daemon.py index 675a205de..2e25d1989 100644 --- a/daemon.py +++ b/daemon.py @@ -2344,15 +2344,15 @@ class PubServer(BaseHTTPRequestHandler): if debug: print('You cannot follow the news actor') else: - if debug: - print('Sending follow request from ' + - followerNickname + ' to ' + followingActor) + print('Sending follow request from ' + + followerNickname + ' to ' + followingActor) sendFollowRequest(self.server.session, baseDir, followerNickname, domain, port, httpPrefix, followingNickname, followingDomain, + followingActor, followingPort, httpPrefix, False, self.server.federationList, self.server.sendThreads, diff --git a/follow.py b/follow.py index 30522720a..cce18f1b2 100644 --- a/follow.py +++ b/follow.py @@ -872,6 +872,7 @@ def followedAccountRejects(session, baseDir: str, httpPrefix: str, def sendFollowRequest(session, baseDir: str, nickname: str, domain: str, port: int, httpPrefix: str, followNickname: str, followDomain: str, + followedActor: str, followPort: int, followHttpPrefix: str, clientToServer: bool, federationList: [], sendThreads: [], postLog: [], cachedWebfingers: {}, @@ -880,6 +881,7 @@ def sendFollowRequest(session, baseDir: str, """Gets the json object for sending a follow request """ if not domainPermitted(followDomain, federationList): + print('You are not permitted to follow the domain ' + followDomain) return None fullDomain = getFullDomain(domain, port) @@ -890,8 +892,7 @@ def sendFollowRequest(session, baseDir: str, statusNumber, published = getStatusNumber() if followNickname: - followedId = followHttpPrefix + '://' + \ - requestDomain + '/users/' + followNickname + followedId = followedActor followHandle = followNickname + '@' + requestDomain else: if debug: diff --git a/tests.py b/tests.py index 284b13678..308e1fb0c 100644 --- a/tests.py +++ b/tests.py @@ -849,10 +849,12 @@ def testFollowBetweenServers(): alicePersonCache = {} aliceCachedWebfingers = {} alicePostLog = [] + bobActor = httpPrefix + '://' + bobAddress + '/users/bob' sendResult = \ sendFollowRequest(sessionAlice, aliceDir, 'alice', aliceDomain, alicePort, httpPrefix, - 'bob', bobDomain, bobPort, httpPrefix, + 'bob', bobDomain, bobActor, + bobPort, httpPrefix, clientToServer, federationList, aliceSendThreads, alicePostLog, aliceCachedWebfingers, alicePersonCache, From e74682a0badc3286d0dc17e9beaae1dadc643661 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Tue, 9 Feb 2021 19:26:41 +0000 Subject: [PATCH 18/19] Content-type can contain multiple things --- daemon.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/daemon.py b/daemon.py index 2e25d1989..43027952f 100644 --- a/daemon.py +++ b/daemon.py @@ -13537,8 +13537,9 @@ class PubServer(BaseHTTPRequestHandler): return # refuse to receive non-json content - if self.headers['Content-type'] != 'application/json' and \ - self.headers['Content-type'] != 'application/activity+json': + contentTypeStr = self.headers['Content-type'] + if not contentTypeStr.startswith('application/json') and \ + not contentTypeStr.startswith('application/activity+json'): print("POST is not json: " + self.headers['Content-type']) if self.server.debug: print(str(self.headers)) From 496e37bc916f6a7dc5bb4b9df143826d388cbec2 Mon Sep 17 00:00:00 2001 From: Bob Mottram Date: Tue, 9 Feb 2021 19:31:39 +0000 Subject: [PATCH 19/19] More jsons! --- daemon.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/daemon.py b/daemon.py index 43027952f..7afb725b9 100644 --- a/daemon.py +++ b/daemon.py @@ -13539,7 +13539,8 @@ class PubServer(BaseHTTPRequestHandler): # refuse to receive non-json content contentTypeStr = self.headers['Content-type'] if not contentTypeStr.startswith('application/json') and \ - not contentTypeStr.startswith('application/activity+json'): + not contentTypeStr.startswith('application/activity+json') and \ + not contentTypeStr.startswith('application/ld+json'): print("POST is not json: " + self.headers['Content-type']) if self.server.debug: print(str(self.headers))