Advertisement
Guest User

SMTP Client

a guest
Aug 31st, 2016
3,684
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 58.64 KB | None | 0 0
  1. #! /usr/bin/env python3
  2. import base64
  3. import contextlib
  4. import email.mime.text
  5. import enum
  6. import functools
  7. import pathlib
  8. import pickle
  9. import pickletools
  10. import re
  11. import smtplib
  12. import socket
  13. import sys
  14. import threading
  15. import tkinter.messagebox
  16. import traceback
  17. import zlib
  18.  
  19.  
  20. # Dump all of tkinter's constants into the local namespace.
  21. from tkinter.constants import *
  22.  
  23.  
  24. class Resource:
  25.  
  26.     """Manager for resources that would normally be held externally."""
  27.  
  28.     WIDTH = 76
  29.     __CACHE = None
  30.     DATA = b'''\
  31. c-rk<31Ab|);?*GmMVrt1Ql(pOIw5jD$f;K5d~ZT6<5%bwt*JfrZg#wC@n6yp}3+xMR@XD0m0u5w\
  32. ?`EPaR+%mcZ;~-O2Mr{ZT@>^?lhBhCYecQl1y?wy=0~{bC<K+_1tqCI^8vGX&NA|%v-RJx4hQnEG\
  33. u!(DyW$~e0f)ZoSd9-weA@eWiE%)G1EEQ;dMJ|>#7|U)gG_2y42;Ua(T<$WgdsSn!VtusGd^ka)_\
  34. }MWMz#nukbjk++}s_kIu?U_e_tYyuwxMtSv3CD0NmkN-LcnkIRFRmbloRyV_Oe@VHq7PA~h%RZ-i\
  35. XkS7b1b>si-2{_Ic5_hnuDm@NoElZ@%>#lNoG4jgUj#_t}mqp0lcGpcQ$Nbf}N-La|S*}^G(mHPi\
  36. i_KL%rJ~wZ;1F`k{x5e`)=aLebj&PwRXa-RDk{rZk$8FoJkH53$H?<XJ4)SUt^!7ytgPZ<mSnlRw\
  37. z$|a)RB9h*Hwcb=3L^cb>}!vEqB(`xGF0hCy#NLdMi8y=S`dKb<cFw7EP&gR#X<0x~qoevM9=&UY\
  38. 8ib368O)UU!MB)^YTp!AIk>8LnCn%c$^paDT_3{*J*}S%(>ntgMQv8h5SNF}bdqrSGovNPlono?K\
  39. B~;hk+pngaJ7#qN@+u2S!bj^biZrQ7Qf2~o^g>m2T$Ra}g`%I(koQe0M1>MbtLH7_eX%{=ey8dv^\
  40. _Sy>K-9O;kM&MH@bM~RckYkyI2F+cqs?#i;EW0+9pI|dAsVrZAz95IsO;&V?MIr_Zu=U&XpHmAM7\
  41. ;>DHxmGe#;Gk)}#)6QbAh!67e#jlHm)I>!S{!r$cEGkQ0X{ATVL4QYG4NB3_ynL2%2S?7SOl-YOz\
  42. no%*9W}MC$*$U3zeu<9mtA<7)QPba<g`c8UQm`R{K;d96-cbe@Az7Q7(<@;<A0&n<*lo&X771;3`\
  43. ox6Vj=VFP9Q7Z1`;+D0mK4`6hN_+m-}pI?KCX4AkL`fyn=C8)l|B?E><;&U$e?VP;^$cR5PN;%8g\
  44. &Ld~qrb#f%sDSrl9>d^@W>XRL6pnZ?Co*20R7EZe8BDza2yn5V|c>ek8bT2=@a8E2o!BWET17g<>\
  45. 0^*9)now96Wb;`6hmuDf@QSJ6JVU#B<J#$pmdA!1NMj4iERt=UNnyU~K*GiSRB%Re7o7fs)bRD9S\
  46. 9*J=&#xuiNS%K`BaH*eDj7Ugcj87o5pP-0}$?Z=?9!i=R_*#491z2TQ3vwlUc>>09X@0;bu1eLo3\
  47. s_WjE>B*5m_+dF;7C0#4LK9FU-&#ffD+SOv->-Q)C1^SP~mk|b;u9PKQ@%kl1f*e@J+tMSzYGHLp\
  48. GK>Jx;H;R{VQ^d4(+ML~g#rUE3j}N)Tf3>k?~ZG5$b>QXo`&-Y2j!UD~dKGi#kSJhSSx&zyDzIFW\
  49. zG?&RXi{+^c~AauDWhA2$#oGT0FT$x+ID9m~ndH&kVpGkRCm*!t7&s=~YNmV-%pVa|K4y*<Q#^WE\
  50. bq^R*j9TNjG<f*H1)#l|FC{!-qx*Cyz6=nIdEOn@S*@yB<3hR9qk;hdzSzk3ZntjN6Q5Bc7ib|}Q\
  51. +IxdD%PUw{uVPBIn>R8-nZ&nb)fCT=zf3$Y%DIqQ0hR{q*#$KNTq1&7fD73lu>}y{#y+LL&&O(ek\
  52. 7GuKlL@=O{o@@$e;$k4`$7enf=5iE{V}G^`pKMp{hYN^SeE<sn>G`#g2Y)WE_<~=?9d32^>-+L98\
  53. 4ybOo(<+5QseXM<<fZ6Dlu?4NN=lQ1uxxSYKYuqXYH7qNFaMKY%%I@1V$q(VuH3*tu0s4{M}^I$q\
  54. 30AVFl5V3CP$Oprdhlt0N*uM?$aK$LteyHe#FShHeT+bhP7S>idb@JRHKYCch~lxv5;a!3_jK<4E\
  55. rVHMys4b^>Mw33JxoWd{iI!Fk9khNf69G`fnRa7%7;`r^eEMfe1u4_sK64t>VwU4@HR(Q)D?IS)>\
  56. 7^<pVWfe?99CaS&6j#s?PH1(Q?6}yVL^gVxT<fk9R9#A-vZ92I@n#D#*0E|06Uq`3@)S5mOC!4W5\
  57. 7{g2og9q6++D{i4L;tR$;N`1AkS$QCRFQYEyzqKtA2zIZW(*8fQ_~)q^ib+RZ=<YB?+TQM-3YqR(\
  58. o?<%t)Zwri|Q<b(~Shs&VyU+aME%ZQhD1SNk;=fXS>TW~{7qPwDW)UF{A2Tb0X$49BV_=!vJa)?L\
  59. Y>^15bq_{!^@*8V3+j?5^gl-?Nk2-jq1U8VP2CIC1*D)LWQ@cu~w7Za=+8LO%*-R>HplIAKb8xwh\
  60. ES2O8gO^WcM1K*TQJ_ypv`bSK=ycJ9mm^ku!P;BJJ*Vf5Y*IPSVp^D?(5x>5=W|g{X{PY`NT6tP+\
  61. t-Chx)4*z?V-<niF7Q;kTs3)8M8(p@6qmQ8&NF+alQq%!2UaJszQ#bu;6Z~1F?w|*m)Mh(K0mv{!\
  62. <wZzxf11AKS{b|T^bWOJGQcHc%9eF^n9ozT;{|V^RrHMR?lF%AN~XJ#e7WU^jg-n(C~@yUVhfuuI\
  63. f4sZ{utES>wvxUiauKral@z@xPp(bzV(HHP*c0zv6GQPrTT%y1-eh<s<3c{H*azc{O~2ud$b>X?b\
  64. -Td(q9B-x;n-EwA%eO%Wo^62$5&zZ8|bN~e|R`BL^pe%5K#OyxCxD7>4WrBw#;6E25mIZHH=7e5i\
  65. +<)2K}k(B=fCd#$$DYdLADA6Qf__ged{H$}GWfg8+G?jh9gqw{<wdPuQ7eB$)NqctN-)En)0nC)z\
  66. iW;q7^4FQz*ScIYDugkbmJg-ZO%eSO6}7A*?$#uV{vJU=`)5olS#Lng$LzHNd>1Q6p|7CfBY&@=f\
  67. PW-$%vGkbN|JX!3gqABXD#UL^cGZ<x`)s0gzFbJ_FuPd`0Idj_Lu$H-||>se`0?T|1N+%>#?^OB=\
  68. ~&3fbiU%{RTXJd%smrF3jJya>0?l<u~N`mS5L*+vD?luUdX>&jG>j%fk!z4q3J4?qhw=-*JfVh1+\
  69. `i9=p1i5FTaW!TLVFN3QO}u6wQu{_c(E=4Y;5I(*wp_YCm8cw2YjoAs-!eILJixe&g5d!cXL0|mn\
  70. 07hloG_wZFcw_RV+HB<Wj)ny}At-b$f;amJ}%PaGRYk9zUzS*)&xL$f?@AiCbSU&#P*B?1s`1_|D\
  71. 7Wn@2&?Un4q8WXBw|fp?`QG36&)MSj687D@FU<2jdvg!p;yElI^Lie;;jyz`XuQ9_kUxC(<c&RjE\
  72. x&E|{q^Tg-+xxr`L=!hGP~a8+wk1=LOGtjV}S79itG0GEuY`}g?IkX`SnY8>@Pf9dHX3s+KAtFZW\
  73. q39`+JXX=g(V(^xs@n-2UDRxAyQoHvizS-+!)L_~w_dU-R96d2b<afA9HINE5%=v3-jW-_mRUA><\
  74. $5duZPNzLoR$6L|Z<^Of!2KXJ<d;Th5ivHI?Dz9$xq6|P@hJ=b@K_W&XNS09@o@B-yy%Zu)>U%Io\
  75. m@OKRN%<Ln?_eb*=0v#|<h~Ia8FI*!Z)~&2$bn4|>y|CZco1VVxh1dTzr2QLAAM+{Y3IB%q!E5{*\
  76. UO#&CFhRzizCG`S|Gro?WXtmv!ZS?&htHlC;=p_$FXS?S_h0_^d7<nmr_bIscnHeC2hWwQdTaR^?\
  77. Qvk5>mL~<<O6w+`Ch)T(D&oln|<#-HOYr^in1=r-|ss<%lz+4RokREP!=#ByMEX#<QsqS9{-MVin\
  78. 9Kn<(|x7J-~cmS&$w`Qz_plA1Grerzq>8bnv|=FD%&nT<NM0)|78U-lBZGy?Vm7Hy$6i>a~BLS`h\
  79. qxvmKx*1E9eshH#MH!*B501^cj$cQGy=7I<fQhhNVgjbb;B^V!gWjo7<pvAasQFvT*{Rmnz=E?kn\
  80. Rnk}q1I_ku?3c6->7025(wQihW5r()k*(lb_My9gGi;~%n5zZMEWsXx_wbl69xJnn!L)W^*HM{EB\
  81. jtY;bPFz@JiA-VR@46D<v8O_4ES%o1S>;}DjpxLH1I4E}q#am_@s$nqRC#NPODikbpjpV%nXcI-7\
  82. 1g*B>Nyeb&&J_W73=#7cVpaMmpEwhV7b`nz1CY==f(L2Hf(ZNvkcF4*G|LP6&#F<jXhtkWn=3ZE+\
  83. LK6gqZ{0cNpL}&sAL}yw01Du34vgYbzZ`Im+3?u{Dey-g3TNE0M4Ll}h%?S?(!3j|;j~;<eCFTr)\
  84. 3CXb8Fh@CKoPT`od~Hi-9mnKpQh{cX}D@&3^}0e<RkgHfZz`|HO8-1)E#ii-IA!vXs4vB980{QV;\
  85. UMhr~@hlAg<*5{{^H0aZZf6wB+(~}m^Q1!hs4Q?1WW@Oi_1G89Gx{e+-;#_=+A+t04nOOViWcJgg\
  86. X4J(VfPL3tux)TZS_Y8WG<w7-<7eIf^XFC7+t&9uee3^@yZDWtMm%)aZBO0t(}-^tAAZ#Ix#NZ}*\
  87. n7hM8=q)<{8zYUUfKK)7JWYQokitq^B?)9ZPPCq<KDd7xBd1nKJB_~h<jzmxJ@}np8UgvA*1%0{L\
  88. HA`O-<XIF1lvw{?GWX^3+^W*Z+stvR7twYs&fF{p6OBYk!*CI^g>yi_-6X=j@+<eD{?nzq{y~4+=\
  89. _lc^1t1Y|9(_tV)~s^w0m7c9>`M|32|;-)@_<tMtex-t6Az1UPH%+i#pdIBi+yhK&pFeZJpmleXM\
  90. #^WFa2@|}Ge^49*6z3$7Q$A6X8bmV2*M(+D?Q|5yGVNu%~M|y9$Wk#3gyD!>(*pk)fH;y{0;WXdp\
  91. Q@fP)_{Z3hm)K_gXoIfTu1IU^J$*Bri8m|H$vxx<`0#QlDm`@URh^*E)z2JKbkt2>?O1=wYp%|>O\
  92. c?)N^_DYNj(s%uHv3O!kIo){&6>Qn8~QDNW21e<^_Oox<IkZ7oxjCvKm75VZY=6|(><+y&f9Xwh3\
  93. 71LDQn#3PcDa1TQ42#zU;+2Pri6PH1B=jxVH|h8MF8CQ=U6x;F3iztjpNlcF4PH(qQVY57TGO2YB\
  94. +O-{FBnp{S%2j9LJ2Lw9ftPJ_cwhlbZO;nqGj=y8~N59^;jq_J#~yKT>o$CtrnBjC+7qju%`zC8T\
  95. W<qJA}H1EuYU+4a_b@ToW6Iw<!EY574-S*XzW2(BZx#s3hAKd`=|N8k5-^{Zn47wg(D1vt{wzm!K\
  96. -MuIyv*@IPoQCz8MH34Ck+G?p?fB{)zq8jyz|GwnhTU^L9O!_*2aV1yDTZF%8`{=f5B&~>D{H&k#\
  97. uvfvZ~8sADf5aO;J2z?_g$Si0(MWyNz2^4=jTQydhd7nVE=jNZ=UnPZIk+~fc4H2Pq%BkH|%5)WE\
  98. ~2nEQ0-tVEs4jueseDp1k*Z7~_C#$8@*#9Rc4?`s(zYzD4luS2<}P&4af$oYtv1Cv8w}@8f!G-K*\
  99. 22%a(uC_ceQF<7X_7W4kr{b#KefBU(D!-gxD;Zyo<Bg4*fbZC4D3UF_$x?hW@(tlW2aC&w{8yN_D\
  100. ++QLpRmhYLo`R`>{{+Y4qV3xgU+omtQ?HJpd@(E{@R6hLk={Y?M-Gde{yBm%h{MVFiC7wZx53{Yg\
  101. !PT~6;t0oGFEg2c@4eS=9#QdAm&Whj8TNKnuT|aty*d}BZE?JRM9=Q`eDMHW#4ZQ)Jg&#tKmQk+y\
  102. WV>D4ez{C@$!LiQ{PVWn{v`V*t)XuqjjfsdU!ajzWXNjs2lWk=A@nHfaBjB(P{ojsF{Dmm!pn7Hm\
  103. #w@#1WH5b%KZ1T;Eue31`$!KWx)D_B-MAZ<_{XLe4$cH+IW}2k%*P+oN+j!EI+%?mOdVSlYRF_w7\
  104. AwMFR?Q-ak04;h(JJGp>cD|LEC$duLnG-bd!VzgJqr1!sLWYD-^R(KPnbKDHwJ#1Tuo2x9hrde61\
  105. @uIZB2@Zni!wN1{1OQw%@m+U(3G<g52GhcZ2kWQKKgJ<wxFYcW3qs`H)=b3F!tWGN$aO9EoGc#f8\
  106. AogGcbldp)*E<fJ1J{35^3<w1E1=seuYaALW-F>#eoyPVrXFy_y=%V6&N>S2EGgM#zYdmm>Ul>!E\
  107. 8~<kYyLHbRh%QA|LCWK4zV?MFF4Y-->hCR=iZBUjlH@T{J~;>WY<(Uxwc!Mt8Um>xcI{K#@@pQ_P\
  108. (S3k~syolc#puzG&K~Kb7tpJ9R94_~<E1&n%d+`;q5zVMj@?TVL;YWPR40;=R_~nECd*hyPtYG;_\
  109. =JuU$nsg9i5e-=4?%UYXr^{-?+7{jIZP*CU>97dPzqq`K4d+t>EFCv#o-#ECo7UmS4zu+D|muV?p\
  110. aUAy_q+%q@6vh#wbmnNS4>mwW9DLR1_=emn*Wo0G1w%r0>vqEj_nRe^dH*H)vaCOF@!ru2?+0yx_\
  111. hUcHZr*+m<@b<J`cVrhf^_Vo~$S3~XnRWsEI(YD3JDE`T?s-S{N!=PwSkuyV=<yA(;Jw!;XYZQ>r\
  112. Ht0o(^jP|c<Z&>vvZ$J>vP8q8yBW!HqJWhthxIQhxJ=ab}d`|Mf$kiBPaFxd-@r@o@bwDzCQ;V#}\
  113. uY@Z}`v3l3j;&8<_qFe*MZtw%<0s_VviUIxQ_c^2kD_y?@tCKh@svFxW7AtLMp&ulpghar#3KJa*\
  114. |>bB}#>IaD#SFFW)C_-;jR+Y>C&^_h()%x&Ir?>(*C!FK$2m+r}FVrd`Nsp+NVm)+EG(?OTNe?Z!\
  115. }_2<9$XZpmSep_}bt5V<Hv1j_GXWy=`*|}ojnC=a`zIgqb4URRPOsy9~J?uYiXv36D==Rnn_NIHk\
  116. NMAi;?y#nNS*z0e?>}M0_O_d@s#szB?zaZdjA^(1c23Kh^Z(xG*8iUN&DxoTkI(IV@b#B>y8#~jd\
  117. <5Kh{VaBsbK2Q$cYgWQIreY2*<RkVXZ)b&AMN$ywu^qavftkq?YC#?utPq&(_U~RyBzWR@i!J;{P\
  118. JDHoVPCgFdYtL&F1FZgTHYVxrf5i!3&T7KD&Ev*XA=9uU(dvc|q&P#RpE=tNF#>p<(`qkM>;ne`&\
  119. K;<42jZ|G0bo#kNtO_G_GAPkXfT=9B-*Ta|Y5#1WltJ#%~CgDduOjNXw0Z>)RJH*Zs#qw0UBesOu\
  120. XqSfbr+ViqUHx<|pS+if`+Y{#<RKMir1L20uNn@Tk<BsA3Pl6uZ8z)ZemAm%Xxo~(6le~Uo=T^LQ\
  121. T-uf2WLMe0ShdHtVEuntMfd3X0r&Rpd&I)i@_u;mkK*zB-m>JD`&yjG{)=@Ua)i#q@F6#SQvb>c#\
  122. }C`G^|d8i&u_SJz$af^Qc-o?ys`_b?z>{^jvr3SIS+=-x$~nD-Nt>$`XoPiH`o6$uyf1R*IwK5Kr\
  123. !~%MxQoj#1q3OU4_H?2C)n0AJ%7T=(ooPV_8_j!h1NgKtWSj%fdny27+~gGWsR)GV1Upr=?XekaX\
  124. R&Iw9KvI=_Q>Wm?#gz->Y?@h4?vppf_ya~VjKw!ltxeq6$P3Bvz{#EZCvc#jLRi3f2X8^#lE;z8W\
  125. Q*f{cE&p3YvmzP8z!^XQlt=Gx_RdMY1CBwKiQRmQh=%8()kNFCTrMjV~9<V3ye9xD}O4IAt!UUGj\
  126. wZux(`&Y#O<iuLyKk=XVPy8qT6aR_-#DC&H@t^ol{3res|B3&^f8sy!pZHJwC;k)viT}iZ;y>}9_\
  127. )q*N{uBQ#%>RMBOm^abO7S20F5fer>UN{4#DBGYWcasY;(yZfKb$>jG4VgC`LAUk;S%D1((+$xSy\
  128. !b{zmWK^zW3Qq{7+f_OZ!R&6941I|3WSU;X<)V{FlZCg~b1)pg|=3Pxi3^@n3tWC;q22|C4ciNc@\
  129. k?FOr@3pR)XyWFVXPpR)W<mbD||fAlg?O#Dw-{#$)~Nc=Z0EFu1f1p1L%1<CEZ-D+z`vEu&{NF9H\
  130. PRvI71g8%g?#Cxl)9mRtGsp`Ai3TsDZ^M7E9^4=<A1GD*0=Qbqv+7a<Tc3}zepU%`su(1L0KRz;$\
  131. P5h@bwut{JF9XHIe;OYW|7mPM{7?B1kF_J>e`=2piT}iZ;y=~@#DD7l5&voYNBpPxAL2jF{}TUc{\
  132. fGEZ>%YW*vi~6dll>Rjf0F%Yq#*n6)aF0!|442A1JM4jROdf}_J5`}|E*G1+W$@bC;tz`fAarA{3\
  133. riU#DDVtM*JuLkHmlS|C-wTC;!i>%YX9!9(&OF52?+6I{$_EPv<`o|LOcU;=d96*Rlpp=RYPd|HH\
  134. *c=f5U5|Fa=E*Kmmc+EEP2w}wOf*N1Fgo<c~DH5}qUf%s1#{u7A*1mZt|_)j4I6Nvu=;y;1-Pays\
  135. ii2nrQKY{p9ApR4G|HOadf9m4B1m=G+Xy2zn&<{Ik-wzS5{a=ZkKdVEJUp{++%V(<Faq7iD(C(LW\
  136. hXvXvDW7g2o6b<k2L1jxSH&^EZb%)!k%=0{##vs82l4e|8*eNmKBTnoayz815Ak^WF-{cFITEq=U\
  137. 7u+E=wjkaN}WxnCIi$Kq$cm>a~JOsuM7`sEjD*j%*Q9hyXePwR`!2@^Ak&`PSCCE3yrSpeLi1H`%\
  138. Q)q(bRr#6Yd*i%MW-j@8YxDHu=;KyKj{b0|TP(c#jD-hqzA{?hAb!!hI7+x4uT<-qwCETsEbv-Z#\
  139. ^|aK9nwK0WZh%@%YI!T0q+_t`=B_MrQWp!;;$JxY)*=pKUa>x1sIgYNA?_ZdO==|T6lpnH(++hmf\
  140. B_pLI?#`_kTB+KtJ_<gfXa^?57pnC|uFATcR4!XAo-Dd>drw85Jg6@%o0r!Pr?(0LxmmVfR_Auos\
  141. 3{4N43O$4A9n6nleg*S0nBOY$kRC>!>|x}wKD0dBLg<5%zSzU)TYYGKwT0Gq-*r?!YPuKOli>Cys\
  142. 67g9uR6A8f$d#Oh<m$2dm7x{2Dir@+Uwx<JfOYLV?UEvXvD|h5dPe7XUzJ6C$O-Hg^!ZF@Ae%FPq\
  143. T0d3tbbS|J{#;XL#P)cnEi`=iNxIW03}4f6u~{IMRM13%^B>|74-RbiI;=y<$xJavDblgeO_(93$\
  144. GF%)(z$$V{@yzjVDQ2DC>#`yk4ECM*3h3^AGhlc=r<8J;(p{?BQUfekEN3);@Fuu@pc!tY_qzMF-\
  145. ^O`?4!3$0=3KZ}JlNM7JD>(elG(Nq)H-a_f#GDr=K3quF9M!gq@;rscJI%p0-2eWLJ!`YyWO(FV+\
  146. T5mI({%U+rHTuU({)zrX|5T<w)&ErgQ~g8qr}~HLACiBnf2jVU`iJTts(-Z5e_s&iQ8S7DF|U8>z\
  147. `t&cbLiPr|HMrGkA#lvzqsG%IFkRE(7zm%*WYm-{bHiO>Ge-2KL)s_Ig9EaQ{{glgttGr6=H8kW+\
  148. nMQ3B<itYGH7c^vCPfypKfmHx_#QsBC7T7u7$e&|j+^+1D)eC;5+y{7c~{7S1O5kBj_E*SN=|l;l\
  149. 5d^p~!$Vj-RAAJh6r$-XW<iR+(zSQrHU^Vy9I%fT4?^Nk78KQ&VR(Z||jd~Ph1t-euq^3lfe8JK5\
  150. d9BcZct@0~iJv;laSQwTB_0I`9WHnLbU%LK-g$pb$|85J@Uz)qC0^IU!UHT`(zMn-Ft$(a8|MKgX\
  151. S;(ULC$9S+-(sOJ)jx3!xVAim>Yupo|HXC9Q>p%m>;7xp3+kl$C+=ZB)jx6dKQN!_AF6*+tp15b`\
  152. %CqY@^!5GpH%<Q_$!e@tmhx7{_$VOWB!fmAM4LQ)A|doKc@2fBZAi7<0t=eko`$~>2D^CvcF0c-*\
  153. Ty9|4H_T@uff6-^Q>0A^Y?A$v^GCh#&o}vj516`|rX9EA79d{imrV|7OvD4_I^mwUze2PY3mNLaX\
  154. {8uu}bV3XuPec+wy7knG)L@IPZk|6}<e`d{h})&}_>qxy&FPyP?7{vr9N`iJTts(+~dq56mDPxTM\
  155. gKP3M|f2x0|{xKSK{-7W6IDc?p82YE$`I~BVFzftH>HOJHbx<muKdTm|nwUPFzZ}2wml2b|di$?q\
  156. d2ROj<7gl90nj<`YGFtW>cEr1e~t*rzyCUxdjQLc{sGrdvd}rkeSwKA{6=*Stc3X4?e$~fnG}`(?\
  157. ^&3VApPPKc>VWLlGFb?7M^C|5*E5fLMWfFg<oa+e7+`rZD-ew{5qXoH}Gp4y9WPtn=BgT>v~@+3y\
  158. 3znw)sTJuNxBax~<TE-J0#cZn4X*Gi2B4vTKlE*Yj&6bfN4zTXt=iU1!LyZL(_+uUk1;@w!>KE)=\
  159. et+Ic8l+hy13D%T*tu9saG%C56z*LK-;hRU^F<=aA)v^3<?-l2^0vdd{Ar&R|UW~k6STY*P%-Uab\
  160. AT}1}$$}%IDr9_r_nQqE-R<6rx`Yx{%0_qBR9U`w={B;gaV2lFM*xm*XaZo$n@b(~^C-I3z`Y=0L\
  161. c!7ly)GZU+$xbZH1^-_6;C4$X81;@(abX*Oj7hY4)TFY<l0K6E&EpNG&s-Dp8*BRDJ3oMNzM>8T(\
  162. H}`N(FcF;HHbb}hDZl}b}dQZ@Cb-|ftBckJm1d%`n;t=pV<(5-$IdKe-<{W$k=&N#*Kaleo>&!hZ\
  163. bAAxj=<JS4J6chDx8}MT^m=uSz+tHh?}#x@9%`9Hb&YL?5CL(TC_m^db5XeTY6r^ba#3@%Bd_lpy\
  164. `Xq2RYayx-*cIxFhenxOZdWuXVrrybuZ%q6_b=a7g#06(hCwQObK*d(M6&hH$j5b|`=N1FE?n`HF\
  165. Si%|5zfNLL9i9TWHgKvS@&OKU<J{VR&?Cic0i9UMPHgRsdn+5uZ=x_2e5YHrtCj1Ke*ISxcI5L6d\
  166. 2iN{uA%1IH_zwEZIw=9^lcPYJc+f{`+a@F!ea?$f*|om&H<+`omMrv<!eWElwq&CZhUW}y+mew!7\
  167. &aL+mz1pZ!GPzf934mcSiwdP`&DPgp?_!v`DrtVKBQk%KNEeZeW(6m@{BKNd`j~hM$8}5{20xznz\
  168. 8;)^YgU6lSuSQw)H_;Uk(kjPcWZ8WZ&Y)Iw1R~Q1lru>x-v~eVtzW%-&p|5@~N(qS$99D#-qs=tJ\
  169. }&`Vf7HK13g)kD2|$M1)!UyU9O5cqD^=iOpb+e+dBcPZAE~-zGfBKT>Fre=Th^N(TRIN1NOp$o>0\
  170. Sfj+wZ`*DAZHldTm-6smyq9aV<KmzHy5U-m$US|u}f@jJm;kr?9^Vl5w>$&y*>npNl*LK-;hU_|B\
  171. c5Q3F_O*iix=?nVExWe!Ya~wwzebXz^Xo=|u>NbPmt7aiuCry=cG-1?>^fcLIz#2#Y?ZX?JLXNEX\
  172. L(uVWehHRp%Sg+Gz_F|h6*oaJepe=ADNL|%Vjt~wq-Kk-0`|TSa)PPg^Qm>EHr@M4=D^ctbW4z`I\
  173. {j8SuODjAMkNkZxiZfJSXf|u!c=J+FTjAo;?Mu=RbdsK{Uwaaar*UfnN>Kg=Jvva};ihat=mG=hs\
  174. 2}*Zq~`bb~@(zl$=?;Q0Q;mOnw~Qw~#+fu0e@SEvxzmkB<nv`rz;*(p^1FRA=gekwnepUO|=k8$}\
  175. A;(f+cD?gTbt*md{JH^U>QqVj9<9+8;DZkPg>OX?H`@UjP{()Ss<>3*H<(I}v7h1UddgjJLjlpN8\
  176. c=<8h7o)ZzQOl13_nq&Z!sVCdsCy?^`Frv*M0$q!DxGt3-|@ON5z3#<^AnG`*^voQ{_7yVbM#Z<T\
  177. >d2x|2gXGQoQ^amV+^SqO4keqsC5FF250DC##n~+OZRrpX8tFKWhJ||3m#>8voGvm*zic{)^^6Y5\
  178. tqmKlCHj*T1xue<Y;#`ez0&bE>X?OEk!a`1>^?`xh!dm7mH_<)`vf`BSg_M(%$|_5ClYzW)i4%KP\
  179. 6Wx~B5}M?@;`f0fU1r0V`>2}!g6o%;{)ai0S9+~+`{uTl8TZvSl%U0nDY&{aW;-8T<^Tch~>i~lx\
  180. BzxDjLkpE^2zrH3r|4rw=HvWq~AnZ*-rd#ceLZ-#vZ2oKKzv=wf#(zQjtq=MQdM`aFo@`k<Qo2$;\
  181. Wcf-D$hTAuf4S-d%e^y7I`UtMFA|@E`6uyJ;xp$v`iU6C!X)tfiD)<?V!g;>VGh>|xvv<X0oS3P{\
  182. l)tt(abR)$n_>T{#fK+eti$<_gRPUzY5xX{KO*E>dgOX3baChcxz)6z7FCA`kU*bBmWD6@;^VueO\
  183. }bNH-luUP{;QxgZP&phkm;|C=Yscc~ubo<FV#2C5Zm1n13n6EB_=v$(etgf7sWe^52E`htR+1ITn\
  184. =vVL@X_PXf{(&-!{#796r?BNh55w}J6CDACYA6h^A_6aNp4wIPaz{*mM#uis{&j|K7{s;`Ul;QeA\
  185. KKLsq@4BGuCzo22vf&M#BjRF1bd`?EsSV`+R3+LlYOwRvtF&I0|Gf{qyi(sr8$(Z-wpx^#O5B<%~\
  186. KmOv(NjD41Pw*K5hgwj6q-(wTPbB$SYhn3`RDa_wKhf5o$=-fN+J6WqFGlqrV%dL|u8r)!QUBSR;\
  187. }@%sA484bi!3mH50!sxL$}9h{=}^L&nz(i411dYr1^7_pVX8etFPZ#ef`wx>(^GdA3#{yenT#Ya{\
  188. HNB1+)eVX+Ps@5H-TbN5O6K-wgI^_xalRuTRhKzV+hoK85@@oBw7AzrNOV{%hmEApAC>4O512zD*\
  189. (j&E~%u{5PHd+Jb)5gWk)Q$0NlnrQ=UGJs@BHd=>_lLvQ&dx=D28bd~5V(LIk3N{T>cr=_n)c?k<\
  190. 2gK;*7zp?NjXtOCV0rPAfalE!ynDoEl@1phF^$@v#2YGl=mWPQU(mp`;-fRQgtW2)AKHh$+AzPNd\
  191. +u(LnnhRS<>FZ9va~#u`<}p|3>?cH${y}_>4TF>aZJ;bqxL2c>iRmk0U{D?Md4zp_qteIlGYdlvO\
  192. 8;~aeT=B}7vEB;OaJ2SM6~H|jG{ho<9(oL(tpo@c4e`KcE!x}eSGeH-xN(h8v4gu{^G5F@veUoss\
  193. 4?3`=iwUVxRf<7`4A@{f{nDj8m!qXPxxpJ^qOI_$v{{pD`PMw*q7^rlezN_W9Pau&q6O$-;FkT<z\
  194. QDo5w<fZ(9S@vrx!_orMe@?EG)_!aMjbej|MQo-95wtdr8iv~ivS7o-Qt<|!s=tKrbj_`ea-;%^q\
  195. #vv8zJ+}*jZVB5Y;)*fgL+xfTX+bK;U{xSUd@(6Va<9wHwMQQilh5XqaQRwSaJdUGO;%|#_{OJbA\
  196. FQ*UIG4Hd`*P!?h<Lfh$AGc*PSsG+O{J~fzn^WG!!l(%1NBTYvfp!g2_!;#3pAC-xAldw}FU+;{d\
  197. qapmhf@5J)CG_6w#khAx0xKj9{PmiuRPvJ_!~|>eNoD1EcGk6{<+2A`XRXf!TP+D#p`Dy;~xWl-{\
  198. xktk7l>;!Tpm^^@Ea*cj%uqGxcp^s=m#)m4%&RsNV@+6|#`M6WZ)MS#OF38=6+~U=#m_&+r}L-TI\
  199. yW?^#?JC&rEaFkBvT$ogSLp(PT#ZEtI=W3fMfV$k>XynlNNh->Ge!V@e!AN2h~K4+o@Gx@k(?R#4\
  200. U-_PgriF4!MvT%aV?<Mr+G(vl|@fyBAmCt`jviMa{zqT&S_gZ|?+#S^4GV=Rq^B*n!g87GH|6K(9\
  201. lgm$9l=2se{6?!k5$SKEO$_h-1@IONudvYk7oRCOfQQ-Q-(KPG;JeaycRlowIQCtTS?;Q=DX1uQ4\
  202. __qw$;KDBu=dGwbb`sqnr`BKIjf0oVSgJmNW6cP)#-m6-U%EIe$U=};EYbt(7?aXw87~UIzdqpza\
  203. I`zUAmW0%L%%k=C9?j;qAFM2!TK#5C{YUfj}S-2m}IwKp+qZ1OkCTAP@)y0)apv5C{YUfnXV+RcK\
  204. cby@#X6<6}YGU4quNxLXBxB%I5_Ss?D#N2^-27(N=r9SrDAEQ82MAS4`6FOT4Qi@O)DW8qN{J!_!\
  205. XMcloy8;m<S7<V<G$Bs2D+|R-lEKC5=f*$LMG-3&XU^s9aNq-hD<~sfih`Wp0AhAwcMlTrXc@$60\
  206. z@0s#LEQaoBNh?}nsEr%^*ce_?YkSSamu_}_yt4{YPhp+1RswO2m}#1EL5=Y2#CA(eMug0H}pF!+\
  207. |I%{st*V$2J{1lrzzbE#(F}tYS_c)EM~AUfW`}iBm}l`==tzT5KlPtQBVzE^S%MDQ_!3u!Ky&dE9\
  208. g;dCFy)rbpd**JC6DV1Pj2%+pVP_dNwB=HZI`$Bd$3fL}ErrFzm;|bQV4&J&noyA)cCzwo9Ez^ay\
  209. dpF)Ta;;)yk+o3Rh*!E-LJe+h(G0OzF6WZ^#vsuO63vlT?!0bF;-bw%`OJ`Y5Hws@-WG!|SeTn3^\
  210. IAD#$>HiXqIctJe*3vG@538I(XS3vao{e6O#8NDRm#lqnvo&=MCj+{#qq>qoM9--&##e7UWk<UBC\
  211. !VVWt{Xh@?xQ1B{;wdxear`6RH;*S>+W36y@g%MUgMed9JUOi?PBQ%i3#)lQ_Z$#UiAmITu`YZ}g\
  212. #8IT3E=fOj*XrLalaFR5DC(GyZUL2<rdq~`?(HbJ8SiIuT0)InaStvevUCcvF;p5;!My8)Hk$C{a\
  213. =ja6Hks?#R47;b4ZHRtH<;Ch>Z}teN1>_VGfBmAp~%b{L2uVd26))J%!IJq%v^segbc&TTG@qjzw\
  214. @@llB}C<md(B>3(L;U*YMRxTkL@kX@@9_GRHx7M=!k_dRas<169=jq$4h527_k&QOMNZv1r6f6{x\
  215. Z15Y!a3gSuLX4M0@4{->QIwgTVHQoU8=J33{UqOg|J_gqm?%{JKX4n(n4B~zbLh>Vn&pnyZzeL|W\
  216. !-4jyn1#K0pWrhvXMe%zMB=0d&N~}zzk_p1xL((f$Q#pf8~QC>Z$drrARl`Wk_TwRgZ4A62FeNU?\
  217. ?c<SK1ANQ0{xFZXK)>Wewd1gv`GQ<ef>HZYhRCh()TCQCKS-d$jChzXqSL%g`J7Cmc;}E=ZtW^0Q\
  218. Z>C{`17fDF)Z~Z-Z!uNw5yk4*dZGWdQ9~k07#I1~~tEJ{Y%W1NU29NMyAHvO!$eHG=QB=k+uqsio\
  219. V2Ib45@kcS08K1UK6sH;C2z~`MTR1&X~2DmT!-v-qI=&QIlku>4@d$Yh8``uME#!p(H&w?)vkP-A\
  220. tdK{56A%OF<OAO%YcPyL-WV@5>KpUc43}`F<<nls@AG(1t`}LLr*?goL(C$B4TQFQB^iZ1-7dSwi\
  221. pNsZvy7P&YDT^!+_kBj&ek=l-D~cuXeERJM%;OwPq$GgoOZ-QOe(y&Xk<xUay=OH0b<qYG=L%^*E\
  222. <p~o<9;E^I^b0heHakT0q(7fW-S+eX`Vu4B&Yz}g!xg{0dKL;lSpYe(C2<M{-My<Twfw1K?BhLV>\
  223. EW-9{}wOGZMJYvL_0D+zaBqe*!@(av{3;<xi-+F$8Btq5HT#Q%xi!L<k(qKOaRMfIg&YpJ?PbHHt\
  224. nz?#~}XBqT(My;!&}iaOwZAls5ia3Vy0hBmGZ(%CNrqk#53v!kd3(9b0K9M=w9-`^P_tv?2F4<Lb\
  225. HG%k)}EQ<CfU5JEX;eg1`ncWPuH;Z5{&W>VCioW3K%mH<vJ%6M&*>AGYgGfk-0Zxr#Y<eS+PZ79}\
  226. FH-+)WOqe~HKMUYEhqAIm|yu<6uM7mHxS|hoMVo}P6%z#M-%zlad{Mderop#@dTdd_(ue?`zs4a5\
  227. %~n4ERmc6y_v@QgaiSe8}t`MwpL|NVY;Dj{$C=b8_n|*5((oVvh!u|-1Agi6Tr35NX~XeKV7uGPe\
  228. ??RL^zI}lVWs=biNPQT#g|zBP1}chRF7*j7%XK6+>j_G@ni4MX*5I_D}@!jsAyHVbAbkAkKa2rB5\
  229. Y^7QrIGz2>h*Amh(La{CfRABh_vGXIJMf|Y<ifSMwZb$1feWL^aH*Z}0agJ4bIJ~q931@Qd=Nl1$\
  230. mLI0TxxDSZV;vrZWlOQtxVVOxp11!tOpx;Kaki>_O1c>B}foqbe{MSTK{uv}Tgd_ouU*Fa#|9g^T\
  231. 98duIeJ0?UUbIh|kW4^-qI&lXqt9w9pHqQ(e-red$whus3CV~uNUimNnh45EXQ~pC6}Yzlgid|H^\
  232. KPtY?~eO(^zMybOd>)^a-dznw>tIWaSN97ew}4Rnsg)aAfy1!iJ)IR%tGZG4Ep!<pF?6nNFl7ysU\
  233. Nsk)(UfhIL6Uy`@Dk0fskT20QCD0EVDql&evI{9h?>fKXCnHe2Rorw(r!t9)R|+iR>>3_xF4P<O?\
  234. -gTm;dbL2aclK85*=edX75(yJkX%j48pmQ7%7`v%f~(*J02tWJ92np)2UEC>4U|59gJP9aq%{U>(\
  235. A13GDTT>_S2iq0}TL2M`eC;e}q3)JHi2W^59>0EK#-?vRi{?S)*4zZo|pY*>Svvkt!<^;-nxz4=b\
  236. O6(^6C;blq`oq>^7x*U&y%HebxKB^doIjqG(udeh`cL}b0aJC-u08?sJXU9(>HJgDf6{+tpr3%Pp\
  237. wDM;mpJEnosRtC9LIjdX3~Gse^uzU37QheydDRs>(hwzpY-1h?8U-oI_UQa#LrjGV-}pRumTR@bw\
  238. H%ywixQ~!YK1fzFs;)?^^oUIOGxc_v`WNbvLm!Df<u9ejAT2d~C0k2hYYKk27@S5BCBeNo*zkCul\
  239. ~!PW?J0hIzArzBRwq#7@$Gf-dypW9CHM7gVHEMurkQN&gADagPo@d<C)FOQ+|2(=CwPXBv|J6U@R\
  240. OoxB(wgFNjE`pz_S6B|kY2@&Hh9lUrf2Kkw&BR^PA^dJ_J{u3g`WF5S~^F{VHIX_S7$j?K>KGJ_e\
  241. )ctKe=LTJ9VqVZc_ii0|A^#qv|AgqVSO-5=nwXDCI`Z)~&>4QD{{$m29Q4`Qb~Py<&+5p>EyOm`e\
  242. }W;vb-nF6crw9+JnRFa-yW@bIG)%>`cE(ncS3yE0nXNuKJH<rHGk57f^it8gC}@~b|-_=U!)`bn}\
  243. }Vc{{%yUb7|k`;LC{yrj2d69^c+45xYqL35Mft9elakz;ttUq`Sj}z6XKyp8zmM2VdSYFkLq!mJK\
  244. 3-^gk7WbMSiXZ;U!4;0YaRI*CoB{{++UvJT#yYtY#LdmU*WNGu}#CzuAW4&E4XM#$kh())neL;6o\
  245. J6$LtY^M(QG6$APBBmF1+PZXdnV6#Tv;9Nnp=L7sxhb&Aa_K^M)OviIN_%kfZw6^F-%R#Im{U?}?\
  246. D|GN@b`<I0xxRmEOb2a~$mW;ypI|;tf!M6aAFU&u<-``!e?m-vK7Lv?^5?@S($O>bH;dRp`cH@%w\
  247. 2sUF=Kc6caI+3MIFndH`cH@%9@fF5qa#e?MIC7zOe`V&C&Uc1bnvJ+!Zft{9Kk(5H1|jPPlz4P1a\
  248. s{G_t6pmE5r`ce?m-gunrzYa{gX_AiF=(f71U%1@^ahY2?wS2;$e{?`sCJgY=&eb8OPcqn#0q&#Q\
  249. FE!1=@q(tkqC@uUtu={<)_&)VM*Vg>0xA?COh%v%FosUz<Fh!v#&gqTB*U#|0X#{Ig+cz*+0`y>4\
  250. )!~<h>@M)^fxIfky@2A8D(tko+@DCk)nyWMJpCEQVMhK+;1pUa-!Kd4F#EU*g+BC-d46%XqpAZ+=\
  251. b@1sS9r5<m5%0f<4W$2sxB%zf_GskO^E%=^QV0L(oL|y^Lj0h0kIvgV;yhLd|F0kxkp2_mh7UFJ=\
  252. @T7so~VQW9%2FMKOt^-M<bt_bi`Stga4Jp0@8m%+^}9FpT5%(=SUs=pG+(u{U^i?jT-s%vyM3R?E\
  253. RZWEFk?S#0}4bb~|0II8O)vFD4d{{uAPc|I^5)-8$mDPzV1nAQq7R6XJ$d8u`?wBhGVl@P7iafb^\
  254. dbH$1J8Pc1s))Z<&QgjhiOPly||`sm)JBhJ%w@V}f`K>AOJ8(!7Oryq61IZOxttBD1q|Ae^V4UK&\
  255. IMn{~->)?MKv4HfS5I4N1kxyUfh;y(G{?8*8kp2_mhEFu|=_4I+I&|>=PGSM+KOt`TMI)cq>xi?f\
  256. 4*owvEFk?S#08x|yPuw?bi|8(1$JwU_XT1D=|3SZ=%s^COLWHlt;TrYB{q=$6XJqA9ei4#Gwu&G#\
  257. `_hqf%Kmc7o4nvPqTE!y;ftq=zlDoSV8(vh&e9S!6%o_xc{vq?n8(br2mANW3~=Hjn*0WH9F!RMy\
  258. w$HC&V28)WN4bopG1yh`X3rLHbXKIbP7gr!G3<K2=BD^~4I&e?rXhDH!kjPlBGag_aR3NdE~j1={\
  259. {-wb9uSLHwOr_*-NA?-4sl{|PZgKOH=J*yR4jU%Wmdkp2@AkH-xWrm<2-8vTeRr2mANVZII?of}~\
  260. q^*YkHh*(1UPly?w*1@C0BTPe&AJFTFC8Ym^m|?3%{`}5DT7+pFu0sx9B9@T;6JmniI{5QO6zQb#\
  261. `a|paA~Xj``cE()=j!0keNm>hPDfft6KhES38v!~9sG&bKlk-I(yAiXkp2@)$3`9e=^tfUdd>oUl\
  262. vraq{U!ak7P3IxbFYOrJ0SA8KM6f`$O8HdvJs0&{|TmHxDMVtYfzg1(~)KYv554aU>a`J!J9b-rF\
  263. p-OG-ncvNdF0@VY3e26d9D}B|6evODrP&Czyi1I{5Mj=->0NhW$XhpK+X<*q2yE`cE(%WjgrsjKO\
  264. K^S#vm-SVj6zFdTaJ;~Bjc-=HJyhlo|A{{%y^5AV~Pz2`>_$LUD>R|Cf$r2nM<1bs5`sR`+$kN=-\
  265. ^<l!7*8R<X4Fg&4yCwG{Xhs8j)fU&?yEUd6dIG)&LI&eO{MThR4Vp2X%*O3R@H@G*kjzEYS6Lsj_\
  266. cVOOmT%B}|0WKrf5eQM^#RTa4OSo4@Ue*!o2!yDh#}7Y-p(f{NxX%3K6AKB1h;gkBUHclM-|rU=>\
  267. 3oc#HBYw?3kigX$J^f~psrn~BTqPAkU^{@5OiaV&c60hG0N9rI`cJ&SV<u0hMsx9^)b%dOFHuQ0g\
  268. zu10znT3>TF}3G0xwGI`cP@SV|yh#)CR^YZt`c)+Ph=*#f*kEF};$0r&au(V<&+$0?tB<^jn!SmF\
  269. VF!=is^oHza#4|qKVVmFsW=!hG2>eYZa<?}$$eg-*)$BDIZ2ks-S18rwJf6Rgh$c{Uf=mp~bS*<$\
  270. tT%7Z}l==Xc!TF%ic0&r^0Qq!@6>ieWn-Ou&Z-LJIt|k`89B8w37ev3;_f|09zATl|2lV;)cpnmD\
  271. U2Tm{IT}K&ju}v29|q(0`#%V@mTMMn)5(j836k$qb>@2wu{vg$561iaXdtp0j)PhFO9v0WVxe<_<\
  272. Q?0Cbvk9}bYi*bz`bB?Cg}g)Kz=`r#=mv)z?ES6FVdO+|FW<bvE9@<`F$|Y@7J3|V#9E}PTGH-pt\
  273. |#t&T`Os!RFx%FyHsz2qLo~^D`TD(!DrA%W{Iwa{L0~`Jx2l@pugMe;JWEYFw(5=4g}ADS^xLtj_\
  274. W*A`vhgxZc+s1O5LIXe}TbpkL2#bkh9d1TRy6K0l<jJUBmfB8h^b$b;DV|2~w+9WnIUWNZX$*k9-\
  275. ^*M~qpLX5<y80!D2MDB=iQUtWN%9;zF7rH}d+2)Z*7>4mN)c<irZr#AP?tePz{BMhu(XF$5yZP9G\
  276. 5Ix4kQ2$2}xpiZnP8#D`8htHR#=Rn_<KBV9v1zh0hQv_+2NJn;^Jb4u8qc+8Sx<>To~|bmi5j@aw\
  277. >1X3kM^o`&Z|DYe4>-a_<p8^%Y2{C^0t9_=a;1tHpD>x8;Q)O>H>yBi<h|@=-+d`jfEa0E>WR62K\
  278. rx4WY&yxBB1ZRNmBN6BPctbcTBb=kppc@c9^L9==+iU0%^k`Ec~pKzTfh;Gg+`Q0vVe@q7xOoCh9\
  279. -!-OvUcyT26yeUr`KQg0T%2mSj@$-X3Vpq==76LkL-Fz<}%WCHeI?+4mfAt91IX+QG*6CpAj#6pv\
  280. Sy8i`;eyRxJF*ySIe#wG8iD`021T?|^6Rk5v4m?BpGXr!V*P<LmPE9~t-M>J8e>d6p!k`}iHv-vP\
  281. 0pxo!8sK`$>L_%7MUu@&nO)!h0OVIq4QMmg5&?Y{kZ47Xu`Ij?`fM`KBC=^lchGP5DxKfiDTU}%8\
  282. euy$l|;-C90%h0O|P=B9mM??yZJT#4gH3apJsi)eLam4$Tap94o*RO-3yWVo;;gGjgVl#vFX1fl;\
  283. >2ncg6a0V+1tAvBPi@IYNQ}*ZJ;@us`cbaoQam;atgYKz@S=iJ|+~N2vRc1KGGD3PImFqEbK~cSj\
  284. TX2yw&}5$gUX5cdNTL|g`u+o98$qJ((B6QRta&(FR@z7CihAw6+##j!*_LJa%-b0U;g9JdZ85~|X\
  285. 5X@qp$#lp!%LP7$M_i?WO3?gAjp#QIzBcw0-Nux7q2xbA-_#&O>Cw~drfqU0CLgYTFE+7&TOlX&u\
  286. MWOq1h=jU;_UfCWpm!b6+6uuKpiS@EC}i<^lEsLyKSX9rErq)wwQQ;h#skkc`Y;L^yn}Q<D&YS0F\
  287. QTCTvwS>3An3(EK)=r#c~}CpXVnN~Lv;Jsu@8|)WF+VS?oVrpQuoP6l5scyBJ&#{g<m20e5w(Q2m\
  288. 0&#Cq(Bb<qm51O#z-O_->TExsK{(f(mdi%9{qr7mfGL1Nxwe)~6-<uOy#>1Ub+z%1=?s)g03Om;m\
  289. =pJRK#k&^P{tM9P#0o?&oLl(K|&eHBE?m;%@J7D4nrEtdkV52PxNVd0}F<p=Hg$)-0xz`hpl!D@?\
  290. =hv;*e?1obZXv>Ft&2~pAH@J_F_VUCZ=dtjo0et~nv!QbblN%>Mbn8e`*bJ%ZvoDeRa$61HE82Tp\
  291. K=U|BhpsH#Za`lV!*U)UA+dn#gD-$F=iIDeA)iQ^6l27TK|E7BTA9FkKH683*ub^@`3BVitt{LM;\
  292. {FeU^??4%UpGh=&_^ZtoV5<df#@I5sI#u|+=0D_td_umd@jeRGY>b=Sl^OB8=qGUlmpy@FcHXpFj\
  293. 3J9#51D)G>G5W-@l6LeanvrX0x!zz<L1p6X6^o*?1=gdhjvdZwB(X3CN#ZvW*2lF;G6FHJ}O9KEw\
  294. ?NaJ}DYFkkT;A3KpZ>43JLxA1<Qk@E62i1Wg9K5lH#pM?iN|M}YC!gnCrk`R)=FR<BQ*^%}bFJvK\
  295. ?NNmbD4()88Gl6dy?xXrUrGR~Q+^^MQk~}r?u|XFibyUDP+y)lDGKo((CM+T{r!0;H(MOoir0@%f\
  296. c4z2IitI+TkJB#Td}%X@FMsfQh3aPlVl+hOYgFq#qZ>f9Ri?Ti5U7XfpXpH$&v7u54-W&a=M$921\
  297. a1(01Ncl1pR;f?i2DvRQk34kK|J^IQ9gHKHqGAxIy0IO9z9vOk@pdOF$mb+Jq4nT;SovPH^6!h*V\
  298. M0J;hh-KRvP2uTnyQ35cHrA3%7xJbA+MxAU*-2Z-e7O+^=rcdh~1%&%am~2Rh<-%L#M_1R*-$S<Q\
  299. Hs$nF@+uM}E%-*qt`pPT~XUdKcMwyTHp@p}WWyFQB(tuWwTzbO{3HwlS}gLqrEBaX5iyeDlVA3tB\
  300. uWquS319@IE4Jb2?MbH*52gEgub9ui6eO#jt#w{@%Cxr`}_<RECJ;6+%|IupRzw{+|z_|tVi}F5*\
  301. ``8*m^pSw`4J%oAoP|{&uC=|y`{`JxppTsGVC=pLvjX~RI1Ok`g%DfdxN<z#8y|(3hFu`~?Hxd3N\
  302. Js!+{}}h8d`G%zYQR2B35YgA1VW+#Z3xi!$ZClFe0Q>jZ$aEw(VxVNU?JeV*`<7*Y&Yq#?lsW6SQ\
  303. rMhZcng2(1)Ot&$aC${Z_S!yc0w}d*ow@kgUM<sUi^f0&GfQeMdiCxStno8gL&L&EFGJ5$LZH&mz\
  304. Qqf@l}pnxy)UetK}N?n>Uq)80D*p(D_~;8d<BxJLgen70qstbjH_c$PNWEaKUi1=Ob}5F!SyccJ}\
  305. V8Hjrlo&nKD=XXd98^p&z^p!iF_vJ~~2?X<jW9I#N-GDwb&@OQvh;!#?7yKd%?}2EKk3MwJrf&zY\
  306. HzeCW^rMISZ+`}Ht*wcLk6Czw*E9bD(Qo4AAlew8$-+@UYw(tWk6<L4AwAUNrqCpB5m=%gLxae(!\
  307. jPY<N6;WKT{(i*cA>~n&wL1}kx5ly$mo?5XqH!Bp>mI?@9?-yN?3R=TbVf7Qa&>jOPAjMrcE*V`V\
  308. N-?rB9b7m90?vY}vaR3Z<9lCB0+zG6KrdF?-nonW^uPJ=tAja0XigQ)CI)gQ;f^BzAgm_6q~E6G*\
  309. |vpbQ7!H-w2Vm|(4e1!(O+@WA}GbP&mgfC!sA(6cF^08JhE(Hun2jE?-Sm#1d$D6WO_+!l6}r)>G\
  310. l9ptT17G<k~Mm7b=OM1wfs#!rFT4V%h>X7}wG#eH4%_qxqgHnYc`=gRRvq%g5#aFD3vfHy_Q>;(y\
  311. ?a$K{Ys-udSAhjg?+{ZU2W=g45=a1Bhmr&lp#6Eh^7Hzj7gY$PSbWS@=;zHm+ifZhMhh=?t4e#&+\
  312. Wy?G@;oCTo{j`e56DMH0!q(S8a!UECS~$5x`lb(2=X5#0@ll-(eS)6u>Iv3_mfxW^A>?HZGQ3xJ#\
  313. XdD?W)i1f$Rx--o~G2t3J<Y&xO)+AAeq``aC`K^M-VR3iYbbZK0nx+5|2HwktAW4)<Jm)M$(Nxsb\
  314. JR&l}QJpSOg4j{TX8(9c2rd286`jI{yU+3|T>i05r#pWB6ZLOpK{@!Y5Bd0~j>8KIvu+O~v!ULVE\
  315. t^w7^6!#{5h``j1#^Cp|=&zo!}K4*!?{dszH&l}Br-a^ll`nd-Ah*mxmTmPc3U*cUqhOfV)Yd`cn\
  316. Zw#J2656lA5YGeKSHqtR?RR#F=YjnTz0bw|i9N*g?6A*$A)br<s|?loyztM1`+vbBw8EhNqrT@I`\
  317. >%SR2luao2T0L9Zw%`H<DEV9^MJuxL(up`m}&_5yftuICOvR`Bupe_2Z(2==WYDCJz(0%9w2@J<2\
  318. zxR&mKI$4tg%1*2xYWKPo?$3qXMW^YN#gWJ=HF<KuwmeEh5WoIhv^tYH+M7b-vJ8EaO1&d2|<0cv\
  319. 3R+vjubiq9p&%jY-b&!xZ186@YQ6mMGq$^6gT=QU*WFLL>4e=eKfk;`8@T^jrZl+OgVPaDbSk7Ue\
  320. j|4dHcri_lNpgaNNpWjlR|7w-ZQ)DPsKIt0g-`c{=-}w~h{~G1<>GdJ!7Zv9p11OoTw4R_im#Q@X\
  321. skG|T62Ns^$obcB^S4U#zX4Oq3JV?qe+oDM+;RT8B_K;p9SYDK@RyGB=Zyg*X$>>~-WI&x5d4SW`\
  322. Tv%n5(lq0GzU?vDd;|U{h>K1zTg$0mcR_O2Ir>@g6U!Ju>MjXkbGe<y#nX^gBc#MB-GMzfvN=p<h\
  323. g@DVw84;Yk3C!`mV4Z(db{mQe1#*ZjYc@as5nK{<BLL8A|#n{9Rnc%vRF(kn4W|>xb!z^_d@b<@x\
  324. qd(aRICsmy1Gj9ylPdbRFesO49G1odjq+d`!-Nl9Src_1Ee<`9fV9AW~U0DA5%1`WXTR?*KA?r%R\
  325. )hy6C5+x8?2_i|fM+!H&4g%u#4yOyo;T_<kGQqMw`$~)+Hauv^0Fwke`SPl8ZbnraM5wgFX&FydH\
  326. =*#Wfq;QaYp_|*b@5ArWzgDpSy+gR|9%{fs9-ka%u}}lz*%XUZ@_G^fe!Rjnw7FZ&?Q_Z^_#W{Dw\
  327. <EeJ!tar%=%3Ub;rGbPeOY)!;n_(%ehkMeJh#ifdk~b*_&9*eVQ+<JwJeO`*J#6tzR`mB9v{zbrH\
  328. &2yyMxnSzK7{zE`#Cx597Db2g#FMCZ6W@;K6z6%I&>YseCH|)xM7N_Hk~ji8OqO>qo%8t5&9s%}k\
  329. otZ3B33C&2xESYVPm>|_>(`NPTV{s9(lVHDZG-rKrP{0{4xK`cy?*E5Ifs%Hq+LN~5AH*r}-fBQd\
  330. iyTZTt^(VYeyph+f@jhQ0>*fi(&W_|fX#58EE?xq$J-?+3ucyA1<pq69yvpr9&*iq6y?DMd`1Miz\
  331. 8QN&CgFwHI-|%)P)8xG48C##r(tVeOau#|-5EuHE$MZ7Ym&J$sWKK6pR_=yCKeK0mG5b|*ypI1s7\
  332. XKXv$`qbqAf2^=ox<s0jz4_#d*lWGncQ!3yX0t)Ba@d;d9z|Rjt)xm&?xBs1&;^!N5s-!JJS7aSs\
  333. C_^ppJP*miK+)P(Nee<}L7_+oVSy?va(fR~*}z{bjOrhmNt5q^koHplv@|rqicukQ+(nrzcSV#0&\
  334. m&fHF0_w<*FpQcwRuZ`~@j4>cj$D9QKa=t#E+n3R8M93%BLlyv!0S)ag&cGZYH2e;EwU*i&m=P&Y\
  335. Sla1Q0VQ~I|&%O9~zf}(Gf3<Nv@1DT<@5%eEe+P{p&f)Q|HL%VwBmcnr-abACl5jKsTfGJHe-)4G\
  336. M%mw_^RvfUAphs`xSo>z?Nk1@d<*3NC|O%3;TQh5o)*Y|FCN!VvcE~b5j$HT{~6%lGnp*;x4QgTU\
  337. 4NzebF2mOFV&w<E3{v8Es+0tJg%D*%Jd%-@@#beU*&O)3F;R~{kLujoc|uYuKZgepVAo`WstZi2m\
  338. g69!MKb+dm9qdkDZN=tuT)hRL1Vx`SXzmq<0t#uYrGV`p_ujJ3}^pR2#$B$;Ob;`X6~cNcLZiBCb\
  339. y0_wyKPUIf>;oA`UxQRYAR+@{@8#5EoK{#nDV`JWAb-;U9o&9WgV|F1<D&mi#o;nzAZavy*H<0$$\
  340. >hx0P*;j(mCgnb>UZ?j0pcUiKz3iRO-kNI0n^G%+IcR>Hz68c1x&R5%DaNkr5-8af)zE2c&;FmI4\
  341. ip5z)gJd#;dC!S5z5`{l^e3;E%<%2hiPtkNGMVXPP~2HEotD~%kzm~V-Dn8(<9iR7<J<CVS^FUMH\
  342. OeCF^Wi+-WLcj8!xy}rH515-8M3~HG#332A3u!c<G7xDJl&aJ=kxg`oFiKc{xt~<IPdL+*!mOg!u\
  343. xzLL1exe@f(~oz7)(~cMpf2d<=9mAB%p<=LB~1@C*Ojv;3WNAhI?4z`;g<!8iWmE&=x2hDM?6SFp\
  344. rozs=cg>G<1N?`y!{c|KnglLB9c&(~7O2x<5E&}OMM+vjVuvtKl5Wxp+GJb4{JGe(cLNw|lM{cgq\
  345. It@z8nWWU?+cPGF){B3594gR*6@!Kr?uEXDEOqT)ertnvmpJx1B=l^Y~XTR5Bx!AAWUrwpq5*^y<\
  346. BGE~rn?y&6uAI(jr{w_A5qZ0vR=1?E)7p>A=oKK^`k|fM{UDx^Jc;+sY$mi{6L{U!#sm6Xdl^K#z\
  347. %{%b{S*9a(^nYOuQ`*ah2!d#T$k0_1YAoznT1<H^!N4~h<@ENBCG@d$;<Ot1by7DoCbgJcIa>&-*\
  348. )3;vS!ZfXmA9GKJsu*@gEw?{VE@e9%YdHpvK|49Qx)9ch1%Q{Jo<Lmf<dZoQ3^HdzkUv7?X4t?Q`\
  349. +$pT#P&`vuqMaKLr)M^!?&vGv8gjKMa(#e6)Mts#9pBOPsQzm~Q6+xXvVH1sLo;q`_bukrlr!3tU\
  350. WdcK{&>yrcceQ)TvZfR!vS-f7D!tc>0y_MG)u}B|y|4gzc$9d!bMIg`7q>tx=e88_CP?>i$GyMu)\
  351. zXJGe#$%cOM9y;r+VlTkjMK-l)$jcJ7CxU5%k<~)@g8tJ!1EK*VwnEJAnr>*6!18Xjv(Dg(zo&UR\
  352. a#R@<Mro!jo;&5k&A-D2|Ch0f#-RM>~Hh=xK;0X?KqA73OOF+^^Gm4ZEVrde#qgH(ch`@n|!W!O7\
  353. CCG<?vt~<&e^UK*Reux6}ygHh9=Be8|FjjePhAZ-X%$qv8DqKIR<~;+=lHUVH+KUGty9=`>ZtyV!\
  354. @{#oMT8#}H{;&-AY09?f-fTNM4lBls9;H*aqunY(zH=Y1U?`*hY&w;apmaSxZZAyLxsBtAdV#`Oc\
  355. +Dzud<<#o?xeBSF{vN=4|?-DT99v}6xl7;15Pj>Ns9QLKQvhV_z@qcI?1B*Li(?#b6=}m>L*{#_v\
  356. ^*(#E&v)HUpKskZpKsfSmW-Vk-fPQlSty3qB?<xmzOaQoXJLb{uw_GA_Rei>*;}`^Wp88gZNPY23\
  357. )`~WGMWnouLeyvVc%*4=UV$b1-qjCWCQ|%Kp+qZgcQNPeEf0&AK&ACa9kH(0RHtZoIl4ko~!w|1I\
  358. Jc4pH>K>T?L)BoA^lQd`4d?xc?k&B)$RT&SJ&+%w-_1!J!SyK1A}k0(BDC{&0N?*M6EIwrBa_T*z\
  359. yD-l7QNZ<mrPJjZ!7^ar&a67PI=oX5wxz0SlcL(!MF%l`#yoac+S5$MBU2=yJJ0{RcbxnZNubH+9\
  360. T=ihOz7j2eb0>8Zx+MRC((FXY^=(r~!(tt9Hc8i^eoto=-^ojR!g!IO>xOaHJ9@l3s1kuhrOJ_ci\
  361. Uua`>CLc54SfLTbHG)X;g|;|oL!3b@R>dd~*DCeW7HtL5=HM(o-ZK*@7q~_}8^ra*KXv9+@*hTW;\
  362. K!l7p4UT1v>nAY_nvXiJMs~I{ovZYp7X^&0dxGXSqVtHIXZOcBmRDNf|dv81TN$Cg4RCE<IvH!Z7\
  363. KkLw0^9SMlbO`ftC8w4iNVtwrD6H+Le1#q>qSp$v0@^`%|FoyVy!VJG7fMwhga9Xy3$E#~~W~cQ_\
  364. s!m}F(geKt307+3xX#`qLSG+^KS=P+f*vH5vPK4;R8_g%y3!2Bd>50IBvL-@J$n;>=8RFEdPXC&M\
  365. lHns;nEL={M-^Ci{#|lz({A1_yRN?4j#qj~|k9#sq*_ME~moJrpb%86)82BqN)?b_1Kz|9Zg(wg9\
  366. g^Ph8M**MTP@~z;78wg6?>B`g$LG8cL{OeTS`mVFxG!N?0_Q&~jNaqE@jk@nkU%@8yCC!$W+L{Nu\
  367. &)<t4j=8Y_9gae?h{-Yf|humjU&PG`#^~N;{GaH@6r!kFIGEm3D=K$B}hK64a56%Vr9fA4nf<uK)\
  368. +ps(HI*de=n2X8-ltJG+rFXeBnCmFDm(Zm)d(nu^@yDOpHT*a2;=hN`BD4R&QdNp+LU_%RqhoI4{\
  369. QexhcflPCl{A6rf%CMisiF?P{!idY%@dF1nQ1WGZmnw?l>Ycg7%n+~@MCO8O5Fdt!ofK;2L75R=o\
  370. M3+n4s<Y6RsxHZI>(5&$``bSq<r^5C7{=|})B7?X2N-|JkLV7sI_l!z<*AqM9f|FJFvBQM%*IA&x\
  371. Hk~N-ekeb-W`*iFmwAZ_U+y<p_uo`W=bQx6hfr&$W`#%NTxL8s`xliq*<tc{?^#GJTg_DDKcZYEZ\
  372. ;K3y{}q+^2S8#WRr!zo{ZAC~hI>yAFsYBRDgpaLsmgzZQzd^3qKx-hm3W6FApfOv1k}RU5%70gm^\
  373. f<^r0lpJvlW!*<0IW8n+@vwbF7FJ7S?Yqn5t5SNXIC1RN@;-{7*>W-28V6Wq3QnF`v@@+$~_;{aM\
  374. M*f5d#1a_F_W{)b9DGb}t`mJ0mOS1HGJI^($$l-HLIBK{{l-c;$wYF!t?_kL7};{~ubQicCjDrG6\
  375. u`2Eq4ICf^lfBitaa3wzjSL+x@2Y-up=-r6_mcf4%%JQ1VZ<W@0-vsL;mH4l;e+cV@a6T4r9_d#|\
  376. &Ncp2;{Qo1<rx*`yAxEtJDvD%DWI+JZwlqPG0b;Ldrk1XxxI=1mcw%j<yjx*8zp}&Z$NS&)%dS8e\
  377. ~-2zq2_Ou_U|tt{#z2~s+1|zzTuw~-k(MMw<L}Ned~8UA@Obei2vG=0m}XZ%2eJz5sX<oBK{{1K3\
  378. 6EyO)BqB1m$_Z4C23Kp=7_cQsv!g3h({^$%9nnztVW_C6#w)fwI3g;=gsFv`=uO%DdNq^4TiHf9p\
  379. cY_wt7-@7@NHuNe{l6BA1FxnHZitK`G&IpV*ip|m$>r^>sJDZIOa_-|>bjp>x|pu)TVNBp-m%0PK\
  380. fh!Pfo@;Ph7f9qnpLYcO!ysLCB)7!*<OGC+D&!;NyUZL>r$Hae2L&;CyyDIOxLHWE=;=grq4=A52\
  381. q=ZsXK6AiE{I@I~S18ldD({X|p+R5bzh&{BLYeMSdDo#rgHwtBmP8sTpF27|#QRGA{iYKCEs29w%\
  382. 5+AEZ<Krr-bVblBu1!|>97#rEQjRr_mS%SSF-2$3qtP$R$2r8iMI{Je`{eWDEny+g>fo$7)boL6m\
  383. ZP`l|p$Q4D%hH-`J+`T}_hmH(B|Am`ZuHoV)rCSaTha`0s~Hr96jd_*QAo7;OeR5&tcNM?m>pCM_\
  384. 5Z*7W;E{1<_H_?7(k{8QuiT|wD@$UVe=OW;hEvRteq4kaHTc&<QZ0+$<o9$aK_IKhJaSF+<nU(kB\
  385. |_gttVL*s#v$gp!>M_Z3|;eEbjwg*f`l&R?LnGwWwKPdYUI*`bmuvo9av+sGIP%nmoIX>kH=0J^K\
  386. 3nPp#jgJ|X&YRsYNoaNih-X`2{dl;=%ksPm{|7}8=M<GVueLa?&^O8!P(DX@j0MXx1k5?B0r&oYq\
  387. Y!U1?`v8S3pI@UZ?ssMR)cwScGFejZm=NlYjn*0R9K`;$Ef806@%hO-+xMeq0m+=$3ndC0m?oR)x\
  388. u5-)N$BHQ#%vhNS~c*XThK?u~qED%R&GAuQS2RG#S*-fi{vaSO<KilGZqj@c;e@_<wf-mk0g${i2\
  389. fZVJ4)fcJBOEu!euw$0FeWzZ1ATi&gUcFO$*>bq4so7U2J_5%51z#{p{RapSnGw~6VZT~P}}zrGj\
  390. `jER8%BNMP3Xv6b!9LFqkRno?D1CB^w{;Qpns}{CE;@LgpJpSt{`F$I-pYaq8(6_wWdGP2jERk&l\
  391. P7G0h1&?hL@r>IWRPu}abdQQ*J`Uw=jZ$8gCis}}G?n=bxzEet37GHkA@V#a4*5AJM1B(eyck@+x\
  392. {8GlK-?39bpp=)&x6R%OEwdy@O-N2TfA^v2pOmaf)N<Z!Y&mWe;DVnH?9wD3?Ty(iG5Lzx3;U$_O\
  393. }?f`D)l7M4v)Rc|?1m3y6&o^ZpAJy5gBkiF76x_6NUH$t&(Bn@a4|UFUBPQLiTATuVWSF@dyRY$M\
  394. ic>g(+aLC5L@&ik-1<7?bQ(ur6ca*X#^2>M-Xfij@KpkG6j0ey;+4RIj$;$!Aeb5ipyRDT}B=TSn\
  395. <v7An97l3|xUJt{2w*|{}Bnw}LC=>PzuSxOt0PT){4nZRvKV4$s@?kx{IgD)M{K3&F#&?{5ye|x0\
  396. eoK-%689579Hy-3ll~6Sw@1;6!1ctkFglOne=H10qH<%u1#Q*-3{!60t8^(OhrcPMG5rPzzxIgsJ\
  397. 9{Uo{No(K#~Ns`3B*0f=}Ay#><h2dP-gViIVlC&zI{NuEhgG)pbc(k3zif85j?B0oM^kz4^j&!Yv\
  398. >zF0d0@31aY1@(fc~6!|ODb3+GFxCP_UP>AXj6nE4{5eJ$v#K2i2&;P@5o1kqOb4;|&ecD^?d5C?\
  399. Jk{jGz3Exdm9^6_IVbts$bIr4Bj=(8!sy&1!Z<%&T2(Z!(be=1x+|3s@;@PO#!`Y;1z!p3zJ`Gr3\
  400. 9(1+G%5ScHjS9v`_YhdAV2%iu56Qc9qg=;D3^Y#f4&o@K+Nj&Gs&D#+4@q|7x(N{8_e~jxZxAVGa\
  401. BZOYh)EaOP_E2JJMA)C}^CmFfx0{&(*Eev#$sl5(VL%_$H%HLk#wuWc1oxPo36c5RA!zS!;~MjIy\
  402. pH}mjy(Sy#CiOS!Mgr1tPZ66*&zD$z<nvZOyV`#+~K_ZGCn7jO{7mqVE_667K%XhUxadj{!!NQc{\
  403. ZGX{)WrR-+aveJ=f*;`P|L3yswXakF)r=Bx%;EedeGM?DAXeI@inYG35;ZZA(yAn#6H+`+Wlhe9(\
  404. &5jJN*JZNeYx+dt11vQXImx%ifpB}kY5urI}w8f{W^^*qzQfZtXgVI%*pjepl5#mg_+Gv1gXz9!|\
  405. XIbDpVJ>NF}zy2r9>C(OUWs{#!4VZx@KV=)UrGI$Av!$1Kg7~V8@XZ*NjLt1AsWzMczBOG8?HRBI\
  406. -s86|8G-lBaz;1Qi}x*@eU0^Ep4#sV1Miy(@qgKUHh$v2Zw|h<`x-cpgi^P3ya)MxYlhDj>b@<#E\
  407. nUyOFTE`z;NI4%?|xpZJ>b3}+<i;v`^FCU*%rIE>$>kikFfL%roSS8TBO>>))su99&n!?c;D9HUX\
  408. -uk`*sD(=y2Z<>K^4=RNr;dePg|(d0amVWt9xO7xYQ0tD3mJN#$iPi~6c^&k}3Nkl*_P??I{$#Cx\
  409. nC+HC&&R=fv)`og<H&cyr)aft<j_WG&OUtbATo2)()E@kzdP_;GtD<Cmao4>vctY2GgLG`crW_!E\
  410. 9Yu5JqyU{MIw*3`(lP$3RZ{`t7?L=$Pdu_a>YLIHq2AlX=MtkE}*xpKs5ejr_&u1%2Pou0sguwO>\
  411. p9Qp!V*BZ@=h2!D<n=Z-sD-low)V$)ymQnbbe0@qO6(6k_Z9<z5Jz<5b3M2|jAxPHK4aXoeLM@fU\
  412. `<;9HSELJ1a9E#1K(>HhvJw8_uD-Q;@T<BF<Qx93iphj%g1E94H}!{oZu}W+E^v(`U;+{hBgdpz6\
  413. XBe<I{&g+=q^SH_=uQ_t2v6x)~sz&4fPq(JrP9)NM(|C6G*@Kaja9^D?*=ioOL;h+xjN2d^9MQkf\
  414. G*|1G29Tz0I#g4YY1xxVKcJSU22tx})^+MXR82cF*r9oJ_skHI`?f9QDrRWq-1Vga;KdKWtSXxKl\
  415. -Jis-_51`{&&SuVI4GPl5?|IuDFJPT=Z4eEfVj;^UddtsD!TH8M2~r229@OrvH&_n`1(p4g1nj3_\
  416. -?2%S$3`&5S5;1s%-@~B{W08+^HEUS5RHvYCZ98x;(OQvebH<P^l5)Z6mi`xi|0QU8k=An`KxR^Q\
  417. 5ZoS@^j|U_k-1J884CL|7#F!ceMh~$CCTz7!KBi+zwyQxjt9PzPsOmeDnoz?|UTSpeW}Wq%n6ZX!\
  418. EDj6a0SUaUYivXW?8F#n?C6Gx;XU;(A=e`}1V)*BK;-@hrbh)`sDjIxmdgOFp-M(OGAN!XXfN?qn\
  419. #xY72wj?Q1~#=WEnw`MHqSm{>1eWFo(nkS-gi?+P(Kko&(rJc-JWsE5%0h3veJ#OJS@+%CnV{D;Z\
  420. XSf%j%Y}xY}NmzcwCfQs~@VG#JZql(yT7G#Mj|qAv?cLo8=I=F$SNY{W(yM~n$Q`n|14z>HA1X`d\
  421. PTAjk$^QO85|<z4TH15*qU`U-%F>^c#N|hP2>!jt5>An&KP}12FZm+cu2L^tmgMDM1OEN^O8w)Dl\
  422. Dzz@W%EK3&XA>FlH}!oN!HFw7z(lV#TC!;Z<Ni6Nf;2LabF^oU-DVDMfSJ7b<UAkwfvdjw<`!918\
  423. oEU8M(<?ez}|npJ5=KuZ(+>_es+7J43Yd@-rYu#jw1wEdMfDI*y=cC&-@Nl!WESbvtQY>~n?Zd&$\
  424. OtTfx|MKJ=BdLSs1Ir1B4gKtIJwxJUL*5hNGn{s`6ftDgvgYrV+=TnCcu)4mKL+cxl@8;||t!%fZ\
  425. ?*0Z<|4bN9J(~d@7w_hIS-3hXH7n_);28Fusaud?QHJqPheg1GhC_C}`QCcrL#)LdMKzUy?_B{?V\
  426. IIRVs-@o{I5a_?v==sp|LbQKl3`_^@Afz>o??Jo&QJZX@0K;s9%YR~s@*6exj(z=iAo8<%aJ@uY2\
  427. S*y7Y+(6qpzITSt$}IWDU0_dh|F$WzIKi_Fh=^PL;L0jLA2AsuoR5*k6QwPHkt=S5trPC1otJx+R\
  428. jxk#>jLY>+ca!#+M<RGm^%~2PA0OaSc=MQ>om*HuNo7yx50Iw6kEHGQMM&YjFHMg6gba3~sBUk<V\
  429. azlDS~cJ{h!|cseMpTOoe_jQa)2CHASNn4D%O=xB3}{!7n+*zHZ)o6leE399Eu#E|y5uPu0A4z3-\
  430. aX(Ao=Wnm_C+((CNnenj2@5cKX!F_r3Nmpl3U*5*&M9{xou)n(PypIy^J#n}%?@3TT1LO@5&$c<8\
  431. x0~Veit|vpyj+X<TvTwpJGl-f+Ia#vzl=UZe-F{W$9ahNcwhJb_?*{sAo`{G7()BT{F0B66a5?o$\
  432. xre#pwIWOTA6WvWEfcU+yInkoLjB|aX$g>BS2e3^da~MuRBmCzTtCEs7E()`kCqvTfxUqOS<HpfV\
  433. -FweF`-3w;SxD=LHb`IG`^Gbl!mhon|znKLQ(j9p7mb{ugfM2_6v&eWII*LZ9IEpf$Tq@XgR>Zxy\
  434. {B*joz)Hwqao;_r1W^}_Gf&EoI6W--NA`0v)Ofxpeczw4BLTO#~z-qtalM%nMyCjQ$j(op(+h2`J\
  435. Jin@-K2VId|&B}-0U&qRgzZv3ho1M|4HG};!`mp!dU$5r$66sgZ>B;HaAh-tcAyLr>2Rc1Lza5f)\
  436. pa#J=PrB&PqM3VEYLz@rG~l1_x|Ey2F4)^Sd2&T{g?DyA&FtZ$*<W*Va!#qNbkB4+9iED+no5^rr\
  437. gOH#>vp)RJ$1D%hijIrw9Z@Mu69&-9Hs7R_O+`FpS!DF4sUs_%UM>Cl{LPcJ*{$=)v@o(T=<TMJ#\
  438. ;xv;i);woL;BHJG%zIae5uKb=4SKnTtguq+C6v(jVO^EU{{j*I8Z4o=+AcDy?*SJdT+am6be4F30\
  439. 4$>Qc;@hs9Aj8zaIO#W=)Ns+?u6Ec~OpWU8yw%OVwCnOsrp@j7ZMou#e<$N2WhSqbVquE}+k_@2`\
  440. -^8C@ctV~XJQ|Wfs6fi<%Wfd1Y>%8Ue+TvozP)F{0URO=ItJ;xsiL2I~<2be4SySU;IXroco8`<?\
  441. aNe}pUiVB_ZP64KO=Ur;yJ}c2i-OU@B}Q<p<BU2+#-j%fJ{lj)aMgNPfrMv+`#T1)|Fg0VGZ<M}j\
  442. Lq&^ufsFjBV87YRFiI}*SYFkW~9tS!{MoPdp&v8&MKE@Xl`!)iCGSYoby~>QDTaVg%=nNW;kmroF\
  443. z;wINM5{m6c3RPzb%QSzaa#-f~AxZN&^mPKOX*L5>g^D_BAC6qk2$E&F5M;QWHg#g%UMwP!+Zd+f\
  444. QFG8#`5UPpNAnB?cN$GMK9gt#1&-L>KcOI?h-)+LBcUT*)~{*GKnZhnEMrn17Dm!EG2sYIES^ykD\
  445. ZkxbHkUcO_%Fu!(5+L9x_CXzT;WNLqhQ&5xKeDNXjptu;nD=yCSxGE=e76|c=W`C}5R#sfj^tW9F\
  446. 1wwPZswi_+dof98b(v$TyF`=~PIw`07Gps%7br$!epSHuQ7n9%mmmDDm_PQe7}q)YZ(cxdk@Y;c{\
  447. RO@6@95{Oo#J6X{ie;tD|sPK6DtT$dkukgLn+IJ7sZF?4D(;>^48U|`lHt6sjKwL3Q;jxDc_EvLj\
  448. #NFPZKe-yn+d9wc8t5t^!w84Kgh7<qpLc5~_C3bfQGbXoL{+DHJC!xZLe8mbofjQ&1Hc%}4O-Jds\
  449. x7C&3>CCSOz6p1c&3|5cs==|RB^XQeq6gedjx`Ip2#WPwl<+Fwvng^r)~cT_PMnbqGRl%>DJRa@(\
  450. >Rn-wm>H-oDr#F*p-BtKST@@1_k05b1tP*k7V&mw>Hn5}KU^0w@_|t(=EKT+Ze}W&08DbB`i{QsX\
  451. NoBgow29S+tPx^A(>m&-@Opu>bb4Jyt-2yAYMjud3-v`{xiQ6=9+B24O|=y+b}cKuw{~`*g7CbEU\
  452. ksI#C$FE3AgBoeM95c)MPM$A<#g5vxY}QE%_?=(cta=4niBRJ6NSm{y!;7+FAWv@_}V&`GA(PFbU\
  453. Holv@EKrb=SCRy|ddXE2P(ftQRtE^aw4Hm>=g1){StMR8&e8aILe#!>G<)kg785XLXdSV2;Y_Ie8\
  454. fZNg+fYK#<`2AmCYD!T$sC`iq1'''
  455.  
  456.     @classmethod
  457.     def package(cls, *paths):
  458.         """Creates a resource string to be copied into the class."""
  459.         cls.__generate_data(paths, {})
  460.  
  461.     @classmethod
  462.     def add(cls, *paths):
  463.         """Include paths in the pre-generated DATA block up above."""
  464.         cls.__preload()
  465.         cls.__generate_data(paths, cls.__CACHE.copy())
  466.  
  467.     @classmethod
  468.     def __generate_data(cls, paths, buffer):
  469.         """Load paths into buffer and output DATA code for the class."""
  470.         for path in map(pathlib.Path, paths):
  471.             if not path.is_file():
  472.                 raise ValueError('{!r} is not a file'.format(path))
  473.             key = path.name
  474.             if key in buffer:
  475.                 raise KeyError('{!r} has already been included'.format(key))
  476.             with path.open('rb') as file:
  477.                 buffer[key] = file.read()
  478.         pickled = pickle.dumps(buffer, pickle.HIGHEST_PROTOCOL)
  479.         optimized = pickletools.optimize(pickled)
  480.         compressed = zlib.compress(optimized, zlib.Z_BEST_COMPRESSION)
  481.         encoded = base64.b85encode(compressed)
  482.         cls.__print("    DATA = b'''")
  483.         for offset in range(0, len(encoded), cls.WIDTH):
  484.             cls.__print("\\\n" + encoded[
  485.                 slice(offset, offset + cls.WIDTH)].decode('ascii'))
  486.         cls.__print("'''")
  487.  
  488.     @staticmethod
  489.     def __print(line):
  490.         """Provides alternative printing interface for simplicity."""
  491.         sys.stdout.write(line)
  492.         sys.stdout.flush()
  493.  
  494.     @classmethod
  495.     @contextlib.contextmanager
  496.     def load(cls, name, delete=True):
  497.         """Dynamically loads resources and makes them usable while needed."""
  498.         cls.__preload()
  499.         if name not in cls.__CACHE:
  500.             raise KeyError('{!r} cannot be found'.format(name))
  501.         path = pathlib.Path(name)
  502.         with path.open('wb') as file:
  503.             file.write(cls.__CACHE[name])
  504.         yield path
  505.         if delete:
  506.             path.unlink()
  507.  
  508.     @classmethod
  509.     def __preload(cls):
  510.         """Warm up the cache if it does not exist in a ready state yet."""
  511.         if cls.__CACHE is None:
  512.             decoded = base64.b85decode(cls.DATA)
  513.             decompressed = zlib.decompress(decoded)
  514.             cls.__CACHE = pickle.loads(decompressed)
  515.  
  516.     def __init__(self):
  517.         """Creates an error explaining class was used improperly."""
  518.         raise NotImplementedError('class was not designed for instantiation')
  519.  
  520.  
  521. # Import custom modules in a way that an IDE will recognize them.
  522. with Resource.load('affinity.py'), \
  523.      Resource.load('threadbox.py'), \
  524.      Resource.load('safetkinter.py'):
  525.     try:
  526.         from .safetkinter import *
  527.     except SystemError:
  528.         from safetkinter import *
  529.     try:
  530.         from . import threadbox
  531.     except SystemError:
  532.         import threadbox
  533.  
  534.  
  535. # Patch the messagebox module to use a thread-safe version of Message.
  536. tkinter.messagebox.Message = Message
  537.  
  538.  
  539. # Create an enumeration to represents various program states.
  540. Status = enum.Enum('Status', 'mail_label login_button connection_error')
  541.  
  542.  
  543. def event_handler(method):
  544.     """Allow command/event handlers to be marked and written more easily."""
  545.     @functools.wraps(method)
  546.     def wrapper(self, event=None):
  547.         nonlocal none_counter
  548.         none_counter += event is None
  549.         method(self)
  550.         # print('Commands handled:', none_counter)
  551.     none_counter = 0
  552.     return wrapper
  553.  
  554.  
  555. class SMTPClient(Frame):
  556.  
  557.     """Widget for sending emails through a GUI program in Python."""
  558.  
  559.     TITLE = 'SMTP Mail Sender'
  560.  
  561.     AT_ICON = 'at.ico'
  562.     HELP_ICON = 'help.ico'
  563.     HELP_TEXT = 'get_help.txt'
  564.  
  565.     IS_TARGET = r'\A(\w|\.)+@\w+\.\w+\Z'
  566.     IS_SUBJECT = r'\S+'
  567.     IS_MESSAGE = r'\S+'
  568.  
  569.     @classmethod
  570.     def main(cls):
  571.         """Create an application containing a single SMTPClient widget."""
  572.         root = cls.create_application_root()
  573.         cls.create_windows_bindings(root)
  574.         cls.attach_window_icon(root)
  575.         cls.setup_class_instance(root)
  576.         root.mainloop()
  577.  
  578.     @classmethod
  579.     def create_application_root(cls):
  580.         """Create and configure the main application window."""
  581.         root = Tk()
  582.         root.title(cls.TITLE)
  583.         # root.resizable(False, False)
  584.         root.minsize(275, 200)
  585.         root.option_add('*tearOff', FALSE)
  586.         return root
  587.  
  588.     @classmethod
  589.     def create_windows_bindings(cls, root):
  590.         """Change some global bindings to work like Microsoft products."""
  591.         root.bind_all('<Control-Key-a>', cls.handle_control_a)
  592.         root.bind_all('<Control-Key-A>', cls.handle_control_a)
  593.         root.bind_class('Text', '<Control-Key-/>', lambda event: 'break')
  594.  
  595.     @staticmethod
  596.     def handle_control_a(event):
  597.         """Treat Control-A as would be expected on a Windows system."""
  598.         widget = event.widget
  599.         if isinstance(widget, Entry):
  600.             widget.selection_range(0, END)
  601.             return 'break'
  602.         if isinstance(widget, Text):
  603.             widget.tag_add(SEL, 1.0, END + '-1c')
  604.             return 'break'
  605.  
  606.     @classmethod
  607.     def attach_window_icon(cls, root):
  608.         """Generate and use the icon in the window's corner."""
  609.         with Resource.load(cls.AT_ICON) as handle:
  610.             root.iconbitmap(handle)
  611.  
  612.     @classmethod
  613.     def setup_class_instance(cls, root):
  614.         """Build a SMTPClient instance that expects to be have size changes."""
  615.         widget = cls(root)
  616.         widget.grid(row=0, column=0, sticky=NSEW)
  617.         root.grid_rowconfigure(0, weight=1)
  618.         root.grid_columnconfigure(0, weight=1)
  619.  
  620.     def __init__(self, master=None, **kw):
  621.         """Initialize the SMTPClient instance and configure for operation."""
  622.         super().__init__(master, **kw)
  623.         self.__tk = self.capture_root()
  624.         self.create_bindings()
  625.         self.__to_entry = self.__subject_entry = self.__message_text = \
  626.             self.__quit_button = self.__send_button = self.__from_label = \
  627.             self.__to_label = self.__subject_label = self.__login_button = \
  628.             self.__grip = None
  629.         self.create_widgets()
  630.         self.configure_grid()
  631.         self.__data_handler = DataHandler()
  632.         self.__connector = self.after_idle(self.try_connect)
  633.  
  634.     def destroy(self):
  635.         """Cancel the connection system before closing."""
  636.         self.after_cancel(self.__connector)
  637.         super().destroy()
  638.  
  639.     def capture_root(self):
  640.         """Capture the rook (Tk instance) of this application."""
  641.         widget = self.master
  642.         while not isinstance(widget, Tk):
  643.             widget = widget.master
  644.         return widget
  645.  
  646.     def create_bindings(self):
  647.         """Bind the frame to any events that it will need to handle."""
  648.         self.__tk.bind('<Control-Key-h>', self.handle_help)
  649.         self.__tk.bind('<Control-Key-H>', self.handle_help)
  650.         self.__tk.bind('<Control-Key-l>', self.handle_login)
  651.         self.__tk.bind('<Control-Key-L>', self.handle_login)
  652.         self.__tk.bind('<Key>', self.handle_update)
  653.         self.__tk.bind('<Return>', self.handle_send)
  654.  
  655.     def create_widgets(self):
  656.         """Create all the widgets that will be placed in this frame."""
  657.         self.__to_entry = Entry(self)
  658.         self.__subject_entry = Entry(self)
  659.         self.__message_text = Text(self)
  660.         self.__quit_button = Button(
  661.             self,
  662.             text='Quit',
  663.             command=self.__tk.destroy
  664.         )
  665.         self.__send_button = Button(
  666.             self,
  667.             text='Send',
  668.             command=self.do_send,
  669.             state=DISABLED
  670.         )
  671.         self.__from_label = Label(self, text='From:')
  672.         self.__to_label = Label(self, text='To:')
  673.         self.__subject_label = Label(self, text='Subject:')
  674.         self.__login_button = Button(
  675.             self,
  676.             text='Login Before Sending',
  677.             command=self.do_login
  678.         )
  679.         self.__grip = Sizegrip(self)
  680.  
  681.     def configure_grid(self):
  682.         """Place all widgets on the grid in their respective locations."""
  683.         pad = dict(padx=5, pady=5)
  684.         self.__from_label.grid(row=0, column=0, **pad)
  685.         self.__login_button.grid(
  686.             row=0,
  687.             column=1,
  688.             columnspan=4,
  689.             sticky=EW,
  690.             **pad
  691.         )
  692.         self.__to_label.grid(row=1, column=0, **pad)
  693.         self.__to_entry.grid(row=1, column=1, columnspan=4, sticky=EW, **pad)
  694.         self.__subject_label.grid(row=2, column=0, **pad)
  695.         self.__subject_entry.grid(
  696.             row=2,
  697.             column=1,
  698.             columnspan=4,
  699.             sticky=EW,
  700.             **pad
  701.         )
  702.         self.__message_text.grid(
  703.             row=3,
  704.             column=0,
  705.             columnspan=5,
  706.             sticky=NSEW,
  707.             **pad
  708.         )
  709.         self.__quit_button.grid(row=4, column=2, **pad)
  710.         self.__send_button.grid(row=4, column=3, **pad)
  711.         self.__grip.grid(row=4, column=4, sticky=SE, **pad)
  712.         self.grid_rowconfigure(3, weight=1)
  713.         self.grid_columnconfigure(1, weight=1)
  714.  
  715.     @event_handler
  716.     def handle_help(self):
  717.         """Open the help window and show a message for the user."""
  718.         with Resource.load(self.HELP_TEXT) as text, \
  719.                 Resource.load(self.HELP_ICON) as icon:
  720.             TextViewer.load(self.__tk, text, icon)
  721.  
  722.     @event_handler
  723.     def handle_login(self):
  724.         """Decide if the user should be able to login to the server."""
  725.         if self.current_status == Status.login_button:
  726.             self.do_login()
  727.  
  728.     @event_handler
  729.     def handle_update(self):
  730.         """Decide if it should be possible to send an e-mail or not."""
  731.         to = self.__to_entry.get()
  732.         subject = self.__subject_entry.get()
  733.         message = self.__message_text.get(0.0, END)
  734.         self.__send_button['state'] = ACTIVE if self.is_target(to) \
  735.             and self.is_subject(subject) \
  736.             and self.is_message(message) \
  737.             and self.__data_handler.is_connected else DISABLED
  738.  
  739.     @classmethod
  740.     def is_target(cls, text):
  741.         """Determine if this is an acceptable e-mail address."""
  742.         return bool(re.search(cls.IS_TARGET, text))
  743.  
  744.     @classmethod
  745.     def is_subject(cls, text):
  746.         """Determine if this is an acceptable subject line."""
  747.         return bool(re.search(cls.IS_SUBJECT, text))
  748.  
  749.     @classmethod
  750.     def is_message(cls, text):
  751.         """Determine if this is an acceptable message to send."""
  752.         return bool(re.search(cls.IS_MESSAGE, text))
  753.  
  754.     @event_handler
  755.     def handle_send(self):
  756.         """Send only if the application is ready to do so."""
  757.         if self.__send_button['state'] == ACTIVE:
  758.             self.do_send()
  759.  
  760.     def do_send(self):
  761.         """Start a thread to send an e-mail in an asynchronous method."""
  762.         threading.Thread(target=self.send_thread).start()
  763.  
  764.     @threadbox.MetaBox.thread
  765.     def send_thread(self):
  766.         """Try to send an e-mail and display the results of the attempt."""
  767.         destination = self.__to_entry.get()
  768.         try:
  769.             self.__data_handler.send(
  770.                 destination,
  771.                 self.__subject_entry.get(),
  772.                 self.__message_text.get(0.0, END)
  773.             )
  774.         except:
  775.             tkinter.messagebox.showerror(
  776.                 'Error',
  777.                 'An exception has occurred.\n'
  778.                 'Continue for more details.',
  779.                 master=self
  780.             )
  781.             TextViewer(self.__tk, 'Traceback', traceback.format_exc())
  782.         else:
  783.             tkinter.messagebox.showinfo(
  784.                 'Success',
  785.                 'The message was sent successfully to {}.'.format(destination),
  786.                 master=self
  787.             )
  788.  
  789.     @property
  790.     def current_status(self):
  791.         """Find out what status the program currently is in."""
  792.         return (Status.connection_error
  793.                 if not self.__data_handler.is_connected else
  794.                 Status.login_button
  795.                 if not self.__data_handler.is_logged_in else
  796.                 Status.mail_label)
  797.  
  798.     def do_login(self):
  799.         """Open the login window and also the user to supply credentials."""
  800.         with Resource.load('login.ico') as icon:
  801.             LoginWindow(self.__tk, icon, self.__data_handler)
  802.         self.__login_button['text'] = self.__data_handler.username or ''
  803.         self.refresh()
  804.  
  805.     def try_connect(self):
  806.         """Repeatedly try to connect to the server every 0.6 seconds."""
  807.         if not self.__data_handler.is_connected:
  808.             self.__data_handler.try_connect(self.refresh)
  809.         self.__connector = self.after(600, self.try_connect)
  810.  
  811.     def refresh(self, is_connected=False):
  812.         """Let the user know if there is a connection to the server."""
  813.         if is_connected:
  814.             tkinter.messagebox.showinfo(
  815.                 'Server',
  816.                 'Your connection is live!',
  817.                 master=self
  818.             )
  819.  
  820.  
  821. class TextViewer(Toplevel):
  822.  
  823.     """Widget designed to show text in a window."""
  824.  
  825.     BACKGROUND = '#FFFFFF'
  826.     FOREGROUND = '#000000'
  827.     WIDTH = 800
  828.     HEIGHT = 600
  829.     X_OFFSET = 10
  830.     Y_OFFSET = 10
  831.  
  832.     @classmethod
  833.     def load(cls, parent, text_handle, icon_handle):
  834.         """Open a TextViewer with information loaded from a file."""
  835.         with text_handle.open('rt') as file:
  836.             title, *text = map(str.strip, file)
  837.         cls(parent, title, '\n'.join(text), icon_handle)
  838.  
  839.     def __init__(self, parent, title, text, icon_handle=None):
  840.         """Initializes the window for the reader to see its contents."""
  841.         super().__init__(parent, borderwidth=5)
  842.         if icon_handle is not None:
  843.             self.iconbitmap(icon_handle)
  844.         self.geometry('={}x{}+{}+{}'.format(
  845.             self.WIDTH,
  846.             self.HEIGHT,
  847.             parent.winfo_rootx() + self.X_OFFSET,
  848.             parent.winfo_rooty() + self.Y_OFFSET
  849.         ))
  850.         self.__frame_text = self.__frame_buttons = self.__okay_button = \
  851.             self.__scrollbar_view = self.__text_view = None
  852.         self.create_widgets()
  853.         self.configure_widgets()
  854.         self.title(title)
  855.         self.transient(parent)
  856.         self.grab_set()
  857.         self.protocol('WM_DELETE_WINDOW', self.okay)
  858.         self.__text_view.focus_set()
  859.         self.create_bindings()
  860.         self.__text_view.insert(0.0, text)
  861.         self.__text_view['state'] = DISABLED
  862.         self.wait_window()
  863.  
  864.     def create_widgets(self):
  865.         """Populates the window with the widgets that will be needed."""
  866.         self.__frame_text = Frame(self, relief=SUNKEN, height=700)
  867.         self.__frame_buttons = Frame(self)
  868.         self.__okay_button = Button(
  869.             self.__frame_buttons,
  870.             text='Close',
  871.             command=self.okay,
  872.             takefocus=FALSE
  873.         )
  874.         self.__scrollbar_view = Scrollbar(
  875.             self.__frame_text,
  876.             orient=VERTICAL,
  877.             takefocus=FALSE
  878.         )
  879.         self.__text_view = Text(
  880.             self.__frame_text,
  881.             wrap=WORD,
  882.             fg=self.FOREGROUND,
  883.             bg=self.BACKGROUND,
  884.             highlightthickness=0
  885.         )
  886.  
  887.     def configure_widgets(self):
  888.         """Put them in their proper places throughout the layout."""
  889.         self.__scrollbar_view['command'] = self.__text_view.yview
  890.         self.__text_view['yscrollcommand'] = self.__scrollbar_view.set
  891.         self.__okay_button.pack()
  892.         self.__scrollbar_view.pack(side=RIGHT, fill=Y)
  893.         self.__text_view.pack(side=LEFT, expand=TRUE, fill=BOTH)
  894.         self.__frame_buttons.pack(side=BOTTOM, fill=X)
  895.         self.__frame_text.pack(side=TOP, expand=TRUE, fill=BOTH)
  896.  
  897.     @event_handler
  898.     def okay(self):
  899.         """Close the window."""
  900.         self.destroy()
  901.  
  902.     def create_bindings(self):
  903.         """Allow the window to respond to certain events."""
  904.         self.bind('<Return>', self.okay)
  905.         self.bind('<Escape>', self.okay)
  906.  
  907.  
  908. class DataHandler:
  909.  
  910.     """Handler for communications with a SMTP server."""
  911.  
  912.     HOST = 'smtp.gmail.com'
  913.     PORT = 587
  914.  
  915.     __slots__ = (
  916.         '__is_logged_in',
  917.         '__is_connected',
  918.         '__server',
  919.         '__username',
  920.         '__password'
  921.     )
  922.  
  923.     def __init__(self):
  924.         """Initializes the DataHandler instance's various flags."""
  925.         self.__is_logged_in = False
  926.         self.__is_connected = self.__server = self.__username = \
  927.             self.__password = None
  928.         self.__cancel_connection()
  929.  
  930.     @property
  931.     def is_logged_in(self):
  932.         """Checks whether or not the instance believes it is logged in."""
  933.         return self.__is_logged_in
  934.  
  935.     @property
  936.     def is_connected(self):
  937.         """Checks whether or not the instance believes it is connected."""
  938.         return self.__is_connected
  939.  
  940.     @staticmethod
  941.     def __check_string(value, name):
  942.         """Verify that the string has a value type and value."""
  943.         if not isinstance(value, str):
  944.             raise TypeError('{} must be of type str'.format(name))
  945.         if not value:
  946.             raise ValueError('{} must not be an empty string'.format(name))
  947.  
  948.     def __get_username(self):
  949.         """Retrieves the value of the username."""
  950.         return self.__username
  951.  
  952.     def __set_username(self, text):
  953.         """Validates the username and sets its value."""
  954.         self.__check_string(text, 'username')
  955.         self.__username = text
  956.  
  957.     username = property(
  958.         __get_username,
  959.         __set_username,
  960.         None,
  961.         'Sets value of username.'
  962.     )
  963.  
  964.     def __set_password(self, text):
  965.         """Validates the password and sets its value."""
  966.         self.__check_string(text, 'password')
  967.         self.__password = text
  968.  
  969.     password = property(fset=__set_password, doc='Sets value of password.')
  970.  
  971.     def try_connect(self, callback=lambda status: None):
  972.         """Attempt to connect to the pre-configured server address."""
  973.         threading.Thread(target=self.__connect, args=(callback,)).start()
  974.  
  975.     def __connect(self, callback):
  976.         """Connect to the server in an asynchronous fashion."""
  977.         try:
  978.             self.__server = smtplib.SMTP(self.HOST, self.PORT, None, 1)
  979.         except (smtplib.SMTPException, socket.gaierror, socket.timeout):
  980.             self.__cancel_connection()
  981.         else:
  982.             self.__is_connected = True
  983.         callback(self.__is_connected)
  984.  
  985.     def send(self, destination, subject, message):
  986.         """Try to send an e-mail with the provided information."""
  987.         if self.__server is None:
  988.             raise RuntimeError('cannot send without a valid connection')
  989.         packet = email.mime.text.MIMEText(message)
  990.         packet['From'] = self.__username
  991.         packet['To'] = destination
  992.         packet['Subject'] = subject
  993.         self.__server.starttls()
  994.         self.__server.login(self.__username, self.__password)
  995.         self.__server.send_message(packet)
  996.         self.__finish()
  997.  
  998.     def __cancel_connection(self):
  999.         """Reset the is_connected and server attributes to default values."""
  1000.         self.__is_connected = False
  1001.         self.__server = None
  1002.  
  1003.     def validate_credentials(self):
  1004.         """Verify if the saved credentials are correct or not."""
  1005.         if self.__server is None:
  1006.             raise RuntimeError('cannot validate without a working connection')
  1007.         try:
  1008.             self.__server.starttls()
  1009.             self.__server.login(self.__username, self.__password)
  1010.         except smtplib.SMTPException:
  1011.             self.__is_logged_in = False
  1012.         else:
  1013.             self.__is_logged_in = True
  1014.         finally:
  1015.             self.__finish()
  1016.         return self.__is_logged_in
  1017.  
  1018.     def __finish(self):
  1019.         """Finish the conversation taking place with the server."""
  1020.         self.__server.close()
  1021.         self.__cancel_connection()
  1022.  
  1023.  
  1024. class Dialog(Toplevel):
  1025.  
  1026.     """Generic widget that should be used as a base class."""
  1027.  
  1028.     X_OFFSET = 50
  1029.     Y_OFFSET = 50
  1030.  
  1031.     def __init__(self, parent, title=None, icon_handle=None):
  1032.         """Initialize a Dialog window that takes focus away from the parent."""
  1033.         super().__init__(parent)
  1034.         self.withdraw()
  1035.         if icon_handle is not None:
  1036.             self.iconbitmap(icon_handle)
  1037.         if parent.winfo_viewable():
  1038.             self.transient(parent)
  1039.         if title:
  1040.             self.title(title)
  1041.         self.parent = parent
  1042.         self.result = None
  1043.         body = Frame(self)
  1044.         self.initial_focus = self.body(body)
  1045.         body.grid(sticky=NSEW, padx=5, pady=5)
  1046.         self.okay_button = self.cancel_button = None
  1047.         self.button_box()
  1048.         if not self.initial_focus:
  1049.             self.initial_focus = self
  1050.         self.protocol('WM_DELETE_WINDOW', self.cancel)
  1051.         parent = self.parent
  1052.         if parent is not None:
  1053.             self.geometry('+{}+{}'.format(
  1054.                 parent.winfo_rootx() + self.X_OFFSET,
  1055.                 parent.winfo_rooty() + self.Y_OFFSET
  1056.             ))
  1057.         self.deiconify()
  1058.         self.initial_focus.focus_set()
  1059.         try:
  1060.             self.wait_visibility()
  1061.         except tkinter.TclError:
  1062.             pass
  1063.         else:
  1064.             self.grab_set()
  1065.             self.wait_window(self)
  1066.  
  1067.     def destroy(self):
  1068.         """Destruct the Dialog window."""
  1069.         self.initial_focus = None
  1070.         super().destroy()
  1071.  
  1072.     def body(self, master):
  1073.         """Create the body of this Dialog window."""
  1074.         pass
  1075.  
  1076.     def button_box(self):
  1077.         """Create the standard buttons and Dialog bindings."""
  1078.         box = Frame(self)
  1079.         self.okay_button = Button(
  1080.             box,
  1081.             text='Okay',
  1082.             width=10,
  1083.             command=self.okay,
  1084.             default=ACTIVE
  1085.         )
  1086.         self.okay_button.grid(row=0, column=0, padx=5, pady=5)
  1087.         self.cancel_button = Button(
  1088.             box,
  1089.             text='Cancel',
  1090.             width=10,
  1091.             command=self.cancel
  1092.         )
  1093.         self.cancel_button.grid(row=0, column=1, padx=5, pady=5)
  1094.         self.bind('<Return>', self.okay)
  1095.         self.bind('<Escape>', self.cancel)
  1096.         box.grid()
  1097.  
  1098.     @event_handler
  1099.     def okay(self):
  1100.         """Validate and apply the changes made by this Dialog."""
  1101.         if self.validate():
  1102.             self.withdraw()
  1103.             self.update_idletasks()
  1104.             try:
  1105.                 self.apply()
  1106.             finally:
  1107.                 self.cancel()
  1108.         else:
  1109.             self.initial_focus.focus_set()
  1110.  
  1111.     @event_handler
  1112.     def cancel(self):
  1113.         """Close the Dialog window and return to its parent."""
  1114.         if self.parent is not None:
  1115.             self.parent.focus_set()
  1116.         self.destroy()
  1117.  
  1118.     def validate(self):
  1119.         """Verify that the Dialog is in a valid state."""
  1120.         return True
  1121.  
  1122.     @staticmethod
  1123.     def apply():
  1124.         """Make any changes the Dialog wishes to accomplish."""
  1125.         pass
  1126.  
  1127.  
  1128. class LoginWindow(Dialog):
  1129.  
  1130.     """Widget to allow easy process for supplying credentials."""
  1131.  
  1132.     TITLE = 'E-mail Login'
  1133.     WIDTH = 45
  1134.     MASK = '*'
  1135.  
  1136.     def __init__(self, parent, icon_handle, data_handler):
  1137.         """Initialize the dialog with the necessary information."""
  1138.         self.__data_handler = data_handler
  1139.         super().__init__(parent, self.TITLE, icon_handle)
  1140.         self.__username_label = self.__password_label = self.__username = \
  1141.             self.__password = None
  1142.  
  1143.     def body(self, master):
  1144.         """Create all the different widgets needed for the body."""
  1145.         self.__username_label = Label(master, text='Gmail Address:')
  1146.         self.__password_label = Label(master, text='Password:')
  1147.         self.__username = Entry(master, width=self.WIDTH)
  1148.         self.__password = Entry(master, width=self.WIDTH, show=self.MASK)
  1149.         self.__username_label.grid(row=0, column=0, padx=5, pady=5, sticky=E)
  1150.         self.__username.grid(row=0, column=1, padx=5, pady=5, sticky=EW)
  1151.         self.__password_label.grid(row=1, column=0, padx=5, pady=5, sticky=E)
  1152.         self.__password.grid(row=1, column=1, padx=5, pady=5, sticky=EW)
  1153.         self.bind('<Key>', self.refresh)
  1154.         return self.__username
  1155.  
  1156.     def button_box(self):
  1157.         """Create the button box and change okay button's options."""
  1158.         super().button_box()
  1159.         self.okay_button.configure(state=DISABLED, text='Login')
  1160.  
  1161.     @event_handler
  1162.     def refresh(self):
  1163.         """Perform a soft validation for the username and password."""
  1164.         username = self.__username.get()
  1165.         password = self.__password.get()
  1166.         valid = re.search(r'\A(\w|\.)+@gmail\.com\Z', username) is not None \
  1167.             and len(password) > 3
  1168.         self.okay_button['state'] = ACTIVE if valid else DISABLED
  1169.  
  1170.     def validate(self):
  1171.         """Attempt to validate username and password with the server."""
  1172.         self.__data_handler.username = self.__username.get()
  1173.         self.__data_handler.password = self.__password.get()
  1174.         valid = self.__data_handler.validate_credentials()
  1175.         if valid:
  1176.             tkinter.messagebox.showinfo(
  1177.                 'Login Success',
  1178.                 'The credentials were accepted.\n'
  1179.                 'You are now logged in!',
  1180.                 master=self
  1181.             )
  1182.         else:
  1183.             self.__password.delete(0, END)
  1184.             tkinter.messagebox.showerror(
  1185.                 'Login Error',
  1186.                 'This username/password combination was not accepted.\n'
  1187.                 'Please try again.',
  1188.                 master=self
  1189.             )
  1190.         return valid
  1191.  
  1192.  
  1193. if __name__ == '__main__':
  1194.     SMTPClient.main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement