From 879aa7876216640bc814aa77f6ebbb81d59c2c25 Mon Sep 17 00:00:00 2001 From: Michael Tremer Date: Thu, 26 Mar 2009 20:46:44 +0100 Subject: [PATCH] Updated website engine. --- www/banners.json | 5 + www/data/404.xml | 0 www/data/500.xml | 0 www/data/feeds/main-en.rss | 170 --- www/data/ipfire-press-release-beta-1.odt | Bin 229923 -> 0 bytes www/data/ipfire-press-release-beta-1.pdf | Bin 105677 -> 0 bytes www/data/links.xml | 140 --- www/data/news.xml | 81 -- www/favicon.ico | Bin 5430 -> 0 bytes www/footer.inc | 17 - www/images/sponsors/isp42.png | Bin 0 -> 7547 bytes www/index.py | 40 - www/ipfire.py | 293 +---- www/menu.json | 14 + www/menu.xml | 26 - www/news.json | 56 + www/pages/static/__init__.py | 97 ++ www/{data => pages/static}/development.xml | 0 www/{data => pages/static}/download.xml | 2 + www/{data => pages/static}/features.xml | 0 www/{data => pages/static}/imprint.xml | 0 www/{data => pages/static}/index.xml | 13 +- www/pages/torrent/__init__.py | 85 ++ www/pages/torrent/client/ConfigDir.py | 401 +++++++ www/pages/torrent/client/ConfigReader.py | 1068 +++++++++++++++++ www/pages/torrent/client/ConnChoice.py | 31 + www/pages/torrent/client/CreateIcons.py | 105 ++ .../torrent/client/CurrentRateMeasure.py | 37 + www/pages/torrent/client/HTTPHandler.py | 167 +++ www/pages/torrent/client/PSYCO.py | 5 + www/pages/torrent/client/RateLimiter.py | 153 +++ www/pages/torrent/client/RateMeasure.py | 75 ++ www/pages/torrent/client/RawServer.py | 195 +++ www/pages/torrent/client/ServerPortHandler.py | 188 +++ www/pages/torrent/client/SocketHandler.py | 375 ++++++ www/pages/torrent/client/__init__.py | 63 + www/pages/torrent/client/bencode.py | 319 +++++ www/pages/torrent/client/bitfield.py | 162 +++ www/pages/torrent/client/clock.py | 27 + www/pages/torrent/client/download_bt1.py | 882 ++++++++++++++ www/pages/torrent/client/inifile.py | 169 +++ www/pages/torrent/client/iprangeparse.py | 194 +++ www/pages/torrent/client/launchmanycore.py | 381 ++++++ www/pages/torrent/client/natpunch.py | 254 ++++ www/pages/torrent/client/parseargs.py | 137 +++ www/pages/torrent/client/parsedir.py | 150 +++ www/pages/torrent/client/piecebuffer.py | 86 ++ www/pages/torrent/client/selectpoll.py | 109 ++ www/pages/torrent/client/subnetparse.py | 218 ++++ www/pages/torrent/client/torrentlistparse.py | 38 + www/pages/torrent/client/zurllib.py | 100 ++ www/redirect.py | 30 + www/robots.txt | 2 - www/{header.inc => template.inc} | 60 +- www/web/__init__.py | 229 ++++ www/web/http.py | 28 + 56 files changed, 6704 insertions(+), 773 deletions(-) create mode 100644 www/banners.json delete mode 100644 www/data/404.xml delete mode 100644 www/data/500.xml delete mode 100644 www/data/feeds/main-en.rss delete mode 100644 www/data/ipfire-press-release-beta-1.odt delete mode 100644 www/data/ipfire-press-release-beta-1.pdf delete mode 100644 www/data/links.xml delete mode 100644 www/data/news.xml delete mode 100644 www/favicon.ico delete mode 100644 www/footer.inc create mode 100644 www/images/sponsors/isp42.png delete mode 100755 www/index.py mode change 100755 => 100644 www/ipfire.py create mode 100644 www/menu.json delete mode 100644 www/menu.xml create mode 100644 www/news.json create mode 100644 www/pages/static/__init__.py rename www/{data => pages/static}/development.xml (100%) rename www/{data => pages/static}/download.xml (99%) rename www/{data => pages/static}/features.xml (100%) rename www/{data => pages/static}/imprint.xml (100%) rename www/{data => pages/static}/index.xml (93%) create mode 100644 www/pages/torrent/__init__.py create mode 100644 www/pages/torrent/client/ConfigDir.py create mode 100644 www/pages/torrent/client/ConfigReader.py create mode 100644 www/pages/torrent/client/ConnChoice.py create mode 100644 www/pages/torrent/client/CreateIcons.py create mode 100644 www/pages/torrent/client/CurrentRateMeasure.py create mode 100644 www/pages/torrent/client/HTTPHandler.py create mode 100644 www/pages/torrent/client/PSYCO.py create mode 100644 www/pages/torrent/client/RateLimiter.py create mode 100644 www/pages/torrent/client/RateMeasure.py create mode 100644 www/pages/torrent/client/RawServer.py create mode 100644 www/pages/torrent/client/ServerPortHandler.py create mode 100644 www/pages/torrent/client/SocketHandler.py create mode 100644 www/pages/torrent/client/__init__.py create mode 100644 www/pages/torrent/client/bencode.py create mode 100644 www/pages/torrent/client/bitfield.py create mode 100644 www/pages/torrent/client/clock.py create mode 100644 www/pages/torrent/client/download_bt1.py create mode 100644 www/pages/torrent/client/inifile.py create mode 100644 www/pages/torrent/client/iprangeparse.py create mode 100644 www/pages/torrent/client/launchmanycore.py create mode 100644 www/pages/torrent/client/natpunch.py create mode 100644 www/pages/torrent/client/parseargs.py create mode 100644 www/pages/torrent/client/parsedir.py create mode 100644 www/pages/torrent/client/piecebuffer.py create mode 100644 www/pages/torrent/client/selectpoll.py create mode 100644 www/pages/torrent/client/subnetparse.py create mode 100644 www/pages/torrent/client/torrentlistparse.py create mode 100644 www/pages/torrent/client/zurllib.py create mode 100755 www/redirect.py delete mode 100644 www/robots.txt rename www/{header.inc => template.inc} (71%) create mode 100644 www/web/__init__.py create mode 100644 www/web/http.py diff --git a/www/banners.json b/www/banners.json new file mode 100644 index 00000000..610f25f9 --- /dev/null +++ b/www/banners.json @@ -0,0 +1,5 @@ +{ + "01" : { "uri" : "/images/sponsors/isp42.png", + "title" : "Hosting-Sponsor", + "link" : "http://www.isp42.de/" } +} diff --git a/www/data/404.xml b/www/data/404.xml deleted file mode 100644 index e69de29b..00000000 diff --git a/www/data/500.xml b/www/data/500.xml deleted file mode 100644 index e69de29b..00000000 diff --git a/www/data/feeds/main-en.rss b/www/data/feeds/main-en.rss deleted file mode 100644 index 42429ea4..00000000 --- a/www/data/feeds/main-en.rss +++ /dev/null @@ -1,170 +0,0 @@ - - - - - IPFire.org - News - http://www.ipfire.org/ - Kurze Beschreibung des Feeds - en - Michael Tremer - Thu, 8 Nov 2007 00:00:00 +0200 - - - - IPFire 2.3 Beta 5 - http://www.ipfire.org/#news - ms@ipfire.org (Michael Tremer) - http://www.ipfire.org/#news-10 - Mon, 13 Oct 2008 19:00:00 +0200 - - Dear Community!
- This day, we released the fifth beta version of IPFire 2.3. -
- Further information and a discussion about that is to find in the forum: - Click! -
- We hope that many of you will install this new version and give some feedback. -
-
- Michael for the team of IPFire - ]]> -
-
- - - Presentation of our project on kbarthel.de - http://www.ipfire.org/#news - ms@ipfire.org (Michael Tremer) - http://www.ipfire.org/#news-07 - Thu, 22 Aug 2008 10:00:00 +0200 - - Dear Community!
- Kim Barthel published on his blog a text about the project ipfire itself. -
- You may view the full (german, sorry) article on - blog.kbarthel.de! -
-
- Thank you, Kim! -
-
- Michael - ]]> -
-
- - - IPFire 2.3 Beta 3 - http://www.ipfire.org/#news - ms@ipfire.org (Michael Tremer) - http://www.ipfire.org/#news-06 - Thu, 20 Aug 2008 19:00:00 +0200 - - Dear Community!
- This day, we released the third beta version of IPFire 2.3. -
- Further information and a discussion about that is to find in the forum: - Click! -
- We hope that many of you will install this new version and give some feedback. -
-
- Michael for the team of IPFire - ]]> -
-
- - - Core Update 16 - http://www.ipfire.org/#news - ms@ipfire.org (Michael Tremer) - http://www.ipfire.org/#news-05 - Thu, 16 Aug 2008 15:00:00 +0200 - - Hello everybody,
- today we are going to release Core Update number 16, the following changes were made: -
- - Fixed Squid init script showing allready started during boot
- - Fixed LineQualitiy Graph not working for some gateways not responding to ping request
- - Fixed Outgoing FW Logging when using Mode 1
- - Fixed Urlfilter autoupdate url has changed
- - Fixed redirect wrapper not working as expected
- - Fixed smaller CGI issues - for detailed informations see git
- - Updated ntfs-3g to current stable - ]]> -
-
- - - Article on www.linux-luenen.de - http://www.ipfire.org/#news - ms@ipfire.org (Michael Tremer) - http://www.ipfire.org/#news-04 - Thu, 30 Jul 2008 18:00:00 +0200 - - to the article - (german) - ]]> - - - - - Core Update 15 - http://www.ipfire.org/#news - ms@ipfire.org (Michael Tremer) - http://www.ipfire.org/#news-03 - Thu, 24 Jul 2008 15:00:00 +0200 - - Read this for more information. - Please install this update as soon as possible. - ]]> - - - - - IPFire's first rss feed - http://www.ipfire.org/#news - ms@ipfire.org (Michael Tremer) - http://www.ipfire.org/#news-02 - Thu, 24 Jul 2008 12:00:00 +0200 - - - - - - - IPFire 2.1 is out - http://www.ipfire.org/#news - ms@ipfire.org (Michael Tremer) - http://www.ipfire.org/#news-01 - Thu, 8 Nov 2007 12:00:00 +0200 - - - - - -
- -
diff --git a/www/data/ipfire-press-release-beta-1.odt b/www/data/ipfire-press-release-beta-1.odt deleted file mode 100644 index eb1e2bfbde0e14ccf8e90223c47b12fe8db0c933..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc-jL100001 literal 229923 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

N3>C8LD7YvFZPyWMcC<{xE^_@Yc>NhO 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%0A?(i7HGE7BG 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

FUZP&ouOPk!B7ggm0`9Nmtf0MPc=c}6 zCOBmn10i$)i7!|bnw!rXzfktNDP-9b;x53&M! zUQR8Q_SLJ5YMf7Ow@6+gWWXoO%@)jRT1i;#4+-JSqyTk3Ii-z1cb4`*!>#0 z#oN#Cm4!1P3}%+9Y(IMK7gurKyPy#4yyaE#tRYL)`|^nHYHTJPf9n?!8&gEC&mZ3`RJ2nsIcA283dylimMA9-|Ou?-UW>uPyB;?R3-hH0!F#DyMV$Y!otFLP~u`0 z6(?A$FNsY=DCYzNCWwCg$klRF|>xAI{`X9Yh!%K00jv z9xE^Rp-u%jpakfu1Z4$D^piR&VzjQ}SIvvqy8jIPw?SqqhYAO3sm~XK>d;- zJki)b*xU<~5D*rZvdEEfI27{t&_aCnet&0latf&-;^9ZlQfNm>&I3q962~J(p_-)I zALR36cnEqE+CKj4V6G!81iYMX#UK+F4i^?GJ+?bB&c@hjqGzx>w;?Fp+7?mD=_}DF z>lS_|AM_1A;B0*y?&^Mif9K!Mz&x_4or{B2pkGUSapg(i&i31;n%p|wvQn+l_FqYh z#TbW8L2A5w&vue3Ri^(rqS%(HTk9z5L8KBClwEEE0ZxrOyjF&-ho=B95F zMQ)~<*GfMPiC`B*DUTK^rNn1$c6M%V2KM&{26kRP9{yYnpPBkQ80k4rUe7h0l|1G6 zm%g%4Eb)53#($~5Aw5K*MBvQOM8NZDQ^NEiPqkr?-a)%VMFPanj@MV*lICP&8R_b8 zt_cn%Wg7MsK^`YDX`-{_Mg~3I{cZeR2-aq2Rz^1_CkMBtmfpm56s(NReK3j1u*uzk z>`c73#%^93V{=OjC(sBO;=}po80|VI;a%iu)}u0T#;-5PNS)spepXd_2bg*JyZm&(h#4>zoWUgx3#mix3#wyxkJxb&sg8sI4d{z?6!ubha{};W`FOX|d4o81FtthXM1kfJlUl2^Kt?b;K!S0R?_2t#owcQ@3j(*N|Zh?L!wOy1{ zYl}0JTT_FhGZV{Xn0xU6m2uXJz_*IZ!n!U-1}5h5+0E&RiS^~<|b5(*NqaD;^Mr;D4<`5Jv zQBqDk@4*i$IhnslybQ|h%$&I2AJ~~$ISJ4DO?Qm9{Co{-q0y{i0b9Gg?iNfAPLGR= zRhnY0t#NX6biB^3|0s~8u{bQ2gsu1Q5)@k71a@`!zat<-$k3G?;jXp-*sk}9NP`b1 zUoItCvEA)LLzdSoCni2I-Z!%XLib87_3g|Q472j8?HxMA`CO$KD7RY=ZQP%={U;qSy2uuaO^3zx(2iL*oZ~p9vD{Z2)DMIlx#t_Hj zKtg0$v>1~7E0W6=PrjnH^JPX(Nm;QoTSIekLqu3;Vb@qEhxq2u(yXSwy4=Oq5{9U$ z0Zw^lU|?cmV*c~TqvLXmv(wwZ%lgocM{~K$S6%EU&M~DvW zw7EXNrD~vMq;8mzPE3FYpQEjB=Aj%-xM@;fP#0_!zaGA-z*Ed)cAX?gi*~RuYhtB zEI>hkg&he1D9N(aIo)1gI(^s8aoe0--q1BcERB;mhY5^Tny#f3)ILe;k9G_qzw5-p zCCW_*;HT*>=y>?50or|oF^%vATPY9BI(((YZm7(dUhg~UiazLx-I{7?DOuxaspm(c z=C@c-xG?DI*1wcrEQYN&=HwK&`+P3Xp#HNq2cV74d;(}=VPkaXn3yAHvk{reE>rMT z{+9!>!S8T}^i@tcA&M7!mMjrt9CNIExe>?dXMSzGqmr_cVcJocp?gdyWfLL~eAG(KorY;VLdp^rJwBr4)Hzn4VyDXJZN1hl; zQ?<7iXl? z0?lRqPunSjnYFeks;;f9EG#U9@5wH#ER73^iK#{n`lOYtrlW-WWbV>c&%r}h_{vT2 zL}%)>1$7AW;mmqUJA=;mohyYjDoQI=dNXg|JvBXJ^`}~cGx6^(-UOR0Wo6cBU>%n& z18!_+aq-tC!FX^+=x|BOQq5M;Z#)cx zF2phGG(K+?WL$bs5RUgqd%SvZ$=hn9C1req0*zo-1OGH5Tz zi2d{9YAFNr3kcPMj18TaS4%uTJgqLOGY*z@=j{zKLRuOGl9n17EiXAu*Wzt?LZ6(a|WTAO(yH7!no_ZVwiJ9Tf@+mtB%G11!1BS7s96@6ke2Ablbs8USNu9gZ51 zJYeZTr{okpIZzGta(pLn7vyPGQ(VWvxwbt@SPHGHsvv{VREiC50=t7fJYMh7gKM>Tr#V*jb~lD#z-xx0SNZtJ0<6rb0|yT?;jK(- zzIOd75wliF9Kc5!CoSLmv+6VieS7A~$;m3>jqsJaowMw(|BTt&PQr!sgGjB$A!TiS zm8sWw+!nCkBrQA~fFKaTm)(NMqpb(T>Ftn3)aq`K4|XcxEy!bET9`ggstiT}8)fE{ zp6RX$Gs=e)1?=M6(o6qEbZuxw6C^3EhulzHXJzVqdqZ+|i5{9$2`#Uoq@|}PP4prB zO~b+9@>jT@kkH4db(+o<>8B8yCqMqW-1G}C*IoguC`VZeT@i)pXb7_U03#=V^j%vJ zsNjj;oyWt$Apr2&y9Jgsv&i>P-AAN!B(?OA#r3o_W|uZTcXvc}acd`M0!r0fNzkvw zB~C8HqJAlgQuxqzteT6CT_lLYml}KfTZ&A*tu2v(LpfY}SX(=+{2{AEWfhePO`jnt zH|-CYfDfEtvtV$S$fj2EH(>wBhLpd{3t0$a0cMX=ORnBX+4Vs zdXT3D^Rm$!15Ig?vC{7BDnHL?M?@&N){%!V88zBs8EwBwW!TjWYYf8Ne(k zyJWTpwiaSe4%vd@*S`pYp(|BLXJ=xnu5cj9(BA}ZZ0cqb4r<~G2;Zq`@lvkW)ahnj zz+#mHmRdj2dC2=S@+fB^qE@6B=TW>VH|Ky!Qz&EzItq|1C$hdRC_u+0Fy0Rvb}bxF zAQJzG>_E+)+rhu7m$r9`^q>^AZTLe~U0n@U#@yM;$;sKg0QI<~DU=M|Ddd;7w)LpE zPjzLbT_KtxE1Agryq)hcdhK7yMxriiAC9GATI|4nUc>ett9& zdAxsglcGG^Yah#Uclk=e%6%l=6i8xbW@a#?EQ^+H zfxGkInLEpXQ*M9c zyiLOii)0mkdVP87dMd$+&r28wQa~i7#pvT|A~cANWFoK@k|HH_#b^@?{H;I3Aqw3( z^K)B3VZr8(rZz8EPqg}iBt|%DYi|o@M_YF@#D<}LWZdX$WZJ2m))WyUVkV({GAIlg zIZzDjbEa+iuP)-*_g3yh=p;P@BO@{@7}p4nvdU@~D7dyR+P`XxEBwu`XmV4mHO`Kg zM5NhyChNMp8)BlvIeDb{U+E_@W4|aiJ4?=>xml3m? zobUck+DASInQT0lFsG{8S2V;f5!##2t#4-!Gl`j6P*Y}QyMI7}pRcECytuO{Cd9|h z{j;H?p{J=wK#U(KBD%Kyjx@*D97V+~h%yEaZ@%}jseaxwj&BZ&OwupWRR@mVOnay8 zN^g=Bi<$``5g=*KbR@1NM|TY`=T^e;I?*@~>=u+f6-`D=UvK~YHe`WgQG9qvGc*L- z+q+$>q|~}B&aUWWIxcI_S|k$(l?fAeRTk=PyEjijs!uD0ErG>aA1s{c+90%Hh7PnW1H?QO0ut}gB^?QHyKrljO$ zrDtcSr)2RuOdLLSfQ);T1Kmj47ZfpNjwg3g=kXp;hq*oo1CruoTDxBVmo;Uo!%gl=$dvYI>Ngy4c97s%lCq^7r-C(_3Hv9HaHH zV=pp`>an9{H4PL>wyQb!Js4zNB7}fDA4B#CHi1W<06@%-T@x}VCdg|m_ncqwx$r*h zpC?kY$j>hM*g;N$~JvUtYuS9{O%5u<;WU^fZ?R8Un<|DL z+{V`ieqkry`De!IeVfYv#CnyuhT=X0&2Z=@z{(dfWCOdo@4D_uoEjS28$o8>@9Bz| zy(rS!skWo-*Y9Z@Oo}X$KC?Ezz}?j3asPDp@Zj$4?a|cS*uv56Xzu7}X>RZAbbAp4 zQJi3}y~$dqa>E6=oCcK*SuYT=;)hHhG_HFJg^5$n@5^9NqOU0ue>SXJM@eQc+qC)uJUmHy+&x?S9EUZca!ne zVE3&lEVVSOGc&3Yx<{s{GT3v-q3}tmhO{0&bgZ=Gcs0%uMyo(q=nFIlnP|2QorbL@ zb0*-l!5rfi;~tkCXGS300vUt<1VpTG1J{}HQ&;&XsZs+tXn~spA#Y06grKyv64F9j zRZ&&tV0%uOx!(OdA`~=Is`KiU&?zx8Q2`f3W=rWq9V|twF(LJ5;^yTAI(+J=F-WLD z{ca4XkW9wgsn+)*Bd+|DUQo6XB>9Qb>hfaP%CGiFNiZQ*UG$T8O}W`Brj#Gr>Uy^b ziN$MXbkJAECmeDDqzICfnD-rvL({2YrL{C2wUO|wQIYEg`6Q04@_oL`>MDP=$TqpJ z`iIoLl$6e93YK%Uj#6ggJEk2Cg||e6owwM}j2T?vC8XDaF=M_;NKzd@K2v|ZiZTSy zmPS9ukp6n7Nt`RsaL9pZ5MyDqtjEcJWo(2Ku;r8BK!aT^s1lY!TQ2!gmIj7Ez<`@} zcc6BpW3V|cAq*wTnj#9mGrhppuE^S^E-A&?b-uCKXySfZPV&%o>Vz@nnsb){v2A5f zn+34Gr?MhPZ@QTx;?z^dIxWpQosAS`QC~-yXs|i-w5XeZty`HUhq60*rG14zt<2AW zycKQMi2~;e|;IG%WpW^x?$|{fi(!jT({u`#u z?YFjxT{o)b-$Ynk+;Zu9)WA$cn72gmx$Sq(u@}7@rs}FZ zukbV7A`c;g_(&ehizWCkg#Et@NKpd6TluxLf0|`=ac!S43m-L?=+8bEb2Dc%`+$n# zLY_x(P)u0g$t43Ye+;sqCjbLIz67jv_Vh(mF?aVZLN#^{NiGviNzQ$b>T5e0{Q#k3 zFuNxY{rPmNXcGT$nX&K>Arj^ry4|NHPU&yKmZy*Bf*Pxn7vVF7@A2Wl1g8X3nNG+>=DD3*4<)HC;b^H1(>PXie{3kqAy%d2}En<#k~W=G^rerv>U_u&T4 z{3|ZAInGvH+2a4{8*0_y12ZsE*N*Iq_GkaW$6LeK*Ui|?`QNrrOL<6buD`0g+jTK0 z&I=Fz{COP-;(6~msBz(SclhwwXLd`#1S&qsEK`4 z_Pbd7#)N5s?VRgxx=HE81Xv_w@V715#6*M!-4V1Ur^l8%N=VNk7R4=nvUsP*>uU4j zM4P@_ij%cYL|MDQk><5OF*RFojvx1v=*tf_&B^o5KtdNtk(96WtrLkGokHl<4u8$6mgcMS9JaPapIk55ic zOzmpQ;SqgwyMJ$Y;bHGx-q?OdK&8`5Q$#RD2G9>~#73tLO}U;7bKJA(T9Sws}tX zY*ZZ6%>c)&K-rP8B@WgNJskO$^=Hl0g#bu`%FT^)RFxH7)zwxQRvS|+LOG@i(zs5Z zUNpf1ZiJ^h)gegjFjYB`SH7-g7}5d6sK!h zrU zNV_L=Pg@l%^R&|Hl+o!_wyFvVF**r+-@BwjJm30^{5-AT2Nb!f`pdix0s4hK`-hl{ z&Efd40Qx|ex@G~U{*g&p6%t6Vh9D~Ecw$pY(|=xxA#=rLg<%WaZJrNtQS?=jX-h!A44xCKSMizfe0Ri%ZZ+oC2+WrstR{ zHXdKjM5M9u<+k$ezCi99gXQ8j9upLtA15Smq9NHWnH)%!#Li`TduzCe zfi?xcZNIk_*}9O>q*#VdHiI7yicjjpXfT7HSbLMJBUU*z@oyz=POydoY+TBU1vY+d zU_$zQX%u@fRe3I;{4QkLTs}m1cD-mOitlWr;OQ z6i>0;32lyWwdZ>Mre{<6lFAA?vMgS@%X%;h zty>V^7PWrwhcg_fqL^+yh6fTke6zX^tw*P`!b68ey)Ef#6hS#Hx(*AuitK1PEUhb& zC9q&)iUm$GXMLRL6#qhKYp6j@e~2_2loZ=0WR33w>ls|E1@qVQcupHi#0cqddj5m& zc7sP0L>5FFT+ua(jz1>o5qZXg%A2!4)9VP`aWD`raf2hcj0MHk(3&T`*kpOkff0xo zMgf}e`duf^Sm$ib-I_CDnNFU3v06=4NdXOfs^qu6dKWMlITvMiru*c1%fh`12Y2lH zE&#*CsX{aFGX2}_rd5q(A+YK9>{16G>t9cRya7;nsxj1~?*j^z2-?E^sL!{m_{#7U zI^pqXcp8P-NdNetKrAKb&GNggPzaIExrPnyt&lX8!Ddp6j% zZX}G8xdF!N^2l>$!H)?w1mya(%z;<6RJvf|EnDNG;#@q{HjjkzAXkb%(!Wudgj!rR zUqLSnQ95Jb8S|&r_0wc>n;q;CXsq$|M%fG=yPD5SpU(;!bTj{e-C!KkNd0O}tUd-w zE9{gM4{+Pu;tQU}iyDSW6#s|Om>&l;?{JR;bN#oM7IvcJS^L2YJp{??Z2m5x_j?Nv zJcUQ8(RBP@$!TU7)p(=@J36%wFkj-RMiWhFAdg-&Uvis1gzL@` zf9CAy;3bocH1|uU(ag#CZ?9_Za(dDH=Gnp<47_)fl@uXx%ky;3-y{VCdcHK0ckHrIV#EMDEBgN-?YCIh~#t@h4ma(Mt4N$a#+D9aBH&LM1ypA zm_AqaW4P^#E@qLI#6;~syS-ru=z7i>-n5|AV3U@G0s!%dIuWjCAcbD(0YI1f7~o(- zc(5s3Mkl@jZgBqJ=*Nj@Fx_k$4Hj%ej#x2phzcc0GkyR_uKF3-|ps49KGv^pvx zFPcXnh#_M0mj|lCCS2JcdHY6713cNU>yGquUjq^(RaMfve+?SNmME!*q<|z>m*&MP zOTO7Vef#F+=I9yVht4wA2D^@hA{D@%t!%@QP%2BxWo_3%Juo7>v7olMvD6al7*G_G z6;5A9FXIpF@*C4`KTh*KxnDHc@1f6kht%Y;+=uZ6K0IxF6kN)PdX ze}A)@injjr40i2ElNtoZ*Fyt?NZE7cht1@8&ena<2LE|F1eU~9KKb(LPII<+{mk@v zb(S!kx*o2HzTxFEfi$ZTf_*-7)ze#sxWzz&q=+7cUjiNsaC?`BC?CqzsVn_@>+hWH zPZEBZq2ynPFXzJV*DOJLjPcX7W*8XCcba_TAfnLRsFgowmwe&st2O>@oHJ7!JM#nc z^Aj{2jeZZIH0@@HGX?l49jwgzmM*7IDpvO$q?7xG25v3&$(eOe$3V)viVBBwBErlJ zW4>nhr{2XDdHkMpo3~}?rlC`=iM5dH>1_?_c?) z3M63*{H++{*J75FZe*ZAK#b>S!ff+WEa5ym>b-~~e)aq>s4*OF%6jG$vZ+Bl@n!u?fLBd4Jw}cGFDy)?3@dl zPTcMfSjcT*J#_F4&UlUub&a;o?5uQcxq_Iw1MlP*nH;j7nM-d&{+ zf$Jd`RR=w9BrA-SOAE8&a?9g~I|AWst4Ea0Zl-h*emu*DdC94%$$8kPx8|&2$Frw} zKbn4Sdh{opRu?BBNUmKE6q3!hrBRuKDz};V;?>ZzJG-MM!+Dv62xgnuKn{=vOKXi#fhqXKw2}JtSIfZ%7*3Brx6!Gcvzv$iggL|E7p+I^i2|(#4VW?U%UR zFHwKoU-*-a#{Mvz3p1mqN!}k1E?pVtc9n99AV10YXgAwSpXjjES(vEvfNfL^WAY0~rUl_kSO-cxPY0~rw8j3aKKI`%v{t&ygp|~udDmw3&?(>l zaz!zu{D&ooJxzat`esP}&AWnU{+LT%au5<-)v{1bvD>TklhVN-ORN9%vEylReblj~ zHZjd|KD#O_D+PZu_MwbJ@!tq0{2e%m}pp$$eO zNexN1N=uL+II2H1ossGdW@5*|mllzeoGM+8DzPGnjQ!d3TMZ}bwXMCasiU)(9_U$# z`<|NdT``}7P!T_7!m@GQP;^U-x&9sztT>NENE$V8e6MFY^`fYu-OyZ`*%_Bx+UMCu zV0gYGNS8Q4OO4f2dDeeM$GvfsNZt>~jusIb&=4@B#IR)`;bvQJ-y*xpHN1)~(ZD~` zV!t83BBLO?rXVFdM8c&fV2@0d6d$8EO^(L^L*&pt`|DKozI!pgX{gTs&8Is{mH}AR z-d&!qt*wK9N|2`A{-TV;gRf$YsDupv3Ej~XK-zGSTH05JD`nBCSTtn71CLI+JUFE; zt?l&{1MLap=@}iO^ySu!(OnLsrWv^d?P}%WV1pbobByw4h#AHgPJTo?D)VMwCr<}V zM;p4)S`n?Otg5M~a=IeU&=aFwNaS>S7)ywh6kN~A&x8MxH~>8vG*m>&_Of(RPG9I$ zl0t@8o=|f1GW-i|+CF;4nbOs_r)r0yA$-k(e#ZNosVV*kz zvX#pB+RMA_QMswFO1bx;yZToa#gO9q_WfbWNRzcycM&7V9%!G0>7-B2zlIa20ZtJ8 zyTVV}Ii!SgrD+abfB&L!;(c`n7JlAtMaE4+0s?^y5JBGY1!px?Gnr(;C#3b)J4>o+U2M%A?H$dnov+WZvcNwb z0^{GV?BOyQ5Q1eHEwR1nf#ljtJQ(bb7LfalzqQxXwa52$7b(?33zIW+cwS0QDfsdr z1C`yUXcnQy9-I5fVeH767zFdifHgpR@GhyJrk<+my{)aYhl9U^on4@nlT}&!4J7-i zDI^D@s=BhW(!ri6V;#Ok^Qk^>PO!*hmp)JwZh~G420HnN-i^O%LLmQT;IFx@gFjPu zb9+k<3xleX(J`OL4Znw!hX&Cq<^EEJ_I3#8b5;E3yA>P}-Q)zEdxICIe>bgbrl#Se zrmwB3{BGM(eyn!cfAr@{^)DHL5tB8@y_euTNOhGHjHc|8vrgCOW79_SgG9zMo9y1A zFkyPwS(%4`KB5SANJHT=o7mwteU%48QSn(!5u9edDTsIkBD1>id^BD?zzSnIU&e}t zyL$tT=4L0%Qs>qHR*q$U`*!$8&>rIwoRpWIX!B#By)F%JD%4v01A-{L$hhM(qW`gV zyh+FQT<%3`WIFreh~VGrO)Xw$-bA;kVt`1x9iZoCwAt> z#3UDY|99PT@Ee}9uB*vRD81mh!ambDrj~r2-aX7vH0GD3Y9?vnRhh7P!K#nu;LfD3 zg`9drcPf=UNvVsuvz^`b8AN$}O8%ueWisSaCAf)ra^Xv_?@Q#AnFk>nbE2XJs^!bS zR9(g&4$1NSqra<~%mc`#J$aUSTRflQV%ZHg{C9iE2o7o<%ktl<#pU!QcR8y9T;V}w zixaf0Cn_OuwJs10B5mJ-qCk2*#jbz9MYa(k;5J;ZDsAnrfKIWYlN(W|F(VLcORI zKgE8@&L)~vpZi)+Qe<_L`={W$bD*`KrK8_rMd5d63MM3t(|Rx)ra{<|U_GrJdcnm68Pz4nKV;v9WqRfs{Q0B4$YJn~_g? zbYf~<5-(v_A$P9St`nRTUDg=lZECxA9lEtqs6>5roLadomo!iAC}j8Ds-u#UYVA0> zkzL+ELqTaWqP~aJbfkY}WQalF!&!Qk8KPW1uNy$*?ldmUx3>~>%yTjZFO+?GX=`6m zT`6}Sx}p=B6x~(V*m}tO-`wy5Bo&7^OtI+Kj5W(dmKz|~vqxw{I|X;oN6$n-Qb|io zT1#7UdaR(N%FNX4_J#mEd9%q?g3^31(n-$@G0{O7;ojDwrj`fm+On#=vaYI#x|wT_ zX@)MYDLl9{rv1CCr}5$u6~0GDe5spNcG1RaamI<@u5)Q3+fRa zBU5G36lre&>>L(RYq3MOfdR>H+BV171JH@zoq*=ni>JS|eF2UH#84Hsum(-T&WW2~ zx4@J~NccGL_2}Tw))Rdi6Uh~2sS0_FXTzLEF8-B$T{#nl{vH~$y}drd%Ic3HYKggE zejE=9FviO(IyRDc>RCVp;t*8 zU5MAozcuq5HLFG>up+{@v+S8-U)nem_}$r!7M>s~zZ4aaP@i6&9?;QZerD@Exq7BE z)A+MWQL|_Jv@99rUb)zmuwg6*$A4vOpcUigJL>9=iHT|M#t;&!_4COdOpbGh-XacP zCv+G`26KItyClr{0!KHNGK}=y8$`mQpx^-X_H=(9@2hcSBDmKb9(u29BK)H8&Fg#{ z^C7IjANKm%*22!-(bD`J+owJ7A(9=n97$E>(QbHp{$mLWk>(v$=v1}Qqq&`jb8eBl z$+g?7-UfX(EeuCMz%xt%Xne^M<<`EG_~N@~0{6jZ9o{7*^r5{YhfyAHXQ+n>A4Dda z-XS)hL$+sHK^On^6uCw5sg<`ij+A;5p*^bK)3P92A8il-+byU{c579~GFt{)P*hr2 zW_6Ld+!t+zWJ!4;D^sT$pXL``oTJ2Ze86+c{W-ctX5@yVU~$QtkG^x8|K5Yp9aDzF z+7zGgz6mONi2~?&7aPKPSwS-NeIX4)G`A|NyPeLEq=_0NcR0R`YvA^TI;HgNY!>GI!lhU+v#XT1kI`P!;Eg3 zr3~c3Hxpul>Fkvw0CQh1&JJ-_T#kE|ZD0PWR#WIF>I9GTq`-S-1N<3`Am+V!w_myM zf$6+vYn_-!hFwQOKGdjn=sS|udujge&KdT`L5`=S^5@x}Os|efoubs_B<(xdrw(Oq z!E2o*>mkYi9FSZKdE@2Q&Zc{vnR972eliQNa+GZOkkkAX2mqLMi+$;r60&Iw)KX=p zl=qj+CY*O9J($_+O*aGSh1V#ItX(yOl5AbvO1HoX-&UvrUM?kTvroz$}F}oJ>LdY=z!9l+U>%7B>zlFs=Dug#R>d30AEskGM zMQz#tClS~JD*gRLoN2}!bZ)D#gqq)zbi>~cPu)=vA7^rn#{bVg|12Gu4wm4*v#qXw zBa$#N47RPl&|>oY>L$~{L>MtmJ46wF1MT%XZ^5#ZxH01D!z1-xQbzwS@-Ncitwj81 z8=fX_Iz>q1*l?o)O@GS;vX*RuF_FHCc8Mj`{flxj{(V!wS0E^GstITL>Zg;_c~v4c zDYW7|FkyAA0Te*71Wnbvhu+G_bzA$R)kcgB&ZY+%;#NfbM@obdD^3e{^+c!t3lqrb z!SLT(QQE|`2DS!Q``P!~bFu$yEQ81z&l<(W*B$+gRjZBj1@fOBLv!=mudE`;oN#_$ z&ciFwT#nHqm^4Lb{;6&`9Zg^FTW9z$DQGtEpPp8M<>Sz)JM`JQEb#B^mSwqaL5-8+ zAyJ}{SVw@Yse69UBA-P|{vRyO9TMM-dI0n*&tsGkW2WX|V!SA4fGzMSq)_7fI?;cn zHBPq2fh^T+-|vI^#oLDEjiX%!l|c{kyb58El*Q9^OdP0+(2F8>@Us5;wCV2&1&)GmpD&i|24`YYw~z0 zk-^@icun7!R*GX!3X}lKx4a6e@3P#O;TaR2$(7p_yQf z3&KW(8QqMxBooQi76o%P{!CQe5bbo6Fms1b;v9PbK`gUa;-j`NnC?=D=F5ueoXovx z`bxY44l|~X`bQ><#vWub1_9}DyTq*MX?XKxB}EXfw)?0rc3CNo-;REbhUMxY9@M`( zF5Ji}mDI$dL|hTBZ%4A&lNkVq7uWk+CysD0W)aPSFns5xWt<~VN@6eRaD$NP=d}fp z1xthg3J-Z-fDHM)o{*owYSIZMO)DuK92Sq} z4R(NZ33jaP@w4fyJBh#_d*Yb%X4O9hB=`XJsVSy>_O}EEYr;((9`dyFnXK=42&ye^ zvYRwo^E#H)ca$veill!J<+Eh)+vD9VxpGJLp^Ydb!XF9k0Q(7m(aWT*&#xGns_oB= z!B6pD?1{5Bg@!SaEfGRumOOTzlK;q4;=4~oaL23nT2UXWscXYt3BW^beuj`WF(JBy zaWp^#1MV;J;xPwt);%>yxdGRoSgXP%gthr4es6#*tNmHN@w$-k;9is^y}59F>3NrI#OhKzRI+3;Y*Hcc~&1Ih0U4VbW*6RKmr_d^iv(V|LP*MlUf>cZ_t5ToiU zUy~<@0e7VpLy4`%vF7&H0$H=5Dh$R~_y`Lb3ixXqzMb@`)L4V%D_B)##rf>bpO@{g zg+>gjLpBgm06TXCfBcxKQg*zQ;)^$4xFC^iZ&~=2?FFG8^yWmZ;{_>kc9w~*-mciF zdhqid+(Y_PWUx&!`KXHT%2g%HOV&x`D*|%Ffc~YYXA10qB|Q zsrCshkn8x)y}AECHjt#94I&nNRu}zBU7(s>SgO)>?za#c3+~5De?npSar5v#0}(4B{*`n zh*Yul1OXOYB77KpRRIg0BJ2HexNx&wUsLB`Z+A_eqi?u5mQYCw**X?B?+4g@?_wfx zLS0LAUIkkNWvs!@(EIn9?{u%fs_iecHxcMh_yBik01#zTy&vSQF=LF@F@CxHee-Mr z4oJL#K(CV{Jl~F^;qmZ@sxWDPDTGLm-^MA$myUrdTI>EICcpZU-p3kS3lx2InkSNjFTjZ5Uvm2kL!z_fz_J zF7v9%Sf?pF?*yI|>@`G2o-IukW!24xk)cH)yOVdCBd!6SZ9AJerq*Q@J`id0-}#JS zHCRX(yj6Bd0#{+@%#fK%TdxllKyW_OI(0KxQWAh`)4E>DwE=NStx2U*M~AMgtTaep zV`f}&^P!dwhBwB4duvZc5!>9^AX+{ZM$lD%$CJdkEaTHEY={9V*FrfL@$OW~M03MsN5%J942v!nDLxbCc zn*>NuKlk>dD|jSVgDZwcYTSVph_gXuG{~}g&DP< z^zi)ez|~(elb8Z!ngkK^dE32!mZUXPsz`Oc4{tL2VpLie)2J6vYnx8vtO{t|Nq$FCAAew$!UvcT1;gOM>YUjO02Wp<1vAeu(0s0>1KmGjw16i z14x7POuJ|aZm*4`@gc;ukk-{S7@VOb`~Jy3uP-@)aGM#lR))qH%{@%s-f%LNR`7)& z9$&k3zUQ=&nLsVQu9oA15(GOXN+-Cwz{iT+`YiR40Yo?7bQeo+xy4n&WJI|FWEhOg zy_)*>v`nbBt_ZO1l*ov;Ca}P1Bh!|*Hg`~r3IFFp?qMNw(Bc>`2>+q?T!#ly^rt*F zvybA>BccpY02=pKleV1H<3w=Dtk3{(zN^XORWDL$A`rvScx9pVa=5zqfGo{CBy&tg z^u6%F+Z}QqCFReRc4VMg-Yok2pJ;kBL6p)bxLlMOh;{no7o9aNwp=29w&TXC`Md0~ zgZ9M6_RVMNePhY4npb zxd?G~`9cDf7~>^yd{>%xh8+Za&W^Bkb((($%^gF9*`YY@YWp%?YUk=ZV92BUr7knJ z(cv+upivww=T{oSOZ=xm39&ujHrU?ogxOcGw_ZM~s7E0}bo< ztzo~5;ePS$#7Wjr-OYt9`DFSPKa7Vg39tt^>^F>%-y&W1mFfCi^RvAp-RkmPM_??3 z;$`bim3)I(+;K`398_WSMcX8y;LQ)L5qI=<|gjTqNPcMl$4sE|s*{F3gnzirZfwRW~!4|}ZB|ER8}HOW$g+^jx#?qPb>$@2^% zZOQWBTcCsW0D62rmc8`)WDB1}?!Pv>MAB|A{kK%(5#2 zt#0s_X#hxVb{tGI@~wX@2mdXyLG@Y%!=&f*_c#(54WXEEV<#tf$Tu+eZ9dSHU9NLw z>gsfPY7F(1yYq=WX)vkLL@H4LS_kPgXyxwhp-lMHy5dPo)ZIouId+t)XxN5EoZd3; zOIEG*UB~QokUz+`6YaMLVPd~)X*0}hhDjGKl6!Czh_K&pk8$WPC&-`4rjz@$L0ed-1?c_|BHtFZ?C0-H2%{R?Phsss

Jh%rsH09$v2UsN?tCtgryv)%V{|Wt|TzXSn{4BSvp@ z{Kqm1VYj3g!v5`dubrc>&1Vkywx??&W1jS!l%%(})yzI#;dPOhgBRv+h@rRh&kj<4 zcfU7L zNnOAi#_pb3M+zG8$+P(YkvS))CQQoSxjCGNd2+Rfv6Y7Y<;rMwlRTX*e&^aEHy`iM z=hdg*Pj9jm(>Nm7FlJ$d#UWq%^IxI-ooilJg_UEkU)FsT*B)-0=+|gzhej0S6_MO# z=T#oio{>k_IDrcX#|-F9ZZCFLg6l5n24mC6H_(kHQ}olel#Dy~3w};yyd|HU2CW06p9-QBsg zs~AU_@bU5Xj}DFm1HOHWMS=^Nnwy&ooDhJ1CO#L%kdxL{UYnR$xUS16Nx4a(=lX%- z4swfx6bquX&g|sm*6uy~>m@TRttoEY4VYYkitewk>lK(JOtP+p$H}+4a5M5#pE=0U z*>qg3q~4weB;<`Ty&es85`KK$^A~y!zlBC}#HvH{7<+Vhl_+{SZE8+Jc3*ue0{{RK z#K>=c<7=<^B?YIyQ&PT=il{1SSuCw2CvV?3FQo4e$#kh((HPV5z{DWIz?@%QwQu;m zJh~ARJ1{ddD=W)nd`${E!K8<0uO9&^EnPQ4S4RgiB^MV52M70iap_qf|03=O4lYjK z71a_li>b#?ZP^u&_4DUbqb(6pZf>q&JY3$A6y!>gJ_S_5?N23Q=?*m|b_jP9vpI@5znLGCy6|tsw3lLo>bYMm`5VOROLY~4jP}yX%*;<(06-QN#(FJ0Cbk(9 zZGZ0wQ2{XB3+~xvd)fuv+G615=H%oKCNx1rdb0e9mzVEnbF;zK^<@hx3I-Y;svAEQ zRJw$|z5q+qeegELd1I2g+NzwQyz=lou+`;{2 z1P%tK6&H^PmoP2;5{m^|2E>?znpqgxcd}`XiH3oJ4r&*4(Q_~7(xbAn0}{95$-dVE zlrpa4Er__`?USX)TGvEeXxQL@KO3UJ(&gpA6aUkbG!8++z24#3+tBM(9Kvmpgw#V&aptlV|6x_B$>Aj^<_-Ztkc0yqGxP+RhJ=5vT$BjSocc zq&nQd?>nl>+?RMQ?YnhQl5ee#dYF{`kX|GX-bYG@Imu@CbI@ZftfZt!I9d$hhN=mw zUF1?yQj(y`nCRH}*m?2E*_o-J3Sd&Pk;8Of@XoRBZ=1T)s=oTbboh$*dUV1!TfKfh zy3^}kTLazSKRtPR3g{+AMC^PzU0Ar^R{qSz|Ffp1t?hZ$=j;=x1yWpG+#7t@8)M3& z4-|M!?~`x`LT%8*Ra&Rm(L=8^@k{I@>Vr5LQCu$paiI2(yAh*~&WqPWI^%=Ak&&pV zs4wMo&(L9swq@_lGAZ2cX$*9$+G?pU;El&N%1hXMMjmG;BLWzLgf2 zm)C&kXo(U9W)t)^v$382xw(TM=AYXFd$WA;9zY*j}k6j*C-{eY-AG2R^aAHM2C;G`BUk{Mc=DRs8AkdsmAS55pXB2ExEuMd!;& zm;?dRoe_?e2cL1WoYUt$oQX}TVad>c?4%!bOm7mT`_q>Bsoo`F?DD4gf8*Uq<} zu|LkZg>CHa?fqUnx_sWcgO--wI{oV3WRD_y?}_!7TitYTue9zy8vL+1N;Ur=5@qoX z7o$dHI50o(9?WnxIas z*ngkLXXk$uhB}!$`(;tI$|L+;tDsy%gEf$R!NJSEEB~ zcw?RoE=iwm+vN5+fyt==f}siLCdn)m>PkxbwA_6lJ9Opd7|^VJ#dsnpVv z*!RxybP)1sbBavt@WW)jV8hXCf3wJVksDU9W* zZPDEziYS6H3q(k8&b?XrjSLJ?HJRoMr%xRhu!$B==&a**HCHtVj+K5+Z@3B>hKA>@ zoR$f73ZrPq7>iHkb1=4uv@b>XQ%=APt=glJE{362NipS#YxTkR$rwv0v`^X$Pf58h zt<=`DKgx#to<7t^4KJUrgU7o=$hz^$qf%Pe>C@`+X@=gTnJnqD1yE+lQ2f|Tf0FD) zox#`N9M5~0pUZvl6Y3n_lALQ(Hox!`G!*c;@FuCjI)@h9Fd|Q&p|GQ3NThm%ab~C` zWph4^6xA*kr!GrOajy9Abxwhi2b2?W!Xs;phR2jry1C)-cSvg+pO~IiOi!`GETg*S zbh5kV%w%F#3K1KE0aq3$o3*KMj+{MpB0mdO+#u;E`T!8Sfx@F&D_8O#9UUFDkl;=* zD~qd(E5DUX*fQ+6EI;BvUWus?HNavbYqk`7!dkk0f>6S9ehR}hgS6nYFmBbh8Tj?< z{KID^CI#N5*nQO@Tw7Vhk>nJ5`E*&?lwP%i>joVG)W`=hp}djZtFVWj0;hPPA%b#f z0rRN?0yCTkhRVcaW{h`Vq#U_>9!H=e0{(NV3mjmXU{IN>l@ z^2ih%8^?zzuePVCt=*G3Z>2A;r=IoQu)V6Yk3wblU?*wVp|QSx*t$iHNmEHaMXm)b z4s)>!--a{V2Ulb4@MdzzNVn!=K~cffWW%>?;n7%KK8Du03}aIs0weA>L~vc`%+LN4 zy?pyOhMfb+Kh_3yZhS7QcjXs3_p{D^9tgd?p~$}!xN`)j1Fx6&8(W-f16d4jg^&?{Xa@M z{tt6)afc*?C(=H1!azboKtRF)pPgV3;1Cet;o#vBkrR zfVG2zl7WDsf`X%hzV(6NgMfhj{R6mvEnpBJ;EMnpPyLMiyb>>))yoKI1_*)6Re0q;^mbq z`+RF8u7P)&W(l;I4O@P*8NYrAO{LHxDeHhqY6u^7QuRgZ=E;C%Htx=CrO6Nru%F|0 zDM1?8dNMSUqV$yCg7E@ZEsdHsoGes#JeG4L2iQ6Ew0QC~ZENmiU^T6RR@4FZd=I`h z@2V*7qHAZ^oe-g**oIbaOq6$aqLM5&CB?}@aP=C;SVJfF=TN0s;p#`|{ui<)zc0^w zg%+fVA4U-<0yTSvB^xnYTbcDa7>Z~^i|5*H$ZM3g_k1Kr0y2NgQ&ph?nmg z1hBO!RTbw@w?>E8h6wG!<|li_UcNWGK@NmlJM>D?$9K`ewe^TXgXHSYz>^ouLfGEyz0q0ZhCp=0OmlPB8Q%F1oqB=#2_J|pAQzvBW_#S}8ETf$H z?5YJ9k?z1o-9$}&?GRQypOQuZt5QClenx5j&!AlY2+OJ+nTb$c=X8+!{$}i{hrz68e1yx22Ls9Rk zGo`+QFLmzuwHAF=yiAbYG)Ne^ zQOfwD&jSMqlr?h6X_DJ9h~(s$^P0L&HV0MAYK2GpAMz(IED5OQ3%se0WMGv?PrD{h zeM5xwt{rkLj~wAr9L!A%dm`-$nm@JE(o3j+%o6jCT4I}@(p6 zA6cINnnGP-Qptrk{$MYEOS82P^-Tv$9VrVc;`0ZHkTG>@-vmC}%ydn_P3Wua;qUQV z!Tzer4(nzaSc)QWSkeF)7MkE$Ix5DA?{xA06Yc_gMYDZ0ED4_uZVwjfw=4U{bZFIY zR4u0qRGI`o@r5il+GsKU;`U+Yim|D@3{wuR8aueva^QqAga&j>Ug)nxs7~d^Tz|lV zV_Nz(AP_8vRav96bJ&p^Dfg&R;+5PkBqe@ zbm;zVu6o|l%h9^|lYEefTy>;I?oY!6c<)-M%!}KM3tmBf~d5A=%*Idqbk((s~C!e2*sUv6e2g;I!Kyk7iED`sp04TOVz#g;R5s>EsH3dRdh& zeF?ZJ*<5O8&d^CXIqF|Z(?n!o_z*Qu2s*?KBA+0pUWZJw&>du}x0fgKuy6N8ACVh! zmq~@N5K=HcRYDuyrbC~(L@`#?;*o$8R_Hgiu$9s7)r&puZnw6sAc1cdj%G7wV_Pp^ z|CHS1t8?IrMfS!gbwI`bv&E+O=cVJEki)T&)j{lC%Y#a}o{Hrc0r|k6)ux{alO(gt zQd;Iz1L~*G?iHOpiclNswe>K2^6l4iS2KP1cx4sQC^i`nFpK(%6um86&DF%Q@@H|@ zj3p=aZ&kf61q$l5;N?F$QOEe?TxHC*W7YfvvnGgU3EXRYkg z{n$(2(f*_P)6&(YU^K;ZpP*$X#nH+Z2Zal}wXDupL8VwFuN;Me-B|X@#o8qY=?}@A zJaYZst#Q|qR&iC9+HISjNY5$d#X2H zV#mA%2CUk)KjKuMU$8dZS{v{KnCVZazdmq5Pd|ruf~(@`M#+;-g`}wlwX}aswT=@C zjs4sWzoNA~nIEX0=w=Q+f`m_*m z{rqel_OiM{c3!Ap`-( z?bW!D1CCfR>T4m$3wXW*Q8E1|B zqtJNz_3!73DS0%gh!59@SSn1A3;BkP-0BLb*|o>p$?5_0DRMC=B#Hd1?y{?L9RL94 z!~CR>Gu}NVk4IPR-NVX{()>>`AH(0c7I}Kq%(JJt<16pCFzb*1>rC9M1$&~c0D|2v+EBC zGT**1Qi>hNKb25^UG~=Hez0l_jtmvCxshSSqbRM`NM3rN{N^kp0Lws&i!A6YPjoBG zPf@{3*)QZqcH>b8H4e;l=ukkw-WN~^P{_aX9Z)b3RB%!VG!#~JNDLBD4oos~3N{v~ zzkhTU5{!hyys(9DR5ySim&7XxSN!nfubFeN9c29Umc&70SEVXEx z0SCud{k$#YkE~f}r$Rh9#od66=3Y9>Edq;=!y4xg{g%jCm))-RO$!G2)wkGE2j7-6 zZ9~so=M~*2%Z@!+m4)2%ss&uxpgel-z&}? z1A8w3OKV;2bI%-m-KC=2^OW(Ih100-dl%ve=`acrczU7K!l0?ET=fBf%^P+irTVa6 zr5i5VWQXPM;1V`r#vD?))i2yd$B+JsCj~(Z7f+}X@C`aqB?guIOkbZ;T&4b_-9HH% z$mhdrk!z95LfQ5OXichRXV;TqK@ZOU(SrA`+WN~bv6w+mCS@6{Zs{$Qq~@c%1Xhv~ zfSL1$22bDhLmV`uh}qb#{TwrXm(pt`z;ybaoi7Y&UM&{Q5=@c&iRHfk#PvV$_$Oh5 z_I|fSC)g z(WIaoJ=(mwyV`d}ns@wi`WCa;eDTs$E_nY^{>2)!&Bj`^1iB7fFCW?DAE+Gpg-o`t zy%+or@rP|)i7ImY2`qJIikV*NMO=)7T?oZ7d2SINI6vRcxm)GrF+V~$Q=JhYDHt5e zP!TXu;Zz_`C6;?HbNo4|FWy%)Kji1;IpN!aD~8wh@P9K1stGWdYZ)P5okpN4*k zDa!gAh&7byu$I$9u;h-!PT#KNPT*$|(U_B>(J%`b!TXb%bF=jNGbD2sZ=*+|PAz`$ zLO0VIKI;D@G);ebvj56geWN>TsoC+Buj;S~C`5nt)56rzw5uG@48iJQ*sm$a5d7e%B4dgp<^9T0@Ye#hJ^oW7IoAlppdO-937khs%Xac845+0qs;*9=f#I8L& zf5qwh%ZT1+bjF2~%c3)<$kIk2^v8CCy}UTUhaff2dB%$onYLg@;HfOn_3AX&Z+g7d zYjkY9?`1IBRO%d$UOtpvOs<4jxn* zzs9CZEcTXj?VgyVhha}RxduOIA*Xl^-n&qw`E8}2OOc>+};2C2iTb_!g~y5XgQriz$m~>=Ty(!S6@-HROg9 zex>P-X+g{QQt;;VdJUM(2v z;{f^e3^Wq-xTa-RD~U|bWpSg(!bU$YwDD0LUXWBlW8#s;AC)$a{t3V01yZ6{kTx?yQ%-a>Kp_<*qPus0Coa(}RA z9Fu7B&tMd?7tnEGRle+0IBHxx0GLCkopkn5`xfxcaxE^N1GbMxpcMnVm~g|J`H@^- zO}Su3BttW?bJ}2|c*Qb$qrJRysf7ypwm%S7si!NASdV4=P_XiQ!B1T9EX%$+yHq~J zrM^K(dW@A3Dm=eh<>n{JcBnQJ%YFk%QKqfBTq4rz9r5(~j?B5SC-8I^mJ4j-_bPk$ zK)tq|=b!kO2<)FR=Dz9G@)<`i1p<9WA%$fZtHAl2p|EZe1|X7cK;{5$axkf?JM-hX zZGwq+v}iQ>W{ac}!VVHcFG(bKEX$m}$Y{hIhk39D6!?ZwRex54LJH6Gbgao9mFaJV zCCf$60b<8+sn>Mjc`{-s{S$14pV3r8@*jD4N7A~(7DHLnVBbInQ=V-veKXWbByVdI zq3Il*BS(D$Qk>W8(-1~Nizv+FVo_PQ4;?S51SaR3hf2)vjqxyP9JkY5bW~9lNajSF zu-QJwl$#KNrlFQ^0lp`iZnHJvA3|kO8_S+Z#mwRtNmvE>D-s_izzyg~&^uPkMNTjH zI%go`XG)x`DAykp+#$TYLidkpU$kYY67X4bwy zSMSm44Me}{{_bM1n@{zqR93xF0i8hLMK%2zVk3(qVc{~<4^i7VX0o(X86bu9y zC=#O(ig~s)GE<>5Hr;M-TY6Cy8K0**>rQzKlRm98@Tibe0oM|n;~2*b%nY?CB3>$T z>a4$?RjpkX5sRiB>UYX``vZ_8wj-Qya}nI5Q*%dO85v&AboeU7QHJuwebM4 zf6P6V(fi;5d}mdz(Lc+{a;m_{@D;oP{l{$UB)eS;r9!gkabwMgIOcSH3?>HWX`0Qyi7|JEUwNN8HI2aQYzU z@3OZ!BGO>pgO@P}%{j9`XRu~V6RFj*>kkD4eb@DX`63a4Jt>35COs&;OC^&*C9pJP znm!<4CH>{&dkqmxK&7eQMGGMn!BL8azsL08y|8xkpo05=fP>{lXJ5KPv+~aHD|fO? z`6s8$eOoH>Pmz2|p*{WSfHt$fR=s1`qjYyeOZ1js;Z=aZd{*ZfM*_YZ()=jazDR1F z5nOVLBh?RuZD%GgwXvqe&^-!HWW$p>5=WSdTkPN&1EaF*Yd!W&KBZLYS*-2cb)=X6 zWF~BHAVNU1C_F&7$vh|<#3!)(Kr0c4^Rkux0@tzRKBt%~g^%l`F%qlaQCkrN$Br9; zMzf}f@$T71aO`mKuwqSn%pY`PO~WbWLa23BCHgF|)~1&3uWtQV!{_Uyp~EV-!^6?AveC@8Wn=G1N}dG41^oI4im}R5>P=e87TmD3Jx#q zuX+7e;rn6V<$I?bpj`kzJtz~pg$1&f&VTh|b9Q6nqtY3Vjp)OgCPKG!8?Pj~(K_lu zBT2tzk=@nXasjtm$t+wP8I^=ODF6~1m&_u0(QwpQQUa+mSnj4^eNqx5#4FKOBht4K=Kdc@5xjq>JPUGd zC$o@BQ+$-m&Tzb7f*CpgQS~6162nuMbpc>dM1n|)nAST;(lNuebJodL*nwUD=0(mG zhAB+5rI0#u`fJ;6oLo*^Pi90QOS{M(*$8ShM;KWWwHTm9F-ZhFdio95TrF<3u%kj4 zovB)WRJLmXt}IYUm2RdP%XoOVw5Ff1BxHXKk|EvunN8J+Aga1=aRsxOiNE5J zo4H%>4Mf`QGvVyLhxYo3QBcF__NwdDuqmYgl9@#bpb_U>A&IV~KXn#gYd<0T(9=A< zewcWRM8kcAcBmdo>}(2PZq^u+jfsgwk(NlLh3n?S!{BdOtcq%NW0%BUO~9JUIutyl zOvWIwDMjf3c|+BP%xd=9$4fg+Q`c#gHnX2J@ z!2j{z11K1fmn5Vttl*-mP9}dS3HDA&x_(A#G~NJ@#%og7>hGmK*g~o6eK&|!JFu&R zp^RTEKKZft@A#Ky?!YGuw1{PRdirR8*!L+5R@I;)tEGvZtjMgn(oqWkd8iyX!qBE` zUN~vh@PUz-3MG;*xNn3G(2HS>=VK)^`)IRbQ7B{?Jt;{RDT$$axnmNohS5(x<;b9h z$!0{nv=AIg;15X^e8#0~M%i6nuZHnUre3uIFQI(KhT(yC=|aOFW=Ktt8vK!z&8e=| zweNDvGBEGJ2RlpPFqNUrkdx@1jSXFp64lX=Dwq{MyFqogubHN*Bg0O?0TzSS2#r7( zruL&ewset(HyKYs91-lIgf>)?%%&DcvNg?=HBF(f30de@dPJQPNh&5502VD#EScD7 z1a_$8LfgnuDi$7I6)9_UR1YpgStOR3Ja%}(RjZD!DN0TMe%;kAgET{GpY+&)DNy?y zC?G(02h=Y(3>f%d0D!WJZh(=Bsj8U-B%q?OfRi|*v57b(7Swh_Orn#ysQm4<2$V2b zg^^sY`k+6V(Nk=$FB9qH%`xRZAY5-U4Q2oF7VX-sk8yt_+||zq)|>4k*0p&bpHO?4 zl!Dk*xYvvuwX+K_-IPnA)2L7@b9O0LqBsNg`hi-6@-KaY+i_Xpz1cPii4A)Uqtip$ zah;IjzMC)uPqiA|940+qJN%J9DMwn(gPJe4_*BV_PMGnLp+CMoLQq=Z%5^o>=2ivGLvQL$0U0{8DHi3l`H`%Pw? z0X%y;m8>&R0L0mDzR#kfy;5J#wLKoAodbjRc$d z;lnz!nP6?3_pk}&#G=D46CCgbHS-P?h=ssm(5UQ@m-7cCTpv>|n&4prJG%edjxoV4 zxGYi5Y#X@5Qa$?7>5?tDj?r;NCQMQaWXsXbQqq&P!zTPeIn*j1(0oeyr%0|jqW>|F zcy=TP?Xj;>i5v!v(Wcp~<(Ro#3e$CkN2SqITGfAT0D1&eNP6=%tiJN%h!J*XH8lW6{&-i zzIf5mg8XC>N>kDQ&Qk>256C7GvnEoQWsqLsl1csj0GF6G-VB$(3>UdIY(@OE;7;YK z+;2wckl5{S1sA6hZy=RGzMnhuwXpEL16qgv4(Kl?1tLZ-Jt2%Mb4`c;HGZf|V`4u) z4D~&kkvC?CxHl$5+zv@R$By|I71fqK^6}&O7ZrHnNe$TzB!uFagyKSp0xu7^yB})R z-Y!t?nkcJA#_9|_`5S=hb0Kgrs{-5PUgv3k2|z$7gmQu{{s)vOhXBD^f8h*lTF-<= ztkm=E0nH_?1j+diHUHR$H$}pX8=sK--~`#qM|??m$cWyfxA7U{royPpQ|&Bc zq%-jyE$z9E6gtXRUI9HFz313>#70mK_ImznC;JzUAwX!V!$we=_JB_O!{gN=++Q9X z=w{!!?RR$Rf7$qq`OLY-elPH$#gBTxj&+#7M|}NZOVc?!@*#ap|32 zWI%?P0eLY$|3Y26IcF&WV7@2*b3WgOTN)LIg|b@zbCt4@&Dnh~`Fo(p11i)9gLg>6 z2yVhl8R3uq{!*0q!NL*jGRos0OJ*b^^E(i&y5`Ict)H0l#v2#c-M1CQrp%4QN*s?H zy0SdJW)n(CD)`SW5H0_uh?r17x1+y&p$uSTnLQMN{gQGk!1$cTe0FKDzbpTcLig@@ z^6J%!_Q(sFbM%m5!}YG-750y)#Jrc+uye<}3-o|M-+BAWJkjw~;h(Bz9e|xL=XbvY z&EUp=x$}YkY{vKf0x{{df0tjSr|nQXi~r?FnfEf(v&V{0fmceW@xA4uBjmwy5f~4p zg?=??3lw>KYF>Hmj&}sty`vx*-JNWYB0ceslIe>RX(`oFnSMc|VA1;I{h*jsI zlRdQfIo#S_0Zq4K$IpiHL6+!G0%Ip$`u9jxeh5_0_w~=*6P*+QCh~5EVjva}0Oo`L z6ck8}#&hDZ;xF@_WfR{(8i0y0Q+QVg+@*XHu=%}m%r!G$8Ie!oCj+?{&_65nnuA*c zVWM$~&lc@@FX&@4!7O{fZ~R-@lRs|v5#x{hh4G{2{hMb7MqXf%a@Vs%!DprIu-TgI zkEHL;^9LC#;rktC?^2@){J8@GDpD_?%@tK(zdUKuc~JAF+#PEC@zCg3GXlGRiHtY;!W;Mh~df%#TL>AHDvu#k-8dk4SWS?0Oj;vhaoP>u0sglpoO@2AEB=Q zMu2n?aX_XEC73r>hNlZ@6R5;-F$fS~sKh$IEI0Y(4sl*rZXiz=iavat%}|LaP>HeV zu7EaNjD<{>F3DbWh^mCA16&IE!X@t2R zB7j{1fMjQ@RvN#R&PMUu#@i|iaigHN)T%6ip1~uuG=Gp+RP%r@4vx*|%+pgfsV6%3 zX_W}W?7WDa{_-wZI(aZm`dL7LIA0(K51ILV;*1bpd}SKriZ3ci}CfYp%9GwF`Iun=4U8o*im#)g#gzYaqUY_Qu)UL?K} z1YRWmpMTklM4|8k_a~&0i$wXMLdA>3WI0O%KTnG{yIS#wxdJ|!ezQjVb@|zDE0d6) zcek&m>d?pR2U>`0XwOfx-BNK(cr?=BDI`USUbKdq+z5aetI1+vx;;3Nk}JpbA!n%) zExQ6qGxjB(rM1}N@e>H@CGtlh(SHaUFTqkcA__I-nob4*7%b(Ec69hPKdB4U2+mtz z3v1GKG=g+IHTtZosIV|eLpIdm)_+n#hKQ$xh&4aKLaG!h)PQL|!D!@eErk3#+xxeI zHU?D9dAUiyJ48)Q`G?+~f-uYlw4R;_+RTj z>T1xWYQlX#?kOM6BPlSuL5$8jC7pWi{U7=Ae<6+@@JK(+I0aZ=b6KwMY>-e>SN`F$ zA`G4$wV zIfu7s0gbi_dY?v&C!M4RI*=t8W_PRSK$e8y3f{E=X$0hu-x)F-8<0b5KD~c$@VsE= zYUNX7>}vIIe!bH%diE!hg<_EJ6%jUEt>lQMf|A}q5RooiBX$pxa_!1EV%_<70fQW$ zC4Co~>#I*Pa_yAi4iGjB>d{q7alBK);xK|lBv~%=C-#PwM4D5o;n|#s0#j!c_A}@x zb?C*QZa2^h)_q`gRryHCLkn~qY~-N8!%*BM79Jpa*cKKduzdy489j{3nB-0~ZpNo&F6w z|62jGAGAt<4hJ|O0|On_f5oT}ZO8?3Afs5$%T2r8+j1C@f+o*TI*=kR3M9ObMC04F6vlgcO5$R#S(BVbd%-l<~+t0 z?U~W8Rj{dC;&V``nAxjbaJ(vzZ-BXH_A>W{xI)ACgwaUVHAnYE%<$L*BwzjnH!)Y~ z0EV4m?5aw!IJ)#hdc5Ic{NT%qaAz$RhDtHs;Zjm`h0`_mx9o3#xTH95VT+k4l#T#9Nv-wLYo)FPt4M-<(mX1S64eB6x?4rz8we_B*asvjLMw+(kbIN5poFjw z_-9=-IBo?ZZUy6=h&b3HvaQgmj-onh>w)KG-CfL5L>SEkRkY{ICHp}WlR1yj-0ITL zL9ZgkhgSX1kjaT|Mf|!9!nIK*|u*v>;lfM3YZT-B;r_*Cdo^ghI`z!yh*j%R+EAA7VWRE!5yCYbYez zfB)}DqH-e`Q+5nm1ic7bbPRcZM_T2jIw(e<%;AfVTqCOM+KhqBlztA0K$&`uMD0pw zX{<`0pWF(?dkc;B3+2m|bT{8gL8qvrP~HKBo2zkDT%p3@(KC`NFW=e*Nu8T;kAk@= zd8QVAPftnrKqnM+PIPM3$5SDnq}Y8=3SvLfEE;%UeY?_E>J4OY4 zyw9i&)1eH8F|lf9(|SaVV*|DR6!5k)wsOA5afr_;bsv&Q0KBD){iH0pnk+l!rXeXZ za=DtULsZ2Si&}jVPoy40MSR;mSG`DR%JIue!!#yd-O1{i&co`VKj3=A8_0rO{w3PM&n#HZd-A8O7! zxZIkC?mYmsyMUV_R3k3q?XFHPP6IO8;$576sGM|jYRNH}Wl_Qki3|zueUUk-uH|wv zg;J{xLUQm{Q;Tc>T&5#CX#^v?enM4&5+Kzp+^o~3G?KnpKFqbBLvOM`%$iSfE>~Ka zOWx!HFyk&hLaT>IHCnVFYHx~QoKtKnM%YT??ryzpApN(PGL)9gUI}X#cLHzrROmw? zVYz@1#*`VmCoC1l-*xU~ezMRm$;;-uaM@BI96B`6vUAK)q}VV^lHJNdxnyPe9w=5= zN3TrNcN2Kr<-BTzRcnV9cL!di1r^4*=-9Y(9iIKU6KHn_E`E`=d(rk=NwUHXz}=${ zVG)}Mz6JwBQDE=g#H>+ZwmK}Hx^Ve#6Bzw1aPmPul3od2qd@D8m;~kRh(fAN+jB-5 zYFHV-0DF8yxE7U;zE$6}T04|%RY4Ucsc0n@UZQv@CP_Ll9~%jVQL|R#(UT8lQT!;~ zHIX1JfkeFTf~6Xi(hh^j^Ep%#w)Y0L0d-QUze5`tAqR8@5&nS)phav(tGyhpKu1y9 zQr#;Fq0p7KnSs+$&`l8)aW)zI?v9BQ{a9Zh(i$7_q1ejm%2 z7e_g6H0MGJn@tj0>ko~q@5&w7iySbSSV;#P~p!qwfv#v6##rQ~&cWZdH? zCpEDVk(3O{SXEACFs)6dU@z4N9{=KbGp_bwVkdE3d$_joWb3Gz8%`@1Edk>tDGpjdQGG~1{ic1K|`-|`IXr!IG~(G zTy=*uPtPWIfDk$}&6{Z*jRl8?K0Jym(V81jn|y#|v2m%)<3hn>3%yQ?8*wS~okbbW zARd*zV9CS-Jx7$*MYIPCq@=b?`@K^0k#NS0-*4B2-yikc3s3Ostw0#l*Cd zWU{DO=Y=T1`X_~~y_%R+lY)vhqN|nH?{Wnx09Jb5Fac`7WO*yhjGqDyAzmynjnnkz z5bj<#<1bZs$iS;B4Ye36G_&uGk=fq(6$Lp-=V|dtfr>)e6XP;C+B8VauQa0P05QyU z&_^d1bBb_x@UT{!njkNWnqXqy)N6MzjQsQ%n4@V9-Pf`jK_2BGW5xvt1TZ}9M@0L= z&QG zO{5eyNtMXFruqT@{#I&Z zk}X++4UbaX?EmBBJ)oNUzID-r8X!RE9TIvc^j;FG^e$ZpUFjgb_g8heZ+68H-_#bQU78^Bx?-?$mmn1G+KFrx8CfscbfB2$uG7 zvt&G}BrD=Q8{fIp#Ew0?xk;Pj$R^4nG)~Y$7h){;T-hDt?cS&7zIUpg;r2wX_gr$b zlpOP>mmQ#|To#u7#yKwl(aTY%TKS%*9^Y z2^jo}}NuO!WWYkULxVkTyrq!Doz0 zrN__THomBU{ES#_c%7cnj!02iWqpR2&_qZrfuag=vbzw$+*e4UpN4m@p_WhIS43$$ zULZx#C@Y!Y$h8RN{FJu76f|FC|I2(tOu?hz=Tn9eG4Z7@L9Odk#Kor(J8AE)THhJW z7!jdsIsVbz@xJ$r=*NfY*Q9h6Ks=+G=xX>IUKATq8%Bu}X2p@4rb=nIP&gTyZ`_kw zeV=iUQ}|xzC1p|UMtN43)owmcw&DXd(MwfXev&-C?mxKMm>*gw!R&Ev`{` z?4#t_yrde6>r7<>NX!=+))~Sf`3|1ZmSJiqmQ&ZifY`(C_fZ*9pLe|;C%sR55Y1$? z7O64ekyWNqZp6?1t{fKI`!F3+71%`HaNtL&MFnBeI86sJ=Z@tHWqP;UzMsjc|Mrf> zV8Z{@iQ)JcdC>7Mj_ogQAva;#o(c-JA+Mgw+$vP1--M}ie4fhai%?%vtjcNF7}-{= zOZ+XTzVv3~EH-=8QDOZn%^fN@ifSeD9r|h-sdJNQ&1*5iO52d>v9{cSSrqS@1hWY| z(a~Q`hrf|ZV!V7SY}8ce7uypnA{AB6$yQBnW_guL9bSKpEjc&eR$gT`pQf7@m*C$b z=We;v{0JhdRvno#BQBa&$CaV{;mL*>HlSo8QsvX5ETSM&&cA@FD>jGhbRQ*&h>0!T zbM_j;%`?{%@lt-&py^ab4v}0!3qPUJO6^do9`VJVxua7z_tx2hE|#}S&0ci98T||Jo%NKQ z9zI_29HS$xQclaSQbiUO7Mxd4gB|#d5J&4q2>|?zLg_Uq@7!2h=fwFXaQ4`9J!Q@pORXIw-+!30 zhLCdkhTsrYFlA2Q$a-IK*jL1*R#!_;@uruauA@hcOE3*LEIyU@JK?FQ_T;HE;~d9R z>FpuS-J9Z5`@qe?{jsTx1qt7&!oL7T$y>#o{z43`Qu}HoOBkm=(83u<=@^;)`3ll( zhitG@9(+D#8Ka=59KB|+ZbTi}KL4w?6 z_-4InP9KFHzOh(5bA4QPrLt!qvOa#1P(;Uv)v5AaFV{)K{u8~a%6bx925+u!URzy%re-#0Runp-kkn%?tG|G6FwRt3nvx2nU3g}gW!n>S*Kx?29LNAO{d*j zEf1-3R!g%|o(YVx!5F=ryCmxM;s=MvmVQ&wK2cn*vPh7z5Nf}`+&DIA%ZRwG<+4*l ze#?hJ&SvaH?dN{+?|nU{kpy3k)AnIK0)`o%(3}bBL+<0WD=zu56ZPMvG4H~%Hq(OG zx5J{yjxrN(Sl3h5$b5$K+ciSXXuxq*b^OIM>qPOc6(vP39+tJ~s3O9IOBKOc0`D@7 zj7V0R8+HoD-$3E~HG19sv3*t-omd@W(kB{Oi+;|p{brk!U3krFV_8nb_#Il(EOwj6WmfE$vKBzVt69@2OkyB#KLV{%=b)o($@`-B8eC-L3nJ`is$L?19vAC_m zgXP7D^_`ikeh<$-w@JU`(o=je5q`5$4x-J^975L^iWL4)B)03Zeojr!+x004~v zK?gv0#|bz9B>(_(jKG0Lg8_XYC>jKh#(`sg(hq{h;6Sp#a0CE?1%Ltp@E9;a2?YPo zGXMteO5nLTfEWlA(+b1AUHLq0sx9Rl``-<=Tv7_(sCSr21a zKpy}yaAyz*5(Ldnxn%o&As4F{9yPie$n}7D=fOEV#s@k;`tvW~T<9&q4*Kp^4B0>b zcmm-4KX1jrAi@X(JMXND{v&AAd-(c)J^2VR99SNpC{eEeUTIUqwz}c$zIY=;igK^W&|B4`p68hj6&v=?lGNf(8tEjWUy0@Zm97&R@i37)*Xv| zVH3{%)lS<4sT>!zKom zHWmjE(i-BIQqgRsyXl_Yq0JSqU%Qk0kv&m*T?mBom(NK&ZIJsGUf8z0k$sVBnWdl{ z0jqJ7h)XR#7_-e*LWcAqKrY0aNtfg(>c&CoS_6^UFE+8t&JlCp#ddqz2eB!KM+}nh z*|}Tp=OwB|8>O-0lBhJ?607HNJtwf>( zQJZKF1AaTLk_^NpaYgQ;+8v(JlN_zs+J6ltPE4UtI@=B8*CISLupi&M zystko{<`zAb#kr)a2d7@ zweIMOt+=hdA9Bngio4!fbXiL2lw6wwXJM$1FNg!_os_KXC4hxs7=pw<9~+GuiXUU7 z#@jiHuv|;Xm0{R47ITBC@@t}vSHU*Jpch!??L+lm{Rw!{!Ig>g#Q3)?U6*7SNU@OJ zt(AZc`1g_tKmZv8CJe=33hw=1k1RqMGsVsyXXGUqa{f`G58cBLZB}FQ3cX=rVcZ`X zSRUreD>Ib2UIVzYUPEPku7XQh8a840mzN3((qWerAyOXwZF_Ra8xinfcs!Ai6~jW% z80arR2!Ck3*Xu^Um^h_ovGau%bDnC+ClNLlpMxK#Q4(G;Ggs97X00g0!BXrLv&=g(`2OlIfI4iZrY)A~t1I4P(xq{~KEEeY{R_&TQloLnbMr5Zo>OG-q5887 zzUGJ?k6sN!y(;agnR=Dw&j;!^8XyQr-9>yTq8{J(C4 zLrwt2hAp(ah=w6iVw=VwQDTw}B&&y#Tq66JwzhPZ#fF*K$~iZpts^=^x!{}Nkk}0d%od5J!r5T9wb}MS)Hh%itt4j(`SRxl$Y*eJ2()^M*P8Pm zhEPJ6EmSom{0nX8eU~cm#^-nu?Hv<%PfLeD5~f*(#3i-=NB_F<>ylhx`a-_)6-`-$ zQ6ZU_u1ucCKUd+!Jw#GrU5I|7?}pUVbW;Irwtb@Em?MZW(aNASC}xr((U_t*GnSWN z@FBsVG&aN^ix^E!$B0EdNG&F;Z}urx8XIIxk$4~bDah(9DjCQ=3a{1%Cbd#K3d8An zl4JlRfR=~QxxG@U$8?OO*jri}i|274X=Eom(pY#c)LwK5c6EPotaiCFoD&bL*q`uy zGr{3-W!P^p@^f>7a0IL0;0DLsl(jT&X2Z-Vvimzx*$})A=o?LQ18s+C0aO;I>};FM zc+L4FKd~UcO}6pjkLR#1{6_Ajlx$n5W#kI5-Ty>mW%W~JPAs&+mHzE)&&EzrURKqA z(-GXGgz-Gwbj1ilGis?&ej>*3Tgn!GuS?9QKQ}zKX6r{S&ECH+>1;uH8tIBr)JjQi z$XkdxD;JJn1Z2=AZf4DDSRr0`pEjk-o+T!@>G$d8+rdAhC-^s=_AxHbtH0!@_PpmlL&dJ8_T)V&eTlbxl z>Bl&_QWJAAO^wRS%@|;uD(irl5p?G^7^hP25c9h8x4TxEXW#eT`5VUdH+_^{A^%YY z$ruaM*TdAfbAL=15u?S*neaFu@i4U*yxfO~9lG?7cR`FPGW)ix3hAmCeY=YaBYD?8 zF7L-8pT_R^0_-c`-cEK4=s}1FRxBP4V6;DvKiPPRd}Riul=I)CQv`*l9H+Sz$5jtaU*i z2ki$6KXUs&aqrr_e`g#j=<+AW7@ZkZECZj$on86qJ^pv_uEfj_cHEh$boSrTAw>B; zD+ZDWax9A?A~8&+ASP!=?6F|9|S|BpCu;S{CFk$zC!zI-z4h2hk7<% z&&t%OxDTc$KL3OzA8N?TT^@vPg6+C=VVju2Ff%O2B;n!)EO9ulO23ENu4H#!ItG15 zF&3%?A}jxMjQzI>QW%QIyZMT>FRr^3O@u|gX$E;Ex`ePruB2d5Z-;>RL6%vV4}Sqb zYQiL2!&@x=T=LOY{ycTmz3i1Xb7sPk>|H5#R7!ax=FPrE21L-*XZ5dem4uLsng&^2 zDl?_p>9MhZ>`_D|AfhlmJVYE1i}6@#2jyvuozzr6 z{QsTr{u6C~XqW>Efk5mAlDE8Ofc6s$!W3v;I1y&oQZ;ZOpXtZ!V=j&S(dL(!{fa+j z^ek%I%{HhEw5cB*l!xG-0H7T+t3*aQ3LR**vxrauSW9^XvEz6z9M$d)W`Hl*H4@Iq zOeSJ6$q@Du|1LpogS`kNTj)Ug&|;8mB<&HP!U$uY{wP9-20AkJtVr zM|yE11t$(iSfSd60fpm-WdI~hww!671{T(c<1sfLaU z@Ld`mpvqoJ9c_9hm@)R>V<$!w1H%*x^=vE*+|mQ&V|g)50%XkG6V0X`{3-% z(jF1(2I1Aox7cZ--|K*hC^CH@ad#m`>R)*YvtdS!84&*q6oLoUH^z;sKuas)-$%Su zjN!Nh_K(skx6oOe;K~z~4GL?ccvDECOR;!Id$Qqm72X5@LH$uXHDCsmGL8gKPz&7>ZK@c^FE#3c)Eq;w5Oj_%D%Bm6CfZ}24 zkk5*X)Jhn*Mk*4JfPnX6q-o0SG-*>t2W_GftHF<=|!db-(cUu;^aXc6b4BGD9^0?v>aME+h?ZWNvn$+P_zzzT$p_K~6}DOd1Vb$l9oR;H_chNt}a1>TSN z4AObPHZf|$NmT0aE?3AE&~$@Lbr>=1W7`Mof^CtX1UtgqNbaKpwatqmCO0j2qng8W<;Qtul!NlBJIT5Njhu@ZtI!%u2=Q zz{ER-d`J35BgtWr$Uq3LqAj#M01ATx(*`YgHUXF%g#c!|%#@<3!EfU<#9wY@^>pvU zHt|)vsh~F7h|!0etN-F<1qo1)4{QZz=M1cmt7L|UV*`!e@H+2LkHj{@f;CXs7gRcc zf?`6la4{Jbb|;VimHqd5A&27Bc=4B{RYdd=#PyjM@!Lw zEmc>K5(2DggyZ1K0F0W`Nu(q3&s3;!1r_75QAEk^e5G{Qpg>$gXzd7Wt&dRi9|;9i zip=J)$Md5cd?;7u62?hQh9On}8kToaT7cm-b3&Uy31w#1x9Zq z;KKMC!XjWxXbc^=&!)04PJJUo!wMtMj^ZiT1cR{2LcywQ_AG3)BeR3nF@!<1Mr(F@ z*o0n^|9pYs4rZ(;-K9MOVpU-5;2%ZGSwU($gsoqVt{RQBOT=!7^s(h-(m*VFpqQ0% zI5?qrkOM^(K;15!+b?pVk2e2=IIl&3_%EQfaac+lXAe>x6^1vzT`i~Shm9-{7;Hi@!Qg3=Fr!+-cRAHh_= z1_<^L8Y>ArRNKVwE>!^-4WfKUc_Ft-goczq@ms*ck+3hM?6`>#P}j-|&IWUTA~(bs z-ZC1U(Fe0vj`Rr!d?#Wsh)W~E08~{XZU~2`MbHatrwYu&ZQq?53X79Uc}n}>DDt)- zO1qB>xp!$BgB*Pb<>3tAS4I@TjOsCL+o&%lq8uR6hbIf2mW36c*=69}SklriMQ;@J z>(_BYlQ((#iL!$6WC7^17EriamgY>X@G_q71l>3-SSe0ygV`yv=ax(hpiY`_cF+Fo zTAQCxMF7asM}RV1GiQ~$g-~vSDB)HWk;9JVBba;vuy0M>c!IT2hRp!7GIFs>S+i&E zFHV{5rs+eM{D*Vu_Qy#IU(#yqt2!58^gCrms%OF-L-~Toi;&&~O7CNX{OOBNbaX?Y zBTx^FTJ-?l#e_SaA4N-@#H4{c*^?*_QLVg{&;bAov2gf+aUjTF02H7RiS$y8joihd z{D~EL1Y7|ML(=zSH!wO~5)+cJM{yM=0UoNEqep-^}}vtiof=DWeUOpDSfh$(g2haSQqvMXL}P5QtcCw z32nGE1IG%ICx&oA&cQG!VNgN~*aM;cWHvf$+p=&oBWMOytEaVV{BIq6Hyb7myAw12 zNGo70H*F&Ziijn`$ZKp$U~(l9J*__E5DSAoH)%z{XW&c4sOV_iBG$gyWt=K^EsSQR zYyo;*nz2g#xxwO(G{R(VkOaKjjrJDG56oFd8GbvDYZ4MICsN+h!qxaw5mi)J)FBSi zg;0KQVu{6rt00*CK*~1|6(h{04G;!LQTKJ#&SExro$cTfGY;HN7i*)WT##oMLtE{0 z&Ttep&RNgE!N`rLNDSjx8ybBb?;C|DLpHYqh1g#@WgW3qfkGQ>&Awe6)g%%npzo%= z)?iLpc~KbtUyO@U5nXD+XM)Cbli_%*nZjw>+lttPm$XJ*cB*HzhEX~b5c}B?Up-~o zeIDn67B$L>#F>OUNyU)qvetmzxH5%_0Q@6j-@3c-O}cZTHk+_FV*jB=JqZ3$su+Q2 zYb15(aW^AGvO-tDQWZl1@cvPkt+BzfW(xywYldS*LJA&2>hT~yvAlJUxF9QdknhCm z*+gv0wsM&;@67nGM}TM|a4a9eC@;aNWK^tVVob9*1~ZINQV9iIkb0A&wTAeyu)Ent zb@qQNP>?4my1aB7p|6Xf`Iw-l?6qJMHf^{lr-{W3LmLcxr?x&eZDxB>#E=V)vZc61u z$!6P(`q7O2I+kT-LIIQMc7U+@eXdNb3)X6i#0V%3DEZYb5Qt^e0eT!?<%10g!tyD{ zUKx~&7RDq5ezQa~IO}&?NHGz1#2^GnjTcRYvq5!7a53?b#po`ppWMyZ7T06z$05Y8 znMQFBo!P~GFm}bXcBgGIw72Wd3Y%aUM%#`SD=)#n6ZggJqPQYgaQH=}@VVCD`e8SW zSR4^}yhOwWt+T;IoZL7IZ+@s0)Ud|HVD|*h)l`tl?IHZsCvD!e(+r*yeACSICt|`c zad87vvPvzHp5&T)VePVBB2)v_xE;75CkE^w3}+F5B;?tv!1)pjyug$)B;48yT}jFQwsiLo!p^ZPtPz!a+3=0+op~=E_BHdeviSuZlkwJe!pqn|ouoByJjrH=4L1 zOT{>Je2wiu>5W4&w?RQ-9?d|InT$p|F69~QL*gO_VwpkSiQoKifg}`W#r0kg3812#Osihq~?FH4$B|9kQB|5#957>+42{Ll5q)!gj-9RIcA_`eq! z4=k>+T29}8NfuW2Vs{5X=S#r>REs>!k{PYaA=<=l10st1!jFF@Xt_INttNl#?=5Yj z9z(U}84-P63BS7h?ld>kCRDcm5;x%YmFFwPzuV_OGgv&Xau)Z?EUy;6HV2g5Ty{C)M_ z$&WMG=`{5*ykD7UKm9yHy81Bu0Y$~5mATN>hYy0R3jcVJ_!c=||3&E(^7PE)I)800 znPSo2*d|xBmwf&B!)IF>h69V?>o|Xhd7EafJ%xF_PjulP$EP|k&PTpRj5^Lch3l%X z*AiCiO}5{aSI^^Ny>*oYu%E!u&(`Bx5Yy>Qe%cMdL>xpI^3zv8MS$X@MLUcM59!NPckE(! zcAaRNk7uVU@0YIgT@P`Y%a1i30g>#C;R0&+eXhnVHcO4$r^kn8?TIgwAn3J&%p zD0_&imgj%2Sw-xoOA4I}9_vVKwz_gvEFpE34G48Fa~~ObLw4cSByTMP$V^hHr{74@ z87pA)R2lJfrC?hY{!CS2S>x@`KDHH_ANuy!6H}*Pg>+}15cR7+gE6p)j=~|wiq+!B z+%9%I%2NqA5-F?ZZDwOW*Jnu&+5)%Dll>N#b95XOGXpPv1wU%)9pc#Ye%)HhpzSkP ztE;xOOviw%B=BdsJWJ8wtVl86Z6uej)zL8$cXgJw9KXHijP~2i>R?!M2$rYR4SE$o z^b$|E@uRY^v)$X(#*On^>)uk_*U6nB!p{5~(Rg2Yp2C8PC>;eFuQjmKHw60iJl<4= z>_p9F;?g?BT|_w9KYjV9&wa-Hs=Pg$vo&Ih&a6Q9)I4%Ed&D`c^R{!rB#sdF-P3bO zG~x#B;nnx}q$p!w{JjFt+Z?EQNC1h(iIWV&Lt7c;PglV3g&DQD0^8mD(a9vMDh^j+ z(HcLISZ1arU+BJHX2liZIQFzhJu(pKB%YR-&i7x=Zyw`Ttv0+zG;MZ3%KgPpA!i63 zMN{LLACQV0-_}9XRYF^!@tVd&(;xngqa6DD-G=Rh>*Us0n^USUybrRpf5?ZNI*?`X z;{Cd8Z#LT8lv(?j~zNIl2o zDG6LE%K}DGUb`QwhaO%$E!=!2aN-_%e5wbv-^|-N2J+7ys0E zR-ObyR&KBMKWMdhv2#3)YVLGcn)jHde^kzLKh+*=cHfm>0=~ivXCK+3d7s5Med1>O z!=Lq;=BS@i|I=OU?H%himm#Hm_6Gz<;@y%4Pd}opAC?0UQ=)r)xQwsemXR;#hE=KXkXW3D1)rv^0fRnVgqA>MlPlXI$tX|<*V{~r4EbI=V@fz*A!-rrA3bpvx3 zj#gm5r`Q_eUJyW*Vs$_8Bbl{svY3RL>|ENE$#A`U4k>%cx8L)QRIL)$>0Aw9*@+;% z`GQIUDjV4ki^_-htz``a8mQViPO!&QsOYC$^n0ub{aFh39i*s2k!}~{*$Pv6l6hdi zp@({tMOLjB{d|x8sN2FiT%3bdJ|*p%4x7F4%gko?6NwO*4>i>@p|J_xK@07us0$G(_v9dm19;njQ)= zznc2BkDILzIQ86e=+q{$+iR-a=J68LTwJ4G=CRZSy!#LGG9PcXWuR<&?;}#YR-duX zq;HD*?2};BtpRR5T9Na$J*lU*HZS^Wuch}S&b7=~xIl)iBaEV+O9L>Z*EmAp@UB;3 za@Y@C+q~aDAbkjL>zcaOJVlr;rBo{(1{YfI_(4=|d z#dUi?pZ+D_QN{eAoJ3TAjHur@FPZp|CDoak-2FeVPZW{^o9{iwk*qW4E#9knxgPDM zVRZ6k1fTbMUpft6WXt%t6KEe$Mvtj)zo;`5J*~#$ z#VYK44Oyb&(Ay|pbZ2bf8`|pkc*gx4N8K4$7T`_o_(nOWh9bz)4sWq(EHUZQ&@OkD zz2ZGtQ!;(-#p90%OIZUt2jSe7?0H;hf|hC9tE`f^L8)l+s5z9yG>@H#mb66Wvs32E z4UhY{=)QyEL5ZgW9@4M#}f%mTim5=nD@ECF0g0q>MF;XZmS|MbWs7oo($D- z7!76JK-0+xeS&Fav>Lq8@DlJ;>;)yK-hXTLG-d*5Xw)-lA6-l?;dH=J)3hXGZ;#8 zg9*#p#qgRiJt!ey58A2nvy+0Yvd2718e#i{y~XOXs1CHi{Q^a+dugW*7FF@izFe@6 zeYNK&n#Cu_N0W@?W>>N=DkP~D%TJcMtWleA7P@NoHHOygYpKzDY0KD+Qj)i(qz|ArHid_D~5#Gfrc)-W)70g@7WF1 zm2wd*d6zR4m1v!#!;<^w9g0A92&QFY$Lh6(JBCW&HpOea zP)8ii%sM%(8o)ay@Nh$xu61eLAAP;IsnqiPUiE3C!B6YYd@Wfng5@g+g-$~Ap<0t6 zQ-y=g2xWr+$AV$ODceCP#LW4-Ph=L=C{w0r^=xd;nE1mVped#s?#!o3sl8<XLUlW5hJ^dMt=$BE|gIwS6(3Q6|9dl}zUGE+{KY-$H;eLi)BekvRs_`nyVnd5b! zu~ph|Una5Ctw3=u@KBX8RRF(7Hiv4dweWrkcUqS0Ui|EZHf^|otx;E+6Q8VOe%DxL zBPPDd2(gy*YUhx=dkJ)B8eicsS`VlT$)e{Z<<0!PkMn5rCrw8SZ|r6kqxV$Z{Vg|g z-X?q0`1jjjKLw5Y`Q`=F&BxkW^wNpSkL2@D^g1HjMPBXTxi^S4jXJs&S&HcnKWP5W zH&$XI-mB_lWAftd%8B2iVW8am_K9KR`nT%a=Wi4P@`E;cvJ{qt-sxcVItg{{GCZRr z{LAsoJYdWA(WcGMJgR%&QdF{x9-gk;d*yX|WtFX_pq4q*Mfr|?TaBLCM*;_MY3?Eof?Y>kgmo4T7d<)O;&U%rYKdu7y@6VBp6zgiJd z5t@n9v(^zNQ}uSJnJAz;!D z@7IBeu(Y6ucFC>Svd^}=vt3#Jf1P#k`J;Zt%?&-O1SqF z)#|KY!AY{6xj!%C_5POXTAAmDO;7-*Rb3>owRIX@ivcS>LkKh)LI`-7tn3t3SC}>rnkjRX)%{4^_I41w5pUhiC^!}l z!Iw(A0sW4T-X_0~F+RNCwPBgE`kmx+QhGzy#@tjK@$1)F@ve$`4LsSd2ElxUDNhVS z7tQ^)p9^Z_>x*#ZBZcEcJpa176^r1p<2MN`r_?4GKTsfhlHozHpiW3EsZ&96u#jTI z&EW_##@{Uq-0U&na8<4_J^uV8vsi*J_iYtENGw|X-JVjUY@VzJcK^g$>Gs;}*X7u# z4x4rY&idiErrkigPB9;3-FMc7Gz#>4Cw7$s>BdSG{lP@B_I6R02NPuJgycUyfXyq_ z%?l187S=rEkc`6zQ*9r`ox?Por97#;7r(3tRGRyQ^!0B z{PyzCt1oD}0Gh3#tS6JNvIO*fjRHM*{_uYKad6g|om(mBwM8f-W2xY&o62u3lO=SU zh-z@tW9E1n3q=41Bzd<7OWhRkq?8xzgr|e6WGt|s^Gx?N9S(C!^?QbsiJ>y`l&vm* zc{IdMT8U|PPN+VmXygM6<=560Ex+p|$HMX0&6CB79V`j_J|PPd%CY9= zOzrEJSkhrX$0rcFD$&V_z3)PP{N#i@_N`t}Vdr1gV#N5P8AAA{Yux&SXrr)jJAAK& znnx06D8~HA@)QP!f@1Ln)I%mu4}r_l)7Rw(iT-eyln(=+N*avzYW$2hzj(0 zUt-+LJI%rPXV;YREvH3`dapj<`FFYm*Lxka2?^&!YMET%&Gnvj&d~IHpyr;egVyu2 zn4js2l()+QKXmo(zv6$ltdbZ+>AmV2Cfz?IXW>orRl{`JWBImBC`ZInL!xh!gqc6x zZS>iZNlNR@lwO!pxwE~+Y5sg}-rugB{Zis9y=l*+pY?nXnsYiN_i(*10Z2Wct57kLWhLj~+wMFnrhsc`Yv z(vwW#>W^G%hkzq{`(vM$Z~6g}2!Acne3ie7ottqQ2~GqdW(&Zh|h5Kmuy*Bryv8zw~BtEBWx#H2`Us)jC7AK{rRyRF)y zJgCoK4?Wv{n4`5w87i~oC&t}J#i%*qnxD&Q@@ZvT;$t^zIicGhQGieX7%b<{GM%{ zx~Vnw^1A|kCZb_NU9I(vx+-s5sBWvbk>Y#Lif|sZTo=ks^1e1_m>?`;Be{=E>QxGU zwKx$#ZN~UM4P7^B3&Bw@8Ts0mGGrKg43}XKKsfsMn2Cu@YM&5JlJ{5>`AGz>bX!6njBwx<$ni!6>KbKRT zyABl^?fEDhcRaMZk`5X2faH=Sz=!Ogw1wD}CBGPJoAF^twehx!7duJivuvWLfbB~Y zDiiUy&VT}V00Y6}I>B#E-bE2gDx;KRKKC!TO&Feh_4PVqkltR+SE6&LO0|?KUx2(U zkPSM_h0ZPplahf0sWru3m<_B`l-gBbg$1l#{_1|SYN&l|k~EPxjz`I(WxYRYW%u~Q ztj^@UP4}36K);Psq>hEJ1=BI19k31P5;SWR_Z{(_>Euws1j{yz)?-_nm>+%PG|FXJ zmxk)26BPwb3<{ZuLC1UH73oWH;!j!??*C^;Cb>Km;jjViPJ zKDOV}YfIkOt`*4zC&T{n^MfLny>)Onb92ExImyPc(p@y;A2iaM?E`NM0? zD&HWPF_5K^rgqpGkD4`?wpr_0a+JPV5a2luQI-9;wNT z&JDN~lgKnt^XgfQjSL`b?$knUDtG*hv5izk9S?{)1IV6?<8CdzIYAe zm}(ZoLYbBn$aI$YJWF=YcfW7C66A3b!L`m-ZL+9(&AqMIdD0WNexWTnK>uYyM|?w= zF*8d!lC?SQ#x}?L<+yrFb=niIC<*5`6vjRga?|jHhr4^~e#P8MW-SkAkDeP{CVF?Q zk>i%6D4W>1AFM47%yGGT9$C*>VmX^MXTMt{a;G1C%cVJ-rQ;sLMs&{cyZN<1_m-TU zybk|wx$cST&(gX+@7IuIUz>V(*`8#0R_~I)`}m|Uu^6J^H_`>8e&)ZOrmNK^zZ2q3 zLbF3;Kvz^Um4;d`z4w9w^-avw+ZO~( z&w}9ObI?h0@GtW&Q+Y5Yv4l1rCa&|43i;6`!tq5k@vd=*?48qpSlG7o-W8@ zj~Y@d&thG8zTQ(SP^Ue%d$AZ0y77tPTaXD}132FKgCTAv8}u9Ox|v^1=f`pJ1CLcG zefrA?a*E#55|liyU`!JKqOY2$H~EW)BxHNmoeMvWR|GhExz%wb(S-rA_cJ57#^ZSc z`q0aCT5W^|s+$5-*<|B$J_2z^1Oj_LxEXCG0LA;)S`4|55$06D$|?(!-YUe7+<-R& z-V~V~7kUUQqKT9g9>%Rr`OSr$X^$i0(0*v}9S);Oq^0Jn7R`Z+ia6(s&ajUG>@hR^ zJha+#&CUgo3`o3jSqt}&Lew7>1D~F)Cki%tzowoY>Vz4w;phoj9aY%N>i@R591G|I zh0k1@{>fKcdBk(Qw#(@4+7Tje+r z_Lr(kaDEPb|2Z0sz+53Z2{zn#bfuW}on+kV;?gqXrNCfBG8x1cb8A^wry` zoVOA4NU?o#v$i?a@Z%ZByXX6b_y?K%uRso#6vF%RiIg;awvC;dwT)?nah*e|y=L3( zmIs7E?@ty%)|#KE4_rM{Vy^;V!rMlmB-@xj4)p>F8y7&EA!?|9SCpev8rmaj4;_W2eh>)|zuLibsofdX*`r{DUyG}$?+?%D=ofjk4xdkifa7Y=_K}? zY+m2`^Xb9;AA7u1LQk%89F(JHmsad?9zsT?^ zu^kcC+WO4a|4sHc$osY6w_NTqX-`67HASkH(I;i64>RjBFV%9=E+$$g;z?#?@QLW6 z@j8l!rpFga+3)#IOsEiF+pS=6yEa*uqdyvUK2K12`b0W~&AU=Uw(bv|y>l7URk0mC zXChWN{Pece|1{Nr)sAFjl;_yFJ|i8o2luHm}WdSL_QLDB7r)2y|ncKWBz zzQ#2L(xi@!(7 zNK_tAo~a!O>k+D^c5?KlU)ne8QEB5?e-Zp#>Bd1G*pR>{_lLLty~nwXP!7Dm>YP@C zxFKA*hWgy8r~ANLxv_BcS^9f!@xP3IeZEU?a!iy?i2R6)q~q&;m($7)VAWN^&QVXo z-AsDVt=heD1@6}G(GKoO7P@_`Lu3{r>Owt#w#y?z!ihYi53P&0I0}nhlXPOi4_#%@wne>`quU>=bQo z=)fEAn5fr;9lLsOqAFKv{yg3(LPyM-Q)-MW)Xa!4K(y$|JLuvusQM4pI5evYXK>e-|! z3r#eYny%zO?`E4u-?o;%c(s8#K!^Fp#@?g2`N7k+hS2Oswrex|*j1r~%UN@~mVUgh zaFk4NUg;Tlmf%&M@gad{tp>Mgi#|iZ0 zE!)e>X%ZGm7z`Unp1ta-@w-K#ST4TJM=8hT+N9_GDaMP=eyq@wK=ba@YZv$N^#=%s z(k%7>f8v?OkBUoDENKse>x1&sJzk>+oyX$`%?Q3Eks)7S^HvjTS{84`fbLw57UIsB37!x4)e;i37B?P4oaHk>2F5KJq>&)-4p@%JDdxnF3D z#I&oa3%Y-C`#{w3DTqx^~LNDuF7QUXc8tJA<`_?Ph@OrGjAiU!Lag^+zQ$bGn zX

fu`iCi?GX+v676}QTEckOXHgu1qU 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 - - - Links - Links - - - - Links - - IPFire-Project. - There are some links to our partners, friends or sponsors and of course references to articles by - some magazines. - ]]> - - - - Friends of IPFire - - - - - http://www.firewall-service.com - Rene Zingel - - - http://www.rowie.at - Ronald Wiesinger - - - http://www.scp-systems.ch - Peter Schaelchli - - - http://www.kbarthel.de - Kim Barthel - - - http://ipfire.earl-net.com - Jan Paul Tücking - - - Seite im Aufbau - Sebastian Winter - - - ]]> - - - - IPFire in Media - IPFire in den Medien (diverse Zeitschriften) - - - http://linuxmini.blogspot.com/2007/10/ipfire-free-firewall-for-your-home-or.html
- http://www.pro-linux.de/news/2006/9219.html
- http://www.kriptopolis.org/ipfire
- http://www.pcmagazine.com.tr/dow71,17@2500.html
- http://freedommafia.net/main/index.php?option=com_content&task=view&id=103&Itemid=47
- http://www.lintelligence.de/news/1026
- http://www.techmonkey.de/2008/09/15/ipfire-der-nachste-star-am-soho-himmel/
- ]]>
-
- - Discussion about IPFire - Boards und Foren (Diskussionen über IPFire) - - - http://forum.linuxcast.eu/viewtopic.php?f=13&p=438
- http://forum.golem.de/read.php?26129,1364598,1364598#msg-1364598
- http://www.ipcop-forum.de/forum/viewtopic.php?f=28&t=21055&hilit=IPFire
- http://forum.cdrinfo.pl/f102/jaki-dysk-sieciowy-78524/
- http://forum.mini-pc-pro.de/projekt-forum/3681-epia-ipcop-router-projekt-wirft-mir-diverse-fragen-auf.html
- http://nachtwandler.blogage.de/entries/2008/10/4/IPFire
- http://zahlenzerkleinerer.de/1085/der-erste-ipfire-test.html
- ]]>
-
- - Sites that link to here - Nach IPFire verlinkende Seiten - - - http://www.linux-luenen.de/?q=node/9
- http://www.ohloh.net/projects/ipfire
- http://forum.softgil.com/weblinks.php?cat_id=1
- ]]>
- - -
- - - - - - - - - - - diff --git a/www/data/news.xml b/www/data/news.xml deleted file mode 100644 index 5f6c362a..00000000 --- a/www/data/news.xml +++ /dev/null @@ -1,81 +0,0 @@ - - - - - 08/11/2008 - news-12 - IPFire 2.3 Final - Get it quick - Dear Community!
- This day, we released the final version of IPFire 2.3. -
-
- Major changes since the first version 2.1 in Oktober 2007 -
-

    -
  • DNS-Securityupdate and many more packet updates
  • -
  • Enhancement of the packet manager
  • -
  • Improvement of the Quality-of-Service rules - Presets for QoS
  • -
  • Adjustable Firewall Logging
  • -
  • Kernel-Modules for better hardware suport
  • -
  • Change the system statistic to „collectd"
  • -
  • Better disk handling (S.M.A.R.T. and Standby)
  • -
  • Status- and Serviceview in the Webinterface
  • -
  • Proxy and Redirector now work more dynamic
  • -
-
-
- In addition the 2.3 will change the following things -
-
    -
  • Kernel update to Linux-2.6.25.19
  • -
  • Update of many more packets (OpenSSL, OpenSSH, Apache, Squid, Snort, collectd, ntfs-3g, Openswan, Updatexlrator, iptables, l7protocols)
  • -
  • With severall Atheros Chips IPFire is able to work as Wireless Access Point
  • -
  • Better support of UMTS-3G-Modems
  • -
  • Use of tmpfs to reduce disk reads and writes
  • -
  • Better hardware monitoring by the use of lmsensors
  • -
  • Vnstat Traffic-Accounting replaces ipac-ng
  • -
-
-
- The IPFire Team - ]]> - Sehr geehrte Community!
- Heute wurde die Final Version von IPFire 2.3 veröffentlicht. -
-
- Wesentliche Änderungen seit der ersten Version 2.1 im Oktober 2007 -
-
    -
  • DNS-Sicherheitsupdate und viele weitere Paket-Aktualisierungen
  • -
  • Erweiterung des Paketmanagers
  • -
  • Verfeinerung der Quality-of-Service Regeln - Voreinstellungsmodell für QoS
  • -
  • Feiner einstellbares Firewall Logging
  • -
  • Kernel-Module zur Hardwareunterstützung wurden nachgeliefert
  • -
  • Umstellung der Systemstatistiken auf „collectd"
  • -
  • Verbessertes Festplatten-Handling (S.M.A.R.T. und Standby)
  • -
  • Status- und Serviceübersicht im Webinterface
  • -
  • Proxy und Redirector arbeiten dynamischer zusammen
  • -
-
-
- Mit der 2.3 wird sich zusätzlich folgendes ändern -
-
    -
  • Der Kernel wurde auf Linux-2.6.25.19 aktualisiert
  • -
  • Aktualisierungen von weiteren Paketen (OpenSSL, OpenSSH, Apache, Squid, Snort, collectd, ntfs-3g, Openswan, Updatexlrator, iptables, l7protocols)
  • -
  • Mit einer passenden WLAN-Karte kann der IPFire als Access-Point für WLAN-Clients dienen
  • -
  • Bessere Unterstützung für UMTS-3G-Modems
  • -
  • Verwendung von tmpfs zur Reduzierung von Schreibzugriffen
  • -
  • Verbesserte Hardwareüberwachung durch Lmsensors
  • -
  • Vnstat Traffic-Accounting ersetzt ipac-ng
  • -
-
-
- Das IPFire-Team - ]]>
- - - diff --git a/www/favicon.ico b/www/favicon.ico deleted file mode 100644 index 52da262f76e9a6b7bd9e07a0229013bc2beae51f..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc-jL100001 literal 5430 zc-pmC30PIt8a;PnQWVV@3@k**96(D;d=3H}dd$bQ-%jC^3C` zU}E~pvPvd$TA}Xu*4?j`}k$nv$uc{y&+YISA zim{Clt!RSiSSy%snjtye0C9v0q983qCn=URKx*6AQC~7f35gA@JF*fcn^WMLk)7eB*HV7= zQ3w1uA+WprZ9pGWH8i3i0T#ina7?I!d9DV7N?7QhBk1eEVv4g8Bc^EJw6g*Bp-N1@ z)Pz}a_1G9&gcmQjK^#l@8y>-sj(wO8i06fs-Q{n`xI09tw@wSsI0ZHpKETVB9Qx-I z*zVQAbwe|P!mF^wrvV2~)F9w$HBR4_?#VzSENbwv)zrz|8{zzh}9M00|7clTcx z#sq1UW;5<0IRv}8Rg_nZI>5?|M=eD6lsl{Qs z7E#qYY%63@+pNX;N-Y-W^VnC$AvLEIy-Wfic5K6e`@*5_@W0cfzP0$5q{QM1g{@q} zkJKAiih)C2a5k+5(=YNEbc$eSiVl;1;SpNIVo?$g-#Z)(e_+ z;9UG|I7RUo_C0~sVS<6{2rRep@Vm=m-FfO4=p6rpa?YW9-jVhttboN2q30kHPl9aF z2~1qDp<(doiRhbOj!c$&?P>hfHyGNqLMc1V!epZYMy8t}F_;ZTq=%h8zQ}LV;$*1~ zpVILTbm4I*lSO8g4vwdJ+^EwbnXW@_qZSIf4lQjgT$Zk)wnh-k-oS`gUc-XZaag1l z;soHC^Y8Oe2$d#LpR$Nxuv-)QzrL5YVTfVFEl^#rgi51BbE^(NXK{$U-HQ112k`z? ziAq&l2WJ-wOYXy{cOQr(#;{%JjfKHCF+RBx?hPdMdFxM)sQyRyNv!@J0?B9|26HqJ z8LS4wh~c?uJ04Q*_V(}8YjNyXIc(_pH=a_Dn5&il{?^>0#oYOeXutKLXSy4Mz0{Cy zW-&2^sEgY$`G4&}odEr5r%0XMUS4CnM5kg5^65A%Ar?z;B=98iOR5kQmJX}8X)GGw zjA8RNShK$oxy22rZ&Jf+(e8QFuiNR^dT6ukY{UKVBXzxpVufjw6BxM* zp}t)ziz9LB!c}blG9QCpYsUMZwIbr08kxl{IB@(n7A;>->xt;trC_ANFfn6c z`!1`B&mb;;#_zPl&!7Lg_aH*Yp{8wZ@8JJzpFdKwijbCFjHBVXaP|5bD>jGWrv8rtbR-0Kiz|^^Bu8ZIbr|-3h1!Ff~v1+3aZqhj^me->}rNO1E8IT#gPVMSoFpTiI z#lobX^g*kGkO&4=l7lObhF(C;0~O9)On~`F zTYT{0cFcBJk9o^B;{*2{Sg>+8j0Yq@_EIa(+#=pRCu2I`9~=e$6Vdo^ZwAChYUt18aqf1{`BQWFIeI4K_dQ4< z<1yjAX81?u0JeO08h^9;=<{%PD2{v=5fFeP`;M_Xse_5-=i1!0-@)Uc2eHMM8%r6n#f9 z`wCyt!eU@gxw}x)D2&c67aXgaG1}CF2x1x&M81^&Ce@UA6w#E^i*g50UVn-}9{n#8 z(CKjaf4QH;=dmPAGMVykqI?%fCw~zI)%<3c=$KQ9!ZC#u&rKnG3x^l}YJNGLgCU+o zBXXlU|18zsxqz&U227fxSjH2is)VJWF{3?q`K!6Ra6CuYXZ$(MabgKxEbh!LhGh|q z%Op5aO9KBc-Y$HdLt>{z@%(q?J03BN z56dv-0>z_F^!@!gtqZjbGfqz5cR@|Rvz_031V;3j+IHYk2SiVRZpUQ(PROTGn0JB= zq4dp`5?ul$jPtcd0s zoN)N5I+?}S{lYoeCa7T;!(zaDT39S^#faS+IG?SD$rYLls{t1liBGdXQp9g}K2U{a zat-EQ(&DG6dWt&4-K{}lQ4O4Y<(P0=P&7QTmf0L{mr+$`zw@|izo+*FEWO2HW10?u zG#A>C!Qns-3%7J0E4?Bxdwtzk=tr_qrYWwZUv0tmgoGk%-1S9pwt7GjDdOjPLPR-aAtVVeB zO_-XQL)tqUGgk1~I{uIMC9Q4fZ9SfsGtyYR>+FJz+%mYYKZK3mN03Ri;ag5i@b;Xl zr03`gL(i@JDMoq+v)}imx?O>g@ECaS^+zGqP!3KX!{lX+PF^cmJ*$l$gUMz_s>XzW z1Y>`|DXiV{1-9(=?||)$M7Zo?6P|bGR&D0bwC4rJ7A!tKQ-#>vaztm8V`UiCjIMMY z6NSCcvro(4s)kRk@_Lj50AP>a7is}{L?x^3;W+R!lE3V;NaU#&a= - - - - - - - - - - - - - - diff --git a/www/images/sponsors/isp42.png b/www/images/sponsors/isp42.png new file mode 100644 index 0000000000000000000000000000000000000000..f1ed2681349d9a27c130217e95f034bcfc4ab71d GIT binary patch literal 7547 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
    """) - for item in self.items: - uri = item.attr("uri") - active = "" - if self.page == uri: - active = "class=\"active\"" - - if not uri.startswith("http://"): - uri = "/%s/%s" % (uri, self.lang) - - for name in item.childs("Name"): - if name.attr("lang") in (self.lang, ""): - self.write("
  • %s
  • " % \ - (active, uri, name.text())) - self.write("
") - return self.read() - - -class Body(SItem): - def __init__(self, xml, page, lang): - SItem.__init__(self, xml, page, lang) - - self.paragraphs = XItem(self.xml.dom, "Paragraphs").childs("Paragraph") - - self.news = News("news", self.page, self.lang) - - def __call__(self): - self.write("""
-
""") - for paragraph in self.paragraphs: - for heading in paragraph.childs("Heading"): - if heading.attr("lang") in (self.lang, ""): - self.write('

' + heading.text() + '

') - for content in paragraph.childs("Content"): - if content.attr("lang") in (self.lang, ""): - if content.attr("raw"): - self.write(content.text()) - else: - self.write("

" + content.text() + "

\n") - self.write("""
\n""") - - if self.page in ("index", "news",): - self.write(self.news(3)) - self.write("""
""") - return self.read() - - -class News(SItem): - def __init__(self, file, page, lang): - SItem.__init__(self, Xml(file, lang), page, lang) - self.xml.load() - - self.posts = XItem(self.xml.dom).childs("Posts") - - def __call__(self, limit=None): - a = 1 - for post in self.posts: - self.write("""
""") - for id in post.childs("Id"): - self.write("""""" % id.text()) - for heading in post.childs("Heading"): - if heading.attr("lang") in (self.lang, ""): - self.write("""

%s - %s

""" % (post.childs("Date")[0].text(), heading.text())) - for subtitle in post.childs("Subtitle"): - if subtitle.attr("lang") in (self.lang, ""): - self.write("""""" % \ - subtitle.text()) - for content in post.childs("Content"): - if content.attr("lang") in (self.lang, ""): - if content.attr("raw"): - self.write(content.text()) - else: - self.write("

" + content.text() + "

\n") - self.write("""
""") - a += 1 - if limit and a > limit: - break - return self.read() - - -class Sidebar(SItem): - def __init__(self, xml, page, lang): - SItem.__init__(self, xml, page, lang) - - self.paragraphs = XItem(self.xml.dom, "Sidebar").childs("Paragraph") - - def __call__(self): - self.write("""
-
""") - for post in self.paragraphs: - for heading in post.childs("Heading"): - if heading.attr("lang") in (self.lang, ""): - self.write("

" + heading.text() + "

") - for content in post.childs("Content"): - if content.attr("lang") in (self.lang, ""): - if content.attr("raw"): - self.write(content.text()) - else: - self.write("

" + content.text() + "

\n") - self.write("""
""") - return self.read() - - -class XItem: - def __init__(self, dom, node=None): - self.dom = self.node = dom - if node: - self.node = self.dom.getElementsByTagName(node)[0] - self.lang = lang - - def attr(self, name): - return self.node.getAttribute(name).strip() - - def text(self): - ret = "" - for i in self.node.childNodes: - ret = ret + i.data - return ret - - def element(self, name): - return XItem(self.node, name) - - def childs(self, name): - ret = [] - for i in self.node.getElementsByTagName(name): - ret.append(XItem(i)) - return ret - - -class Xml: - def __init__(self, page, lang): - self.page = page - self.lang = lang - - self.path = None - - self.data = None - self.dom = None - - self._config = {} - - def load(self): - self.path = \ - os.path.join(os.path.dirname(os.environ['SCRIPT_FILENAME']), "data/%s.xml" % self.page) - try: - f = open(self.path) - self.data = f.read() - f.close() - self.dom = \ - xml.dom.minidom.parseString(self.data).getElementsByTagName("Site")[0] - #except IOError: - #self.page = "404" - #self.load() - # raise Error404 - except: - #self.page = "500" - #self.load() - raise - - def config(self): - elements = ("Title", "Columns",) - for element in elements: - self._config[element.lower()] = "" - - config = XItem(self.dom, "Config") - for element in elements: - for lang in config.childs(element): - if lang.attr("lang") == self.lang: - self._config[element.lower()] = lang.text() - return self._config - - -class Site: - def __init__(self, page, lang="en"): - self.code = "200 - OK" - self.mime = "text/html" - - self.page = page - self.lang = lang - self.xml = Xml(page=page, lang=lang) - - self.data = u"" - - self.menu = Menu("../menu", self.page, self.lang) - - self.config = { "document_name" : page, - "lang" : self.lang, - "menu" : self.menu() } - - try: - self.xml.load() - except Error404: - self.code = "404 - Not found" - #except: - # self.code = "500 - Internal Server Error" - - def write(self, s): - self.data += s #.encode("utf-8") - - def include(self, file): - f = open(file) - data = f.read() - f.close() - self.write(data % self.config) - - def prepare(self): - for key, val in self.xml.config().items(): - self.config[key] = val - - self.config["title"] = "%s - %s" % \ - (os.environ["SERVER_NAME"], self.config["title"],) - - self.body = Body(self.xml, self.page, self.lang) - self.sidebar = Sidebar(self.xml, self.page, self.lang) - - def run(self): - # First, return the http header - print "Status: %s" % self.code - print "Content-Type: %s" % self.mime - print # End header - - # Include the site's header - self.include("header.inc") - - # Import body and side elements - self.write(self.body()) - self.write(self.sidebar()) - - # Include the site's footer - self.include("footer.inc") - - return self.data.encode("utf-8") - - -page = cgi.FieldStorage().getfirst("page") -lang = cgi.FieldStorage().getfirst("lang") - -if not lang: - lang = "en" - -site = Site(page=page, lang=lang) -site.prepare() - -print site.run() +p = Page(site, c, s) +p() diff --git a/www/menu.json b/www/menu.json new file mode 100644 index 00000000..ea27b87d --- /dev/null +++ b/www/menu.json @@ -0,0 +1,14 @@ +{ + "10" : { "uri" : "/index", + "name" : "Home" }, + "20" : { "uri" : "/download", + "name" : "Downloads" }, + "30" : { "uri" : "http://wiki.ipfire.org/", + "name" : { "de" : "Wiki", "en" : "Docs" }}, + "40" : { "uri" : "http://forum.ipfire.org/", + "name" : "Forum" }, + "50" : { "uri" : "/development", + "name" : { "de" : "Entwicklung", "en" : "Development" }}, + "60" : { "uri" : "/links", + "name" : "Links" } +} diff --git a/www/menu.xml b/www/menu.xml deleted file mode 100644 index 57a397b8..00000000 --- a/www/menu.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - Home - Start - - - Downloads - - - Docs - Wiki - - - Forum - - - Development - Entwicklung - - - Links - Links - - - diff --git a/www/news.json b/www/news.json new file mode 100644 index 00000000..8c70648a --- /dev/null +++ b/www/news.json @@ -0,0 +1,56 @@ +{ + "1" : { "author" : "Michael Tremer", + "subject" : "IPFire 2.3 Final", + "date" : "2008-11-08", + "content" : + { "en" : "

Dear Community!

+

This day, we released the final version of IPFire 2.3.

+

Major changes since the first version 2.1 in Oktober 2007:

+
    +
  • DNS-Securityupdate and many more packet updates
  • +
  • Enhancement of the packet manager
  • +
  • Improvement of the Quality-of-Service rules - Presets for QoS
  • +
  • Adjustable Firewall Logging
  • +
  • Kernel-Modules for better hardware suport
  • +
  • Change the system statistic to collectd
  • +
  • Better disk handling (S.M.A.R.T. and Standby)
  • +
  • Status- and Serviceview in the Webinterface
  • +
  • Proxy and Redirector now work more dynamic
  • +
+

In addition, release 2.3 will change the following things:

+
    +
  • Kernel update to Linux-2.6.25.19
  • +
  • Update of many more packages (OpenSSL, OpenSSH, Apache, Squid, Snort, collectd, ntfs-3g, Openswan, Updatexlrator, iptables, l7protocols)
  • +
  • With several Atheros Chips IPFire is able to work as Wireless Access Point
  • +
  • Better support of UMTS-3G-Modems
  • +
  • Use of tmpfs to reduce disk reads and writes
  • +
  • Better hardware monitoring by the use of lmsensors
  • +
  • Vnstat Traffic-Accounting replaces ipac-ng
  • +
+

The IPFire Team

", + "de" : "Sehr geehrte Community!
+

Heute wurde die Final Version von IPFire 2.3 veröffentlicht.

+

Wesentliche Änderungen seit der ersten Version 2.1 im Oktober 2007:

+
    +
  • DNS-Sicherheitsupdate und viele weitere Paket-Aktualisierungen
  • +
  • Erweiterung des Paketmanagers
  • +
  • Verfeinerung der Quality-of-Service Regeln - Voreinstellungsmodell für QoS
  • +
  • Feiner einstellbares Firewall Logging
  • +
  • Kernel-Module zur Hardwareunterstützung wurden nachgeliefert
  • +
  • Umstellung der Systemstatistiken auf collectd
  • +
  • Verbessertes Festplatten-Handling (S.M.A.R.T. und Standby)
  • +
  • Status- und Serviceübersicht im Webinterface
  • +
  • Proxy und Redirector arbeiten dynamischer zusammen
  • +
+

Mit der 2.3 wird sich zusätzlich folgendes ändern:

+
    +
  • Der Kernel wurde auf Linux-2.6.25.19 aktualisiert
  • +
  • Aktualisierungen von weiteren Paketen (OpenSSL, OpenSSH, Apache, Squid, Snort, collectd, ntfs-3g, Openswan, Updatexlrator, iptables, l7protocols)
  • +
  • Mit einer passenden WLAN-Karte kann der IPFire als Access-Point für WLAN-Clients dienen
  • +
  • Bessere Unterstützung für UMTS-3G-Modems
  • +
  • Verwendung von tmpfs zur Reduzierung von Schreibzugriffen
  • +
  • Verbesserte Hardwareüberwachung durch Lmsensors
  • +
  • Vnstat Traffic-Accounting ersetzt ipac-ng
  • +
+

Das IPFire-Team

" }} +} diff --git a/www/pages/static/__init__.py b/www/pages/static/__init__.py new file mode 100644 index 00000000..802278b4 --- /dev/null +++ b/www/pages/static/__init__.py @@ -0,0 +1,97 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +import os +from xml.dom.minidom import parseString + +import web + +class Xml: + def __init__(self, file): + file = "%s/pages/static/%s.xml" % (os.getcwd(), file,) + f = open(file) + data = f.read() + f.close() + + self.xml = parseString(data).getElementsByTagName("Site")[0] + + def getAttribute(self, node, attr): + return node.getAttribute(attr).strip() + + def getText(self, node): + ret = "" + for i in node.childNodes: + ret += i.data + return ret.encode("utf-8") + + +class Content(Xml): + def __init__(self, file,): + Xml.__init__(self, file) + + def __call__(self, lang="en"): + ret = "" + for paragraphs in self.xml.getElementsByTagName("Paragraphs"): + for paragraph in paragraphs.getElementsByTagName("Paragraph"): + if self.getAttribute(paragraph, "news") == "1": + news = web.News(int(self.getAttribute(paragraph, "count"))) + ret += news(lang).encode("utf-8") + continue + + # Heading + for heading in paragraph.getElementsByTagName("Heading"): + if self.getAttribute(heading, "lang") == lang or \ + not self.getAttribute(heading, "lang"): + heading = self.getText(heading) + break + + b = web.Box(heading) + + # Content + for content in paragraph.getElementsByTagName("Content"): + if self.getAttribute(content, "lang") == lang or \ + not self.getAttribute(content, "lang"): + if self.getAttribute(content, "raw") == "1": + s = self.getText(content) + else: + s = "

%s

" % self.getText(content) + b.w(s) + + ret += b() + return ret + + +class Sidebar(Xml): + def __init__(self, file): + Xml.__init__(self, file) + + def __call__(self, lang="en"): + ret = "" + sidebar = self.xml.getElementsByTagName("Sidebar")[0] + for paragraph in sidebar.getElementsByTagName("Paragraph"): + if self.getAttribute(paragraph, "banner") == "1": + b = web.Banners() + ret += """

%(title)s

+ """ % b.random() + continue + + # Heading + for heading in paragraph.getElementsByTagName("Heading"): + if self.getAttribute(heading, "lang") == lang or \ + not self.getAttribute(heading, "lang"): + heading = self.getText(heading) + break + + ret += "

%s

" % heading + + # Content + for content in paragraph.getElementsByTagName("Content"): + if self.getAttribute(content, "lang") == lang or \ + not self.getAttribute(content, "lang"): + if self.getAttribute(content, "raw") == "1": + s = self.getText(content) + else: + s = "

%s

" % self.getText(content) + ret += s + + return ret diff --git a/www/data/development.xml b/www/pages/static/development.xml similarity index 100% rename from www/data/development.xml rename to www/pages/static/development.xml diff --git a/www/data/download.xml b/www/pages/static/download.xml similarity index 99% rename from www/data/download.xml rename to www/pages/static/download.xml index ec59456a..2f5b4bec 100644 --- a/www/data/download.xml +++ b/www/pages/static/download.xml @@ -159,5 +159,7 @@ ]]> + + diff --git a/www/data/features.xml b/www/pages/static/features.xml similarity index 100% rename from www/data/features.xml rename to www/pages/static/features.xml diff --git a/www/data/imprint.xml b/www/pages/static/imprint.xml similarity index 100% rename from www/data/imprint.xml rename to www/pages/static/imprint.xml diff --git a/www/data/index.xml b/www/pages/static/index.xml similarity index 93% rename from www/data/index.xml rename to www/pages/static/index.xml index e8ba711c..05eaafe2 100644 --- a/www/data/index.xml +++ b/www/pages/static/index.xml @@ -53,7 +53,7 @@
  • Voice-over-IP solution with Asterisk and Teamspeak plus traffic prioritization
  • Multimedia addons (video- & audio-streaming, jukebox)
  • WLan Access Point with hostap and many Atheros Chips
  • -
  • and many more - List of all Addons
  • +
  • and many more - List of all Addons
  • ]]> Voice-over-IP-Lösung mittels Asterisk und Teamspeak, sowie Traffic-Priorisierung
  • Multimedia-Addons (Video- & Audio-Streaming, Jukebox)
  • WLan Access Point mittels hostap für viele Atheros Chips
  • -
  • und weiteren Addons - Alle Addons im Wiki
  • +
  • und weiteren Addons - Alle Addons im Wiki
  • - ]]>
    - + ]]>
    + + @@ -85,7 +86,10 @@ ]]> + + + RSS feed]]> diff --git a/www/pages/torrent/__init__.py b/www/pages/torrent/__init__.py new file mode 100644 index 00000000..3490bc78 --- /dev/null +++ b/www/pages/torrent/__init__.py @@ -0,0 +1,85 @@ +#!/usr/bin/python + +TRACKER_URL ="http://tracker.ipfire.org:6969/stats?format=txt&mode=tpbs" +TORRENT_BASE="/srv/pakfire/data/torrent" + +import os +import sha +import urllib2 + +from client.bencode import bencode, bdecode +import web + +class TrackerInfo: + def __init__(self, url): + self.info = {} + + f = urllib2.urlopen(url) + for line in f.readlines(): + (hash, seeds, peers,) = line.split(":") + self.info[hash] = (seeds, peers.rstrip("\n"),) + f.close() + + def __call__(self): + print self.info + + def get(self, hash): + try: + return self.info[hash] + except KeyError: + return 0, 0 + + +class TorrentObject: + def __init__(self, file): + self.name = os.path.basename(file) + f = open(file, "rb") + self.info = bdecode(f.read()) + f.close() + + def __call__(self): + print "File : %s" % self.get_file() + print "Hash : %s" % self.get_hash() + + def get_hash(self): + return sha.sha(bencode(self.info["info"])).hexdigest().upper() + + def get_file(self): + return self.name + + +torrent_files = [] +for file in os.listdir(TORRENT_BASE): + if not file.endswith(".torrent"): + continue + file = os.path.join(TORRENT_BASE, file) + torrent_files.insert(0, TorrentObject(file)) + + +tracker = TrackerInfo(TRACKER_URL) + +class TorrentBox(web.Box): + def __init__(self, file): + web.Box.__init__(self, file.name, file.get_hash()) + self.w(""" +

    + Seeders: %s
    + Leechers: %s +

    """ % tracker.get(file.get_hash())) + self.w(""" +

    + Download +

    """ % (file.name,)) + + +class Content(web.Content): + def __init__(self, name): + web.Content.__init__(self, name) + + def content(self): + self.w("

    IPFire Torrent Tracker

    ") + for t in torrent_files: + b = TorrentBox(t) + self.w(b()) + +Sidebar = web.Sidebar diff --git a/www/pages/torrent/client/ConfigDir.py b/www/pages/torrent/client/ConfigDir.py new file mode 100644 index 00000000..ee2b23de --- /dev/null +++ b/www/pages/torrent/client/ConfigDir.py @@ -0,0 +1,401 @@ +#written by John Hoffman + +from inifile import ini_write, ini_read +from bencode import bencode, bdecode +from types import IntType, LongType, StringType, FloatType +from CreateIcons import GetIcons, CreateIcon +from parseargs import defaultargs +from __init__ import product_name, version_short +import sys,os +from time import time, strftime + +try: + True +except: + True = 1 + False = 0 + +try: + realpath = os.path.realpath +except: + realpath = lambda x:x +OLDICONPATH = os.path.abspath(os.path.dirname(realpath(sys.argv[0]))) + +DIRNAME = '.'+product_name + +hexchars = '0123456789abcdef' +hexmap = [] +revmap = {} +for i in xrange(256): + x = hexchars[(i&0xF0)/16]+hexchars[i&0x0F] + hexmap.append(x) + revmap[x] = chr(i) + +def tohex(s): + r = [] + for c in s: + r.append(hexmap[ord(c)]) + return ''.join(r) + +def unhex(s): + r = [ revmap[s[x:x+2]] for x in xrange(0, len(s), 2) ] + return ''.join(r) + +def copyfile(oldpath, newpath): # simple file copy, all in RAM + try: + f = open(oldpath,'rb') + r = f.read() + success = True + except: + success = False + try: + f.close() + except: + pass + if not success: + return False + try: + f = open(newpath,'wb') + f.write(r) + except: + success = False + try: + f.close() + except: + pass + return success + + +class ConfigDir: + + ###### INITIALIZATION TASKS ###### + + def __init__(self, config_type = None): + self.config_type = config_type + if config_type: + config_ext = '.'+config_type + else: + config_ext = '' + + def check_sysvars(x): + y = os.path.expandvars(x) + if y != x and os.path.isdir(y): + return y + return None + + for d in ['${APPDATA}', '${HOME}', '${HOMEPATH}', '${USERPROFILE}']: + dir_root = check_sysvars(d) + if dir_root: + break + else: + dir_root = os.path.expanduser('~') + if not os.path.isdir(dir_root): + dir_root = os.path.abspath(os.path.dirname(sys.argv[0])) + + dir_root = os.path.join(dir_root,DIRNAME) + self.dir_root = dir_root + + if not os.path.isdir(self.dir_root): + os.mkdir(self.dir_root,0700) # exception if failed + + self.dir_icons = os.path.join(dir_root,'icons') + if not os.path.isdir(self.dir_icons): + os.mkdir(self.dir_icons) + for icon in GetIcons(): + i = os.path.join(self.dir_icons,icon) + if not os.path.exists(i): + if not copyfile(os.path.join(OLDICONPATH,icon),i): + CreateIcon(icon,self.dir_icons) + + self.dir_torrentcache = os.path.join(dir_root,'torrentcache') + if not os.path.isdir(self.dir_torrentcache): + os.mkdir(self.dir_torrentcache) + + self.dir_datacache = os.path.join(dir_root,'datacache') + if not os.path.isdir(self.dir_datacache): + os.mkdir(self.dir_datacache) + + self.dir_piececache = os.path.join(dir_root,'piececache') + if not os.path.isdir(self.dir_piececache): + os.mkdir(self.dir_piececache) + + self.configfile = os.path.join(dir_root,'config'+config_ext+'.ini') + self.statefile = os.path.join(dir_root,'state'+config_ext) + + self.TorrentDataBuffer = {} + + + ###### CONFIG HANDLING ###### + + def setDefaults(self, defaults, ignore=[]): + self.config = defaultargs(defaults) + for k in ignore: + if self.config.has_key(k): + del self.config[k] + + def checkConfig(self): + return os.path.exists(self.configfile) + + def loadConfig(self): + try: + r = ini_read(self.configfile)[''] + except: + return self.config + l = self.config.keys() + for k,v in r.items(): + if self.config.has_key(k): + t = type(self.config[k]) + try: + if t == StringType: + self.config[k] = v + elif t == IntType or t == LongType: + self.config[k] = long(v) + elif t == FloatType: + self.config[k] = float(v) + l.remove(k) + except: + pass + if l: # new default values since last save + self.saveConfig() + return self.config + + def saveConfig(self, new_config = None): + if new_config: + for k,v in new_config.items(): + if self.config.has_key(k): + self.config[k] = v + try: + ini_write( self.configfile, self.config, + 'Generated by '+product_name+'/'+version_short+'\n' + + strftime('%x %X') ) + return True + except: + return False + + def getConfig(self): + return self.config + + + ###### STATE HANDLING ###### + + def getState(self): + try: + f = open(self.statefile,'rb') + r = f.read() + except: + r = None + try: + f.close() + except: + pass + try: + r = bdecode(r) + except: + r = None + return r + + def saveState(self, state): + try: + f = open(self.statefile,'wb') + f.write(bencode(state)) + success = True + except: + success = False + try: + f.close() + except: + pass + return success + + + ###### TORRENT HANDLING ###### + + def getTorrents(self): + d = {} + for f in os.listdir(self.dir_torrentcache): + f = os.path.basename(f) + try: + f, garbage = f.split('.') + except: + pass + d[unhex(f)] = 1 + return d.keys() + + def getTorrentVariations(self, t): + t = tohex(t) + d = [] + for f in os.listdir(self.dir_torrentcache): + f = os.path.basename(f) + if f[:len(t)] == t: + try: + garbage, ver = f.split('.') + except: + ver = '0' + d.append(int(ver)) + d.sort() + return d + + def getTorrent(self, t, v = -1): + t = tohex(t) + if v == -1: + v = max(self.getTorrentVariations(t)) # potential exception + if v: + t += '.'+str(v) + try: + f = open(os.path.join(self.dir_torrentcache,t),'rb') + r = bdecode(f.read()) + except: + r = None + try: + f.close() + except: + pass + return r + + def writeTorrent(self, data, t, v = -1): + t = tohex(t) + if v == -1: + try: + v = max(self.getTorrentVariations(t))+1 + except: + v = 0 + if v: + t += '.'+str(v) + try: + f = open(os.path.join(self.dir_torrentcache,t),'wb') + f.write(bencode(data)) + except: + v = None + try: + f.close() + except: + pass + return v + + + ###### TORRENT DATA HANDLING ###### + + def getTorrentData(self, t): + if self.TorrentDataBuffer.has_key(t): + return self.TorrentDataBuffer[t] + t = os.path.join(self.dir_datacache,tohex(t)) + if not os.path.exists(t): + return None + try: + f = open(t,'rb') + r = bdecode(f.read()) + except: + r = None + try: + f.close() + except: + pass + self.TorrentDataBuffer[t] = r + return r + + def writeTorrentData(self, t, data): + self.TorrentDataBuffer[t] = data + try: + f = open(os.path.join(self.dir_datacache,tohex(t)),'wb') + f.write(bencode(data)) + success = True + except: + success = False + try: + f.close() + except: + pass + if not success: + self.deleteTorrentData(t) + return success + + def deleteTorrentData(self, t): + try: + os.remove(os.path.join(self.dir_datacache,tohex(t))) + except: + pass + + def getPieceDir(self, t): + return os.path.join(self.dir_piececache,tohex(t)) + + + ###### EXPIRATION HANDLING ###### + + def deleteOldCacheData(self, days, still_active = [], delete_torrents = False): + if not days: + return + exptime = time() - (days*24*3600) + names = {} + times = {} + + for f in os.listdir(self.dir_torrentcache): + p = os.path.join(self.dir_torrentcache,f) + f = os.path.basename(f) + try: + f, garbage = f.split('.') + except: + pass + try: + f = unhex(f) + assert len(f) == 20 + except: + continue + if delete_torrents: + names.setdefault(f,[]).append(p) + try: + t = os.path.getmtime(p) + except: + t = time() + times.setdefault(f,[]).append(t) + + for f in os.listdir(self.dir_datacache): + p = os.path.join(self.dir_datacache,f) + try: + f = unhex(os.path.basename(f)) + assert len(f) == 20 + except: + continue + names.setdefault(f,[]).append(p) + try: + t = os.path.getmtime(p) + except: + t = time() + times.setdefault(f,[]).append(t) + + for f in os.listdir(self.dir_piececache): + p = os.path.join(self.dir_piececache,f) + try: + f = unhex(os.path.basename(f)) + assert len(f) == 20 + except: + continue + for f2 in os.listdir(p): + p2 = os.path.join(p,f2) + names.setdefault(f,[]).append(p2) + try: + t = os.path.getmtime(p2) + except: + t = time() + times.setdefault(f,[]).append(t) + names.setdefault(f,[]).append(p) + + for k,v in times.items(): + if max(v) < exptime and not k in still_active: + for f in names[k]: + try: + os.remove(f) + except: + try: + os.removedirs(f) + except: + pass + + + def deleteOldTorrents(self, days, still_active = []): + self.deleteOldCacheData(days, still_active, True) + + + ###### OTHER ###### + + def getIconDir(self): + return self.dir_icons diff --git a/www/pages/torrent/client/ConfigReader.py b/www/pages/torrent/client/ConfigReader.py new file mode 100644 index 00000000..e9353bb9 --- /dev/null +++ b/www/pages/torrent/client/ConfigReader.py @@ -0,0 +1,1068 @@ +#written by John Hoffman + +from ConnChoice import * +from wxPython.wx import * +from types import IntType, FloatType, StringType +from download_bt1 import defaults +from ConfigDir import ConfigDir +import sys,os +import socket +from parseargs import defaultargs + +try: + True +except: + True = 1 + False = 0 + +try: + wxFULL_REPAINT_ON_RESIZE +except: + wxFULL_REPAINT_ON_RESIZE = 0 # fix for wx pre-2.5 + +if (sys.platform == 'win32'): + _FONT = 9 +else: + _FONT = 10 + +def HexToColor(s): + r,g,b = s.split(' ') + return wxColour(red=int(r,16), green=int(g,16), blue=int(b,16)) + +def hex2(c): + h = hex(c)[2:] + if len(h) == 1: + h = '0'+h + return h +def ColorToHex(c): + return hex2(c.Red()) + ' ' + hex2(c.Green()) + ' ' + hex2(c.Blue()) + +ratesettingslist = [] +for x in connChoices: + if not x.has_key('super-seed'): + ratesettingslist.append(x['name']) + + +configFileDefaults = [ + #args only available for the gui client + ('win32_taskbar_icon', 1, + "whether to iconize do system try or not on win32"), + ('gui_stretchwindow', 0, + "whether to stretch the download status window to fit the torrent name"), + ('gui_displaystats', 1, + "whether to display statistics on peers and seeds"), + ('gui_displaymiscstats', 1, + "whether to display miscellaneous other statistics"), + ('gui_ratesettingsdefault', ratesettingslist[0], + "the default setting for maximum upload rate and users"), + ('gui_ratesettingsmode', 'full', + "what rate setting controls to display; options are 'none', 'basic', and 'full'"), + ('gui_forcegreenonfirewall', 0, + "forces the status icon to be green even if the client seems to be firewalled"), + ('gui_default_savedir', '', + "default save directory"), + ('last_saved', '', # hidden; not set in config + "where the last torrent was saved"), + ('gui_font', _FONT, + "the font size to use"), + ('gui_saveas_ask', -1, + "whether to ask where to download to (0 = never, 1 = always, -1 = automatic resume"), +] + +def setwxconfigfiledefaults(): + CHECKINGCOLOR = ColorToHex(wxSystemSettings_GetColour(wxSYS_COLOUR_3DSHADOW)) + DOWNLOADCOLOR = ColorToHex(wxSystemSettings_GetColour(wxSYS_COLOUR_ACTIVECAPTION)) + + configFileDefaults.extend([ + ('gui_checkingcolor', CHECKINGCOLOR, + "progress bar checking color"), + ('gui_downloadcolor', DOWNLOADCOLOR, + "progress bar downloading color"), + ('gui_seedingcolor', '00 FF 00', + "progress bar seeding color"), + ]) + +defaultsToIgnore = ['responsefile', 'url', 'priority'] + + +class configReader: + + def __init__(self): + self.configfile = wxConfig("BitTorrent",style=wxCONFIG_USE_LOCAL_FILE) + self.configMenuBox = None + self.advancedMenuBox = None + self._configReset = True # run reset for the first time + + setwxconfigfiledefaults() + + defaults.extend(configFileDefaults) + self.defaults = defaultargs(defaults) + + self.configDir = ConfigDir('gui') + self.configDir.setDefaults(defaults,defaultsToIgnore) + if self.configDir.checkConfig(): + self.config = self.configDir.loadConfig() + else: + self.config = self.configDir.getConfig() + self.importOldGUIConfig() + self.configDir.saveConfig() + + updated = False # make all config default changes here + + if self.config['gui_ratesettingsdefault'] not in ratesettingslist: + self.config['gui_ratesettingsdefault'] = ( + self.defaults['gui_ratesettingsdefault'] ) + updated = True + if self.config['ipv6_enabled'] and ( + sys.version_info < (2,3) or not socket.has_ipv6 ): + self.config['ipv6_enabled'] = 0 + updated = True + for c in ['gui_checkingcolor','gui_downloadcolor','gui_seedingcolor']: + try: + HexToColor(self.config[c]) + except: + self.config[c] = self.defaults[c] + updated = True + + if updated: + self.configDir.saveConfig() + + self.configDir.deleteOldCacheData(self.config['expire_cache_data']) + + + def importOldGUIConfig(self): + oldconfig = wxConfig("BitTorrent",style=wxCONFIG_USE_LOCAL_FILE) + cont, s, i = oldconfig.GetFirstEntry() + if not cont: + oldconfig.DeleteAll() + return False + while cont: # import old config data + if self.config.has_key(s): + t = oldconfig.GetEntryType(s) + try: + if t == 1: + assert type(self.config[s]) == type('') + self.config[s] = oldconfig.Read(s) + elif t == 2 or t == 3: + assert type(self.config[s]) == type(1) + self.config[s] = int(oldconfig.ReadInt(s)) + elif t == 4: + assert type(self.config[s]) == type(1.0) + self.config[s] = oldconfig.ReadFloat(s) + except: + pass + cont, s, i = oldconfig.GetNextEntry(i) + +# oldconfig.DeleteAll() + return True + + + def resetConfigDefaults(self): + for p,v in self.defaults.items(): + if not p in defaultsToIgnore: + self.config[p] = v + self.configDir.saveConfig() + + def writeConfigFile(self): + self.configDir.saveConfig() + + def WriteLastSaved(self, l): + self.config['last_saved'] = l + self.configDir.saveConfig() + + + def getcheckingcolor(self): + return HexToColor(self.config['gui_checkingcolor']) + def getdownloadcolor(self): + return HexToColor(self.config['gui_downloadcolor']) + def getseedingcolor(self): + return HexToColor(self.config['gui_seedingcolor']) + + def configReset(self): + r = self._configReset + self._configReset = False + return r + + def getConfigDir(self): + return self.configDir + + def getIconDir(self): + return self.configDir.getIconDir() + + def getTorrentData(self,t): + return self.configDir.getTorrentData(t) + + def setColorIcon(self, xxicon, xxiconptr, xxcolor): + idata = wxMemoryDC() + idata.SelectObject(xxicon) + idata.SetBrush(wxBrush(xxcolor,wxSOLID)) + idata.DrawRectangle(0,0,16,16) + idata.SelectObject(wxNullBitmap) + xxiconptr.Refresh() + + + def getColorFromUser(self, parent, colInit): + data = wxColourData() + if colInit.Ok(): + data.SetColour(colInit) + data.SetCustomColour(0, self.checkingcolor) + data.SetCustomColour(1, self.downloadcolor) + data.SetCustomColour(2, self.seedingcolor) + dlg = wxColourDialog(parent,data) + if not dlg.ShowModal(): + return colInit + return dlg.GetColourData().GetColour() + + + def configMenu(self, parent): + self.parent = parent + try: + self.FONT = self.config['gui_font'] + self.default_font = wxFont(self.FONT, wxDEFAULT, wxNORMAL, wxNORMAL, False) + self.checkingcolor = HexToColor(self.config['gui_checkingcolor']) + self.downloadcolor = HexToColor(self.config['gui_downloadcolor']) + self.seedingcolor = HexToColor(self.config['gui_seedingcolor']) + + if (self.configMenuBox is not None): + try: + self.configMenuBox.Close() + except wxPyDeadObjectError, e: + self.configMenuBox = None + + self.configMenuBox = wxFrame(None, -1, 'BitTorrent Preferences', size = (1,1), + style = wxDEFAULT_FRAME_STYLE|wxFULL_REPAINT_ON_RESIZE) + if (sys.platform == 'win32'): + self.icon = self.parent.icon + self.configMenuBox.SetIcon(self.icon) + + panel = wxPanel(self.configMenuBox, -1) + self.panel = panel + + def StaticText(text, font = self.FONT, underline = False, color = None, panel = panel): + x = wxStaticText(panel, -1, text, style = wxALIGN_LEFT) + x.SetFont(wxFont(font, wxDEFAULT, wxNORMAL, wxNORMAL, underline)) + if color is not None: + x.SetForegroundColour(color) + return x + + colsizer = wxFlexGridSizer(cols = 1, vgap = 8) + + self.gui_stretchwindow_checkbox = wxCheckBox(panel, -1, "Stretch window to fit torrent name *") + self.gui_stretchwindow_checkbox.SetFont(self.default_font) + self.gui_stretchwindow_checkbox.SetValue(self.config['gui_stretchwindow']) + + self.gui_displaystats_checkbox = wxCheckBox(panel, -1, "Display peer and seed statistics") + self.gui_displaystats_checkbox.SetFont(self.default_font) + self.gui_displaystats_checkbox.SetValue(self.config['gui_displaystats']) + + self.gui_displaymiscstats_checkbox = wxCheckBox(panel, -1, "Display miscellaneous other statistics") + self.gui_displaymiscstats_checkbox.SetFont(self.default_font) + self.gui_displaymiscstats_checkbox.SetValue(self.config['gui_displaymiscstats']) + + self.security_checkbox = wxCheckBox(panel, -1, "Don't allow multiple connections from the same IP") + self.security_checkbox.SetFont(self.default_font) + self.security_checkbox.SetValue(self.config['security']) + + self.autokick_checkbox = wxCheckBox(panel, -1, "Kick/ban clients that send you bad data *") + self.autokick_checkbox.SetFont(self.default_font) + self.autokick_checkbox.SetValue(self.config['auto_kick']) + + self.buffering_checkbox = wxCheckBox(panel, -1, "Enable read/write buffering *") + self.buffering_checkbox.SetFont(self.default_font) + self.buffering_checkbox.SetValue(self.config['buffer_reads']) + + self.breakup_checkbox = wxCheckBox(panel, -1, "Break-up seed bitfield to foil ISP manipulation") + self.breakup_checkbox.SetFont(self.default_font) + self.breakup_checkbox.SetValue(self.config['breakup_seed_bitfield']) + + self.autoflush_checkbox = wxCheckBox(panel, -1, "Flush data to disk every 5 minutes") + self.autoflush_checkbox.SetFont(self.default_font) + self.autoflush_checkbox.SetValue(self.config['auto_flush']) + + if sys.version_info >= (2,3) and socket.has_ipv6: + self.ipv6enabled_checkbox = wxCheckBox(panel, -1, "Initiate and receive connections via IPv6 *") + self.ipv6enabled_checkbox.SetFont(self.default_font) + self.ipv6enabled_checkbox.SetValue(self.config['ipv6_enabled']) + + self.gui_forcegreenonfirewall_checkbox = wxCheckBox(panel, -1, + "Force icon to display green when firewalled") + self.gui_forcegreenonfirewall_checkbox.SetFont(self.default_font) + self.gui_forcegreenonfirewall_checkbox.SetValue(self.config['gui_forcegreenonfirewall']) + + + self.minport_data = wxSpinCtrl(panel, -1, '', (-1,-1), (self.FONT*8, -1)) + self.minport_data.SetFont(self.default_font) + self.minport_data.SetRange(1,65535) + self.minport_data.SetValue(self.config['minport']) + + self.maxport_data = wxSpinCtrl(panel, -1, '', (-1,-1), (self.FONT*8, -1)) + self.maxport_data.SetFont(self.default_font) + self.maxport_data.SetRange(1,65535) + self.maxport_data.SetValue(self.config['maxport']) + + self.randomport_checkbox = wxCheckBox(panel, -1, "randomize") + self.randomport_checkbox.SetFont(self.default_font) + self.randomport_checkbox.SetValue(self.config['random_port']) + + self.gui_font_data = wxSpinCtrl(panel, -1, '', (-1,-1), (self.FONT*5, -1)) + self.gui_font_data.SetFont(self.default_font) + self.gui_font_data.SetRange(8,16) + self.gui_font_data.SetValue(self.config['gui_font']) + + self.gui_ratesettingsdefault_data=wxChoice(panel, -1, choices = ratesettingslist) + self.gui_ratesettingsdefault_data.SetFont(self.default_font) + self.gui_ratesettingsdefault_data.SetStringSelection(self.config['gui_ratesettingsdefault']) + + self.maxdownload_data = wxSpinCtrl(panel, -1, '', (-1,-1), (self.FONT*7, -1)) + self.maxdownload_data.SetFont(self.default_font) + self.maxdownload_data.SetRange(0,5000) + self.maxdownload_data.SetValue(self.config['max_download_rate']) + + self.gui_ratesettingsmode_data=wxRadioBox(panel, -1, 'Rate Settings Mode', + choices = [ 'none', 'basic', 'full' ] ) + self.gui_ratesettingsmode_data.SetFont(self.default_font) + self.gui_ratesettingsmode_data.SetStringSelection(self.config['gui_ratesettingsmode']) + + if (sys.platform == 'win32'): + self.win32_taskbar_icon_checkbox = wxCheckBox(panel, -1, "Minimize to system tray") + self.win32_taskbar_icon_checkbox.SetFont(self.default_font) + self.win32_taskbar_icon_checkbox.SetValue(self.config['win32_taskbar_icon']) + +# self.upnp_checkbox = wxCheckBox(panel, -1, "Enable automatic UPnP port forwarding") +# self.upnp_checkbox.SetFont(self.default_font) +# self.upnp_checkbox.SetValue(self.config['upnp_nat_access']) + self.upnp_data=wxChoice(panel, -1, + choices = ['disabled', 'type 1 (fast)', 'type 2 (slow)']) + self.upnp_data.SetFont(self.default_font) + self.upnp_data.SetSelection(self.config['upnp_nat_access']) + + self.gui_default_savedir_ctrl = wxTextCtrl(parent = panel, id = -1, + value = self.config['gui_default_savedir'], + size = (26*self.FONT, -1), style = wxTE_PROCESS_TAB) + self.gui_default_savedir_ctrl.SetFont(self.default_font) + + self.gui_savemode_data=wxRadioBox(panel, -1, 'Ask where to save: *', + choices = [ 'always', 'never', 'auto-resume' ] ) + self.gui_savemode_data.SetFont(self.default_font) + self.gui_savemode_data.SetSelection(1-self.config['gui_saveas_ask']) + + self.checkingcolor_icon = wxEmptyBitmap(16,16) + self.checkingcolor_iconptr = wxStaticBitmap(panel, -1, self.checkingcolor_icon) + self.setColorIcon(self.checkingcolor_icon, self.checkingcolor_iconptr, self.checkingcolor) + + self.downloadcolor_icon = wxEmptyBitmap(16,16) + self.downloadcolor_iconptr = wxStaticBitmap(panel, -1, self.downloadcolor_icon) + self.setColorIcon(self.downloadcolor_icon, self.downloadcolor_iconptr, self.downloadcolor) + + self.seedingcolor_icon = wxEmptyBitmap(16,16) + self.seedingcolor_iconptr = wxStaticBitmap(panel, -1, self.seedingcolor_icon) + self.setColorIcon(self.seedingcolor_icon, self.downloadcolor_iconptr, self.seedingcolor) + + rowsizer = wxFlexGridSizer(cols = 2, hgap = 20) + + block12sizer = wxFlexGridSizer(cols = 1, vgap = 7) + + block1sizer = wxFlexGridSizer(cols = 1, vgap = 2) + if (sys.platform == 'win32'): + block1sizer.Add(self.win32_taskbar_icon_checkbox) +# block1sizer.Add(self.upnp_checkbox) + block1sizer.Add(self.gui_stretchwindow_checkbox) + block1sizer.Add(self.gui_displaystats_checkbox) + block1sizer.Add(self.gui_displaymiscstats_checkbox) + block1sizer.Add(self.security_checkbox) + block1sizer.Add(self.autokick_checkbox) + block1sizer.Add(self.buffering_checkbox) + block1sizer.Add(self.breakup_checkbox) + block1sizer.Add(self.autoflush_checkbox) + if sys.version_info >= (2,3) and socket.has_ipv6: + block1sizer.Add(self.ipv6enabled_checkbox) + block1sizer.Add(self.gui_forcegreenonfirewall_checkbox) + + block12sizer.Add(block1sizer) + + colorsizer = wxStaticBoxSizer(wxStaticBox(panel, -1, "Gauge Colors:"), wxVERTICAL) + colorsizer1 = wxFlexGridSizer(cols = 7) + colorsizer1.Add(StaticText(' Checking: '), 1, wxALIGN_BOTTOM) + colorsizer1.Add(self.checkingcolor_iconptr, 1, wxALIGN_BOTTOM) + colorsizer1.Add(StaticText(' Downloading: '), 1, wxALIGN_BOTTOM) + colorsizer1.Add(self.downloadcolor_iconptr, 1, wxALIGN_BOTTOM) + colorsizer1.Add(StaticText(' Seeding: '), 1, wxALIGN_BOTTOM) + colorsizer1.Add(self.seedingcolor_iconptr, 1, wxALIGN_BOTTOM) + colorsizer1.Add(StaticText(' ')) + minsize = self.checkingcolor_iconptr.GetBestSize() + minsize.SetHeight(minsize.GetHeight()+5) + colorsizer1.SetMinSize(minsize) + colorsizer.Add(colorsizer1) + + block12sizer.Add(colorsizer, 1, wxALIGN_LEFT) + + rowsizer.Add(block12sizer) + + block3sizer = wxFlexGridSizer(cols = 1) + + portsettingsSizer = wxStaticBoxSizer(wxStaticBox(panel, -1, "Port Range:*"), wxVERTICAL) + portsettingsSizer1 = wxGridSizer(cols = 2, vgap = 1) + portsettingsSizer1.Add(StaticText('From: '), 1, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT) + portsettingsSizer1.Add(self.minport_data, 1, wxALIGN_BOTTOM) + portsettingsSizer1.Add(StaticText('To: '), 1, wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT) + portsettingsSizer1.Add(self.maxport_data, 1, wxALIGN_BOTTOM) + portsettingsSizer.Add(portsettingsSizer1) + portsettingsSizer.Add(self.randomport_checkbox, 1, wxALIGN_CENTER) + block3sizer.Add(portsettingsSizer, 1, wxALIGN_CENTER) + block3sizer.Add(StaticText(' ')) + block3sizer.Add(self.gui_ratesettingsmode_data, 1, wxALIGN_CENTER) + block3sizer.Add(StaticText(' ')) + ratesettingsSizer = wxFlexGridSizer(cols = 1, vgap = 2) + ratesettingsSizer.Add(StaticText('Default Rate Setting: *'), 1, wxALIGN_CENTER) + ratesettingsSizer.Add(self.gui_ratesettingsdefault_data, 1, wxALIGN_CENTER) + block3sizer.Add(ratesettingsSizer, 1, wxALIGN_CENTER) + if (sys.platform == 'win32'): + block3sizer.Add(StaticText(' ')) + upnpSizer = wxFlexGridSizer(cols = 1, vgap = 2) + upnpSizer.Add(StaticText('UPnP Port Forwarding: *'), 1, wxALIGN_CENTER) + upnpSizer.Add(self.upnp_data, 1, wxALIGN_CENTER) + block3sizer.Add(upnpSizer, 1, wxALIGN_CENTER) + + rowsizer.Add(block3sizer) + colsizer.Add(rowsizer) + + block4sizer = wxFlexGridSizer(cols = 3, hgap = 15) + savepathsizer = wxFlexGridSizer(cols = 2, vgap = 1) + savepathsizer.Add(StaticText('Default Save Path: *')) + savepathsizer.Add(StaticText(' ')) + savepathsizer.Add(self.gui_default_savedir_ctrl, 1, wxEXPAND) + savepathButton = wxButton(panel, -1, '...', size = (18,18)) +# savepathButton.SetFont(self.default_font) + savepathsizer.Add(savepathButton, 0, wxALIGN_CENTER) + savepathsizer.Add(self.gui_savemode_data, 0, wxALIGN_CENTER) + block4sizer.Add(savepathsizer, -1, wxALIGN_BOTTOM) + + fontsizer = wxFlexGridSizer(cols = 1, vgap = 2) + fontsizer.Add(StaticText('')) + fontsizer.Add(StaticText('Font: *'), 1, wxALIGN_CENTER) + fontsizer.Add(self.gui_font_data, 1, wxALIGN_CENTER) + block4sizer.Add(fontsizer, 1, wxALIGN_CENTER_VERTICAL) + + dratesettingsSizer = wxFlexGridSizer(cols = 1, vgap = 2) + dratesettingsSizer.Add(StaticText('Default Max'), 1, wxALIGN_CENTER) + dratesettingsSizer.Add(StaticText('Download Rate'), 1, wxALIGN_CENTER) + dratesettingsSizer.Add(StaticText('(kB/s): *'), 1, wxALIGN_CENTER) + dratesettingsSizer.Add(self.maxdownload_data, 1, wxALIGN_CENTER) + dratesettingsSizer.Add(StaticText('(0 = disabled)'), 1, wxALIGN_CENTER) + + block4sizer.Add(dratesettingsSizer, 1, wxALIGN_CENTER_VERTICAL) + + colsizer.Add(block4sizer, 0, wxALIGN_CENTER) +# colsizer.Add(StaticText(' ')) + + savesizer = wxGridSizer(cols = 4, hgap = 10) + saveButton = wxButton(panel, -1, 'Save') +# saveButton.SetFont(self.default_font) + savesizer.Add(saveButton, 0, wxALIGN_CENTER) + + cancelButton = wxButton(panel, -1, 'Cancel') +# cancelButton.SetFont(self.default_font) + savesizer.Add(cancelButton, 0, wxALIGN_CENTER) + + defaultsButton = wxButton(panel, -1, 'Revert to Defaults') +# defaultsButton.SetFont(self.default_font) + savesizer.Add(defaultsButton, 0, wxALIGN_CENTER) + + advancedButton = wxButton(panel, -1, 'Advanced...') +# advancedButton.SetFont(self.default_font) + savesizer.Add(advancedButton, 0, wxALIGN_CENTER) + colsizer.Add(savesizer, 1, wxALIGN_CENTER) + + resizewarningtext=StaticText('* These settings will not take effect until the next time you start BitTorrent', self.FONT-2) + colsizer.Add(resizewarningtext, 1, wxALIGN_CENTER) + + border = wxBoxSizer(wxHORIZONTAL) + border.Add(colsizer, 1, wxEXPAND | wxALL, 4) + + panel.SetSizer(border) + panel.SetAutoLayout(True) + + self.advancedConfig = {} + + def setDefaults(evt, self = self): + try: + self.minport_data.SetValue(self.defaults['minport']) + self.maxport_data.SetValue(self.defaults['maxport']) + self.randomport_checkbox.SetValue(self.defaults['random_port']) + self.gui_stretchwindow_checkbox.SetValue(self.defaults['gui_stretchwindow']) + self.gui_displaystats_checkbox.SetValue(self.defaults['gui_displaystats']) + self.gui_displaymiscstats_checkbox.SetValue(self.defaults['gui_displaymiscstats']) + self.security_checkbox.SetValue(self.defaults['security']) + self.autokick_checkbox.SetValue(self.defaults['auto_kick']) + self.buffering_checkbox.SetValue(self.defaults['buffer_reads']) + self.breakup_checkbox.SetValue(self.defaults['breakup_seed_bitfield']) + self.autoflush_checkbox.SetValue(self.defaults['auto_flush']) + if sys.version_info >= (2,3) and socket.has_ipv6: + self.ipv6enabled_checkbox.SetValue(self.defaults['ipv6_enabled']) + self.gui_forcegreenonfirewall_checkbox.SetValue(self.defaults['gui_forcegreenonfirewall']) + self.gui_font_data.SetValue(self.defaults['gui_font']) + self.gui_ratesettingsdefault_data.SetStringSelection(self.defaults['gui_ratesettingsdefault']) + self.maxdownload_data.SetValue(self.defaults['max_download_rate']) + self.gui_ratesettingsmode_data.SetStringSelection(self.defaults['gui_ratesettingsmode']) + self.gui_default_savedir_ctrl.SetValue(self.defaults['gui_default_savedir']) + self.gui_savemode_data.SetSelection(1-self.defaults['gui_saveas_ask']) + + self.checkingcolor = HexToColor(self.defaults['gui_checkingcolor']) + self.setColorIcon(self.checkingcolor_icon, self.checkingcolor_iconptr, self.checkingcolor) + self.downloadcolor = HexToColor(self.defaults['gui_downloadcolor']) + self.setColorIcon(self.downloadcolor_icon, self.downloadcolor_iconptr, self.downloadcolor) + self.seedingcolor = HexToColor(self.defaults['gui_seedingcolor']) + self.setColorIcon(self.seedingcolor_icon, self.seedingcolor_iconptr, self.seedingcolor) + + if (sys.platform == 'win32'): + self.win32_taskbar_icon_checkbox.SetValue(self.defaults['win32_taskbar_icon']) +# self.upnp_checkbox.SetValue(self.defaults['upnp_nat_access']) + self.upnp_data.SetSelection(self.defaults['upnp_nat_access']) + + # reset advanced too + self.advancedConfig = {} + for key in ['ip', 'bind', 'min_peers', 'max_initiate', 'display_interval', + 'alloc_type', 'alloc_rate', 'max_files_open', 'max_connections', 'super_seeder', + 'ipv6_binds_v4', 'double_check', 'triple_check', 'lock_files', 'lock_while_reading', + 'expire_cache_data']: + self.advancedConfig[key] = self.defaults[key] + self.CloseAdvanced() + except: + self.parent.exception() + + + def saveConfigs(evt, self = self): + try: + self.config['gui_stretchwindow']=int(self.gui_stretchwindow_checkbox.GetValue()) + self.config['gui_displaystats']=int(self.gui_displaystats_checkbox.GetValue()) + self.config['gui_displaymiscstats']=int(self.gui_displaymiscstats_checkbox.GetValue()) + self.config['security']=int(self.security_checkbox.GetValue()) + self.config['auto_kick']=int(self.autokick_checkbox.GetValue()) + buffering=int(self.buffering_checkbox.GetValue()) + self.config['buffer_reads']=buffering + if buffering: + self.config['write_buffer_size']=self.defaults['write_buffer_size'] + else: + self.config['write_buffer_size']=0 + self.config['breakup_seed_bitfield']=int(self.breakup_checkbox.GetValue()) + if self.autoflush_checkbox.GetValue(): + self.config['auto_flush']=5 + else: + self.config['auto_flush']=0 + if sys.version_info >= (2,3) and socket.has_ipv6: + self.config['ipv6_enabled']=int(self.ipv6enabled_checkbox.GetValue()) + self.config['gui_forcegreenonfirewall']=int(self.gui_forcegreenonfirewall_checkbox.GetValue()) + self.config['minport']=self.minport_data.GetValue() + self.config['maxport']=self.maxport_data.GetValue() + self.config['random_port']=int(self.randomport_checkbox.GetValue()) + self.config['gui_font']=self.gui_font_data.GetValue() + self.config['gui_ratesettingsdefault']=self.gui_ratesettingsdefault_data.GetStringSelection() + self.config['max_download_rate']=self.maxdownload_data.GetValue() + self.config['gui_ratesettingsmode']=self.gui_ratesettingsmode_data.GetStringSelection() + self.config['gui_default_savedir']=self.gui_default_savedir_ctrl.GetValue() + self.config['gui_saveas_ask']=1-self.gui_savemode_data.GetSelection() + self.config['gui_checkingcolor']=ColorToHex(self.checkingcolor) + self.config['gui_downloadcolor']=ColorToHex(self.downloadcolor) + self.config['gui_seedingcolor']=ColorToHex(self.seedingcolor) + + if (sys.platform == 'win32'): + self.config['win32_taskbar_icon']=int(self.win32_taskbar_icon_checkbox.GetValue()) +# self.config['upnp_nat_access']=int(self.upnp_checkbox.GetValue()) + self.config['upnp_nat_access']=self.upnp_data.GetSelection() + + if self.advancedConfig: + for key,val in self.advancedConfig.items(): + self.config[key] = val + + self.writeConfigFile() + self._configReset = True + self.Close() + except: + self.parent.exception() + + def cancelConfigs(evt, self = self): + self.Close() + + def savepath_set(evt, self = self): + try: + d = self.gui_default_savedir_ctrl.GetValue() + if d == '': + d = self.config['last_saved'] + dl = wxDirDialog(self.panel, 'Choose a default directory to save to', + d, style = wxDD_DEFAULT_STYLE | wxDD_NEW_DIR_BUTTON) + if dl.ShowModal() == wxID_OK: + self.gui_default_savedir_ctrl.SetValue(dl.GetPath()) + except: + self.parent.exception() + + def checkingcoloricon_set(evt, self = self): + try: + newcolor = self.getColorFromUser(self.panel,self.checkingcolor) + self.setColorIcon(self.checkingcolor_icon, self.checkingcolor_iconptr, newcolor) + self.checkingcolor = newcolor + except: + self.parent.exception() + + def downloadcoloricon_set(evt, self = self): + try: + newcolor = self.getColorFromUser(self.panel,self.downloadcolor) + self.setColorIcon(self.downloadcolor_icon, self.downloadcolor_iconptr, newcolor) + self.downloadcolor = newcolor + except: + self.parent.exception() + + def seedingcoloricon_set(evt, self = self): + try: + newcolor = self.getColorFromUser(self.panel,self.seedingcolor) + self.setColorIcon(self.seedingcolor_icon, self.seedingcolor_iconptr, newcolor) + self.seedingcolor = newcolor + except: + self.parent.exception() + + EVT_BUTTON(self.configMenuBox, saveButton.GetId(), saveConfigs) + EVT_BUTTON(self.configMenuBox, cancelButton.GetId(), cancelConfigs) + EVT_BUTTON(self.configMenuBox, defaultsButton.GetId(), setDefaults) + EVT_BUTTON(self.configMenuBox, advancedButton.GetId(), self.advancedMenu) + EVT_BUTTON(self.configMenuBox, savepathButton.GetId(), savepath_set) + EVT_LEFT_DOWN(self.checkingcolor_iconptr, checkingcoloricon_set) + EVT_LEFT_DOWN(self.downloadcolor_iconptr, downloadcoloricon_set) + EVT_LEFT_DOWN(self.seedingcolor_iconptr, seedingcoloricon_set) + + self.configMenuBox.Show () + border.Fit(panel) + self.configMenuBox.Fit() + except: + self.parent.exception() + + + def Close(self): + self.CloseAdvanced() + if self.configMenuBox is not None: + try: + self.configMenuBox.Close () + except wxPyDeadObjectError, e: + pass + self.configMenuBox = None + + def advancedMenu(self, event = None): + try: + if not self.advancedConfig: + for key in ['ip', 'bind', 'min_peers', 'max_initiate', 'display_interval', + 'alloc_type', 'alloc_rate', 'max_files_open', 'max_connections', 'super_seeder', + 'ipv6_binds_v4', 'double_check', 'triple_check', 'lock_files', 'lock_while_reading', + 'expire_cache_data']: + self.advancedConfig[key] = self.config[key] + + if (self.advancedMenuBox is not None): + try: + self.advancedMenuBox.Close () + except wxPyDeadObjectError, e: + self.advancedMenuBox = None + + self.advancedMenuBox = wxFrame(None, -1, 'BitTorrent Advanced Preferences', size = (1,1), + style = wxDEFAULT_FRAME_STYLE|wxFULL_REPAINT_ON_RESIZE) + if (sys.platform == 'win32'): + self.advancedMenuBox.SetIcon(self.icon) + + panel = wxPanel(self.advancedMenuBox, -1) +# self.panel = panel + + def StaticText(text, font = self.FONT, underline = False, color = None, panel = panel): + x = wxStaticText(panel, -1, text, style = wxALIGN_LEFT) + x.SetFont(wxFont(font, wxDEFAULT, wxNORMAL, wxNORMAL, underline)) + if color is not None: + x.SetForegroundColour(color) + return x + + colsizer = wxFlexGridSizer(cols = 1, hgap = 13, vgap = 13) + warningtext = StaticText('CHANGE THESE SETTINGS AT YOUR OWN RISK', self.FONT+4, True, 'Red') + colsizer.Add(warningtext, 1, wxALIGN_CENTER) + + self.ip_data = wxTextCtrl(parent = panel, id = -1, + value = self.advancedConfig['ip'], + size = (self.FONT*13, int(self.FONT*2.2)), style = wxTE_PROCESS_TAB) + self.ip_data.SetFont(self.default_font) + + self.bind_data = wxTextCtrl(parent = panel, id = -1, + value = self.advancedConfig['bind'], + size = (self.FONT*13, int(self.FONT*2.2)), style = wxTE_PROCESS_TAB) + self.bind_data.SetFont(self.default_font) + + if sys.version_info >= (2,3) and socket.has_ipv6: + self.ipv6bindsv4_data=wxChoice(panel, -1, + choices = ['separate sockets', 'single socket']) + self.ipv6bindsv4_data.SetFont(self.default_font) + self.ipv6bindsv4_data.SetSelection(self.advancedConfig['ipv6_binds_v4']) + + self.minpeers_data = wxSpinCtrl(panel, -1, '', (-1,-1), (self.FONT*7, -1)) + self.minpeers_data.SetFont(self.default_font) + self.minpeers_data.SetRange(10,100) + self.minpeers_data.SetValue(self.advancedConfig['min_peers']) + # max_initiate = 2*minpeers + + self.displayinterval_data = wxSpinCtrl(panel, -1, '', (-1,-1), (self.FONT*7, -1)) + self.displayinterval_data.SetFont(self.default_font) + self.displayinterval_data.SetRange(100,2000) + self.displayinterval_data.SetValue(int(self.advancedConfig['display_interval']*1000)) + + self.alloctype_data=wxChoice(panel, -1, + choices = ['normal', 'background', 'pre-allocate', 'sparse']) + self.alloctype_data.SetFont(self.default_font) + self.alloctype_data.SetStringSelection(self.advancedConfig['alloc_type']) + + self.allocrate_data = wxSpinCtrl(panel, -1, '', (-1,-1), (self.FONT*7,-1)) + self.allocrate_data.SetFont(self.default_font) + self.allocrate_data.SetRange(1,100) + self.allocrate_data.SetValue(int(self.advancedConfig['alloc_rate'])) + + self.locking_data=wxChoice(panel, -1, + choices = ['no locking', 'lock while writing', 'lock always']) + self.locking_data.SetFont(self.default_font) + if self.advancedConfig['lock_files']: + if self.advancedConfig['lock_while_reading']: + self.locking_data.SetSelection(2) + else: + self.locking_data.SetSelection(1) + else: + self.locking_data.SetSelection(0) + + self.doublecheck_data=wxChoice(panel, -1, + choices = ['no extra checking', 'double-check', 'triple-check']) + self.doublecheck_data.SetFont(self.default_font) + if self.advancedConfig['double_check']: + if self.advancedConfig['triple_check']: + self.doublecheck_data.SetSelection(2) + else: + self.doublecheck_data.SetSelection(1) + else: + self.doublecheck_data.SetSelection(0) + + self.maxfilesopen_choices = ['50', '100', '200', 'no limit '] + self.maxfilesopen_data=wxChoice(panel, -1, choices = self.maxfilesopen_choices) + self.maxfilesopen_data.SetFont(self.default_font) + setval = self.advancedConfig['max_files_open'] + if setval == 0: + setval = 'no limit ' + else: + setval = str(setval) + if not setval in self.maxfilesopen_choices: + setval = self.maxfilesopen_choices[0] + self.maxfilesopen_data.SetStringSelection(setval) + + self.maxconnections_choices = ['no limit ', '20', '30', '40', '50', '60', '100', '200'] + self.maxconnections_data=wxChoice(panel, -1, choices = self.maxconnections_choices) + self.maxconnections_data.SetFont(self.default_font) + setval = self.advancedConfig['max_connections'] + if setval == 0: + setval = 'no limit ' + else: + setval = str(setval) + if not setval in self.maxconnections_choices: + setval = self.maxconnections_choices[0] + self.maxconnections_data.SetStringSelection(setval) + + self.superseeder_data=wxChoice(panel, -1, + choices = ['normal', 'super-seed']) + self.superseeder_data.SetFont(self.default_font) + self.superseeder_data.SetSelection(self.advancedConfig['super_seeder']) + + self.expirecache_choices = ['never ', '3', '5', '7', '10', '15', '30', '60', '90'] + self.expirecache_data=wxChoice(panel, -1, choices = self.expirecache_choices) + setval = self.advancedConfig['expire_cache_data'] + if setval == 0: + setval = 'never ' + else: + setval = str(setval) + if not setval in self.expirecache_choices: + setval = self.expirecache_choices[0] + self.expirecache_data.SetFont(self.default_font) + self.expirecache_data.SetStringSelection(setval) + + + twocolsizer = wxFlexGridSizer(cols = 2, hgap = 20) + datasizer = wxFlexGridSizer(cols = 2, vgap = 2) + datasizer.Add(StaticText('Local IP: '), 1, wxALIGN_CENTER_VERTICAL) + datasizer.Add(self.ip_data) + datasizer.Add(StaticText('IP to bind to: '), 1, wxALIGN_CENTER_VERTICAL) + datasizer.Add(self.bind_data) + if sys.version_info >= (2,3) and socket.has_ipv6: + datasizer.Add(StaticText('IPv6 socket handling: '), 1, wxALIGN_CENTER_VERTICAL) + datasizer.Add(self.ipv6bindsv4_data) + datasizer.Add(StaticText('Minimum number of peers: '), 1, wxALIGN_CENTER_VERTICAL) + datasizer.Add(self.minpeers_data) + datasizer.Add(StaticText('Display interval (ms): '), 1, wxALIGN_CENTER_VERTICAL) + datasizer.Add(self.displayinterval_data) + datasizer.Add(StaticText('Disk allocation type:'), 1, wxALIGN_CENTER_VERTICAL) + datasizer.Add(self.alloctype_data) + datasizer.Add(StaticText('Allocation rate (MiB/s):'), 1, wxALIGN_CENTER_VERTICAL) + datasizer.Add(self.allocrate_data) + datasizer.Add(StaticText('File locking:'), 1, wxALIGN_CENTER_VERTICAL) + datasizer.Add(self.locking_data) + datasizer.Add(StaticText('Extra data checking:'), 1, wxALIGN_CENTER_VERTICAL) + datasizer.Add(self.doublecheck_data) + datasizer.Add(StaticText('Max files open:'), 1, wxALIGN_CENTER_VERTICAL) + datasizer.Add(self.maxfilesopen_data) + datasizer.Add(StaticText('Max peer connections:'), 1, wxALIGN_CENTER_VERTICAL) + datasizer.Add(self.maxconnections_data) + datasizer.Add(StaticText('Default seeding mode:'), 1, wxALIGN_CENTER_VERTICAL) + datasizer.Add(self.superseeder_data) + datasizer.Add(StaticText('Expire resume data(days):'), 1, wxALIGN_CENTER_VERTICAL) + datasizer.Add(self.expirecache_data) + + twocolsizer.Add(datasizer) + + infosizer = wxFlexGridSizer(cols = 1) + self.hinttext = StaticText('', self.FONT, False, 'Blue') + infosizer.Add(self.hinttext, 1, wxALIGN_LEFT|wxALIGN_CENTER_VERTICAL) + infosizer.SetMinSize((180,100)) + twocolsizer.Add(infosizer, 1, wxEXPAND) + + colsizer.Add(twocolsizer) + + savesizer = wxGridSizer(cols = 3, hgap = 20) + okButton = wxButton(panel, -1, 'OK') +# okButton.SetFont(self.default_font) + savesizer.Add(okButton, 0, wxALIGN_CENTER) + + cancelButton = wxButton(panel, -1, 'Cancel') +# cancelButton.SetFont(self.default_font) + savesizer.Add(cancelButton, 0, wxALIGN_CENTER) + + defaultsButton = wxButton(panel, -1, 'Revert to Defaults') +# defaultsButton.SetFont(self.default_font) + savesizer.Add(defaultsButton, 0, wxALIGN_CENTER) + colsizer.Add(savesizer, 1, wxALIGN_CENTER) + + resizewarningtext=StaticText('None of these settings will take effect until the next time you start BitTorrent', self.FONT-2) + colsizer.Add(resizewarningtext, 1, wxALIGN_CENTER) + + border = wxBoxSizer(wxHORIZONTAL) + border.Add(colsizer, 1, wxEXPAND | wxALL, 4) + + panel.SetSizer(border) + panel.SetAutoLayout(True) + + def setDefaults(evt, self = self): + try: + self.ip_data.SetValue(self.defaults['ip']) + self.bind_data.SetValue(self.defaults['bind']) + if sys.version_info >= (2,3) and socket.has_ipv6: + self.ipv6bindsv4_data.SetSelection(self.defaults['ipv6_binds_v4']) + self.minpeers_data.SetValue(self.defaults['min_peers']) + self.displayinterval_data.SetValue(int(self.defaults['display_interval']*1000)) + self.alloctype_data.SetStringSelection(self.defaults['alloc_type']) + self.allocrate_data.SetValue(int(self.defaults['alloc_rate'])) + if self.defaults['lock_files']: + if self.defaults['lock_while_reading']: + self.locking_data.SetSelection(2) + else: + self.locking_data.SetSelection(1) + else: + self.locking_data.SetSelection(0) + if self.defaults['double_check']: + if self.defaults['triple_check']: + self.doublecheck_data.SetSelection(2) + else: + self.doublecheck_data.SetSelection(1) + else: + self.doublecheck_data.SetSelection(0) + setval = self.defaults['max_files_open'] + if setval == 0: + setval = 'no limit ' + else: + setval = str(setval) + if not setval in self.maxfilesopen_choices: + setval = self.maxfilesopen_choices[0] + self.maxfilesopen_data.SetStringSelection(setval) + setval = self.defaults['max_connections'] + if setval == 0: + setval = 'no limit ' + else: + setval = str(setval) + if not setval in self.maxconnections_choices: + setval = self.maxconnections_choices[0] + self.maxconnections_data.SetStringSelection(setval) + self.superseeder_data.SetSelection(int(self.defaults['super_seeder'])) + setval = self.defaults['expire_cache_data'] + if setval == 0: + setval = 'never ' + else: + setval = str(setval) + if not setval in self.expirecache_choices: + setval = self.expirecache_choices[0] + self.expirecache_data.SetStringSelection(setval) + except: + self.parent.exception() + + def saveConfigs(evt, self = self): + try: + self.advancedConfig['ip'] = self.ip_data.GetValue() + self.advancedConfig['bind'] = self.bind_data.GetValue() + if sys.version_info >= (2,3) and socket.has_ipv6: + self.advancedConfig['ipv6_binds_v4'] = self.ipv6bindsv4_data.GetSelection() + self.advancedConfig['min_peers'] = self.minpeers_data.GetValue() + self.advancedConfig['display_interval'] = float(self.displayinterval_data.GetValue())/1000 + self.advancedConfig['alloc_type'] = self.alloctype_data.GetStringSelection() + self.advancedConfig['alloc_rate'] = float(self.allocrate_data.GetValue()) + self.advancedConfig['lock_files'] = int(self.locking_data.GetSelection() >= 1) + self.advancedConfig['lock_while_reading'] = int(self.locking_data.GetSelection() > 1) + self.advancedConfig['double_check'] = int(self.doublecheck_data.GetSelection() >= 1) + self.advancedConfig['triple_check'] = int(self.doublecheck_data.GetSelection() > 1) + try: + self.advancedConfig['max_files_open'] = int(self.maxfilesopen_data.GetStringSelection()) + except: # if it ain't a number, it must be "no limit" + self.advancedConfig['max_files_open'] = 0 + try: + self.advancedConfig['max_connections'] = int(self.maxconnections_data.GetStringSelection()) + self.advancedConfig['max_initiate'] = min( + 2*self.advancedConfig['min_peers'], self.advancedConfig['max_connections']) + except: # if it ain't a number, it must be "no limit" + self.advancedConfig['max_connections'] = 0 + self.advancedConfig['max_initiate'] = 2*self.advancedConfig['min_peers'] + self.advancedConfig['super_seeder']=int(self.superseeder_data.GetSelection()) + try: + self.advancedConfig['expire_cache_data'] = int(self.expirecache_data.GetStringSelection()) + except: + self.advancedConfig['expire_cache_data'] = 0 + self.advancedMenuBox.Close() + except: + self.parent.exception() + + def cancelConfigs(evt, self = self): + self.advancedMenuBox.Close() + + def ip_hint(evt, self = self): + self.hinttext.SetLabel('\n\n\nThe IP reported to the tracker.\n' + + 'unless the tracker is on the\n' + + 'same intranet as this client,\n' + + 'the tracker will autodetect the\n' + + "client's IP and ignore this\n" + + "value.") + + def bind_hint(evt, self = self): + self.hinttext.SetLabel('\n\n\nThe IP the client will bind to.\n' + + 'Only useful if your machine is\n' + + 'directly handling multiple IPs.\n' + + "If you don't know what this is,\n" + + "leave it blank.") + + def ipv6bindsv4_hint(evt, self = self): + self.hinttext.SetLabel('\n\n\nCertain operating systems will\n' + + 'open IPv4 protocol connections on\n' + + 'an IPv6 socket; others require you\n' + + "to open two sockets on the same\n" + + "port, one IPv4 and one IPv6.") + + def minpeers_hint(evt, self = self): + self.hinttext.SetLabel('\n\n\nThe minimum number of peers the\n' + + 'client tries to stay connected\n' + + 'with. Do not set this higher\n' + + 'unless you have a very fast\n' + + "connection and a lot of system\n" + + "resources.") + + def displayinterval_hint(evt, self = self): + self.hinttext.SetLabel('\n\n\nHow often to update the\n' + + 'graphical display, in 1/1000s\n' + + 'of a second. Setting this too low\n' + + "will strain your computer's\n" + + "processor and video access.") + + def alloctype_hint(evt, self = self): + self.hinttext.SetLabel('\n\nHow to allocate disk space.\n' + + 'normal allocates space as data is\n' + + 'received, background also adds\n' + + "space in the background, pre-\n" + + "allocate reserves up front, and\n" + + 'sparse is only for filesystems\n' + + 'that support it by default.') + + def allocrate_hint(evt, self = self): + self.hinttext.SetLabel('\n\n\nAt what rate to allocate disk\n' + + 'space when allocating in the\n' + + 'background. Set this too high on a\n' + + "slow filesystem and your download\n" + + "will slow to a crawl.") + + def locking_hint(evt, self = self): + self.hinttext.SetLabel('\n\n\n\nFile locking prevents other\n' + + 'programs (including other instances\n' + + 'of BitTorrent) from accessing files\n' + + "you are downloading.") + + def doublecheck_hint(evt, self = self): + self.hinttext.SetLabel('\n\n\nHow much extra checking to do\n' + + 'making sure no data is corrupted.\n' + + 'Double-check mode uses more CPU,\n' + + "while triple-check mode increases\n" + + "disk accesses.") + + def maxfilesopen_hint(evt, self = self): + self.hinttext.SetLabel('\n\n\nThe maximum number of files to\n' + + 'keep open at the same time. Zero\n' + + 'means no limit. Please note that\n' + + "if this option is in effect,\n" + + "files are not guaranteed to be\n" + + "locked.") + + def maxconnections_hint(evt, self = self): + self.hinttext.SetLabel('\n\nSome operating systems, most\n' + + 'notably Windows 9x/ME combined\n' + + 'with certain network drivers,\n' + + "cannot handle more than a certain\n" + + "number of open ports. If the\n" + + "client freezes, try setting this\n" + + "to 60 or below.") + + def superseeder_hint(evt, self = self): + self.hinttext.SetLabel('\n\nThe "super-seed" method allows\n' + + 'a single source to more efficiently\n' + + 'seed a large torrent, but is not\n' + + "necessary in a well-seeded torrent,\n" + + "and causes problems with statistics.\n" + + "Unless you routinely seed torrents\n" + + "you can enable this by selecting\n" + + '"SUPER-SEED" for connection type.\n' + + '(once enabled it does not turn off.)') + + def expirecache_hint(evt, self = self): + self.hinttext.SetLabel('\n\nThe client stores temporary data\n' + + 'in order to handle downloading only\n' + + 'specific files from the torrent and\n' + + "so it can resume downloads more\n" + + "quickly. This sets how long the\n" + + "client will keep this data before\n" + + "deleting it to free disk space.") + + EVT_BUTTON(self.advancedMenuBox, okButton.GetId(), saveConfigs) + EVT_BUTTON(self.advancedMenuBox, cancelButton.GetId(), cancelConfigs) + EVT_BUTTON(self.advancedMenuBox, defaultsButton.GetId(), setDefaults) + EVT_ENTER_WINDOW(self.ip_data, ip_hint) + EVT_ENTER_WINDOW(self.bind_data, bind_hint) + if sys.version_info >= (2,3) and socket.has_ipv6: + EVT_ENTER_WINDOW(self.ipv6bindsv4_data, ipv6bindsv4_hint) + EVT_ENTER_WINDOW(self.minpeers_data, minpeers_hint) + EVT_ENTER_WINDOW(self.displayinterval_data, displayinterval_hint) + EVT_ENTER_WINDOW(self.alloctype_data, alloctype_hint) + EVT_ENTER_WINDOW(self.allocrate_data, allocrate_hint) + EVT_ENTER_WINDOW(self.locking_data, locking_hint) + EVT_ENTER_WINDOW(self.doublecheck_data, doublecheck_hint) + EVT_ENTER_WINDOW(self.maxfilesopen_data, maxfilesopen_hint) + EVT_ENTER_WINDOW(self.maxconnections_data, maxconnections_hint) + EVT_ENTER_WINDOW(self.superseeder_data, superseeder_hint) + EVT_ENTER_WINDOW(self.expirecache_data, expirecache_hint) + + self.advancedMenuBox.Show () + border.Fit(panel) + self.advancedMenuBox.Fit() + except: + self.parent.exception() + + + def CloseAdvanced(self): + if self.advancedMenuBox is not None: + try: + self.advancedMenuBox.Close() + except wxPyDeadObjectError, e: + self.advancedMenuBox = None + diff --git a/www/pages/torrent/client/ConnChoice.py b/www/pages/torrent/client/ConnChoice.py new file mode 100644 index 00000000..8d073968 --- /dev/null +++ b/www/pages/torrent/client/ConnChoice.py @@ -0,0 +1,31 @@ +connChoices=( + {'name':'automatic', + 'rate':{'min':0, 'max':5000, 'def': 0}, + 'conn':{'min':0, 'max':100, 'def': 0}, + 'automatic':1}, + {'name':'unlimited', + 'rate':{'min':0, 'max':5000, 'def': 0, 'div': 50}, + 'conn':{'min':4, 'max':100, 'def': 4}}, + {'name':'dialup/isdn', + 'rate':{'min':3, 'max': 8, 'def': 5}, + 'conn':{'min':2, 'max': 3, 'def': 2}, + 'initiate': 12}, + {'name':'dsl/cable slow', + 'rate':{'min':10, 'max': 48, 'def': 13}, + 'conn':{'min':4, 'max': 20, 'def': 4}}, + {'name':'dsl/cable fast', + 'rate':{'min':20, 'max': 100, 'def': 40}, + 'conn':{'min':4, 'max': 30, 'def': 6}}, + {'name':'T1', + 'rate':{'min':100, 'max': 300, 'def':150}, + 'conn':{'min':4, 'max': 40, 'def':10}}, + {'name':'T3+', + 'rate':{'min':400, 'max':2000, 'def':500}, + 'conn':{'min':4, 'max':100, 'def':20}}, + {'name':'seeder', + 'rate':{'min':0, 'max':5000, 'def':0, 'div': 50}, + 'conn':{'min':1, 'max':100, 'def':1}}, + {'name':'SUPER-SEED', 'super-seed':1} + ) + +connChoiceList = map(lambda x:x['name'], connChoices) diff --git a/www/pages/torrent/client/CreateIcons.py b/www/pages/torrent/client/CreateIcons.py new file mode 100644 index 00000000..72e0241c --- /dev/null +++ b/www/pages/torrent/client/CreateIcons.py @@ -0,0 +1,105 @@ +# Generated from bt_MakeCreateIcons - 05/10/04 22:15:33 +# T-0.3.0 (BitTornado) + +from binascii import a2b_base64 +from zlib import decompress +from os.path import join + +icons = { + "icon_bt.ico": + "eJyt1K+OFEEQx/FaQTh5GDRZhSQpiUHwCrxCBYXFrjyJLXeXEARPsZqUPMm+" + + "AlmP+PGtngoLDji69zMz2zt/qqtr1mxHv7621d4+MnvK/jl66Bl2drV+e7Wz" + + "S/v12A7rY4fDtuvOwfF4tOPXo52/fLLz+WwpWd6nqRXHKXux39sTrtnjNd7g" + + "PW7wGSd860f880kffjvJ2QYS1Zcw4AjcoaA5yRFIFDQXOgKJguZmjkCioB4T" + + "Y2CqxpTXA7sHEgVNEC8RSBQ0gfk7xtknCupgk3EEEgXlNgFHIFHQTMoRSBQ0" + + "E+1ouicKmsk7AomCJiGOQKKgSZIjkChoEucIJAqaZDoCiYImwb4iydULmqQ7" + + "AomC1kLcEQ/jSBQ0i+MIJAqaBXMEElVdi9siOgKJgmZhfWWlVjTddXW/FtsR" + + "SBQ0BeAIJAqaonAEEgVNoTgCiYKmeByBREHaqiVWRtSRrAJzBBIFTdE5AomC" + + "phBPpxPP57dVkDfrTl063nUVnWe383fZx9tb3uN+o7U+BLDtuvcQm8d/27Y/" + + "jO3o5/ay+YPv/+f6y30e1OyB7QcsGWFj", + "icon_done.ico": + "eJyt1K2OVEEQhuEaQbJyMWgyCklSEoPgFvYWKigsduRKbLndhCC4itGk5Erm" + + "Fsh4xMdbfSoMOGDpnuf89Jyf6uqaMdvRr69ttbdPzJ6xf4Eeeo6dXa3vXu/s" + + "0n49tsP62OGw7bpzcDwe7fj1aOcvn+x8PltKlg9pasVxyl7u9/aUe/Z4gxu8" + + "xy0+44Rv/Yp/vujDbxc520Ci+hYGHIF7FDQXOQKJguZGRyBR0DzMEUgU1GNi" + + "DEzVmPJ6YfdAoqAJ4hUCiYImMH/HOPtEQR1sMo5AoqDcJuAIJAqaSTkCiYJm" + + "oh1N90RBM3lHIFHQJMQRSBQ0SXIEEgVN4hyBREGTTEcgUdAk2FckuXpBk3RH" + + "IFHQWoh74mEciYJmcRyBREGzYI5AoqprcVtERyBR0Cysr6zUiqa7rh7WYjsC" + + "iYKmAByBREFTFI5AoqApFEcgUdAUjyOQKEhbtcTKiDqSVWCOQKKgKTpHIFHQ" + + "FOLpdOL9fLcK8nY9qUvHu66i8+x2/i77eHfH77h/0VofAth23Xuoz/+2bX8Y" + + "29HP7WXzB+f/5/7Lcx7V7JHtB9dPG3I=", + "black.ico": + "eJzt1zsOgkAYReFLLCztjJ2UlpLY485kOS7DpbgESwqTcQZDghjxZwAfyfl0" + + "LIieGzUWSom/pan840rHnbSUtPHHX9Je9+tAh2ybNe8TZZ/vk8ajJ4zl6JVJ" + + "+xFx+0R03Djx1/2B8bcT9L/bt0+4Wq+4se8e/VTfMvGqb4n3nYiIGz+lvt9s" + + "9EpE2T4xJN4xNFYWU6t+JWXuXDFzTom7SodSyi/S+iwtwjlJ80KaNY/C34rW" + + "aT8nvK5uhF7ohn7Yqfb87kffLAAAAAAAAAAAAAAAAAAAGMUNy7dADg==", + "blue.ico": + "eJzt10EOwUAYhuGv6cLSTux06QD2dTM9jmM4iiNYdiEZ81cIFTWddtDkfbQW" + + "De8XogtS5h9FIf+81H4jLSSt/ekvaavrdaCDez4SZV+PpPHoicBy9ErSfkQ8" + + "fCI6Hjgx6f7A+McJ+r/t95i46xMP7bf8Uz9o4k0/XMT338voP5shK0MkjXcM" + + "YSqam6Qunatyf7Nk7iztaqk8SaujNLfzIM0qKX88ZX8rWmf7Nfa+W8N61rW+" + + "7TR7fverHxYAAAAAAAAAAAAAAAAAAIziApVZ444=", + "green.ico": + "eJzt1zEOgjAAheFHGBzdjJuMHsAdbybxNB7Do3gERwaT2mJIBCOWlqok/yc4" + + "EP1fNDIoZfZRFLLPa5120krS1p72kvZ6XAeGHLtHouzrkTQePOFZDl5J2g+I" + + "+08Exz0nZt2PjH+coP/bvveEaY2L+/VN13/1PSbe9v0FfP+jTP6ziVmJkTQ+" + + "MISZaO6SujSmyu3dkpmbdKil8iptLtLSnWdpUUn58yn3t6J39l/j3tc2XM91" + + "Xd/tNHt296sfFgAAAAAAAAAAAAAAAAAATOIOVLEoDg==", + "red.ico": + "eJzt10EOwUAYhuGv6cLSTux06QD2dTOO4xiO4giWXUjG/BVCRTuddtDkfbQW" + + "De8XogtS5h9FIf+81GEjLSSt/ekvaavbdaCVez0SZd+PpPHoicBy9ErSfkQ8" + + "fCI6Hjgx6f7AeOcE/d/2QyceesaD+g1/1u+e+NwPF/H99zL6z2bIyhBJ4y1D" + + "mIb6LqlK5/a5v1syd5F2lVSepdVJmtt5lGZ7KX8+ZX8rGmfzNfa+e8N61rW+" + + "7dR7fverHxYAAAAAAAAAAAAAAAAAAIziCpgs444=", + "white.ico": + "eJzt1zsOgkAYReFLKCztjJ2ULsAed6bLcRnuwYTaJVhSmIwzGBLEiD8D+EjO" + + "p2NB9NyosVBK/C3L5B+XOmykhaS1P/6StrpfBzoUp6J5nyj7fJ80Hj1hLEev" + + "TNqPiNsnouPGib/uD4y/naD/3b59wtV6xY199+in+paJV31LvO9ERNz4KfX9" + + "ZqNXIsr2iSHxjqGxspha9Sspc+f2qXNK3FXalVJ+kVZnaR7OUZrtpbR5FP5W" + + "tE77OeF1dSP0Qjf0w06153c/+mYBAAAAAAAAAAAAAAAAAMAobj//I7s=", + "yellow.ico": + "eJzt1zsOgkAYReFLKCztjJ2ULsAedybLcRkuxSVYUpiM82M0ihGHgVFJzidY" + + "ED03vgqlzN+KQv5+qf1GWkha+9Nf0lbX60AX556ORNnXI2k8eiKwHL2StB8R" + + "D5+IjgdOTLo/MP5xgv5v+8ETd/3iYf2W/+oHTLzth4t4/3sZ/WszZGWIpPGO" + + "IUxE8yupS+eq3H9smTtLu1oqT9LqKM3tPEizSsofT9nfitbZfow979awnnWt" + + "bzvNnt/96osFAAAAAAAAAAAAAAAAAACjuABhjmIs", + "black1.ico": + "eJzt0zEOgkAUANEhFpZSGTstTWzkVt5Cj8ZROAIHMNGPWBCFDYgxMZkHn2Iz" + + "G5YCyOLKc+K54XSANbCPiSV2tOt/qjgW3XtSnN41FH/Qv29Jx/P7qefp7W8P" + + "4z85HQ+9JRG/7BpTft31DPUKyiVcFjEZzQ/TTtdzrWnKmCr6evv780qSJEmS" + + "JEmSJEmSJEmSpPnunVFDcA==", + "green1.ico": + "eJzt0zEKwkAQRuEXLCyTSuy0DHgxb6F4shzFI+QAgpkkFoombowIwvt2Z4vh" + + "X5gtFrJYRUGca/Y7WAFlVLTY0vf/1elxTwqP3xoKf5B/vjIenp+fOs+r/LWT" + + "/uQ34aGpUqQnv+1ygDqHagnHRVRG+2H6unfrtZkq6hz5evP7eSVJkiRJkiRJ" + + "kiRJkiRJ0nwNoWQ+AA==", + "yellow1.ico": + "eJzt0zEKwkAQRuEXLCxNJXZaCl7MW8Sj5SgeIQcQ4oS1UDTJxkhAeN/ubDH8" + + "C7PFQhGrLIlzx/kEW+AYFS0OpP6/atuXPSk8fKsv/EX+/cpweH5+6jyf8kn+" + + "k0fCfVPlyE/+2q2CZgP1Gi6rqILuw6R69uh1mTrqGvlmv/y8kiRJkiRJkiRJ" + + "kiRJkiRpvjsp9L8k", + "alloc.gif": + "eJxz93SzsEw0YRBh+M4ABi0MS3ue///P8H8UjIIRBhR/sjAyMDAx6IAyAihP" + + "MHAcYWDlkPHYsOBgM4ewVsyJDQsPNzEoebF8CHjo0smjH3dmRsDjI33C7Dw3" + + "MiYuOtjNyDShRSNwyemJguJJKhaGS32nGka61Vg2NJyYKRd+bY+nwtMzjbqV" + + "Qh84gxMCJgnlL4vJuqJyaa5NfFLNLsNVV2a7syacfVWkHd4bv7RN1ltM7ejm" + + "tMtNZ19Oyb02p8C3aqr3dr2GbXl/7fZyOej5rW653WZ7MzzHZV+v7O2/EZM+" + + "Pt45kbX6ScWHNWfOilo3n5thucXv8org1XF3DRQYrAEWiVY3" +} + +def GetIcons(): + return icons.keys() + +def CreateIcon(icon, savedir): + try: + f = open(join(savedir,icon),"wb") + f.write(decompress(a2b_base64(icons[icon]))) + success = 1 + except: + success = 0 + try: + f.close() + except: + pass + return success diff --git a/www/pages/torrent/client/CurrentRateMeasure.py b/www/pages/torrent/client/CurrentRateMeasure.py new file mode 100644 index 00000000..a363565a --- /dev/null +++ b/www/pages/torrent/client/CurrentRateMeasure.py @@ -0,0 +1,37 @@ +# Written by Bram Cohen +# see LICENSE.txt for license information + +from clock import clock + +class Measure: + def __init__(self, max_rate_period, fudge = 1): + self.max_rate_period = max_rate_period + self.ratesince = clock() - fudge + self.last = self.ratesince + self.rate = 0.0 + self.total = 0l + + def update_rate(self, amount): + self.total += amount + t = clock() + self.rate = (self.rate * (self.last - self.ratesince) + + amount) / (t - self.ratesince + 0.0001) + self.last = t + if self.ratesince < t - self.max_rate_period: + self.ratesince = t - self.max_rate_period + + def get_rate(self): + self.update_rate(0) + return self.rate + + def get_rate_noupdate(self): + return self.rate + + def time_until_rate(self, newrate): + if self.rate <= newrate: + return 0 + t = clock() - self.ratesince + return ((self.rate * t) / newrate) - t + + def get_total(self): + return self.total \ No newline at end of file diff --git a/www/pages/torrent/client/HTTPHandler.py b/www/pages/torrent/client/HTTPHandler.py new file mode 100644 index 00000000..b8146e55 --- /dev/null +++ b/www/pages/torrent/client/HTTPHandler.py @@ -0,0 +1,167 @@ +# Written by Bram Cohen +# see LICENSE.txt for license information + +from cStringIO import StringIO +from sys import stdout +import time +from clock import clock +from gzip import GzipFile +try: + True +except: + True = 1 + False = 0 + +DEBUG = False + +weekdays = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] + +months = [None, 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', + 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] + +class HTTPConnection: + def __init__(self, handler, connection): + self.handler = handler + self.connection = connection + self.buf = '' + self.closed = False + self.done = False + self.donereading = False + self.next_func = self.read_type + + def get_ip(self): + return self.connection.get_ip() + + def data_came_in(self, data): + if self.donereading or self.next_func is None: + return True + self.buf += data + while True: + try: + i = self.buf.index('\n') + except ValueError: + return True + val = self.buf[:i] + self.buf = self.buf[i+1:] + self.next_func = self.next_func(val) + if self.donereading: + return True + if self.next_func is None or self.closed: + return False + + def read_type(self, data): + self.header = data.strip() + words = data.split() + if len(words) == 3: + self.command, self.path, garbage = words + self.pre1 = False + elif len(words) == 2: + self.command, self.path = words + self.pre1 = True + if self.command != 'GET': + return None + else: + return None + if self.command not in ('HEAD', 'GET'): + return None + self.headers = {} + return self.read_header + + def read_header(self, data): + data = data.strip() + if data == '': + self.donereading = True + if self.headers.get('accept-encoding','').find('gzip') > -1: + self.encoding = 'gzip' + else: + self.encoding = 'identity' + r = self.handler.getfunc(self, self.path, self.headers) + if r is not None: + self.answer(r) + return None + try: + i = data.index(':') + except ValueError: + return None + self.headers[data[:i].strip().lower()] = data[i+1:].strip() + if DEBUG: + print data[:i].strip() + ": " + data[i+1:].strip() + return self.read_header + + def answer(self, (responsecode, responsestring, headers, data)): + if self.closed: + return + if self.encoding == 'gzip': + compressed = StringIO() + gz = GzipFile(fileobj = compressed, mode = 'wb', compresslevel = 9) + gz.write(data) + gz.close() + cdata = compressed.getvalue() + if len(cdata) >= len(data): + self.encoding = 'identity' + else: + if DEBUG: + print "Compressed: %i Uncompressed: %i\n" % (len(cdata),len(data)) + data = cdata + headers['Content-Encoding'] = 'gzip' + + # i'm abusing the identd field here, but this should be ok + if self.encoding == 'identity': + ident = '-' + else: + ident = self.encoding + self.handler.log( self.connection.get_ip(), ident, '-', + self.header, responsecode, len(data), + self.headers.get('referer','-'), + self.headers.get('user-agent','-') ) + self.done = True + r = StringIO() + r.write('HTTP/1.0 ' + str(responsecode) + ' ' + + responsestring + '\r\n') + if not self.pre1: + headers['Content-Length'] = len(data) + for key, value in headers.items(): + r.write(key + ': ' + str(value) + '\r\n') + r.write('\r\n') + if self.command != 'HEAD': + r.write(data) + self.connection.write(r.getvalue()) + if self.connection.is_flushed(): + self.connection.shutdown(1) + +class HTTPHandler: + def __init__(self, getfunc, minflush): + self.connections = {} + self.getfunc = getfunc + self.minflush = minflush + self.lastflush = clock() + + def external_connection_made(self, connection): + self.connections[connection] = HTTPConnection(self, connection) + + def connection_flushed(self, connection): + if self.connections[connection].done: + connection.shutdown(1) + + def connection_lost(self, connection): + ec = self.connections[connection] + ec.closed = True + del ec.connection + del ec.next_func + del self.connections[connection] + + def data_came_in(self, connection, data): + c = self.connections[connection] + if not c.data_came_in(data) and not c.closed: + c.connection.shutdown(1) + + def log(self, ip, ident, username, header, + responsecode, length, referrer, useragent): + year, month, day, hour, minute, second, a, b, c = time.localtime(time.time()) + print '%s %s %s [%02d/%3s/%04d:%02d:%02d:%02d] "%s" %i %i "%s" "%s"' % ( + ip, ident, username, day, months[month], year, hour, + minute, second, header, responsecode, length, referrer, useragent) + t = clock() + if t - self.lastflush > self.minflush: + self.lastflush = t + stdout.flush() diff --git a/www/pages/torrent/client/PSYCO.py b/www/pages/torrent/client/PSYCO.py new file mode 100644 index 00000000..e5e7dae9 --- /dev/null +++ b/www/pages/torrent/client/PSYCO.py @@ -0,0 +1,5 @@ +# edit this file to enable/disable Psyco +# psyco = 1 -- enabled +# psyco = 0 -- disabled + +psyco = 0 diff --git a/www/pages/torrent/client/RateLimiter.py b/www/pages/torrent/client/RateLimiter.py new file mode 100644 index 00000000..4e64966f --- /dev/null +++ b/www/pages/torrent/client/RateLimiter.py @@ -0,0 +1,153 @@ +# Written by Bram Cohen +# see LICENSE.txt for license information + +from traceback import print_exc +from binascii import b2a_hex +from clock import clock +from CurrentRateMeasure import Measure +from cStringIO import StringIO +from math import sqrt + +try: + True +except: + True = 1 + False = 0 +try: + sum([1]) +except: + sum = lambda a: reduce(lambda x,y: x+y, a, 0) + +DEBUG = False + +MAX_RATE_PERIOD = 20.0 +MAX_RATE = 10e10 +PING_BOUNDARY = 1.2 +PING_SAMPLES = 7 +PING_DISCARDS = 1 +PING_THRESHHOLD = 5 +PING_DELAY = 5 # cycles 'til first upward adjustment +PING_DELAY_NEXT = 2 # 'til next +ADJUST_UP = 1.05 +ADJUST_DOWN = 0.95 +UP_DELAY_FIRST = 5 +UP_DELAY_NEXT = 2 +SLOTS_STARTING = 6 +SLOTS_FACTOR = 1.66/1000 + +class RateLimiter: + def __init__(self, sched, unitsize, slotsfunc = lambda x: None): + self.sched = sched + self.last = None + self.unitsize = unitsize + self.slotsfunc = slotsfunc + self.measure = Measure(MAX_RATE_PERIOD) + self.autoadjust = False + self.upload_rate = MAX_RATE * 1000 + self.slots = SLOTS_STARTING # garbage if not automatic + + def set_upload_rate(self, rate): + # rate = -1 # test automatic + if rate < 0: + if self.autoadjust: + return + self.autoadjust = True + self.autoadjustup = 0 + self.pings = [] + rate = MAX_RATE + self.slots = SLOTS_STARTING + self.slotsfunc(self.slots) + else: + self.autoadjust = False + if not rate: + rate = MAX_RATE + self.upload_rate = rate * 1000 + self.lasttime = clock() + self.bytes_sent = 0 + + def queue(self, conn): + assert conn.next_upload is None + if self.last is None: + self.last = conn + conn.next_upload = conn + self.try_send(True) + else: + conn.next_upload = self.last.next_upload + self.last.next_upload = conn + self.last = conn + + def try_send(self, check_time = False): + t = clock() + self.bytes_sent -= (t - self.lasttime) * self.upload_rate + self.lasttime = t + if check_time: + self.bytes_sent = max(self.bytes_sent, 0) + cur = self.last.next_upload + while self.bytes_sent <= 0: + bytes = cur.send_partial(self.unitsize) + self.bytes_sent += bytes + self.measure.update_rate(bytes) + if bytes == 0 or cur.backlogged(): + if self.last is cur: + self.last = None + cur.next_upload = None + break + else: + self.last.next_upload = cur.next_upload + cur.next_upload = None + cur = self.last.next_upload + else: + self.last = cur + cur = cur.next_upload + else: + self.sched(self.try_send, self.bytes_sent / self.upload_rate) + + def adjust_sent(self, bytes): + self.bytes_sent = min(self.bytes_sent+bytes, self.upload_rate*3) + self.measure.update_rate(bytes) + + + def ping(self, delay): + if DEBUG: + print delay + if not self.autoadjust: + return + self.pings.append(delay > PING_BOUNDARY) + if len(self.pings) < PING_SAMPLES+PING_DISCARDS: + return + if DEBUG: + print 'cycle' + pings = sum(self.pings[PING_DISCARDS:]) + del self.pings[:] + if pings >= PING_THRESHHOLD: # assume flooded + if self.upload_rate == MAX_RATE: + self.upload_rate = self.measure.get_rate()*ADJUST_DOWN + else: + self.upload_rate = min(self.upload_rate, + self.measure.get_rate()*1.1) + self.upload_rate = max(int(self.upload_rate*ADJUST_DOWN),2) + self.slots = int(sqrt(self.upload_rate*SLOTS_FACTOR)) + self.slotsfunc(self.slots) + if DEBUG: + print 'adjust down to '+str(self.upload_rate) + self.lasttime = clock() + self.bytes_sent = 0 + self.autoadjustup = UP_DELAY_FIRST + else: # not flooded + if self.upload_rate == MAX_RATE: + return + self.autoadjustup -= 1 + if self.autoadjustup: + return + self.upload_rate = int(self.upload_rate*ADJUST_UP) + self.slots = int(sqrt(self.upload_rate*SLOTS_FACTOR)) + self.slotsfunc(self.slots) + if DEBUG: + print 'adjust up to '+str(self.upload_rate) + self.lasttime = clock() + self.bytes_sent = 0 + self.autoadjustup = UP_DELAY_NEXT + + + + diff --git a/www/pages/torrent/client/RateMeasure.py b/www/pages/torrent/client/RateMeasure.py new file mode 100644 index 00000000..7b7310ac --- /dev/null +++ b/www/pages/torrent/client/RateMeasure.py @@ -0,0 +1,75 @@ +# Written by Bram Cohen +# see LICENSE.txt for license information + +from clock import clock +try: + True +except: + True = 1 + False = 0 + +FACTOR = 0.999 + +class RateMeasure: + def __init__(self): + self.last = None + self.time = 1.0 + self.got = 0.0 + self.remaining = None + self.broke = False + self.got_anything = False + self.last_checked = None + self.rate = 0 + self.lastten = False + + def data_came_in(self, amount): + if not self.got_anything: + self.got_anything = True + self.last = clock() + return + self.update(amount) + + def data_rejected(self, amount): + pass + + def get_time_left(self, left): + t = clock() + if not self.got_anything: + return None + if t - self.last > 15: + self.update(0) + try: + remaining = left/self.rate + if not self.lastten and remaining <= 10: + self.lastten = True + if self.lastten: + return remaining + delta = max(remaining/20,2) + if self.remaining is None: + self.remaining = remaining + elif abs(self.remaining-remaining) > delta: + self.remaining = remaining + else: + self.remaining -= t - self.last_checked + except ZeroDivisionError: + self.remaining = None + if self.remaining is not None and self.remaining < 0.1: + self.remaining = 0.1 + self.last_checked = t + return self.remaining + + def update(self, amount): + t = clock() + t1 = int(t) + l1 = int(self.last) + for i in xrange(l1,t1): + self.time *= FACTOR + self.got *= FACTOR + self.got += amount + if t - self.last < 20: + self.time += t - self.last + self.last = t + try: + self.rate = self.got / self.time + except ZeroDivisionError: + pass diff --git a/www/pages/torrent/client/RawServer.py b/www/pages/torrent/client/RawServer.py new file mode 100644 index 00000000..12749343 --- /dev/null +++ b/www/pages/torrent/client/RawServer.py @@ -0,0 +1,195 @@ +# Written by Bram Cohen +# see LICENSE.txt for license information + +from bisect import insort +from SocketHandler import SocketHandler, UPnP_ERROR +import socket +from cStringIO import StringIO +from traceback import print_exc +from select import error +from threading import Thread, Event +from time import sleep +from clock import clock +import sys +try: + True +except: + True = 1 + False = 0 + + +def autodetect_ipv6(): + try: + assert sys.version_info >= (2,3) + assert socket.has_ipv6 + socket.socket(socket.AF_INET6, socket.SOCK_STREAM) + except: + return 0 + return 1 + +def autodetect_socket_style(): + if sys.platform.find('linux') < 0: + return 1 + else: + try: + f = open('/proc/sys/net/ipv6/bindv6only','r') + dual_socket_style = int(f.read()) + f.close() + return int(not dual_socket_style) + except: + return 0 + + +READSIZE = 100000 + +class RawServer: + def __init__(self, doneflag, timeout_check_interval, timeout, noisy = True, + ipv6_enable = True, failfunc = lambda x: None, errorfunc = None, + sockethandler = None, excflag = Event()): + self.timeout_check_interval = timeout_check_interval + self.timeout = timeout + self.servers = {} + self.single_sockets = {} + self.dead_from_write = [] + self.doneflag = doneflag + self.noisy = noisy + self.failfunc = failfunc + self.errorfunc = errorfunc + self.exccount = 0 + self.funcs = [] + self.externally_added = [] + self.finished = Event() + self.tasks_to_kill = [] + self.excflag = excflag + + if sockethandler is None: + sockethandler = SocketHandler(timeout, ipv6_enable, READSIZE) + self.sockethandler = sockethandler + self.add_task(self.scan_for_timeouts, timeout_check_interval) + + def get_exception_flag(self): + return self.excflag + + def _add_task(self, func, delay, id = None): + assert float(delay) >= 0 + insort(self.funcs, (clock() + delay, func, id)) + + def add_task(self, func, delay = 0, id = None): + assert float(delay) >= 0 + self.externally_added.append((func, delay, id)) + + def scan_for_timeouts(self): + self.add_task(self.scan_for_timeouts, self.timeout_check_interval) + self.sockethandler.scan_for_timeouts() + + def bind(self, port, bind = '', reuse = False, + ipv6_socket_style = 1, upnp = False): + self.sockethandler.bind(port, bind, reuse, ipv6_socket_style, upnp) + + def find_and_bind(self, minport, maxport, bind = '', reuse = False, + ipv6_socket_style = 1, upnp = 0, randomizer = False): + return self.sockethandler.find_and_bind(minport, maxport, bind, reuse, + ipv6_socket_style, upnp, randomizer) + + def start_connection_raw(self, dns, socktype, handler = None): + return self.sockethandler.start_connection_raw(dns, socktype, handler) + + def start_connection(self, dns, handler = None, randomize = False): + return self.sockethandler.start_connection(dns, handler, randomize) + + def get_stats(self): + return self.sockethandler.get_stats() + + def pop_external(self): + while self.externally_added: + (a, b, c) = self.externally_added.pop(0) + self._add_task(a, b, c) + + + def listen_forever(self, handler): + self.sockethandler.set_handler(handler) + try: + while not self.doneflag.isSet(): + try: + self.pop_external() + self._kill_tasks() + if self.funcs: + period = self.funcs[0][0] + 0.001 - clock() + else: + period = 2 ** 30 + if period < 0: + period = 0 + events = self.sockethandler.do_poll(period) + if self.doneflag.isSet(): + return + while self.funcs and self.funcs[0][0] <= clock(): + garbage1, func, id = self.funcs.pop(0) + if id in self.tasks_to_kill: + pass + try: +# print func.func_name + func() + except (SystemError, MemoryError), e: + self.failfunc(str(e)) + return + except KeyboardInterrupt: +# self.exception(True) + return + except: + if self.noisy: + self.exception() + self.sockethandler.close_dead() + self.sockethandler.handle_events(events) + if self.doneflag.isSet(): + return + self.sockethandler.close_dead() + except (SystemError, MemoryError), e: + self.failfunc(str(e)) + return + except error: + if self.doneflag.isSet(): + return + except KeyboardInterrupt: +# self.exception(True) + return + except: + self.exception() + if self.exccount > 10: + return + finally: +# self.sockethandler.shutdown() + self.finished.set() + + def is_finished(self): + return self.finished.isSet() + + def wait_until_finished(self): + self.finished.wait() + + def _kill_tasks(self): + if self.tasks_to_kill: + new_funcs = [] + for (t, func, id) in self.funcs: + if id not in self.tasks_to_kill: + new_funcs.append((t, func, id)) + self.funcs = new_funcs + self.tasks_to_kill = [] + + def kill_tasks(self, id): + self.tasks_to_kill.append(id) + + def exception(self, kbint = False): + if not kbint: + self.excflag.set() + self.exccount += 1 + if self.errorfunc is None: + print_exc() + else: + data = StringIO() + print_exc(file = data) +# print data.getvalue() # report exception here too + if not kbint: # don't report here if it's a keyboard interrupt + self.errorfunc(data.getvalue()) + + def shutdown(self): + self.sockethandler.shutdown() diff --git a/www/pages/torrent/client/ServerPortHandler.py b/www/pages/torrent/client/ServerPortHandler.py new file mode 100644 index 00000000..63df0891 --- /dev/null +++ b/www/pages/torrent/client/ServerPortHandler.py @@ -0,0 +1,188 @@ +# Written by John Hoffman +# see LICENSE.txt for license information + +from cStringIO import StringIO +#from RawServer import RawServer +try: + True +except: + True = 1 + False = 0 + +from BT1.Encrypter import protocol_name + +default_task_id = [] + +class SingleRawServer: + def __init__(self, info_hash, multihandler, doneflag, protocol): + self.info_hash = info_hash + self.doneflag = doneflag + self.protocol = protocol + self.multihandler = multihandler + self.rawserver = multihandler.rawserver + self.finished = False + self.running = False + self.handler = None + self.taskqueue = [] + + def shutdown(self): + if not self.finished: + self.multihandler.shutdown_torrent(self.info_hash) + + def _shutdown(self): + if not self.finished: + self.finished = True + self.running = False + self.rawserver.kill_tasks(self.info_hash) + if self.handler: + self.handler.close_all() + + def _external_connection_made(self, c, options, already_read): + if self.running: + c.set_handler(self.handler) + self.handler.externally_handshaked_connection_made( + c, options, already_read) + + ### RawServer functions ### + + def add_task(self, func, delay=0, id = default_task_id): + if id is default_task_id: + id = self.info_hash + if not self.finished: + self.rawserver.add_task(func, delay, id) + +# def bind(self, port, bind = '', reuse = False): +# pass # not handled here + + def start_connection(self, dns, handler = None): + if not handler: + handler = self.handler + c = self.rawserver.start_connection(dns, handler) + return c + +# def listen_forever(self, handler): +# pass # don't call with this + + def start_listening(self, handler): + self.handler = handler + self.running = True + return self.shutdown # obviously, doesn't listen forever + + def is_finished(self): + return self.finished + + def get_exception_flag(self): + return self.rawserver.get_exception_flag() + + +class NewSocketHandler: # hand a new socket off where it belongs + def __init__(self, multihandler, connection): + self.multihandler = multihandler + self.connection = connection + connection.set_handler(self) + self.closed = False + self.buffer = StringIO() + self.complete = False + self.next_len, self.next_func = 1, self.read_header_len + self.multihandler.rawserver.add_task(self._auto_close, 15) + + def _auto_close(self): + if not self.complete: + self.close() + + def close(self): + if not self.closed: + self.connection.close() + self.closed = True + + +# header format: +# connection.write(chr(len(protocol_name)) + protocol_name + +# (chr(0) * 8) + self.encrypter.download_id + self.encrypter.my_id) + + # copied from Encrypter and modified + + def read_header_len(self, s): + l = ord(s) + return l, self.read_header + + def read_header(self, s): + self.protocol = s + return 8, self.read_reserved + + def read_reserved(self, s): + self.options = s + return 20, self.read_download_id + + def read_download_id(self, s): + if self.multihandler.singlerawservers.has_key(s): + if self.multihandler.singlerawservers[s].protocol == self.protocol: + return True + return None + + def read_dead(self, s): + return None + + def data_came_in(self, garbage, s): + while True: + if self.closed: + return + i = self.next_len - self.buffer.tell() + if i > len(s): + self.buffer.write(s) + return + self.buffer.write(s[:i]) + s = s[i:] + m = self.buffer.getvalue() + self.buffer.reset() + self.buffer.truncate() + try: + x = self.next_func(m) + except: + self.next_len, self.next_func = 1, self.read_dead + raise + if x is None: + self.close() + return + if x == True: # ready to process + self.multihandler.singlerawservers[m]._external_connection_made( + self.connection, self.options, s) + self.complete = True + return + self.next_len, self.next_func = x + + def connection_flushed(self, ss): + pass + + def connection_lost(self, ss): + self.closed = True + +class MultiHandler: + def __init__(self, rawserver, doneflag): + self.rawserver = rawserver + self.masterdoneflag = doneflag + self.singlerawservers = {} + self.connections = {} + self.taskqueues = {} + + def newRawServer(self, info_hash, doneflag, protocol=protocol_name): + new = SingleRawServer(info_hash, self, doneflag, protocol) + self.singlerawservers[info_hash] = new + return new + + def shutdown_torrent(self, info_hash): + self.singlerawservers[info_hash]._shutdown() + del self.singlerawservers[info_hash] + + def listen_forever(self): + self.rawserver.listen_forever(self) + for srs in self.singlerawservers.values(): + srs.finished = True + srs.running = False + srs.doneflag.set() + + ### RawServer handler functions ### + # be wary of name collisions + + def external_connection_made(self, ss): + NewSocketHandler(self, ss) diff --git a/www/pages/torrent/client/SocketHandler.py b/www/pages/torrent/client/SocketHandler.py new file mode 100644 index 00000000..76768300 --- /dev/null +++ b/www/pages/torrent/client/SocketHandler.py @@ -0,0 +1,375 @@ +# Written by Bram Cohen +# see LICENSE.txt for license information + +import socket +from errno import EWOULDBLOCK, ECONNREFUSED, EHOSTUNREACH +try: + from select import poll, error, POLLIN, POLLOUT, POLLERR, POLLHUP + timemult = 1000 +except ImportError: + from selectpoll import poll, error, POLLIN, POLLOUT, POLLERR, POLLHUP + timemult = 1 +from time import sleep +from clock import clock +import sys +from random import shuffle, randrange +from natpunch import UPnP_open_port, UPnP_close_port +# from BT1.StreamCheck import StreamCheck +# import inspect +try: + True +except: + True = 1 + False = 0 + +all = POLLIN | POLLOUT + +UPnP_ERROR = "unable to forward port via UPnP" + +class SingleSocket: + def __init__(self, socket_handler, sock, handler, ip = None): + self.socket_handler = socket_handler + self.socket = sock + self.handler = handler + self.buffer = [] + self.last_hit = clock() + self.fileno = sock.fileno() + self.connected = False + self.skipped = 0 +# self.check = StreamCheck() + try: + self.ip = self.socket.getpeername()[0] + except: + if ip is None: + self.ip = 'unknown' + else: + self.ip = ip + + def get_ip(self, real=False): + if real: + try: + self.ip = self.socket.getpeername()[0] + except: + pass + return self.ip + + def close(self): + ''' + for x in xrange(5,0,-1): + try: + f = inspect.currentframe(x).f_code + print (f.co_filename,f.co_firstlineno,f.co_name) + del f + except: + pass + print '' + ''' + assert self.socket + self.connected = False + sock = self.socket + self.socket = None + self.buffer = [] + del self.socket_handler.single_sockets[self.fileno] + self.socket_handler.poll.unregister(sock) + sock.close() + + def shutdown(self, val): + self.socket.shutdown(val) + + def is_flushed(self): + return not self.buffer + + def write(self, s): +# self.check.write(s) + assert self.socket is not None + self.buffer.append(s) + if len(self.buffer) == 1: + self.try_write() + + def try_write(self): + if self.connected: + dead = False + try: + while self.buffer: + buf = self.buffer[0] + amount = self.socket.send(buf) + if amount == 0: + self.skipped += 1 + break + self.skipped = 0 + if amount != len(buf): + self.buffer[0] = buf[amount:] + break + del self.buffer[0] + except socket.error, e: + try: + dead = e[0] != EWOULDBLOCK + except: + dead = True + self.skipped += 1 + if self.skipped >= 3: + dead = True + if dead: + self.socket_handler.dead_from_write.append(self) + return + if self.buffer: + self.socket_handler.poll.register(self.socket, all) + else: + self.socket_handler.poll.register(self.socket, POLLIN) + + def set_handler(self, handler): + self.handler = handler + +class SocketHandler: + def __init__(self, timeout, ipv6_enable, readsize = 100000): + self.timeout = timeout + self.ipv6_enable = ipv6_enable + self.readsize = readsize + self.poll = poll() + # {socket: SingleSocket} + self.single_sockets = {} + self.dead_from_write = [] + self.max_connects = 1000 + self.port_forwarded = None + self.servers = {} + + def scan_for_timeouts(self): + t = clock() - self.timeout + tokill = [] + for s in self.single_sockets.values(): + if s.last_hit < t: + tokill.append(s) + for k in tokill: + if k.socket is not None: + self._close_socket(k) + + def bind(self, port, bind = '', reuse = False, ipv6_socket_style = 1, upnp = 0): + port = int(port) + addrinfos = [] + self.servers = {} + self.interfaces = [] + # if bind != "" thread it as a comma seperated list and bind to all + # addresses (can be ips or hostnames) else bind to default ipv6 and + # ipv4 address + if bind: + if self.ipv6_enable: + socktype = socket.AF_UNSPEC + else: + socktype = socket.AF_INET + bind = bind.split(',') + for addr in bind: + if sys.version_info < (2,2): + addrinfos.append((socket.AF_INET, None, None, None, (addr, port))) + else: + addrinfos.extend(socket.getaddrinfo(addr, port, + socktype, socket.SOCK_STREAM)) + else: + if self.ipv6_enable: + addrinfos.append([socket.AF_INET6, None, None, None, ('', port)]) + if not addrinfos or ipv6_socket_style != 0: + addrinfos.append([socket.AF_INET, None, None, None, ('', port)]) + for addrinfo in addrinfos: + try: + server = socket.socket(addrinfo[0], socket.SOCK_STREAM) + if reuse: + server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + server.setblocking(0) + server.bind(addrinfo[4]) + self.servers[server.fileno()] = server + if bind: + self.interfaces.append(server.getsockname()[0]) + server.listen(64) + self.poll.register(server, POLLIN) + except socket.error, e: + for server in self.servers.values(): + try: + server.close() + except: + pass + if self.ipv6_enable and ipv6_socket_style == 0 and self.servers: + raise socket.error('blocked port (may require ipv6_binds_v4 to be set)') + raise socket.error(str(e)) + if not self.servers: + raise socket.error('unable to open server port') + if upnp: + if not UPnP_open_port(port): + for server in self.servers.values(): + try: + server.close() + except: + pass + self.servers = None + self.interfaces = None + raise socket.error(UPnP_ERROR) + self.port_forwarded = port + self.port = port + + def find_and_bind(self, minport, maxport, bind = '', reuse = False, + ipv6_socket_style = 1, upnp = 0, randomizer = False): + e = 'maxport less than minport - no ports to check' + if maxport-minport < 50 or not randomizer: + portrange = range(minport, maxport+1) + if randomizer: + shuffle(portrange) + portrange = portrange[:20] # check a maximum of 20 ports + else: + portrange = [] + while len(portrange) < 20: + listen_port = randrange(minport, maxport+1) + if not listen_port in portrange: + portrange.append(listen_port) + for listen_port in portrange: + try: + self.bind(listen_port, bind, + ipv6_socket_style = ipv6_socket_style, upnp = upnp) + return listen_port + except socket.error, e: + pass + raise socket.error(str(e)) + + + def set_handler(self, handler): + self.handler = handler + + + def start_connection_raw(self, dns, socktype = socket.AF_INET, handler = None): + if handler is None: + handler = self.handler + sock = socket.socket(socktype, socket.SOCK_STREAM) + sock.setblocking(0) + try: + sock.connect_ex(dns) + except socket.error: + raise + except Exception, e: + raise socket.error(str(e)) + self.poll.register(sock, POLLIN) + s = SingleSocket(self, sock, handler, dns[0]) + self.single_sockets[sock.fileno()] = s + return s + + + def start_connection(self, dns, handler = None, randomize = False): + if handler is None: + handler = self.handler + if sys.version_info < (2,2): + s = self.start_connection_raw(dns,socket.AF_INET,handler) + else: + if self.ipv6_enable: + socktype = socket.AF_UNSPEC + else: + socktype = socket.AF_INET + try: + addrinfos = socket.getaddrinfo(dns[0], int(dns[1]), + socktype, socket.SOCK_STREAM) + except socket.error, e: + raise + except Exception, e: + raise socket.error(str(e)) + if randomize: + shuffle(addrinfos) + for addrinfo in addrinfos: + try: + s = self.start_connection_raw(addrinfo[4],addrinfo[0],handler) + break + except: + pass + else: + raise socket.error('unable to connect') + return s + + + def _sleep(self): + sleep(1) + + def handle_events(self, events): + for sock, event in events: + s = self.servers.get(sock) + if s: + if event & (POLLHUP | POLLERR) != 0: + self.poll.unregister(s) + s.close() + del self.servers[sock] + print "lost server socket" + elif len(self.single_sockets) < self.max_connects: + try: + newsock, addr = s.accept() + newsock.setblocking(0) + nss = SingleSocket(self, newsock, self.handler) + self.single_sockets[newsock.fileno()] = nss + self.poll.register(newsock, POLLIN) + self.handler.external_connection_made(nss) + except socket.error: + self._sleep() + else: + s = self.single_sockets.get(sock) + if not s: + continue + s.connected = True + if (event & (POLLHUP | POLLERR)): + self._close_socket(s) + continue + if (event & POLLIN): + try: + s.last_hit = clock() + data = s.socket.recv(100000) + if not data: + self._close_socket(s) + else: + s.handler.data_came_in(s, data) + except socket.error, e: + code, msg = e + if code != EWOULDBLOCK: + self._close_socket(s) + continue + if (event & POLLOUT) and s.socket and not s.is_flushed(): + s.try_write() + if s.is_flushed(): + s.handler.connection_flushed(s) + + def close_dead(self): + while self.dead_from_write: + old = self.dead_from_write + self.dead_from_write = [] + for s in old: + if s.socket: + self._close_socket(s) + + def _close_socket(self, s): + s.close() + s.handler.connection_lost(s) + + def do_poll(self, t): + r = self.poll.poll(t*timemult) + if r is None: + connects = len(self.single_sockets) + to_close = int(connects*0.05)+1 # close 5% of sockets + self.max_connects = connects-to_close + closelist = self.single_sockets.values() + shuffle(closelist) + closelist = closelist[:to_close] + for sock in closelist: + self._close_socket(sock) + return [] + return r + + def get_stats(self): + return { 'interfaces': self.interfaces, + 'port': self.port, + 'upnp': self.port_forwarded is not None } + + + def shutdown(self): + for ss in self.single_sockets.values(): + try: + ss.close() + except: + pass + for server in self.servers.values(): + try: + server.close() + except: + pass + if self.port_forwarded is not None: + UPnP_close_port(self.port_forwarded) + diff --git a/www/pages/torrent/client/__init__.py b/www/pages/torrent/client/__init__.py new file mode 100644 index 00000000..20a831bc --- /dev/null +++ b/www/pages/torrent/client/__init__.py @@ -0,0 +1,63 @@ +product_name = 'BitTornado' +version_short = 'T-0.3.17' + +version = version_short+' ('+product_name+')' +report_email = version_short+'@degreez.net' + +from types import StringType +from sha import sha +from time import time, clock +try: + from os import getpid +except ImportError: + def getpid(): + return 1 + +mapbase64 = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz.-' + +_idprefix = version_short[0] +for subver in version_short[2:].split('.'): + try: + subver = int(subver) + except: + subver = 0 + _idprefix += mapbase64[subver] +_idprefix += ('-' * (6-len(_idprefix))) +_idrandom = [None] + +def resetPeerIDs(): + try: + f = open('/dev/urandom','rb') + x = f.read(20) + f.close() + except: + x = '' + + l1 = 0 + t = clock() + while t == clock(): + l1 += 1 + l2 = 0 + t = long(time()*100) + while t == long(time()*100): + l2 += 1 + l3 = 0 + if l2 < 1000: + t = long(time()*10) + while t == long(clock()*10): + l3 += 1 + x += ( repr(time()) + '/' + str(time()) + '/' + + str(l1) + '/' + str(l2) + '/' + str(l3) + '/' + + str(getpid()) ) + + s = '' + for i in sha(x).digest()[-11:]: + s += mapbase64[ord(i) & 0x3F] + _idrandom[0] = s + +resetPeerIDs() + +def createPeerID(ins = '---'): + assert type(ins) is StringType + assert len(ins) == 3 + return _idprefix + ins + _idrandom[0] diff --git a/www/pages/torrent/client/bencode.py b/www/pages/torrent/client/bencode.py new file mode 100644 index 00000000..1ff0736f --- /dev/null +++ b/www/pages/torrent/client/bencode.py @@ -0,0 +1,319 @@ +# Written by Petru Paler, Uoti Urpala, Ross Cohen and John Hoffman +# see LICENSE.txt for license information + +from types import IntType, LongType, StringType, ListType, TupleType, DictType +try: + from types import BooleanType +except ImportError: + BooleanType = None +try: + from types import UnicodeType +except ImportError: + UnicodeType = None +from cStringIO import StringIO + +def decode_int(x, f): + f += 1 + newf = x.index('e', f) + try: + n = int(x[f:newf]) + except: + n = long(x[f:newf]) + if x[f] == '-': + if x[f + 1] == '0': + raise ValueError + elif x[f] == '0' and newf != f+1: + raise ValueError + return (n, newf+1) + +def decode_string(x, f): + colon = x.index(':', f) + try: + n = int(x[f:colon]) + except (OverflowError, ValueError): + n = long(x[f:colon]) + if x[f] == '0' and colon != f+1: + raise ValueError + colon += 1 + return (x[colon:colon+n], colon+n) + +def decode_unicode(x, f): + s, f = decode_string(x, f+1) + return (s.decode('UTF-8'),f) + +def decode_list(x, f): + r, f = [], f+1 + while x[f] != 'e': + v, f = decode_func[x[f]](x, f) + r.append(v) + return (r, f + 1) + +def decode_dict(x, f): + r, f = {}, f+1 + lastkey = None + while x[f] != 'e': + k, f = decode_string(x, f) + if lastkey >= k: + raise ValueError + lastkey = k + r[k], f = decode_func[x[f]](x, f) + return (r, f + 1) + +decode_func = {} +decode_func['l'] = decode_list +decode_func['d'] = decode_dict +decode_func['i'] = decode_int +decode_func['0'] = decode_string +decode_func['1'] = decode_string +decode_func['2'] = decode_string +decode_func['3'] = decode_string +decode_func['4'] = decode_string +decode_func['5'] = decode_string +decode_func['6'] = decode_string +decode_func['7'] = decode_string +decode_func['8'] = decode_string +decode_func['9'] = decode_string +#decode_func['u'] = decode_unicode + +def bdecode(x, sloppy = 0): + try: + r, l = decode_func[x[0]](x, 0) +# except (IndexError, KeyError): + except (IndexError, KeyError, ValueError): + raise ValueError, "bad bencoded data" + if not sloppy and l != len(x): + raise ValueError, "bad bencoded data" + return r + +def test_bdecode(): + try: + bdecode('0:0:') + assert 0 + except ValueError: + pass + try: + bdecode('ie') + assert 0 + except ValueError: + pass + try: + bdecode('i341foo382e') + assert 0 + except ValueError: + pass + assert bdecode('i4e') == 4L + assert bdecode('i0e') == 0L + assert bdecode('i123456789e') == 123456789L + assert bdecode('i-10e') == -10L + try: + bdecode('i-0e') + assert 0 + except ValueError: + pass + try: + bdecode('i123') + assert 0 + except ValueError: + pass + try: + bdecode('') + assert 0 + except ValueError: + pass + try: + bdecode('i6easd') + assert 0 + except ValueError: + pass + try: + bdecode('35208734823ljdahflajhdf') + assert 0 + except ValueError: + pass + try: + bdecode('2:abfdjslhfld') + assert 0 + except ValueError: + pass + assert bdecode('0:') == '' + assert bdecode('3:abc') == 'abc' + assert bdecode('10:1234567890') == '1234567890' + try: + bdecode('02:xy') + assert 0 + except ValueError: + pass + try: + bdecode('l') + assert 0 + except ValueError: + pass + assert bdecode('le') == [] + try: + bdecode('leanfdldjfh') + assert 0 + except ValueError: + pass + assert bdecode('l0:0:0:e') == ['', '', ''] + try: + bdecode('relwjhrlewjh') + assert 0 + except ValueError: + pass + assert bdecode('li1ei2ei3ee') == [1, 2, 3] + assert bdecode('l3:asd2:xye') == ['asd', 'xy'] + assert bdecode('ll5:Alice3:Bobeli2ei3eee') == [['Alice', 'Bob'], [2, 3]] + try: + bdecode('d') + assert 0 + except ValueError: + pass + try: + bdecode('defoobar') + assert 0 + except ValueError: + pass + assert bdecode('de') == {} + assert bdecode('d3:agei25e4:eyes4:bluee') == {'age': 25, 'eyes': 'blue'} + assert bdecode('d8:spam.mp3d6:author5:Alice6:lengthi100000eee') == {'spam.mp3': {'author': 'Alice', 'length': 100000}} + try: + bdecode('d3:fooe') + assert 0 + except ValueError: + pass + try: + bdecode('di1e0:e') + assert 0 + except ValueError: + pass + try: + bdecode('d1:b0:1:a0:e') + assert 0 + except ValueError: + pass + try: + bdecode('d1:a0:1:a0:e') + assert 0 + except ValueError: + pass + try: + bdecode('i03e') + assert 0 + except ValueError: + pass + try: + bdecode('l01:ae') + assert 0 + except ValueError: + pass + try: + bdecode('9999:x') + assert 0 + except ValueError: + pass + try: + bdecode('l0:') + assert 0 + except ValueError: + pass + try: + bdecode('d0:0:') + assert 0 + except ValueError: + pass + try: + bdecode('d0:') + assert 0 + except ValueError: + pass + +bencached_marker = [] + +class Bencached: + def __init__(self, s): + self.marker = bencached_marker + self.bencoded = s + +BencachedType = type(Bencached('')) # insufficient, but good as a filter + +def encode_bencached(x,r): + assert x.marker == bencached_marker + r.append(x.bencoded) + +def encode_int(x,r): + r.extend(('i',str(x),'e')) + +def encode_bool(x,r): + encode_int(int(x),r) + +def encode_string(x,r): + r.extend((str(len(x)),':',x)) + +def encode_unicode(x,r): + #r.append('u') + encode_string(x.encode('UTF-8'),r) + +def encode_list(x,r): + r.append('l') + for e in x: + encode_func[type(e)](e, r) + r.append('e') + +def encode_dict(x,r): + r.append('d') + ilist = x.items() + ilist.sort() + for k,v in ilist: + r.extend((str(len(k)),':',k)) + encode_func[type(v)](v, r) + r.append('e') + +encode_func = {} +encode_func[BencachedType] = encode_bencached +encode_func[IntType] = encode_int +encode_func[LongType] = encode_int +encode_func[StringType] = encode_string +encode_func[ListType] = encode_list +encode_func[TupleType] = encode_list +encode_func[DictType] = encode_dict +if BooleanType: + encode_func[BooleanType] = encode_bool +if UnicodeType: + encode_func[UnicodeType] = encode_unicode + +def bencode(x): + r = [] + try: + encode_func[type(x)](x, r) + except: + print "*** error *** could not encode type %s (value: %s)" % (type(x), x) + assert 0 + return ''.join(r) + +def test_bencode(): + assert bencode(4) == 'i4e' + assert bencode(0) == 'i0e' + assert bencode(-10) == 'i-10e' + assert bencode(12345678901234567890L) == 'i12345678901234567890e' + assert bencode('') == '0:' + assert bencode('abc') == '3:abc' + assert bencode('1234567890') == '10:1234567890' + assert bencode([]) == 'le' + assert bencode([1, 2, 3]) == 'li1ei2ei3ee' + assert bencode([['Alice', 'Bob'], [2, 3]]) == 'll5:Alice3:Bobeli2ei3eee' + assert bencode({}) == 'de' + assert bencode({'age': 25, 'eyes': 'blue'}) == 'd3:agei25e4:eyes4:bluee' + assert bencode({'spam.mp3': {'author': 'Alice', 'length': 100000}}) == 'd8:spam.mp3d6:author5:Alice6:lengthi100000eee' + try: + bencode({1: 'foo'}) + assert 0 + except AssertionError: + pass + + +try: + import psyco + psyco.bind(bdecode) + psyco.bind(bencode) +except ImportError: + pass diff --git a/www/pages/torrent/client/bitfield.py b/www/pages/torrent/client/bitfield.py new file mode 100644 index 00000000..3d532e30 --- /dev/null +++ b/www/pages/torrent/client/bitfield.py @@ -0,0 +1,162 @@ +# Written by Bram Cohen, Uoti Urpala, and John Hoffman +# see LICENSE.txt for license information + +try: + True +except: + True = 1 + False = 0 + bool = lambda x: not not x + +try: + sum([1]) + negsum = lambda a: len(a)-sum(a) +except: + negsum = lambda a: reduce(lambda x,y: x+(not y), a, 0) + +def _int_to_booleans(x): + r = [] + for i in range(8): + r.append(bool(x & 0x80)) + x <<= 1 + return tuple(r) + +lookup_table = [] +reverse_lookup_table = {} +for i in xrange(256): + x = _int_to_booleans(i) + lookup_table.append(x) + reverse_lookup_table[x] = chr(i) + + +class Bitfield: + def __init__(self, length = None, bitstring = None, copyfrom = None): + if copyfrom is not None: + self.length = copyfrom.length + self.array = copyfrom.array[:] + self.numfalse = copyfrom.numfalse + return + if length is None: + raise ValueError, "length must be provided unless copying from another array" + self.length = length + if bitstring is not None: + extra = len(bitstring) * 8 - length + if extra < 0 or extra >= 8: + raise ValueError + t = lookup_table + r = [] + for c in bitstring: + r.extend(t[ord(c)]) + if extra > 0: + if r[-extra:] != [0] * extra: + raise ValueError + del r[-extra:] + self.array = r + self.numfalse = negsum(r) + else: + self.array = [False] * length + self.numfalse = length + + def __setitem__(self, index, val): + val = bool(val) + self.numfalse += self.array[index]-val + self.array[index] = val + + def __getitem__(self, index): + return self.array[index] + + def __len__(self): + return self.length + + def tostring(self): + booleans = self.array + t = reverse_lookup_table + s = len(booleans) % 8 + r = [ t[tuple(booleans[x:x+8])] for x in xrange(0, len(booleans)-s, 8) ] + if s: + r += t[tuple(booleans[-s:] + ([0] * (8-s)))] + return ''.join(r) + + def complete(self): + return not self.numfalse + + +def test_bitfield(): + try: + x = Bitfield(7, 'ab') + assert False + except ValueError: + pass + try: + x = Bitfield(7, 'ab') + assert False + except ValueError: + pass + try: + x = Bitfield(9, 'abc') + assert False + except ValueError: + pass + try: + x = Bitfield(0, 'a') + assert False + except ValueError: + pass + try: + x = Bitfield(1, '') + assert False + except ValueError: + pass + try: + x = Bitfield(7, '') + assert False + except ValueError: + pass + try: + x = Bitfield(8, '') + assert False + except ValueError: + pass + try: + x = Bitfield(9, 'a') + assert False + except ValueError: + pass + try: + x = Bitfield(7, chr(1)) + assert False + except ValueError: + pass + try: + x = Bitfield(9, chr(0) + chr(0x40)) + assert False + except ValueError: + pass + assert Bitfield(0, '').tostring() == '' + assert Bitfield(1, chr(0x80)).tostring() == chr(0x80) + assert Bitfield(7, chr(0x02)).tostring() == chr(0x02) + assert Bitfield(8, chr(0xFF)).tostring() == chr(0xFF) + assert Bitfield(9, chr(0) + chr(0x80)).tostring() == chr(0) + chr(0x80) + x = Bitfield(1) + assert x.numfalse == 1 + x[0] = 1 + assert x.numfalse == 0 + x[0] = 1 + assert x.numfalse == 0 + assert x.tostring() == chr(0x80) + x = Bitfield(7) + assert len(x) == 7 + x[6] = 1 + assert x.numfalse == 6 + assert x.tostring() == chr(0x02) + x = Bitfield(8) + x[7] = 1 + assert x.tostring() == chr(1) + x = Bitfield(9) + x[8] = 1 + assert x.numfalse == 8 + assert x.tostring() == chr(0) + chr(0x80) + x = Bitfield(8, chr(0xC4)) + assert len(x) == 8 + assert x.numfalse == 5 + assert x.tostring() == chr(0xC4) diff --git a/www/pages/torrent/client/clock.py b/www/pages/torrent/client/clock.py new file mode 100644 index 00000000..e42c59b1 --- /dev/null +++ b/www/pages/torrent/client/clock.py @@ -0,0 +1,27 @@ +# Written by John Hoffman +# see LICENSE.txt for license information + +from time import * +import sys + +_MAXFORWARD = 100 +_FUDGE = 1 + +class RelativeTime: + def __init__(self): + self.time = time() + self.offset = 0 + + def get_time(self): + t = time() + self.offset + if t < self.time or t > self.time + _MAXFORWARD: + self.time += _FUDGE + self.offset += self.time - t + return self.time + self.time = t + return t + +if sys.platform != 'win32': + _RTIME = RelativeTime() + def clock(): + return _RTIME.get_time() \ No newline at end of file diff --git a/www/pages/torrent/client/download_bt1.py b/www/pages/torrent/client/download_bt1.py new file mode 100644 index 00000000..f4aef0c7 --- /dev/null +++ b/www/pages/torrent/client/download_bt1.py @@ -0,0 +1,882 @@ +# Written by Bram Cohen +# see LICENSE.txt for license information + +from zurllib import urlopen +from urlparse import urlparse +from BT1.btformats import check_message +from BT1.Choker import Choker +from BT1.Storage import Storage +from BT1.StorageWrapper import StorageWrapper +from BT1.FileSelector import FileSelector +from BT1.Uploader import Upload +from BT1.Downloader import Downloader +from BT1.HTTPDownloader import HTTPDownloader +from BT1.Connecter import Connecter +from RateLimiter import RateLimiter +from BT1.Encrypter import Encoder +from RawServer import RawServer, autodetect_ipv6, autodetect_socket_style +from BT1.Rerequester import Rerequester +from BT1.DownloaderFeedback import DownloaderFeedback +from RateMeasure import RateMeasure +from CurrentRateMeasure import Measure +from BT1.PiecePicker import PiecePicker +from BT1.Statistics import Statistics +from ConfigDir import ConfigDir +from bencode import bencode, bdecode +from natpunch import UPnP_test +from sha import sha +from os import path, makedirs, listdir +from parseargs import parseargs, formatDefinitions, defaultargs +from socket import error as socketerror +from random import seed +from threading import Thread, Event +from clock import clock +from __init__ import createPeerID + +try: + True +except: + True = 1 + False = 0 + +defaults = [ + ('max_uploads', 7, + "the maximum number of uploads to allow at once."), + ('keepalive_interval', 120.0, + 'number of seconds to pause between sending keepalives'), + ('download_slice_size', 2 ** 14, + "How many bytes to query for per request."), + ('upload_unit_size', 1460, + "when limiting upload rate, how many bytes to send at a time"), + ('request_backlog', 10, + "maximum number of requests to keep in a single pipe at once."), + ('max_message_length', 2 ** 23, + "maximum length prefix encoding you'll accept over the wire - larger values get the connection dropped."), + ('ip', '', + "ip to report you have to the tracker."), + ('minport', 10000, 'minimum port to listen on, counts up if unavailable'), + ('maxport', 60000, 'maximum port to listen on'), + ('random_port', 1, 'whether to choose randomly inside the port range ' + + 'instead of counting up linearly'), + ('responsefile', '', + 'file the server response was stored in, alternative to url'), + ('url', '', + 'url to get file from, alternative to responsefile'), + ('selector_enabled', 1, + 'whether to enable the file selector and fast resume function'), + ('expire_cache_data', 10, + 'the number of days after which you wish to expire old cache data ' + + '(0 = disabled)'), + ('priority', '', + 'a list of file priorities separated by commas, must be one per file, ' + + '0 = highest, 1 = normal, 2 = lowest, -1 = download disabled'), + ('saveas', '', + 'local file name to save the file as, null indicates query user'), + ('timeout', 300.0, + 'time to wait between closing sockets which nothing has been received on'), + ('timeout_check_interval', 60.0, + 'time to wait between checking if any connections have timed out'), + ('max_slice_length', 2 ** 17, + "maximum length slice to send to peers, larger requests are ignored"), + ('max_rate_period', 20.0, + "maximum amount of time to guess the current rate estimate represents"), + ('bind', '', + 'comma-separated list of ips/hostnames to bind to locally'), +# ('ipv6_enabled', autodetect_ipv6(), + ('ipv6_enabled', 0, + 'allow the client to connect to peers via IPv6'), + ('ipv6_binds_v4', autodetect_socket_style(), + "set if an IPv6 server socket won't also field IPv4 connections"), + ('upnp_nat_access', 1, + 'attempt to autoconfigure a UPnP router to forward a server port ' + + '(0 = disabled, 1 = mode 1 [fast], 2 = mode 2 [slow])'), + ('upload_rate_fudge', 5.0, + 'time equivalent of writing to kernel-level TCP buffer, for rate adjustment'), + ('tcp_ack_fudge', 0.03, + 'how much TCP ACK download overhead to add to upload rate calculations ' + + '(0 = disabled)'), + ('display_interval', .5, + 'time between updates of displayed information'), + ('rerequest_interval', 5 * 60, + 'time to wait between requesting more peers'), + ('min_peers', 20, + 'minimum number of peers to not do rerequesting'), + ('http_timeout', 60, + 'number of seconds to wait before assuming that an http connection has timed out'), + ('max_initiate', 40, + 'number of peers at which to stop initiating new connections'), + ('check_hashes', 1, + 'whether to check hashes on disk'), + ('max_upload_rate', 0, + 'maximum kB/s to upload at (0 = no limit, -1 = automatic)'), + ('max_download_rate', 0, + 'maximum kB/s to download at (0 = no limit)'), + ('alloc_type', 'normal', + 'allocation type (may be normal, background, pre-allocate or sparse)'), + ('alloc_rate', 2.0, + 'rate (in MiB/s) to allocate space at using background allocation'), + ('buffer_reads', 1, + 'whether to buffer disk reads'), + ('write_buffer_size', 4, + 'the maximum amount of space to use for buffering disk writes ' + + '(in megabytes, 0 = disabled)'), + ('breakup_seed_bitfield', 1, + 'sends an incomplete bitfield and then fills with have messages, ' + 'in order to get around stupid ISP manipulation'), + ('snub_time', 30.0, + "seconds to wait for data to come in over a connection before assuming it's semi-permanently choked"), + ('spew', 0, + "whether to display diagnostic info to stdout"), + ('rarest_first_cutoff', 2, + "number of downloads at which to switch from random to rarest first"), + ('rarest_first_priority_cutoff', 5, + 'the number of peers which need to have a piece before other partials take priority over rarest first'), + ('min_uploads', 4, + "the number of uploads to fill out to with extra optimistic unchokes"), + ('max_files_open', 50, + 'the maximum number of files to keep open at a time, 0 means no limit'), + ('round_robin_period', 30, + "the number of seconds between the client's switching upload targets"), + ('super_seeder', 0, + "whether to use special upload-efficiency-maximizing routines (only for dedicated seeds)"), + ('security', 1, + "whether to enable extra security features intended to prevent abuse"), + ('max_connections', 0, + "the absolute maximum number of peers to connect with (0 = no limit)"), + ('auto_kick', 1, + "whether to allow the client to automatically kick/ban peers that send bad data"), + ('double_check', 1, + "whether to double-check data being written to the disk for errors (may increase CPU load)"), + ('triple_check', 0, + "whether to thoroughly check data being written to the disk (may slow disk access)"), + ('lock_files', 1, + "whether to lock files the client is working with"), + ('lock_while_reading', 0, + "whether to lock access to files being read"), + ('auto_flush', 0, + "minutes between automatic flushes to disk (0 = disabled)"), + ('dedicated_seed_id', '', + "code to send to tracker identifying as a dedicated seed"), + ] + +argslistheader = 'Arguments are:\n\n' + + +def _failfunc(x): + print x + +# old-style downloader +def download(params, filefunc, statusfunc, finfunc, errorfunc, doneflag, cols, + pathFunc = None, presets = {}, exchandler = None, + failed = _failfunc, paramfunc = None): + + try: + config = parse_params(params, presets) + except ValueError, e: + failed('error: ' + str(e) + '\nrun with no args for parameter explanations') + return + if not config: + errorfunc(get_usage()) + return + + myid = createPeerID() + seed(myid) + + rawserver = RawServer(doneflag, config['timeout_check_interval'], + config['timeout'], ipv6_enable = config['ipv6_enabled'], + failfunc = failed, errorfunc = exchandler) + + upnp_type = UPnP_test(config['upnp_nat_access']) + try: + listen_port = rawserver.find_and_bind(config['minport'], config['maxport'], + config['bind'], ipv6_socket_style = config['ipv6_binds_v4'], + upnp = upnp_type, randomizer = config['random_port']) + except socketerror, e: + failed("Couldn't listen - " + str(e)) + return + + response = get_response(config['responsefile'], config['url'], failed) + if not response: + return + + infohash = sha(bencode(response['info'])).digest() + + d = BT1Download(statusfunc, finfunc, errorfunc, exchandler, doneflag, + config, response, infohash, myid, rawserver, listen_port) + + if not d.saveAs(filefunc): + return + + if pathFunc: + pathFunc(d.getFilename()) + + hashcheck = d.initFiles(old_style = True) + if not hashcheck: + return + if not hashcheck(): + return + if not d.startEngine(): + return + d.startRerequester() + d.autoStats() + + statusfunc(activity = 'connecting to peers') + + if paramfunc: + paramfunc({ 'max_upload_rate' : d.setUploadRate, # change_max_upload_rate() + 'max_uploads': d.setConns, # change_max_uploads() + 'listen_port' : listen_port, # int + 'peer_id' : myid, # string + 'info_hash' : infohash, # string + 'start_connection' : d._startConnection, # start_connection((, ), ) + }) + + rawserver.listen_forever(d.getPortHandler()) + + d.shutdown() + + +def parse_params(params, presets = {}): + if len(params) == 0: + return None + config, args = parseargs(params, defaults, 0, 1, presets = presets) + if args: + if config['responsefile'] or config['url']: + raise ValueError,'must have responsefile or url as arg or parameter, not both' + if path.isfile(args[0]): + config['responsefile'] = args[0] + else: + try: + urlparse(args[0]) + except: + raise ValueError, 'bad filename or url' + config['url'] = args[0] + elif (config['responsefile'] == '') == (config['url'] == ''): + raise ValueError, 'need responsefile or url, must have one, cannot have both' + return config + + +def get_usage(defaults = defaults, cols = 100, presets = {}): + return (argslistheader + formatDefinitions(defaults, cols, presets)) + + +def get_response(file, url, errorfunc): + try: + if file: + h = open(file, 'rb') + try: + line = h.read(10) # quick test to see if responsefile contains a dict + front,garbage = line.split(':',1) + assert front[0] == 'd' + int(front[1:]) + except: + errorfunc(file+' is not a valid responsefile') + return None + try: + h.seek(0) + except: + try: + h.close() + except: + pass + h = open(file, 'rb') + else: + try: + h = urlopen(url) + except: + errorfunc(url+' bad url') + return None + response = h.read() + + except IOError, e: + errorfunc('problem getting response info - ' + str(e)) + return None + try: + h.close() + except: + pass + try: + try: + response = bdecode(response) + except: + errorfunc("warning: bad data in responsefile") + response = bdecode(response, sloppy=1) + check_message(response) + except ValueError, e: + errorfunc("got bad file info - " + str(e)) + return None + + return response + + +class BT1Download: + def __init__(self, statusfunc, finfunc, errorfunc, excfunc, doneflag, + config, response, infohash, id, rawserver, port, + appdataobj = None): + self.statusfunc = statusfunc + self.finfunc = finfunc + self.errorfunc = errorfunc + self.excfunc = excfunc + self.doneflag = doneflag + self.config = config + self.response = response + self.infohash = infohash + self.myid = id + self.rawserver = rawserver + self.port = port + + self.info = self.response['info'] + self.pieces = [self.info['pieces'][x:x+20] + for x in xrange(0, len(self.info['pieces']), 20)] + self.len_pieces = len(self.pieces) + self.argslistheader = argslistheader + self.unpauseflag = Event() + self.unpauseflag.set() + self.downloader = None + self.storagewrapper = None + self.fileselector = None + self.super_seeding_active = False + self.filedatflag = Event() + self.spewflag = Event() + self.superseedflag = Event() + self.whenpaused = None + self.finflag = Event() + self.rerequest = None + self.tcp_ack_fudge = config['tcp_ack_fudge'] + + self.selector_enabled = config['selector_enabled'] + if appdataobj: + self.appdataobj = appdataobj + elif self.selector_enabled: + self.appdataobj = ConfigDir() + self.appdataobj.deleteOldCacheData( config['expire_cache_data'], + [self.infohash] ) + + self.excflag = self.rawserver.get_exception_flag() + self.failed = False + self.checking = False + self.started = False + + self.picker = PiecePicker(self.len_pieces, config['rarest_first_cutoff'], + config['rarest_first_priority_cutoff']) + self.choker = Choker(config, rawserver.add_task, + self.picker, self.finflag.isSet) + + + def checkSaveLocation(self, loc): + if self.info.has_key('length'): + return path.exists(loc) + for x in self.info['files']: + if path.exists(path.join(loc, x['path'][0])): + return True + return False + + + def saveAs(self, filefunc, pathfunc = None): + try: + def make(f, forcedir = False): + if not forcedir: + f = path.split(f)[0] + if f != '' and not path.exists(f): + makedirs(f) + + if self.info.has_key('length'): + file_length = self.info['length'] + file = filefunc(self.info['name'], file_length, + self.config['saveas'], False) + if file is None: + return None + make(file) + files = [(file, file_length)] + else: + file_length = 0L + for x in self.info['files']: + file_length += x['length'] + file = filefunc(self.info['name'], file_length, + self.config['saveas'], True) + if file is None: + return None + + # if this path exists, and no files from the info dict exist, we assume it's a new download and + # the user wants to create a new directory with the default name + existing = 0 + if path.exists(file): + if not path.isdir(file): + self.errorfunc(file + 'is not a dir') + return None + if len(listdir(file)) > 0: # if it's not empty + for x in self.info['files']: + if path.exists(path.join(file, x['path'][0])): + existing = 1 + if not existing: + file = path.join(file, self.info['name']) + if path.exists(file) and not path.isdir(file): + if file[-8:] == '.torrent': + file = file[:-8] + if path.exists(file) and not path.isdir(file): + self.errorfunc("Can't create dir - " + self.info['name']) + return None + make(file, True) + + # alert the UI to any possible change in path + if pathfunc != None: + pathfunc(file) + + files = [] + for x in self.info['files']: + n = file + for i in x['path']: + n = path.join(n, i) + files.append((n, x['length'])) + make(n) + except OSError, e: + self.errorfunc("Couldn't allocate dir - " + str(e)) + return None + + self.filename = file + self.files = files + self.datalength = file_length + + return file + + + def getFilename(self): + return self.filename + + + def _finished(self): + self.finflag.set() + try: + self.storage.set_readonly() + except (IOError, OSError), e: + self.errorfunc('trouble setting readonly at end - ' + str(e)) + if self.superseedflag.isSet(): + self._set_super_seed() + self.choker.set_round_robin_period( + max( self.config['round_robin_period'], + self.config['round_robin_period'] * + self.info['piece length'] / 200000 ) ) + self.rerequest_complete() + self.finfunc() + + def _data_flunked(self, amount, index): + self.ratemeasure_datarejected(amount) + if not self.doneflag.isSet(): + self.errorfunc('piece %d failed hash check, re-downloading it' % index) + + def _failed(self, reason): + self.failed = True + self.doneflag.set() + if reason is not None: + self.errorfunc(reason) + + + def initFiles(self, old_style = False, statusfunc = None): + if self.doneflag.isSet(): + return None + if not statusfunc: + statusfunc = self.statusfunc + + disabled_files = None + if self.selector_enabled: + self.priority = self.config['priority'] + if self.priority: + try: + self.priority = self.priority.split(',') + assert len(self.priority) == len(self.files) + self.priority = [int(p) for p in self.priority] + for p in self.priority: + assert p >= -1 + assert p <= 2 + except: + self.errorfunc('bad priority list given, ignored') + self.priority = None + + data = self.appdataobj.getTorrentData(self.infohash) + try: + d = data['resume data']['priority'] + assert len(d) == len(self.files) + disabled_files = [x == -1 for x in d] + except: + try: + disabled_files = [x == -1 for x in self.priority] + except: + pass + + try: + try: + self.storage = Storage(self.files, self.info['piece length'], + self.doneflag, self.config, disabled_files) + except IOError, e: + self.errorfunc('trouble accessing files - ' + str(e)) + return None + if self.doneflag.isSet(): + return None + + self.storagewrapper = StorageWrapper(self.storage, self.config['download_slice_size'], + self.pieces, self.info['piece length'], self._finished, self._failed, + statusfunc, self.doneflag, self.config['check_hashes'], + self._data_flunked, self.rawserver.add_task, + self.config, self.unpauseflag) + + except ValueError, e: + self._failed('bad data - ' + str(e)) + except IOError, e: + self._failed('IOError - ' + str(e)) + if self.doneflag.isSet(): + return None + + if self.selector_enabled: + self.fileselector = FileSelector(self.files, self.info['piece length'], + self.appdataobj.getPieceDir(self.infohash), + self.storage, self.storagewrapper, + self.rawserver.add_task, + self._failed) + if data: + data = data.get('resume data') + if data: + self.fileselector.unpickle(data) + + self.checking = True + if old_style: + return self.storagewrapper.old_style_init() + return self.storagewrapper.initialize + + + def getCachedTorrentData(self): + return self.appdataobj.getTorrentData(self.infohash) + + + def _make_upload(self, connection, ratelimiter, totalup): + return Upload(connection, ratelimiter, totalup, + self.choker, self.storagewrapper, self.picker, + self.config) + + def _kick_peer(self, connection): + def k(connection = connection): + connection.close() + self.rawserver.add_task(k,0) + + def _ban_peer(self, ip): + self.encoder_ban(ip) + + def _received_raw_data(self, x): + if self.tcp_ack_fudge: + x = int(x*self.tcp_ack_fudge) + self.ratelimiter.adjust_sent(x) +# self.upmeasure.update_rate(x) + + def _received_data(self, x): + self.downmeasure.update_rate(x) + self.ratemeasure.data_came_in(x) + + def _received_http_data(self, x): + self.downmeasure.update_rate(x) + self.ratemeasure.data_came_in(x) + self.downloader.external_data_received(x) + + def _cancelfunc(self, pieces): + self.downloader.cancel_piece_download(pieces) + self.httpdownloader.cancel_piece_download(pieces) + def _reqmorefunc(self, pieces): + self.downloader.requeue_piece_download(pieces) + + def startEngine(self, ratelimiter = None, statusfunc = None): + if self.doneflag.isSet(): + return False + if not statusfunc: + statusfunc = self.statusfunc + + self.checking = False + + for i in xrange(self.len_pieces): + if self.storagewrapper.do_I_have(i): + self.picker.complete(i) + self.upmeasure = Measure(self.config['max_rate_period'], + self.config['upload_rate_fudge']) + self.downmeasure = Measure(self.config['max_rate_period']) + + if ratelimiter: + self.ratelimiter = ratelimiter + else: + self.ratelimiter = RateLimiter(self.rawserver.add_task, + self.config['upload_unit_size'], + self.setConns) + self.ratelimiter.set_upload_rate(self.config['max_upload_rate']) + + self.ratemeasure = RateMeasure() + self.ratemeasure_datarejected = self.ratemeasure.data_rejected + + self.downloader = Downloader(self.storagewrapper, self.picker, + self.config['request_backlog'], self.config['max_rate_period'], + self.len_pieces, self.config['download_slice_size'], + self._received_data, self.config['snub_time'], self.config['auto_kick'], + self._kick_peer, self._ban_peer) + self.downloader.set_download_rate(self.config['max_download_rate']) + self.connecter = Connecter(self._make_upload, self.downloader, self.choker, + self.len_pieces, self.upmeasure, self.config, + self.ratelimiter, self.rawserver.add_task) + self.encoder = Encoder(self.connecter, self.rawserver, + self.myid, self.config['max_message_length'], self.rawserver.add_task, + self.config['keepalive_interval'], self.infohash, + self._received_raw_data, self.config) + self.encoder_ban = self.encoder.ban + + self.httpdownloader = HTTPDownloader(self.storagewrapper, self.picker, + self.rawserver, self.finflag, self.errorfunc, self.downloader, + self.config['max_rate_period'], self.infohash, self._received_http_data, + self.connecter.got_piece) + if self.response.has_key('httpseeds') and not self.finflag.isSet(): + for u in self.response['httpseeds']: + self.httpdownloader.make_download(u) + + if self.selector_enabled: + self.fileselector.tie_in(self.picker, self._cancelfunc, + self._reqmorefunc, self.rerequest_ondownloadmore) + if self.priority: + self.fileselector.set_priorities_now(self.priority) + self.appdataobj.deleteTorrentData(self.infohash) + # erase old data once you've started modifying it + + if self.config['super_seeder']: + self.set_super_seed() + + self.started = True + return True + + + def rerequest_complete(self): + if self.rerequest: + self.rerequest.announce(1) + + def rerequest_stopped(self): + if self.rerequest: + self.rerequest.announce(2) + + def rerequest_lastfailed(self): + if self.rerequest: + return self.rerequest.last_failed + return False + + def rerequest_ondownloadmore(self): + if self.rerequest: + self.rerequest.hit() + + def startRerequester(self, seededfunc = None, force_rapid_update = False): + if self.response.has_key('announce-list'): + trackerlist = self.response['announce-list'] + else: + trackerlist = [[self.response['announce']]] + + self.rerequest = Rerequester(trackerlist, self.config['rerequest_interval'], + self.rawserver.add_task, self.connecter.how_many_connections, + self.config['min_peers'], self.encoder.start_connections, + self.rawserver.add_task, self.storagewrapper.get_amount_left, + self.upmeasure.get_total, self.downmeasure.get_total, self.port, self.config['ip'], + self.myid, self.infohash, self.config['http_timeout'], + self.errorfunc, self.excfunc, self.config['max_initiate'], + self.doneflag, self.upmeasure.get_rate, self.downmeasure.get_rate, + self.unpauseflag, self.config['dedicated_seed_id'], + seededfunc, force_rapid_update ) + + self.rerequest.start() + + + def _init_stats(self): + self.statistics = Statistics(self.upmeasure, self.downmeasure, + self.connecter, self.httpdownloader, self.ratelimiter, + self.rerequest_lastfailed, self.filedatflag) + if self.info.has_key('files'): + self.statistics.set_dirstats(self.files, self.info['piece length']) + if self.config['spew']: + self.spewflag.set() + + def autoStats(self, displayfunc = None): + if not displayfunc: + displayfunc = self.statusfunc + + self._init_stats() + DownloaderFeedback(self.choker, self.httpdownloader, self.rawserver.add_task, + self.upmeasure.get_rate, self.downmeasure.get_rate, + self.ratemeasure, self.storagewrapper.get_stats, + self.datalength, self.finflag, self.spewflag, self.statistics, + displayfunc, self.config['display_interval']) + + def startStats(self): + self._init_stats() + d = DownloaderFeedback(self.choker, self.httpdownloader, self.rawserver.add_task, + self.upmeasure.get_rate, self.downmeasure.get_rate, + self.ratemeasure, self.storagewrapper.get_stats, + self.datalength, self.finflag, self.spewflag, self.statistics) + return d.gather + + + def getPortHandler(self): + return self.encoder + + + def shutdown(self, torrentdata = {}): + if self.checking or self.started: + self.storagewrapper.sync() + self.storage.close() + self.rerequest_stopped() + if self.fileselector and self.started: + if not self.failed: + self.fileselector.finish() + torrentdata['resume data'] = self.fileselector.pickle() + try: + self.appdataobj.writeTorrentData(self.infohash,torrentdata) + except: + self.appdataobj.deleteTorrentData(self.infohash) # clear it + return not self.failed and not self.excflag.isSet() + # if returns false, you may wish to auto-restart the torrent + + + def setUploadRate(self, rate): + try: + def s(self = self, rate = rate): + self.config['max_upload_rate'] = rate + self.ratelimiter.set_upload_rate(rate) + self.rawserver.add_task(s) + except AttributeError: + pass + + def setConns(self, conns, conns2 = None): + if not conns2: + conns2 = conns + try: + def s(self = self, conns = conns, conns2 = conns2): + self.config['min_uploads'] = conns + self.config['max_uploads'] = conns2 + if (conns > 30): + self.config['max_initiate'] = conns + 10 + self.rawserver.add_task(s) + except AttributeError: + pass + + def setDownloadRate(self, rate): + try: + def s(self = self, rate = rate): + self.config['max_download_rate'] = rate + self.downloader.set_download_rate(rate) + self.rawserver.add_task(s) + except AttributeError: + pass + + def startConnection(self, ip, port, id): + self.encoder._start_connection((ip, port), id) + + def _startConnection(self, ipandport, id): + self.encoder._start_connection(ipandport, id) + + def setInitiate(self, initiate): + try: + def s(self = self, initiate = initiate): + self.config['max_initiate'] = initiate + self.rawserver.add_task(s) + except AttributeError: + pass + + def getConfig(self): + return self.config + + def getDefaults(self): + return defaultargs(defaults) + + def getUsageText(self): + return self.argslistheader + + def reannounce(self, special = None): + try: + def r(self = self, special = special): + if special is None: + self.rerequest.announce() + else: + self.rerequest.announce(specialurl = special) + self.rawserver.add_task(r) + except AttributeError: + pass + + def getResponse(self): + try: + return self.response + except: + return None + +# def Pause(self): +# try: +# if self.storagewrapper: +# self.rawserver.add_task(self._pausemaker, 0) +# except: +# return False +# self.unpauseflag.clear() +# return True +# +# def _pausemaker(self): +# self.whenpaused = clock() +# self.unpauseflag.wait() # sticks a monkey wrench in the main thread +# +# def Unpause(self): +# self.unpauseflag.set() +# if self.whenpaused and clock()-self.whenpaused > 60: +# def r(self = self): +# self.rerequest.announce(3) # rerequest automatically if paused for >60 seconds +# self.rawserver.add_task(r) + + def Pause(self): + if not self.storagewrapper: + return False + self.unpauseflag.clear() + self.rawserver.add_task(self.onPause) + return True + + def onPause(self): + self.whenpaused = clock() + if not self.downloader: + return + self.downloader.pause(True) + self.encoder.pause(True) + self.choker.pause(True) + + def Unpause(self): + self.unpauseflag.set() + self.rawserver.add_task(self.onUnpause) + + def onUnpause(self): + if not self.downloader: + return + self.downloader.pause(False) + self.encoder.pause(False) + self.choker.pause(False) + if self.rerequest and self.whenpaused and clock()-self.whenpaused > 60: + self.rerequest.announce(3) # rerequest automatically if paused for >60 seconds + + def set_super_seed(self): + try: + self.superseedflag.set() + def s(self = self): + if self.finflag.isSet(): + self._set_super_seed() + self.rawserver.add_task(s) + except AttributeError: + pass + + def _set_super_seed(self): + if not self.super_seeding_active: + self.super_seeding_active = True + self.errorfunc(' ** SUPER-SEED OPERATION ACTIVE **\n' + + ' please set Max uploads so each peer gets 6-8 kB/s') + def s(self = self): + self.downloader.set_super_seed() + self.choker.set_super_seed() + self.rawserver.add_task(s) + if self.finflag.isSet(): # mode started when already finished + def r(self = self): + self.rerequest.announce(3) # so after kicking everyone off, reannounce + self.rawserver.add_task(r) + + def am_I_finished(self): + return self.finflag.isSet() + + def get_transfer_stats(self): + return self.upmeasure.get_total(), self.downmeasure.get_total() diff --git a/www/pages/torrent/client/inifile.py b/www/pages/torrent/client/inifile.py new file mode 100644 index 00000000..032d3396 --- /dev/null +++ b/www/pages/torrent/client/inifile.py @@ -0,0 +1,169 @@ +# Written by John Hoffman +# see LICENSE.txt for license information + +''' +reads/writes a Windows-style INI file +format: + + aa = "bb" + cc = 11 + + [eee] + ff = "gg" + +decodes to: +d = { '': {'aa':'bb','cc':'11'}, 'eee': {'ff':'gg'} } + +the encoder can also take this as input: + +d = { 'aa': 'bb, 'cc': 11, 'eee': {'ff':'gg'} } + +though it will only decode in the above format. Keywords must be strings. +Values that are strings are written surrounded by quotes, and the decoding +routine automatically strips any. +Booleans are written as integers. Anything else aside from string/int/float +may have unpredictable results. +''' + +from cStringIO import StringIO +from traceback import print_exc +from types import DictType, StringType +try: + from types import BooleanType +except ImportError: + BooleanType = None + +try: + True +except: + True = 1 + False = 0 + +DEBUG = False + +def ini_write(f, d, comment=''): + try: + a = {'':{}} + for k,v in d.items(): + assert type(k) == StringType + k = k.lower() + if type(v) == DictType: + if DEBUG: + print 'new section:' +k + if k: + assert not a.has_key(k) + a[k] = {} + aa = a[k] + for kk,vv in v: + assert type(kk) == StringType + kk = kk.lower() + assert not aa.has_key(kk) + if type(vv) == BooleanType: + vv = int(vv) + if type(vv) == StringType: + vv = '"'+vv+'"' + aa[kk] = str(vv) + if DEBUG: + print 'a['+k+']['+kk+'] = '+str(vv) + else: + aa = a[''] + assert not aa.has_key(k) + if type(v) == BooleanType: + v = int(v) + if type(v) == StringType: + v = '"'+v+'"' + aa[k] = str(v) + if DEBUG: + print 'a[\'\']['+k+'] = '+str(v) + r = open(f,'w') + if comment: + for c in comment.split('\n'): + r.write('# '+c+'\n') + r.write('\n') + l = a.keys() + l.sort() + for k in l: + if k: + r.write('\n['+k+']\n') + aa = a[k] + ll = aa.keys() + ll.sort() + for kk in ll: + r.write(kk+' = '+aa[kk]+'\n') + success = True + except: + if DEBUG: + print_exc() + success = False + try: + r.close() + except: + pass + return success + + +if DEBUG: + def errfunc(lineno, line, err): + print '('+str(lineno)+') '+err+': '+line +else: + errfunc = lambda lineno, line, err: None + +def ini_read(f, errfunc = errfunc): + try: + r = open(f,'r') + ll = r.readlines() + d = {} + dd = {'':d} + for i in xrange(len(ll)): + l = ll[i] + l = l.strip() + if not l: + continue + if l[0] == '#': + continue + if l[0] == '[': + if l[-1] != ']': + errfunc(i,l,'syntax error') + continue + l1 = l[1:-1].strip().lower() + if not l1: + errfunc(i,l,'syntax error') + continue + if dd.has_key(l1): + errfunc(i,l,'duplicate section') + d = dd[l1] + continue + d = {} + dd[l1] = d + continue + try: + k,v = l.split('=',1) + except: + try: + k,v = l.split(':',1) + except: + errfunc(i,l,'syntax error') + continue + k = k.strip().lower() + v = v.strip() + if len(v) > 1 and ( (v[0] == '"' and v[-1] == '"') or + (v[0] == "'" and v[-1] == "'") ): + v = v[1:-1] + if not k: + errfunc(i,l,'syntax error') + continue + if d.has_key(k): + errfunc(i,l,'duplicate entry') + continue + d[k] = v + if DEBUG: + print dd + except: + if DEBUG: + print_exc() + dd = None + try: + r.close() + except: + pass + return dd diff --git a/www/pages/torrent/client/iprangeparse.py b/www/pages/torrent/client/iprangeparse.py new file mode 100644 index 00000000..52f140e5 --- /dev/null +++ b/www/pages/torrent/client/iprangeparse.py @@ -0,0 +1,194 @@ +# Written by John Hoffman +# see LICENSE.txt for license information + +from bisect import bisect, insort + +try: + True +except: + True = 1 + False = 0 + bool = lambda x: not not x + + +def to_long_ipv4(ip): + ip = ip.split('.') + if len(ip) != 4: + raise ValueError, "bad address" + b = 0L + for n in ip: + b *= 256 + b += int(n) + return b + + +def to_long_ipv6(ip): + if ip == '': + raise ValueError, "bad address" + if ip == '::': # boundary handling + ip = '' + elif ip[:2] == '::': + ip = ip[1:] + elif ip[0] == ':': + raise ValueError, "bad address" + elif ip[-2:] == '::': + ip = ip[:-1] + elif ip[-1] == ':': + raise ValueError, "bad address" + + b = [] + doublecolon = False + for n in ip.split(':'): + if n == '': # double-colon + if doublecolon: + raise ValueError, "bad address" + doublecolon = True + b.append(None) + continue + if n.find('.') >= 0: # IPv4 + n = n.split('.') + if len(n) != 4: + raise ValueError, "bad address" + for i in n: + b.append(int(i)) + continue + n = ('0'*(4-len(n))) + n + b.append(int(n[:2],16)) + b.append(int(n[2:],16)) + bb = 0L + for n in b: + if n is None: + for i in xrange(17-len(b)): + bb *= 256 + continue + bb *= 256 + bb += n + return bb + +ipv4addrmask = 65535L*256*256*256*256 + +class IP_List: + def __init__(self): + self.ipv4list = [] # starts of ranges + self.ipv4dict = {} # start: end of ranges + self.ipv6list = [] # " + self.ipv6dict = {} # " + + def __nonzero__(self): + return bool(self.ipv4list or self.ipv6list) + + + def append(self, ip_beg, ip_end = None): + if ip_end is None: + ip_end = ip_beg + else: + assert ip_beg <= ip_end + if ip_beg.find(':') < 0: # IPv4 + ip_beg = to_long_ipv4(ip_beg) + ip_end = to_long_ipv4(ip_end) + l = self.ipv4list + d = self.ipv4dict + else: + ip_beg = to_long_ipv6(ip_beg) + ip_end = to_long_ipv6(ip_end) + bb = ip_beg % (256*256*256*256) + if bb == ipv4addrmask: + ip_beg -= bb + ip_end -= bb + l = self.ipv4list + d = self.ipv4dict + else: + l = self.ipv6list + d = self.ipv6dict + + pos = bisect(l,ip_beg)-1 + done = pos < 0 + while not done: + p = pos + while p < len(l): + range_beg = l[p] + if range_beg > ip_end+1: + done = True + break + range_end = d[range_beg] + if range_end < ip_beg-1: + p += 1 + if p == len(l): + done = True + break + continue + # if neither of the above conditions is true, the ranges overlap + ip_beg = min(ip_beg, range_beg) + ip_end = max(ip_end, range_end) + del l[p] + del d[range_beg] + break + + insort(l,ip_beg) + d[ip_beg] = ip_end + + + def includes(self, ip): + if not (self.ipv4list or self.ipv6list): + return False + if ip.find(':') < 0: # IPv4 + ip = to_long_ipv4(ip) + l = self.ipv4list + d = self.ipv4dict + else: + ip = to_long_ipv6(ip) + bb = ip % (256*256*256*256) + if bb == ipv4addrmask: + ip -= bb + l = self.ipv4list + d = self.ipv4dict + else: + l = self.ipv6list + d = self.ipv6dict + for ip_beg in l[bisect(l,ip)-1:]: + if ip == ip_beg: + return True + ip_end = d[ip_beg] + if ip > ip_beg and ip <= ip_end: + return True + return False + + + # reads a list from a file in the format 'whatever:whatever:ip-ip' + # (not IPv6 compatible at all) + def read_rangelist(self, file): + f = open(file, 'r') + while True: + line = f.readline() + if not line: + break + line = line.strip() + if not line or line[0] == '#': + continue + line = line.split(':')[-1] + try: + ip1,ip2 = line.split('-') + except: + ip1 = line + ip2 = line + try: + self.append(ip1.strip(),ip2.strip()) + except: + print '*** WARNING *** could not parse IP range: '+line + f.close() + +def is_ipv4(ip): + return ip.find(':') < 0 + +def is_valid_ip(ip): + try: + if is_ipv4(ip): + a = ip.split('.') + assert len(a) == 4 + for i in a: + chr(int(i)) + return True + to_long_ipv6(ip) + return True + except: + return False diff --git a/www/pages/torrent/client/launchmanycore.py b/www/pages/torrent/client/launchmanycore.py new file mode 100644 index 00000000..8dbb59c2 --- /dev/null +++ b/www/pages/torrent/client/launchmanycore.py @@ -0,0 +1,381 @@ +#!/usr/bin/env python + +# Written by John Hoffman +# see LICENSE.txt for license information + +from BitTornado import PSYCO +if PSYCO.psyco: + try: + import psyco + assert psyco.__version__ >= 0x010100f0 + psyco.full() + except: + pass + +from download_bt1 import BT1Download +from RawServer import RawServer, UPnP_ERROR +from RateLimiter import RateLimiter +from ServerPortHandler import MultiHandler +from parsedir import parsedir +from natpunch import UPnP_test +from random import seed +from socket import error as socketerror +from threading import Event +from sys import argv, exit +import sys, os +from clock import clock +from __init__ import createPeerID, mapbase64, version +from cStringIO import StringIO +from traceback import print_exc + +try: + True +except: + True = 1 + False = 0 + + +def fmttime(n): + try: + n = int(n) # n may be None or too large + assert n < 5184000 # 60 days + except: + return 'downloading' + m, s = divmod(n, 60) + h, m = divmod(m, 60) + return '%d:%02d:%02d' % (h, m, s) + +class SingleDownload: + def __init__(self, controller, hash, response, config, myid): + self.controller = controller + self.hash = hash + self.response = response + self.config = config + + self.doneflag = Event() + self.waiting = True + self.checking = False + self.working = False + self.seed = False + self.closed = False + + self.status_msg = '' + self.status_err = [''] + self.status_errtime = 0 + self.status_done = 0.0 + + self.rawserver = controller.handler.newRawServer(hash, self.doneflag) + + d = BT1Download(self.display, self.finished, self.error, + controller.exchandler, self.doneflag, config, response, + hash, myid, self.rawserver, controller.listen_port) + self.d = d + + def start(self): + if not self.d.saveAs(self.saveAs): + self._shutdown() + return + self._hashcheckfunc = self.d.initFiles() + if not self._hashcheckfunc: + self._shutdown() + return + self.controller.hashchecksched(self.hash) + + + def saveAs(self, name, length, saveas, isdir): + return self.controller.saveAs(self.hash, name, saveas, isdir) + + def hashcheck_start(self, donefunc): + if self.is_dead(): + self._shutdown() + return + self.waiting = False + self.checking = True + self._hashcheckfunc(donefunc) + + def hashcheck_callback(self): + self.checking = False + if self.is_dead(): + self._shutdown() + return + if not self.d.startEngine(ratelimiter = self.controller.ratelimiter): + self._shutdown() + return + self.d.startRerequester() + self.statsfunc = self.d.startStats() + self.rawserver.start_listening(self.d.getPortHandler()) + self.working = True + + def is_dead(self): + return self.doneflag.isSet() + + def _shutdown(self): + self.shutdown(False) + + def shutdown(self, quiet=True): + if self.closed: + return + self.doneflag.set() + self.rawserver.shutdown() + if self.checking or self.working: + self.d.shutdown() + self.waiting = False + self.checking = False + self.working = False + self.closed = True + self.controller.was_stopped(self.hash) + if not quiet: + self.controller.died(self.hash) + + + def display(self, activity = None, fractionDone = None): + # really only used by StorageWrapper now + if activity: + self.status_msg = activity + if fractionDone is not None: + self.status_done = float(fractionDone) + + def finished(self): + self.seed = True + + def error(self, msg): + if self.doneflag.isSet(): + self._shutdown() + self.status_err.append(msg) + self.status_errtime = clock() + + +class LaunchMany: + def __init__(self, config, Output): + try: + self.config = config + self.Output = Output + + self.torrent_dir = config['torrent_dir'] + self.torrent_cache = {} + self.file_cache = {} + self.blocked_files = {} + self.scan_period = config['parse_dir_interval'] + self.stats_period = config['display_interval'] + + self.torrent_list = [] + self.downloads = {} + self.counter = 0 + self.doneflag = Event() + + self.hashcheck_queue = [] + self.hashcheck_current = None + + self.rawserver = RawServer(self.doneflag, config['timeout_check_interval'], + config['timeout'], ipv6_enable = config['ipv6_enabled'], + failfunc = self.failed, errorfunc = self.exchandler) + upnp_type = UPnP_test(config['upnp_nat_access']) + while True: + try: + self.listen_port = self.rawserver.find_and_bind( + config['minport'], config['maxport'], config['bind'], + ipv6_socket_style = config['ipv6_binds_v4'], + upnp = upnp_type, randomizer = config['random_port']) + break + except socketerror, e: + if upnp_type and e == UPnP_ERROR: + self.Output.message('WARNING: COULD NOT FORWARD VIA UPnP') + upnp_type = 0 + continue + self.failed("Couldn't listen - " + str(e)) + return + + self.ratelimiter = RateLimiter(self.rawserver.add_task, + config['upload_unit_size']) + self.ratelimiter.set_upload_rate(config['max_upload_rate']) + + self.handler = MultiHandler(self.rawserver, self.doneflag) + seed(createPeerID()) + self.rawserver.add_task(self.scan, 0) + self.rawserver.add_task(self.stats, 0) + + self.handler.listen_forever() + + self.Output.message('shutting down') + self.hashcheck_queue = [] + for hash in self.torrent_list: + self.Output.message('dropped "'+self.torrent_cache[hash]['path']+'"') + self.downloads[hash].shutdown() + self.rawserver.shutdown() + + except: + data = StringIO() + print_exc(file = data) + Output.exception(data.getvalue()) + + + def scan(self): + self.rawserver.add_task(self.scan, self.scan_period) + + r = parsedir(self.torrent_dir, self.torrent_cache, + self.file_cache, self.blocked_files, + return_metainfo = True, errfunc = self.Output.message) + + ( self.torrent_cache, self.file_cache, self.blocked_files, + added, removed ) = r + + for hash, data in removed.items(): + self.Output.message('dropped "'+data['path']+'"') + self.remove(hash) + for hash, data in added.items(): + self.Output.message('added "'+data['path']+'"') + self.add(hash, data) + + def stats(self): + self.rawserver.add_task(self.stats, self.stats_period) + data = [] + for hash in self.torrent_list: + cache = self.torrent_cache[hash] + if self.config['display_path']: + name = cache['path'] + else: + name = cache['name'] + size = cache['length'] + d = self.downloads[hash] + progress = '0.0%' + peers = 0 + seeds = 0 + seedsmsg = "S" + dist = 0.0 + uprate = 0.0 + dnrate = 0.0 + upamt = 0 + dnamt = 0 + t = 0 + if d.is_dead(): + status = 'stopped' + elif d.waiting: + status = 'waiting for hash check' + elif d.checking: + status = d.status_msg + progress = '%.1f%%' % (d.status_done*100) + else: + stats = d.statsfunc() + s = stats['stats'] + if d.seed: + status = 'seeding' + progress = '100.0%' + seeds = s.numOldSeeds + seedsmsg = "s" + dist = s.numCopies + else: + if s.numSeeds + s.numPeers: + t = stats['time'] + if t == 0: # unlikely + t = 0.01 + status = fmttime(t) + else: + t = -1 + status = 'connecting to peers' + progress = '%.1f%%' % (int(stats['frac']*1000)/10.0) + seeds = s.numSeeds + dist = s.numCopies2 + dnrate = stats['down'] + peers = s.numPeers + uprate = stats['up'] + upamt = s.upTotal + dnamt = s.downTotal + + if d.is_dead() or d.status_errtime+300 > clock(): + msg = d.status_err[-1] + else: + msg = '' + + data.append(( name, status, progress, peers, seeds, seedsmsg, dist, + uprate, dnrate, upamt, dnamt, size, t, msg )) + stop = self.Output.display(data) + if stop: + self.doneflag.set() + + def remove(self, hash): + self.torrent_list.remove(hash) + self.downloads[hash].shutdown() + del self.downloads[hash] + + def add(self, hash, data): + c = self.counter + self.counter += 1 + x = '' + for i in xrange(3): + x = mapbase64[c & 0x3F]+x + c >>= 6 + peer_id = createPeerID(x) + d = SingleDownload(self, hash, data['metainfo'], self.config, peer_id) + self.torrent_list.append(hash) + self.downloads[hash] = d + d.start() + + + def saveAs(self, hash, name, saveas, isdir): + x = self.torrent_cache[hash] + style = self.config['saveas_style'] + if style == 1 or style == 3: + if saveas: + saveas = os.path.join(saveas,x['file'][:-1-len(x['type'])]) + else: + saveas = x['path'][:-1-len(x['type'])] + if style == 3: + if not os.path.isdir(saveas): + try: + os.mkdir(saveas) + except: + raise OSError("couldn't create directory for "+x['path'] + +" ("+saveas+")") + if not isdir: + saveas = os.path.join(saveas, name) + else: + if saveas: + saveas = os.path.join(saveas, name) + else: + saveas = os.path.join(os.path.split(x['path'])[0], name) + + if isdir and not os.path.isdir(saveas): + try: + os.mkdir(saveas) + except: + raise OSError("couldn't create directory for "+x['path'] + +" ("+saveas+")") + return saveas + + + def hashchecksched(self, hash = None): + if hash: + self.hashcheck_queue.append(hash) + if not self.hashcheck_current: + self._hashcheck_start() + + def _hashcheck_start(self): + self.hashcheck_current = self.hashcheck_queue.pop(0) + self.downloads[self.hashcheck_current].hashcheck_start(self.hashcheck_callback) + + def hashcheck_callback(self): + self.downloads[self.hashcheck_current].hashcheck_callback() + if self.hashcheck_queue: + self._hashcheck_start() + else: + self.hashcheck_current = None + + def died(self, hash): + if self.torrent_cache.has_key(hash): + self.Output.message('DIED: "'+self.torrent_cache[hash]['path']+'"') + + def was_stopped(self, hash): + try: + self.hashcheck_queue.remove(hash) + except: + pass + if self.hashcheck_current == hash: + self.hashcheck_current = None + if self.hashcheck_queue: + self._hashcheck_start() + + def failed(self, s): + self.Output.message('FAILURE: '+s) + + def exchandler(self, s): + self.Output.exception(s) diff --git a/www/pages/torrent/client/natpunch.py b/www/pages/torrent/client/natpunch.py new file mode 100644 index 00000000..896827bf --- /dev/null +++ b/www/pages/torrent/client/natpunch.py @@ -0,0 +1,254 @@ +# Written by John Hoffman +# derived from NATPortMapping.py by Yejun Yang +# and from example code by Myers Carpenter +# see LICENSE.txt for license information + +import socket +from traceback import print_exc +from subnetparse import IP_List +from clock import clock +from __init__ import createPeerID +try: + True +except: + True = 1 + False = 0 + +DEBUG = False + +EXPIRE_CACHE = 30 # seconds +ID = "BT-"+createPeerID()[-4:] + +try: + import pythoncom, win32com.client + _supported = 1 +except ImportError: + _supported = 0 + + + +class _UPnP1: # derived from Myers Carpenter's code + # seems to use the machine's local UPnP + # system for its operation. Runs fairly fast + + def __init__(self): + self.map = None + self.last_got_map = -10e10 + + def _get_map(self): + if self.last_got_map + EXPIRE_CACHE < clock(): + try: + dispatcher = win32com.client.Dispatch("HNetCfg.NATUPnP") + self.map = dispatcher.StaticPortMappingCollection + self.last_got_map = clock() + except: + self.map = None + return self.map + + def test(self): + try: + assert self._get_map() # make sure a map was found + success = True + except: + success = False + return success + + + def open(self, ip, p): + map = self._get_map() + try: + map.Add(p,'TCP',p,ip,True,ID) + if DEBUG: + print 'port opened: '+ip+':'+str(p) + success = True + except: + if DEBUG: + print "COULDN'T OPEN "+str(p) + print_exc() + success = False + return success + + + def close(self, p): + map = self._get_map() + try: + map.Remove(p,'TCP') + success = True + if DEBUG: + print 'port closed: '+str(p) + except: + if DEBUG: + print 'ERROR CLOSING '+str(p) + print_exc() + success = False + return success + + + def clean(self, retry = False): + if not _supported: + return + try: + map = self._get_map() + ports_in_use = [] + for i in xrange(len(map)): + try: + mapping = map[i] + port = mapping.ExternalPort + prot = str(mapping.Protocol).lower() + desc = str(mapping.Description).lower() + except: + port = None + if port and prot == 'tcp' and desc[:3] == 'bt-': + ports_in_use.append(port) + success = True + for port in ports_in_use: + try: + map.Remove(port,'TCP') + except: + success = False + if not success and not retry: + self.clean(retry = True) + except: + pass + + +class _UPnP2: # derived from Yejun Yang's code + # apparently does a direct search for UPnP hardware + # may work in some cases where _UPnP1 won't, but is slow + # still need to implement "clean" method + + def __init__(self): + self.services = None + self.last_got_services = -10e10 + + def _get_services(self): + if not self.services or self.last_got_services + EXPIRE_CACHE < clock(): + self.services = [] + try: + f=win32com.client.Dispatch("UPnP.UPnPDeviceFinder") + for t in ( "urn:schemas-upnp-org:service:WANIPConnection:1", + "urn:schemas-upnp-org:service:WANPPPConnection:1" ): + try: + conns = f.FindByType(t,0) + for c in xrange(len(conns)): + try: + svcs = conns[c].Services + for s in xrange(len(svcs)): + try: + self.services.append(svcs[s]) + except: + pass + except: + pass + except: + pass + except: + pass + self.last_got_services = clock() + return self.services + + def test(self): + try: + assert self._get_services() # make sure some services can be found + success = True + except: + success = False + return success + + + def open(self, ip, p): + svcs = self._get_services() + success = False + for s in svcs: + try: + s.InvokeAction('AddPortMapping',['',p,'TCP',p,ip,True,ID,0],'') + success = True + except: + pass + if DEBUG and not success: + print "COULDN'T OPEN "+str(p) + print_exc() + return success + + + def close(self, p): + svcs = self._get_services() + success = False + for s in svcs: + try: + s.InvokeAction('DeletePortMapping', ['',p,'TCP'], '') + success = True + except: + pass + if DEBUG and not success: + print "COULDN'T OPEN "+str(p) + print_exc() + return success + + +class _UPnP: # master holding class + def __init__(self): + self.upnp1 = _UPnP1() + self.upnp2 = _UPnP2() + self.upnplist = (None, self.upnp1, self.upnp2) + self.upnp = None + self.local_ip = None + self.last_got_ip = -10e10 + + def get_ip(self): + if self.last_got_ip + EXPIRE_CACHE < clock(): + local_ips = IP_List() + local_ips.set_intranet_addresses() + try: + for info in socket.getaddrinfo(socket.gethostname(),0,socket.AF_INET): + # exception if socket library isn't recent + self.local_ip = info[4][0] + if local_ips.includes(self.local_ip): + self.last_got_ip = clock() + if DEBUG: + print 'Local IP found: '+self.local_ip + break + else: + raise ValueError('couldn\'t find intranet IP') + except: + self.local_ip = None + if DEBUG: + print 'Error finding local IP' + print_exc() + return self.local_ip + + def test(self, upnp_type): + if DEBUG: + print 'testing UPnP type '+str(upnp_type) + if not upnp_type or not _supported or self.get_ip() is None: + if DEBUG: + print 'not supported' + return 0 + pythoncom.CoInitialize() # leave initialized + self.upnp = self.upnplist[upnp_type] # cache this + if self.upnp.test(): + if DEBUG: + print 'ok' + return upnp_type + if DEBUG: + print 'tested bad' + return 0 + + def open(self, p): + assert self.upnp, "must run UPnP_test() with the desired UPnP access type first" + return self.upnp.open(self.get_ip(), p) + + def close(self, p): + assert self.upnp, "must run UPnP_test() with the desired UPnP access type first" + return self.upnp.close(p) + + def clean(self): + return self.upnp1.clean() + +_upnp_ = _UPnP() + +UPnP_test = _upnp_.test +UPnP_open_port = _upnp_.open +UPnP_close_port = _upnp_.close +UPnP_reset = _upnp_.clean + diff --git a/www/pages/torrent/client/parseargs.py b/www/pages/torrent/client/parseargs.py new file mode 100644 index 00000000..646af104 --- /dev/null +++ b/www/pages/torrent/client/parseargs.py @@ -0,0 +1,137 @@ +# Written by Bill Bumgarner and Bram Cohen +# see LICENSE.txt for license information + +from types import * +from cStringIO import StringIO + + +def splitLine(line, COLS=80, indent=10): + indent = " " * indent + width = COLS - (len(indent) + 1) + if indent and width < 15: + width = COLS - 2 + indent = " " + s = StringIO() + i = 0 + for word in line.split(): + if i == 0: + s.write(indent+word) + i = len(word) + continue + if i + len(word) >= width: + s.write('\n'+indent+word) + i = len(word) + continue + s.write(' '+word) + i += len(word) + 1 + return s.getvalue() + +def formatDefinitions(options, COLS, presets = {}): + s = StringIO() + for (longname, default, doc) in options: + s.write('--' + longname + ' \n') + default = presets.get(longname, default) + if type(default) in (IntType, LongType): + try: + default = int(default) + except: + pass + if default is not None: + doc += ' (defaults to ' + repr(default) + ')' + s.write(splitLine(doc,COLS,10)) + s.write('\n\n') + return s.getvalue() + + +def usage(str): + raise ValueError(str) + + +def defaultargs(options): + l = {} + for (longname, default, doc) in options: + if default is not None: + l[longname] = default + return l + + +def parseargs(argv, options, minargs = None, maxargs = None, presets = {}): + config = {} + longkeyed = {} + for option in options: + longname, default, doc = option + longkeyed[longname] = option + config[longname] = default + for longname in presets.keys(): # presets after defaults but before arguments + config[longname] = presets[longname] + options = [] + args = [] + pos = 0 + while pos < len(argv): + if argv[pos][:2] != '--': + args.append(argv[pos]) + pos += 1 + else: + if pos == len(argv) - 1: + usage('parameter passed in at end with no value') + key, value = argv[pos][2:], argv[pos+1] + pos += 2 + if not longkeyed.has_key(key): + usage('unknown key --' + key) + longname, default, doc = longkeyed[key] + try: + t = type(config[longname]) + if t is NoneType or t is StringType: + config[longname] = value + elif t in (IntType, LongType): + config[longname] = long(value) + elif t is FloatType: + config[longname] = float(value) + else: + assert 0 + except ValueError, e: + usage('wrong format of --%s - %s' % (key, str(e))) + for key, value in config.items(): + if value is None: + usage("Option --%s is required." % key) + if minargs is not None and len(args) < minargs: + usage("Must supply at least %d args." % minargs) + if maxargs is not None and len(args) > maxargs: + usage("Too many args - %d max." % maxargs) + return (config, args) + +def test_parseargs(): + assert parseargs(('d', '--a', 'pq', 'e', '--b', '3', '--c', '4.5', 'f'), (('a', 'x', ''), ('b', 1, ''), ('c', 2.3, ''))) == ({'a': 'pq', 'b': 3, 'c': 4.5}, ['d', 'e', 'f']) + assert parseargs([], [('a', 'x', '')]) == ({'a': 'x'}, []) + assert parseargs(['--a', 'x', '--a', 'y'], [('a', '', '')]) == ({'a': 'y'}, []) + try: + parseargs([], [('a', 'x', '')]) + except ValueError: + pass + try: + parseargs(['--a', 'x'], []) + except ValueError: + pass + try: + parseargs(['--a'], [('a', 'x', '')]) + except ValueError: + pass + try: + parseargs([], [], 1, 2) + except ValueError: + pass + assert parseargs(['x'], [], 1, 2) == ({}, ['x']) + assert parseargs(['x', 'y'], [], 1, 2) == ({}, ['x', 'y']) + try: + parseargs(['x', 'y', 'z'], [], 1, 2) + except ValueError: + pass + try: + parseargs(['--a', '2.0'], [('a', 3, '')]) + except ValueError: + pass + try: + parseargs(['--a', 'z'], [('a', 2.1, '')]) + except ValueError: + pass + diff --git a/www/pages/torrent/client/parsedir.py b/www/pages/torrent/client/parsedir.py new file mode 100644 index 00000000..51613ce8 --- /dev/null +++ b/www/pages/torrent/client/parsedir.py @@ -0,0 +1,150 @@ +# Written by John Hoffman and Uoti Urpala +# see LICENSE.txt for license information +from bencode import bencode, bdecode +from BT1.btformats import check_info +from os.path import exists, isfile +from sha import sha +import sys, os + +try: + True +except: + True = 1 + False = 0 + +NOISY = False + +def _errfunc(x): + print ":: "+x + +def parsedir(directory, parsed, files, blocked, + exts = ['.torrent'], return_metainfo = False, errfunc = _errfunc): + if NOISY: + errfunc('checking dir') + dirs_to_check = [directory] + new_files = {} + new_blocked = {} + torrent_type = {} + while dirs_to_check: # first, recurse directories and gather torrents + directory = dirs_to_check.pop() + newtorrents = False + for f in os.listdir(directory): + newtorrent = None + for ext in exts: + if f.endswith(ext): + newtorrent = ext[1:] + break + if newtorrent: + newtorrents = True + p = os.path.join(directory, f) + new_files[p] = [(os.path.getmtime(p), os.path.getsize(p)), 0] + torrent_type[p] = newtorrent + if not newtorrents: + for f in os.listdir(directory): + p = os.path.join(directory, f) + if os.path.isdir(p): + dirs_to_check.append(p) + + new_parsed = {} + to_add = [] + added = {} + removed = {} + # files[path] = [(modification_time, size), hash], hash is 0 if the file + # has not been successfully parsed + for p,v in new_files.items(): # re-add old items and check for changes + oldval = files.get(p) + if not oldval: # new file + to_add.append(p) + continue + h = oldval[1] + if oldval[0] == v[0]: # file is unchanged from last parse + if h: + if blocked.has_key(p): # parseable + blocked means duplicate + to_add.append(p) # other duplicate may have gone away + else: + new_parsed[h] = parsed[h] + new_files[p] = oldval + else: + new_blocked[p] = 1 # same broken unparseable file + continue + if parsed.has_key(h) and not blocked.has_key(p): + if NOISY: + errfunc('removing '+p+' (will re-add)') + removed[h] = parsed[h] + to_add.append(p) + + to_add.sort() + for p in to_add: # then, parse new and changed torrents + new_file = new_files[p] + v,h = new_file + if new_parsed.has_key(h): # duplicate + if not blocked.has_key(p) or files[p][0] != v: + errfunc('**warning** '+ + p +' is a duplicate torrent for '+new_parsed[h]['path']) + new_blocked[p] = 1 + continue + + if NOISY: + errfunc('adding '+p) + try: + ff = open(p, 'rb') + d = bdecode(ff.read()) + check_info(d['info']) + h = sha(bencode(d['info'])).digest() + new_file[1] = h + if new_parsed.has_key(h): + errfunc('**warning** '+ + p +' is a duplicate torrent for '+new_parsed[h]['path']) + new_blocked[p] = 1 + continue + + a = {} + a['path'] = p + f = os.path.basename(p) + a['file'] = f + a['type'] = torrent_type[p] + i = d['info'] + l = 0 + nf = 0 + if i.has_key('length'): + l = i.get('length',0) + nf = 1 + elif i.has_key('files'): + for li in i['files']: + nf += 1 + if li.has_key('length'): + l += li['length'] + a['numfiles'] = nf + a['length'] = l + a['name'] = i.get('name', f) + def setkey(k, d = d, a = a): + if d.has_key(k): + a[k] = d[k] + setkey('failure reason') + setkey('warning message') + setkey('announce-list') + if return_metainfo: + a['metainfo'] = d + except: + errfunc('**warning** '+p+' has errors') + new_blocked[p] = 1 + continue + try: + ff.close() + except: + pass + if NOISY: + errfunc('... successful') + new_parsed[h] = a + added[h] = a + + for p,v in files.items(): # and finally, mark removed torrents + if not new_files.has_key(p) and not blocked.has_key(p): + if NOISY: + errfunc('removing '+p) + removed[v[1]] = parsed[v[1]] + + if NOISY: + errfunc('done checking') + return (new_parsed, new_files, new_blocked, added, removed) + diff --git a/www/pages/torrent/client/piecebuffer.py b/www/pages/torrent/client/piecebuffer.py new file mode 100644 index 00000000..96cc918c --- /dev/null +++ b/www/pages/torrent/client/piecebuffer.py @@ -0,0 +1,86 @@ +# Written by John Hoffman +# see LICENSE.txt for license information + +from array import array +from threading import Lock +# import inspect +try: + True +except: + True = 1 + False = 0 + +DEBUG = False + +class SingleBuffer: + def __init__(self, pool): + self.pool = pool + self.buf = array('c') + + def init(self): + if DEBUG: + print self.count + ''' + for x in xrange(6,1,-1): + try: + f = inspect.currentframe(x).f_code + print (f.co_filename,f.co_firstlineno,f.co_name) + del f + except: + pass + print '' + ''' + self.length = 0 + + def append(self, s): + l = self.length+len(s) + self.buf[self.length:l] = array('c',s) + self.length = l + + def __len__(self): + return self.length + + def __getslice__(self, a, b): + if b > self.length: + b = self.length + if b < 0: + b += self.length + if a == 0 and b == self.length and len(self.buf) == b: + return self.buf # optimization + return self.buf[a:b] + + def getarray(self): + return self.buf[:self.length] + + def release(self): + if DEBUG: + print -self.count + self.pool.release(self) + + +class BufferPool: + def __init__(self): + self.pool = [] + self.lock = Lock() + if DEBUG: + self.count = 0 + + def new(self): + self.lock.acquire() + if self.pool: + x = self.pool.pop() + else: + x = SingleBuffer(self) + if DEBUG: + self.count += 1 + x.count = self.count + x.init() + self.lock.release() + return x + + def release(self, x): + self.pool.append(x) + + +_pool = BufferPool() +PieceBuffer = _pool.new diff --git a/www/pages/torrent/client/selectpoll.py b/www/pages/torrent/client/selectpoll.py new file mode 100644 index 00000000..c9d694d6 --- /dev/null +++ b/www/pages/torrent/client/selectpoll.py @@ -0,0 +1,109 @@ +# Written by Bram Cohen +# see LICENSE.txt for license information + +from select import select, error +from time import sleep +from types import IntType +from bisect import bisect +POLLIN = 1 +POLLOUT = 2 +POLLERR = 8 +POLLHUP = 16 + +class poll: + def __init__(self): + self.rlist = [] + self.wlist = [] + + def register(self, f, t): + if type(f) != IntType: + f = f.fileno() + if (t & POLLIN): + insert(self.rlist, f) + else: + remove(self.rlist, f) + if (t & POLLOUT): + insert(self.wlist, f) + else: + remove(self.wlist, f) + + def unregister(self, f): + if type(f) != IntType: + f = f.fileno() + remove(self.rlist, f) + remove(self.wlist, f) + + def poll(self, timeout = None): + if self.rlist or self.wlist: + try: + r, w, e = select(self.rlist, self.wlist, [], timeout) + except ValueError: + return None + else: + sleep(timeout) + return [] + result = [] + for s in r: + result.append((s, POLLIN)) + for s in w: + result.append((s, POLLOUT)) + return result + +def remove(list, item): + i = bisect(list, item) + if i > 0 and list[i-1] == item: + del list[i-1] + +def insert(list, item): + i = bisect(list, item) + if i == 0 or list[i-1] != item: + list.insert(i, item) + +def test_remove(): + x = [2, 4, 6] + remove(x, 2) + assert x == [4, 6] + x = [2, 4, 6] + remove(x, 4) + assert x == [2, 6] + x = [2, 4, 6] + remove(x, 6) + assert x == [2, 4] + x = [2, 4, 6] + remove(x, 5) + assert x == [2, 4, 6] + x = [2, 4, 6] + remove(x, 1) + assert x == [2, 4, 6] + x = [2, 4, 6] + remove(x, 7) + assert x == [2, 4, 6] + x = [2, 4, 6] + remove(x, 5) + assert x == [2, 4, 6] + x = [] + remove(x, 3) + assert x == [] + +def test_insert(): + x = [2, 4] + insert(x, 1) + assert x == [1, 2, 4] + x = [2, 4] + insert(x, 3) + assert x == [2, 3, 4] + x = [2, 4] + insert(x, 5) + assert x == [2, 4, 5] + x = [2, 4] + insert(x, 2) + assert x == [2, 4] + x = [2, 4] + insert(x, 4) + assert x == [2, 4] + x = [2, 3, 4] + insert(x, 3) + assert x == [2, 3, 4] + x = [] + insert(x, 3) + assert x == [3] diff --git a/www/pages/torrent/client/subnetparse.py b/www/pages/torrent/client/subnetparse.py new file mode 100644 index 00000000..55b46dcf --- /dev/null +++ b/www/pages/torrent/client/subnetparse.py @@ -0,0 +1,218 @@ +# Written by John Hoffman +# see LICENSE.txt for license information + +from bisect import bisect, insort + +try: + True +except: + True = 1 + False = 0 + bool = lambda x: not not x + +hexbinmap = { + '0': '0000', + '1': '0001', + '2': '0010', + '3': '0011', + '4': '0100', + '5': '0101', + '6': '0110', + '7': '0111', + '8': '1000', + '9': '1001', + 'a': '1010', + 'b': '1011', + 'c': '1100', + 'd': '1101', + 'e': '1110', + 'f': '1111', + 'x': '0000', +} + +chrbinmap = {} +for n in xrange(256): + b = [] + nn = n + for i in xrange(8): + if nn & 0x80: + b.append('1') + else: + b.append('0') + nn <<= 1 + chrbinmap[n] = ''.join(b) + + +def to_bitfield_ipv4(ip): + ip = ip.split('.') + if len(ip) != 4: + raise ValueError, "bad address" + b = [] + for i in ip: + b.append(chrbinmap[int(i)]) + return ''.join(b) + +def to_bitfield_ipv6(ip): + b = '' + doublecolon = False + + if ip == '': + raise ValueError, "bad address" + if ip == '::': # boundary handling + ip = '' + elif ip[:2] == '::': + ip = ip[1:] + elif ip[0] == ':': + raise ValueError, "bad address" + elif ip[-2:] == '::': + ip = ip[:-1] + elif ip[-1] == ':': + raise ValueError, "bad address" + for n in ip.split(':'): + if n == '': # double-colon + if doublecolon: + raise ValueError, "bad address" + doublecolon = True + b += ':' + continue + if n.find('.') >= 0: # IPv4 + n = to_bitfield_ipv4(n) + b += n + '0'*(32-len(n)) + continue + n = ('x'*(4-len(n))) + n + for i in n: + b += hexbinmap[i] + if doublecolon: + pos = b.find(':') + b = b[:pos]+('0'*(129-len(b)))+b[pos+1:] + if len(b) != 128: # always check size + raise ValueError, "bad address" + return b + +ipv4addrmask = to_bitfield_ipv6('::ffff:0:0')[:96] + +class IP_List: + def __init__(self): + self.ipv4list = [] + self.ipv6list = [] + + def __nonzero__(self): + return bool(self.ipv4list or self.ipv6list) + + + def append(self, ip, depth = 256): + if ip.find(':') < 0: # IPv4 + insort(self.ipv4list,to_bitfield_ipv4(ip)[:depth]) + else: + b = to_bitfield_ipv6(ip) + if b.startswith(ipv4addrmask): + insort(self.ipv4list,b[96:][:depth-96]) + else: + insort(self.ipv6list,b[:depth]) + + + def includes(self, ip): + if not (self.ipv4list or self.ipv6list): + return False + if ip.find(':') < 0: # IPv4 + b = to_bitfield_ipv4(ip) + else: + b = to_bitfield_ipv6(ip) + if b.startswith(ipv4addrmask): + b = b[96:] + if len(b) > 32: + l = self.ipv6list + else: + l = self.ipv4list + for map in l[bisect(l,b)-1:]: + if b.startswith(map): + return True + if map > b: + return False + return False + + + def read_fieldlist(self, file): # reads a list from a file in the format 'ip/len ' + f = open(file, 'r') + while True: + line = f.readline() + if not line: + break + line = line.strip().expandtabs() + if not line or line[0] == '#': + continue + try: + line, garbage = line.split(' ',1) + except: + pass + try: + line, garbage = line.split('#',1) + except: + pass + try: + ip, depth = line.split('/') + except: + ip = line + depth = None + try: + if depth is not None: + depth = int(depth) + self.append(ip,depth) + except: + print '*** WARNING *** could not parse IP range: '+line + f.close() + + + def set_intranet_addresses(self): + self.append('127.0.0.1',8) + self.append('10.0.0.0',8) + self.append('172.16.0.0',12) + self.append('192.168.0.0',16) + self.append('169.254.0.0',16) + self.append('::1') + self.append('fe80::',16) + self.append('fec0::',16) + + def set_ipv4_addresses(self): + self.append('::ffff:0:0',96) + +def ipv6_to_ipv4(ip): + ip = to_bitfield_ipv6(ip) + if not ip.startswith(ipv4addrmask): + raise ValueError, "not convertible to IPv4" + ip = ip[-32:] + x = '' + for i in range(4): + x += str(int(ip[:8],2)) + if i < 3: + x += '.' + ip = ip[8:] + return x + +def to_ipv4(ip): + if is_ipv4(ip): + _valid_ipv4(ip) + return ip + return ipv6_to_ipv4(ip) + +def is_ipv4(ip): + return ip.find(':') < 0 + +def _valid_ipv4(ip): + ip = ip.split('.') + if len(ip) != 4: + raise ValueError + for i in ip: + chr(int(i)) + +def is_valid_ip(ip): + try: + if not ip: + return False + if is_ipv4(ip): + _valid_ipv4(ip) + return True + to_bitfield_ipv6(ip) + return True + except: + return False diff --git a/www/pages/torrent/client/torrentlistparse.py b/www/pages/torrent/client/torrentlistparse.py new file mode 100644 index 00000000..5ed464b0 --- /dev/null +++ b/www/pages/torrent/client/torrentlistparse.py @@ -0,0 +1,38 @@ +# Written by John Hoffman +# see LICENSE.txt for license information + +from binascii import unhexlify + +try: + True +except: + True = 1 + False = 0 + + +# parses a list of torrent hashes, in the format of one hash per line in hex format + +def parsetorrentlist(filename, parsed): + new_parsed = {} + added = {} + removed = parsed + f = open(filename, 'r') + while True: + l = f.readline() + if not l: + break + l = l.strip() + try: + if len(l) != 40: + raise ValueError, 'bad line' + h = unhexlify(l) + except: + print '*** WARNING *** could not parse line in torrent list: '+l + if parsed.has_key(h): + del removed[h] + else: + added[h] = True + new_parsed[h] = True + f.close() + return (new_parsed, added, removed) + diff --git a/www/pages/torrent/client/zurllib.py b/www/pages/torrent/client/zurllib.py new file mode 100644 index 00000000..2af40324 --- /dev/null +++ b/www/pages/torrent/client/zurllib.py @@ -0,0 +1,100 @@ +# Written by John Hoffman +# see LICENSE.txt for license information + +from httplib import HTTPConnection, HTTPSConnection, HTTPException +from urlparse import urlparse +from bencode import bdecode +import socket +from gzip import GzipFile +from StringIO import StringIO +from urllib import quote, unquote +from __init__ import product_name, version_short + +VERSION = product_name+'/'+version_short +MAX_REDIRECTS = 10 + + +class btHTTPcon(HTTPConnection): # attempt to add automatic connection timeout + def connect(self): + HTTPConnection.connect(self) + try: + self.sock.settimeout(30) + except: + pass + +class btHTTPScon(HTTPSConnection): # attempt to add automatic connection timeout + def connect(self): + HTTPSConnection.connect(self) + try: + self.sock.settimeout(30) + except: + pass + +class urlopen: + def __init__(self, url): + self.tries = 0 + self._open(url.strip()) + self.error_return = None + + def _open(self, url): + self.tries += 1 + if self.tries > MAX_REDIRECTS: + raise IOError, ('http error', 500, + "Internal Server Error: Redirect Recursion") + (scheme, netloc, path, pars, query, fragment) = urlparse(url) + if scheme != 'http' and scheme != 'https': + raise IOError, ('url error', 'unknown url type', scheme, url) + url = path + if pars: + url += ';'+pars + if query: + url += '?'+query +# if fragment: + try: + if scheme == 'http': + self.connection = btHTTPcon(netloc) + else: + self.connection = btHTTPScon(netloc) + self.connection.request('GET', url, None, + { 'User-Agent': VERSION, + 'Accept-Encoding': 'gzip' } ) + self.response = self.connection.getresponse() + except HTTPException, e: + raise IOError, ('http error', str(e)) + status = self.response.status + if status in (301,302): + try: + self.connection.close() + except: + pass + self._open(self.response.getheader('Location')) + return + if status != 200: + try: + data = self._read() + d = bdecode(data) + if d.has_key('failure reason'): + self.error_return = data + return + except: + pass + raise IOError, ('http error', status, self.response.reason) + + def read(self): + if self.error_return: + return self.error_return + return self._read() + + def _read(self): + data = self.response.read() + if self.response.getheader('Content-Encoding','').find('gzip') >= 0: + try: + compressed = StringIO(data) + f = GzipFile(fileobj = compressed) + data = f.read() + except: + raise IOError, ('http error', 'got corrupt response') + return data + + def close(self): + self.connection.close() diff --git a/www/redirect.py b/www/redirect.py new file mode 100755 index 00000000..ada125b5 --- /dev/null +++ b/www/redirect.py @@ -0,0 +1,30 @@ +#!/usr/bin/python + +import os +import cgi + +from web.http import HTTPResponse + +for language in ("de", "en",): + if os.environ["HTTP_ACCEPT_LANGUAGE"].startswith(language): + break + +site = cgi.FieldStorage().getfirst("site") or "index" + +sites = { "ipfire.org" : "www.ipfire.org", + "www.ipfire.org" : "/%s/%s" % (language, site,), + "source.ipfire.org" : "http://www.ipfire.org/%s/source" % language, + "tracker.ipfire.org" : "http://www.ipfire.org/%s/tracker" % language, + "torrent.ipfire.org" : "http://www.ipfire.org/%s/tracker" % language, + "download.ipfire.org" : "http://www.ipfire.org/%s/download" % language, + "people.ipfire.org" : "http://wiki.ipfire.org/%s/people/start" % language, } + +httpheader = [] + +try: + httpheader.append(("Location", sites[os.environ["SERVER_NAME"]])) +except KeyError: + httpheader.append(("Location", sites["www.ipfire.org"])) + +h = HTTPResponse(302, httpheader, None) +h.execute() diff --git a/www/robots.txt b/www/robots.txt deleted file mode 100644 index eb053628..00000000 --- a/www/robots.txt +++ /dev/null @@ -1,2 +0,0 @@ -User-agent: * -Disallow: diff --git a/www/header.inc b/www/template.inc similarity index 71% rename from www/header.inc rename to www/template.inc index 2f727074..6fd1f28b 100644 --- a/www/header.inc +++ b/www/template.inc @@ -49,28 +49,54 @@
    %(menu)s
    - english - german + %(languages)s
    -
    +
    -

    IPFire.org

    -
    +

    %(server)s

    +
    -

    Security today!

    -
    +

    %(slogan)s

    +
    - - - - - - - - -
    - + + + + + + + + + + + + + + + + +
    +
    +
    + %(content)s +
    +
    +
    +
    +
    + %(sidebar)s +
    +
    +
    +
    + + + + + diff --git a/www/web/__init__.py b/www/web/__init__.py new file mode 100644 index 00000000..92088388 --- /dev/null +++ b/www/web/__init__.py @@ -0,0 +1,229 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +import os +import cgi +import time +import random +import simplejson as json + +from http import HTTPResponse, WebError + +class Data: + def __init__(self): + self.output = "" + + def w(self, s): + self.output += "%s\n" % s + + def __call__(self): + return self.output + + +class Json: + def __init__(self, file): + f = open(file) + data = f.read() + data = data.replace('\n', '') # Remove all \n + data = data.replace('\t', '') # Remove all \t + self.json = json.loads(data) + f.close() + + +class Page(Data): + def include(self, file): + f = open(file) + output = f.read() + f.close() + self.w(output % self.data) + + def menu(self): + m = Menu(self.langs.current) + return m() + + def __init__(self, title, content, sidebar=None): + self.output = "" + self.langs = Languages() + self.data = {"server": os.environ["SERVER_NAME"].replace("ipfire", "ipfire"), + "title" : "%s - %s" % (os.environ["SERVER_NAME"], title,), + "menu" : self.menu(), + "document_name" : title, + "lang" : self.langs.current, + "languages" : self.langs.menu(title), + "year" : time.strftime("%Y"), + "slogan" : "Security today!", + "content" : content(self.langs.current), + "sidebar" : "", } + if sidebar: + self.data["sidebar"] = sidebar(self.langs.current) + + def __call__(self): + try: + self.include("template.inc") + code = 200 + except WebError: + code = 500 + h = HTTPResponse(code) + h.execute(self.output) + + +class News(Json): + def __init__(self, limit=3): + Json.__init__(self, "news.json") + self.news = self.json.values() + if limit: + self.news = self.news[:limit] + self.news.reverse() + + def html(self, lang="en"): + s = "" + for item in self.news: + for i in ("content", "subject",): + if type(item[i]) == type({}): + item[i] = item[i][lang] + b = Box(item["date"] + " - " + item["subject"], "by %s" % item["author"]) + b.w(item["content"]) + s += b() + return s + + __call__ = html + + def headlines(self, lang="en"): + headlines = [] + for item in self.news: + if type(item["subject"]) == type({}): + item["subject"] = item["subject"][lang] + headlines.append((item["subject"],)) + return headlines + + +class Menu(Json): + def __init__(self, lang): + self.lang = lang + Json.__init__(self, "menu.json") + + def __call__(self): + s = """" + return s + + +class Banners(Json): + def __init__(self, lang="en"): + self.lang = lang + Json.__init__(self, "banners.json") + + def random(self): + banner = random.choice(self.json.values()) + return banner + + +class Languages: + def __init__(self, doc=""): + self.available = [] + + for lang in ("de", "en",): + self.append(lang,) + + self.current = cgi.FieldStorage().getfirst("lang") or "en" + + def append(self, lang): + self.available.append(lang) + + def menu(self, doc): + s = "" + for lang in self.available: + s += """%(lang)s""" % \ + { "lang" : lang, "doc" : doc, } + return s + + +class Box(Data): + def __init__(self, headline, subtitle=""): + Data.__init__(self) + self.w("""

    %s

    """ % (headline,)) + if subtitle: + self.w("""""" % (subtitle,)) + + def __call__(self): + self.w("""
    """) + return Data.__call__(self) + + +class Sidebar(Data): + def __init__(self, name): + Data.__init__(self) + + def content(self, lang): + self.w("""

    Test Page

    +

    Lorem ipsum dolor sit amet, consectetuer sadipscing elitr, + sed diam nonumy eirmod tempor invidunt ut labore et dolore magna + aliquyam erat, sed diam voluptua. At vero eos et accusam et justo + duo dolores et ea rebum.

    """) + banners = Banners() + self.w("""

    %(title)s

    + """ % banners.random()) + + def __call__(self, lang): + self.content(lang) + return Data.__call__(self) + + +class Content(Data): + def __init__(self, name): + Data.__init__(self) + + def content(self): + self.w("""

    Test Page

    +

    Lorem ipsum dolor sit amet, consectetuer sadipscing elitr, + sed diam nonumy eirmod tempor invidunt ut labore et dolore magna + aliquyam erat, sed diam voluptua. At vero eos et accusam et justo + duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata + sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, + consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt + ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero + eos et accusam et justo duo dolores et ea rebum. Stet clita kasd + gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. + Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam + nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, + sed diam voluptua. At vero eos et accusam et justo duo dolores et ea + rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem + ipsum dolor sit amet.

    """) + + b = Box("Test box one", "Subtitle of box") + b.write("""

    Duis autem vel eum iriure dolor in hendrerit in vulputate velit + esse molestie consequat, vel illum dolore eu feugiat nulla facilisis + at vero eros et accumsan et iusto odio dignissim qui blandit praesent + luptatum zzril delenit augue duis dolore te feugait nulla facilisi. + Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam + nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat + volutpat.

    """) + self.w(b()) + + b = Box("Test box two", "Subtitle of box") + b.write("""

    Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper + suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem + vel eum iriure dolor in hendrerit in vulputate velit esse molestie + consequat, vel illum dolore eu feugiat nulla facilisis at vero eros + et accumsan et iusto odio dignissim qui blandit praesent luptatum + zzril delenit augue duis dolore te feugait nulla facilisi.

    """) + self.w(b()) + + def __call__(self, lang="en"): + self.content() + return Data.__call__(self) diff --git a/www/web/http.py b/www/web/http.py new file mode 100644 index 00000000..76f547ad --- /dev/null +++ b/www/web/http.py @@ -0,0 +1,28 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +code2msg = { 200 : "OK", + 302 : "Temporarily Moved", + 404 : "Not found", + 500 : "Internal Server Error", } + +class HTTPResponse: + def __init__(self, code, header=None, type="text/html"): + self.code = code + + print "Status: %s - %s" % (self.code, code2msg[self.code],) + if self.code == 302: + print "Pragma: no-cache" + if type: + print "Content-type: " + type + if header: + for (key, value,) in header: + print "%s: %s" % (key, value,) + print + + def execute(self, content=""): + if self.code == 200: + print content + +class WebError(Exception): + pass -- 2.47.3