[or-cvs] r10032: Simulator is running, everything but impl package has JavaDo (in puppetor/trunk: . doc src src/de src/de/uniba src/de/uniba/wiai src/de/uniba/wiai/lspi src/de/uniba/wiai/lspi/puppetor src/de/uniba/wiai/lspi/puppetor/examples src/de/uniba/wiai/lspi/puppetor/impl src/net src/net/freehaven src/net/freehaven/tor src/net/freehaven/tor/control src/net/freehaven/tor/control/examples)

kloesing at seul.org kloesing at seul.org
Wed Apr 25 20:14:58 UTC 2007


Author: kloesing
Date: 2007-04-25 16:14:34 -0400 (Wed, 25 Apr 2007)
New Revision: 10032

Added:
   puppetor/trunk/doc/
   puppetor/trunk/doc/howto.aux
   puppetor/trunk/doc/howto.dvi
   puppetor/trunk/doc/howto.pdf
   puppetor/trunk/doc/howto.tex
   puppetor/trunk/doc/logging
   puppetor/trunk/license
   puppetor/trunk/logging.properties
   puppetor/trunk/readme
   puppetor/trunk/src/
   puppetor/trunk/src/de/
   puppetor/trunk/src/de/uniba/
   puppetor/trunk/src/de/uniba/wiai/
   puppetor/trunk/src/de/uniba/wiai/lspi/
   puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/
   puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/ClientApplication.java
   puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/DirectoryNode.java
   puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/Event.java
   puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/EventListener.java
   puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/EventManager.java
   puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/EventSource.java
   puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/Network.java
   puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/NetworkFactory.java
   puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/NetworkState.java
   puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/NodeState.java
   puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/ProxyNode.java
   puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/RouterNode.java
   puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/ServerApplication.java
   puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/TorProcessException.java
   puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/
   puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/AccessingPublicWebServerOverTor.java
   puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/AdvertisingAndAccessingHiddenServiceOverPrivateTorNetwork.java
   puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/AdvertisingAndAccessingHiddenServiceOverPublicTorNetwork.java
   puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/AdvertisingHiddenServiceToPublicTorNetwork.java
   puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/
   puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/ClientApplicationImpl.java
   puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/DirectoryNodeImpl.java
   puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/EventManagerImpl.java
   puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/NetworkImpl.java
   puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/ProxyNodeImpl.java
   puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/RouterNodeImpl.java
   puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/ServerApplicationImpl.java
   puppetor/trunk/src/net/
   puppetor/trunk/src/net/freehaven/
   puppetor/trunk/src/net/freehaven/tor/
   puppetor/trunk/src/net/freehaven/tor/control/
   puppetor/trunk/src/net/freehaven/tor/control/.cvsignore
   puppetor/trunk/src/net/freehaven/tor/control/Bytes.java
   puppetor/trunk/src/net/freehaven/tor/control/ConfigEntry.java
   puppetor/trunk/src/net/freehaven/tor/control/EventHandler.java
   puppetor/trunk/src/net/freehaven/tor/control/NullEventHandler.java
   puppetor/trunk/src/net/freehaven/tor/control/PasswordDigest.java
   puppetor/trunk/src/net/freehaven/tor/control/TorControlCommands.java
   puppetor/trunk/src/net/freehaven/tor/control/TorControlConnection.java
   puppetor/trunk/src/net/freehaven/tor/control/TorControlConnection0.java
   puppetor/trunk/src/net/freehaven/tor/control/TorControlConnection1.java
   puppetor/trunk/src/net/freehaven/tor/control/TorControlError.java
   puppetor/trunk/src/net/freehaven/tor/control/TorControlSyntaxError.java
   puppetor/trunk/src/net/freehaven/tor/control/examples/
   puppetor/trunk/src/net/freehaven/tor/control/examples/.cvsignore
   puppetor/trunk/src/net/freehaven/tor/control/examples/DebuggingEventHandler.java
   puppetor/trunk/src/net/freehaven/tor/control/examples/Main.java
Log:
Simulator is running, everything but impl package has JavaDocs

Added: puppetor/trunk/doc/howto.aux
===================================================================
--- puppetor/trunk/doc/howto.aux	2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/doc/howto.aux	2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,8 @@
+\relax 
+\@writefile{toc}{\contentsline {section}{\numberline {1}Introduction}{1}}
+\@writefile{toc}{\contentsline {section}{\numberline {2}Installation}{2}}
+\@writefile{toc}{\contentsline {section}{\numberline {3}Example 1: Accessing public Web server over Tor}{3}}
+\@writefile{toc}{\contentsline {section}{\numberline {4}Example 2: Advertising hidden service to public Tor network}{5}}
+\@writefile{toc}{\contentsline {section}{\numberline {5}Example 3: Advertising and accessing hidden service over public Tor network}{6}}
+\@writefile{toc}{\contentsline {section}{\numberline {6}Example 4: Advertising and accessing hidden service over private Tor network}{7}}
+\@writefile{toc}{\contentsline {section}{\numberline {7}Known issues}{9}}


Property changes on: puppetor/trunk/doc/howto.aux
___________________________________________________________________
Name: svn:executable
   + *

Added: puppetor/trunk/doc/howto.dvi
===================================================================
(Binary files differ)


Property changes on: puppetor/trunk/doc/howto.dvi
___________________________________________________________________
Name: svn:executable
   + *
Name: svn:mime-type
   + application/octet-stream

Added: puppetor/trunk/doc/howto.pdf
===================================================================
--- puppetor/trunk/doc/howto.pdf	2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/doc/howto.pdf	2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,1159 @@
+%PDF-1.2
+9 0 obj
+<<
+/Type/Font
+/Subtype/Type1
+/Name/F1
+/FontDescriptor 8 0 R
+/BaseFont/XEEAJR+CMR17
+/FirstChar 33
+/LastChar 196
+/Widths[249.6 458.6 772.1 458.6 772.1 719.8 249.6 354.1 354.1 458.6 719.8 249.6 301.9
+249.6 458.6 458.6 458.6 458.6 458.6 458.6 458.6 458.6 458.6 458.6 458.6 249.6 249.6
+249.6 719.8 432.5 432.5 719.8 693.3 654.3 667.6 706.6 628.2 602.1 726.3 693.3 327.6
+471.5 719.4 576 850 693.3 719.8 628.2 719.8 680.5 510.9 667.6 693.3 693.3 954.5 693.3
+693.3 563.1 249.6 458.6 249.6 458.6 249.6 249.6 458.6 510.9 406.4 510.9 406.4 275.8
+458.6 510.9 249.6 275.8 484.7 249.6 772.1 510.9 458.6 510.9 484.7 354.1 359.4 354.1
+510.9 484.7 667.6 484.7 484.7 406.4 458.6 917.2 458.6 458.6 458.6 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 576 772.1 719.8 641.1 615.3 693.3
+667.6 719.8 667.6 719.8 0 0 667.6 525.4 499.3 499.3 748.9 748.9 249.6 275.8 458.6
+458.6 458.6 458.6 458.6 693.3 406.4 458.6 667.6 719.8 458.6 837.2 941.7 719.8 249.6
+458.6]
+>>
+endobj
+12 0 obj
+<<
+/Type/Font
+/Subtype/Type1
+/Name/F2
+/FontDescriptor 11 0 R
+/BaseFont/NJEYML+CMR12
+/FirstChar 33
+/LastChar 196
+/Widths[272 489.6 816 489.6 816 761.6 272 380.8 380.8 489.6 761.6 272 326.4 272 489.6
+489.6 489.6 489.6 489.6 489.6 489.6 489.6 489.6 489.6 489.6 272 272 272 761.6 462.4
+462.4 761.6 734 693.4 707.2 747.8 666.2 639 768.3 734 353.2 503 761.2 611.8 897.2
+734 761.6 666.2 761.6 720.6 544 707.2 734 734 1006 734 734 598.4 272 489.6 272 489.6
+272 272 489.6 544 435.2 544 435.2 299.2 489.6 544 272 299.2 516.8 272 816 544 489.6
+544 516.8 380.8 386.2 380.8 544 516.8 707.2 516.8 516.8 435.2 489.6 979.2 489.6 489.6
+489.6 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 611.8 816
+761.6 679.6 652.8 734 707.2 761.6 707.2 761.6 0 0 707.2 571.2 544 544 816 816 272
+299.2 489.6 489.6 489.6 489.6 489.6 734 435.2 489.6 707.2 761.6 489.6 883.8 992.6
+761.6 272 489.6]
+>>
+endobj
+15 0 obj
+<<
+/Type/Font
+/Subtype/Type1
+/Name/F3
+/FontDescriptor 14 0 R
+/BaseFont/SFIZFA+CMBX12
+/FirstChar 33
+/LastChar 196
+/Widths[342.6 581 937.5 562.5 937.5 875 312.5 437.5 437.5 562.5 875 312.5 375 312.5
+562.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 562.5 312.5 312.5 342.6
+875 531.3 531.3 875 849.5 799.8 812.5 862.3 738.4 707.2 884.3 879.6 419 581 880.8
+675.9 1067.1 879.6 844.9 768.5 844.9 839.1 625 782.4 864.6 849.5 1162 849.5 849.5
+687.5 312.5 581 312.5 562.5 312.5 312.5 546.9 625 500 625 513.3 343.8 562.5 625 312.5
+343.8 593.8 312.5 937.5 625 562.5 625 593.8 459.5 443.8 437.5 625 593.8 812.5 593.8
+593.8 500 562.5 1125 562.5 562.5 562.5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 675.9 937.5 875 787 750 879.6 812.5 875 812.5 875 0 0 812.5
+656.3 625 625 937.5 937.5 312.5 343.8 562.5 562.5 562.5 562.5 562.5 849.5 500 574.1
+812.5 875 562.5 1018.5 1143.5 875 312.5 562.5]
+>>
+endobj
+18 0 obj
+<<
+/Type/Font
+/Subtype/Type1
+/Name/F4
+/FontDescriptor 17 0 R
+/BaseFont/XCHVXE+CMR10
+/FirstChar 33
+/LastChar 196
+/Widths[277.8 500 833.3 500 833.3 777.8 277.8 388.9 388.9 500 777.8 277.8 333.3 277.8
+500 500 500 500 500 500 500 500 500 500 500 277.8 277.8 277.8 777.8 472.2 472.2 777.8
+750 708.3 722.2 763.9 680.6 652.8 784.7 750 361.1 513.9 777.8 625 916.7 750 777.8
+680.6 777.8 736.1 555.6 722.2 750 750 1027.8 750 750 611.1 277.8 500 277.8 500 277.8
+277.8 500 555.6 444.4 555.6 444.4 305.6 500 555.6 277.8 305.6 527.8 277.8 833.3 555.6
+500 555.6 527.8 391.7 394.4 388.9 555.6 527.8 722.2 527.8 527.8 444.4 500 1000 500
+500 500 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 625 833.3
+777.8 694.4 666.7 750 722.2 777.8 722.2 777.8 0 0 722.2 583.3 555.6 555.6 833.3 833.3
+277.8 305.6 500 500 500 500 500 750 444.4 500 722.2 777.8 500 902.8 1013.9 777.8
+277.8 500]
+>>
+endobj
+21 0 obj
+<<
+/Type/Font
+/Subtype/Type1
+/Name/F5
+/FontDescriptor 20 0 R
+/BaseFont/GCOOBE+CMR7
+/FirstChar 33
+/LastChar 196
+/Widths[323.4 569.4 938.5 569.4 938.5 877 323.4 446.4 446.4 569.4 877 323.4 384.9
+323.4 569.4 569.4 569.4 569.4 569.4 569.4 569.4 569.4 569.4 569.4 569.4 323.4 323.4
+323.4 877 538.7 538.7 877 843.3 798.6 815.5 860.1 767.9 737.1 883.9 843.3 412.7 583.3
+874 706.4 1027.8 843.3 877 767.9 877 829.4 631 815.5 843.3 843.3 1150.8 843.3 843.3
+692.5 323.4 569.4 323.4 569.4 323.4 323.4 569.4 631 507.9 631 507.9 354.2 569.4 631
+323.4 354.2 600.2 323.4 938.5 631 569.4 631 600.2 446.4 452.6 446.4 631 600.2 815.5
+600.2 600.2 507.9 569.4 1138.9 569.4 569.4 569.4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 706.4 938.5 877 781.8 754 843.3 815.5 877 815.5
+877 0 0 815.5 677.6 646.8 646.8 970.2 970.2 323.4 354.2 569.4 569.4 569.4 569.4 569.4
+843.3 507.9 569.4 815.5 877 569.4 1013.9 1136.9 877 323.4 569.4]
+>>
+endobj
+24 0 obj
+<<
+/Type/Font
+/Subtype/Type1
+/Name/F6
+/FontDescriptor 23 0 R
+/BaseFont/DMIVYN+CMR6
+/FirstChar 33
+/LastChar 196
+/Widths[351.8 611.1 1000 611.1 1000 935.2 351.8 481.5 481.5 611.1 935.2 351.8 416.7
+351.8 611.1 611.1 611.1 611.1 611.1 611.1 611.1 611.1 611.1 611.1 611.1 351.8 351.8
+351.8 935.2 578.7 578.7 935.2 896.3 850.9 870.4 915.7 818.5 786.1 941.7 896.3 442.6
+624.1 928.7 753.7 1090.7 896.3 935.2 818.5 935.2 883.3 675.9 870.4 896.3 896.3 1220.4
+896.3 896.3 740.7 351.8 611.1 351.8 611.1 351.8 351.8 611.1 675.9 546.3 675.9 546.3
+384.3 611.1 675.9 351.8 384.3 643.5 351.8 1000 675.9 611.1 675.9 643.5 481.5 488
+481.5 675.9 643.5 870.4 643.5 643.5 546.3 611.1 1222.2 611.1 611.1 611.1 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 753.7 1000 935.2 831.5
+805.5 896.3 870.4 935.2 870.4 935.2 0 0 870.4 736.1 703.7 703.7 1055.5 1055.5 351.8
+384.3 611.1 611.1 611.1 611.1 611.1 896.3 546.3 611.1 870.4 935.2 611.1 1077.8 1207.4
+935.2 351.8 611.1]
+>>
+endobj
+27 0 obj
+<<
+/Type/Font
+/Subtype/Type1
+/Name/F7
+/FontDescriptor 26 0 R
+/BaseFont/MAMUVX+CMR8
+/FirstChar 33
+/LastChar 196
+/Widths[295.1 531.3 885.4 531.3 885.4 826.4 295.1 413.2 413.2 531.3 826.4 295.1 354.2
+295.1 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 531.3 295.1 295.1
+295.1 826.4 501.7 501.7 826.4 795.8 752.1 767.4 811.1 722.6 693.1 833.5 795.8 382.6
+545.5 825.4 663.6 972.9 795.8 826.4 722.6 826.4 781.6 590.3 767.4 795.8 795.8 1091
+795.8 795.8 649.3 295.1 531.3 295.1 531.3 295.1 295.1 531.3 590.3 472.2 590.3 472.2
+324.7 531.3 590.3 295.1 324.7 560.8 295.1 885.4 590.3 531.3 590.3 560.8 414.1 419.1
+413.2 590.3 560.8 767.4 560.8 560.8 472.2 531.3 1062.5 531.3 531.3 531.3 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 663.6 885.4 826.4 736.8
+708.3 795.8 767.4 826.4 767.4 826.4 0 0 767.4 619.8 590.3 590.3 885.4 885.4 295.1
+324.7 531.3 531.3 531.3 531.3 531.3 795.8 472.2 531.3 767.4 826.4 531.3 958.7 1076.8
+826.4 295.1 531.3]
+>>
+endobj
+29 0 obj
+<<
+/Filter[/FlateDecode]
+/Length 2118
+>>
+stream
+xڍXK“㶾çWðfªj„%+“ºí‹›|Ëô¸™&¿u'äZz7Ëy…QE¹>ßMÌþóÒµÙ>|2‰–eÝêZ(B­ê†ö|ÞyêÂ4ûAõ#=uÃᯇÓW5ø™n2¥Êa“U¹e¹Ï¡ëáÇØ'T©É²Šï³‰Î•Íù>[¨¢J¶¹Q|ŸÞlµ†}6V§s€+áÛ.»¹ÞŸ'jJÜž‘vµYµB£ÖFŒúáS‘”ª©;·ÊÚĪÒÈMw§UJ›d{]ï&Ô¶L~*rB…N(˜R¦ûàNþ©cø̤ùèfYt»®ïf7û‰÷ÏGσÝ8üaŠê°G:qÜ¿¹lòóš^¥dú:ݹžC¥ÈT^€ùØq -èl)xè„ÝOÝ2º¡ÅA-‚ ¾úÝ""¹
+—y<h;ž‚óÄ;%ôˆY6Áa“÷öRµÙæY“>)ÈÐS7Y»aCÃÄalÁ·`Ä‘¿'÷ù¼¬ÎLõnê|àñ~”Aë¿k?J„‰wųÜàúË<O$>¾žÛÙÁŸY ”¼5¼tspáò¨Æ?ìyE=(l‘¡ïfHÌ3LX4»-4I‚ß(°/Ä£g6!QúÝ<1}ßݼytÃÁO|x<E6ꨋ\=.a''îF²DëÕ£Üdjä8ü…>±uÂ+<À=³yCwòì÷‰WvnXKV’d/ì>à°yÚŽ,ù¿nà•e˜–xî—Ž–_ܼÃ+Ž85ƒÓ„b+vbèØÄZ±Z)ÒXC ¼œ˜7ø/\q}™Þ!QhÉVÅæÍce,êZlGŠNãã)Ö:$Bxâ­h+©Ô…¸Óe`XãÐQ¢@"./´ñè·""é*ý„—/£þé[†ÏíÛˆ@0Gno).Ü÷J ­]»#§@ëo_µJ¸Â™˜%¦Ô{ÐÂ02ö½@’ç*+n at bïÐVihÉ55ÊÔꁤ.º8¸ùzäÚG„˹ã Ž©›—ˆËë’úzìn5ÓJIÃÑ]IC›m„aœ@YÜ/ý÷Œ=FUÐÎk^ºlw¨1ßëÐlVPðs¦æf¾Ó’š/ɤ}ÆÕu§„Ì{Ù,΂!«›¦ÕÁ×3®}rsÄ>IƒöMcÖ$ðØÛäݹ1Ô­bue홆!MZÉ
ÛW/ìzO”k#€tÔ‰×$ÃÉ%›äƁ™OÔsõÿÕ™>qPL#·OÜáP+	€1ù/Ù+endstream
+endobj
+31 0 obj
+<<
+/F1 9 0 R
+/F2 12 0 R
+/F3 15 0 R
+/F4 18 0 R
+/F5 21 0 R
+/F6 24 0 R
+/F7 27 0 R
+>>
+endobj
+6 0 obj
+<<
+/ProcSet[/PDF/Text/ImageC]
+/Font 31 0 R
+>>
+endobj
+36 0 obj
+<<
+/Type/Font
+/Subtype/Type1
+/Name/F8
+/FontDescriptor 35 0 R
+/BaseFont/PQEIII+CMTT10
+/FirstChar 33
+/LastChar 196
+/Widths[525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525
+525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525
+525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525
+525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525
+525 525 525 525 525 525 525 525 525 525 525 525 525 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 525 525 525 525 525 525 525 525 525 525 0 0 525
+525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525 525
+525 525]
+>>
+endobj
+37 0 obj
+<<
+/Filter[/FlateDecode]
+/Length 2329
+>>
+stream
+xÚÍXKã6¾ï¯ÐQÚŒD½s	:ÉÌîØd°c`¤÷ K´ÍmYòêўίO½(ÉîÎ1ÀQÅb±XõÕƒòÞÑ£×ß½ïwß|Œ½B©·;xyªÒÄÛ¦Ze‰·ûñ7·É#¿ë7Û(ÏüÖŒù׍΁öÌIJ­qûU×ÖS52õ²Ù“é].ÛÊ0ËÙ”ÃÔ›³iQÎ8<l¶qøçr£SÿukR_XmËï’åí˱B¦Óæ?»Ÿ¼ÀÛ†¡*XÅî+ >ÊgrŠ
0†Î4B…‚L,Ü­¡@Áu¿íùÒÌY‚e¯m…Ÿ§¾kíïΔ»	×”"‚±„».±ù¾‹ÄnqèjúÓªÞ€ÙkþØ;½a¼¤Dø@¢Y+34÷w}˜)0;Âa'ŸV„—üj»v»Ø8Ödcá€àx>UÙ> <°‚3MÙc8¨»Öà(A•\öpæÚ§%¹ß˜²f^R
+×+A`­‚ö,>X§çh‰%—ÌRÿÃ|ätO›?gtNHö¦±²#áÓɇ)Ò$ué#¥Œ‡ôCߝy4³Š§‚D@‹É¯iL%›pnƒÁÙôGYÒtGHD´€)vTü.Û÷A
+ƐÃó‰/*RXƒswèa.ÐúH‰…‰qªŒ,!<_gXÊÊåN9Ú,…šÊ¬ã©y´aҒʁz¶ÇCäMn‘èÖb(ØRŠ#}jSöÆ9Ð;Ð
+
À䮯É!+Qùšöð*kK(ð² 6-°UÖüiïíѶdgq2Å)9þ¦z"X¨zâÜže›Öag°GÆ÷
+ë>ˆ)æ«+M–ОrÆɝHr.}¿äŸl]ÓžPïÓ¿ØJ”sùGº•÷+)¿Ú"ðè8{LãB{üü‰…\O–ýÍt¨ÃoÍÌØ鸋Wñ†%NÜÚteÍ%P»¢ÅK}û2ífÔºJ»¨ÓÖ5t’KuŒW	ށà6t䋃/UE†æ†Ä¨‚Ä‹T’Úñ½? ž†p´D%œÃ#›$¦“¤rˆ~Œ£"rpbÎøKdCKB÷$°3’µïâ;ß-Ëfî=c·Ÿ›Ú÷³#¶q¦%…ÇÙ]Ï€$ÁFLQÿ¼á+'’ñjÊt.w1e
+~SÖç)áiA,›.ŸM§“X%À‚n+Q¨<Y]	£Š²!ö4ÐDÄþ8$Z¢ã/¹ß®PÜÁæùè·©ÇÄŽ)-×G©vÅ>8ߥœ¸Ó9q"¥ä×m8"å_“Ôò³p~hùzÀª1„‰u€ð‡ÅÉíµÿí	µtÅ¢TdêÛ(Z²0ܹäp·-÷h¿€'¹ã•K,Ê6éÇ»ð6_K¼Ø·×6,ªæ݆+sÓpênYC6?ŸñÆ·÷ÐqÁ 1
+þ[¾€iÈ¿Ûꃽe‹S¡×積šZ»/ÕÕ–V5ÃŪäbÃ(f½Õc…%¨òyÚCaú·ÙRnú_àÙ¹Že%ú¯R£†ýF‹Šüƒº¡/ÜPì:ÖTùÙŒWŒ÷{’¿\#¨³™n´C#ýßë—Q¸ÝÝ)øaç*ö®^âÁmBávö¢PÚ}6Þú™˜z‰*rd¦UãtA­ÁRk2/“ì«äiWý7º k&\=8ùÃàÜ-Á¯WŠkˆëºR.ßŪ|Ë7èg¬€m3A~à?X¢¡_Ã+ƒˆ¼¢Ù÷D×åB®Ë0IÕbžæߍ8ªP9ê©J‰Ò1^3âT9·¢¬9wÙtÃ!kž¯4Ï0ÈIµÚPŸZÐ=LÏ…I»:¤Wõ­|°ºæÕÔ.eòWý$²ü
[ÄÀŽSß/fÎôʺðqg]é}æÎê*®¾ÿ,QÐëÒA’w¦’h¹—$1[5w3 õ›0±6+Í=QÇùÝÓFJ|Ç|œ,“x±*2ß[i„äÇÃ}À|ǐ\P‘PQTZ¬+â;˜åþiÇ—ÅD~îÀûØq²NâYÇ=Îw[þò,‘ëæÚu_ŽCè0‚¶/TžK‹Í!ú·?+endstream
+endobj
+38 0 obj
+<<
+/F4 18 0 R
+/F3 15 0 R
+/F5 21 0 R
+/F8 36 0 R
+/F6 24 0 R
+/F7 27 0 R
+>>
+endobj
+33 0 obj
+<<
+/ProcSet[/PDF/Text/ImageC]
+/Font 38 0 R
+>>
+endobj
+43 0 obj
+<<
+/Type/Font
+/Subtype/Type1
+/Name/F9
+/FontDescriptor 42 0 R
+/BaseFont/NENDNC+CMSY10
+/FirstChar 33
+/LastChar 196
+/Widths[1000 500 500 1000 1000 1000 777.8 1000 1000 611.1 611.1 1000 1000 1000 777.8
+275 1000 666.7 666.7 888.9 888.9 0 0 555.6 555.6 666.7 500 722.2 722.2 777.8 777.8
+611.1 798.5 656.8 526.5 771.4 527.8 718.7 594.9 844.5 544.5 677.8 762 689.7 1200.9
+820.5 796.1 695.6 816.7 847.5 605.6 544.6 625.8 612.8 987.8 713.3 668.3 724.7 666.7
+666.7 666.7 666.7 666.7 611.1 611.1 444.4 444.4 444.4 444.4 500 500 388.9 388.9 277.8
+500 500 611.1 500 277.8 833.3 750 833.3 416.7 666.7 666.7 777.8 777.8 444.4 444.4
+444.4 611.1 777.8 777.8 777.8 777.8 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+0 0 0 0 0 0 0 0 0 0 777.8 277.8 777.8 500 777.8 500 777.8 777.8 777.8 777.8 0 0 777.8
+777.8 777.8 1000 500 500 777.8 777.8 777.8 777.8 777.8 777.8 777.8 777.8 777.8 777.8
+777.8 777.8 1000 1000 777.8 777.8 1000 777.8]
+>>
+endobj
+44 0 obj
+<<
+/Filter[/FlateDecode]
+/Length 2104
+>>
+stream
+xڍXKã6¾çW}‰´½³È!ÉÎ$Ù²¦sˆ÷ ¶i[hYô’T{üï·^”Õêž`.UU®"ëñUQAÅqpèñkðóò É£,öA™DU¬Ëÿü+ÌVë$I‹ðý—ötîÕjey˜¼[­ó:Ún•µÝp at ržÇǾ۲ÈçU“†ê_²Ð*ó¼ÊâPfêU–„Ïø”ÿ>ü+Hó(
ÖI5Y}X5Y¨‰÷Ç<h¢¦ÄÝ­Y.M¢:a¹#î(K­í”uø’…—UZ…í*­Ã+sfÆh/V5é'ng™ÈRiØzX¥ex=éÑö¤"	[:*¶ü˜Žÿù¼ªå¸ðBÇ…-(Ÿò¶	6ðœ12‰v-Ùï\Ž­“š%§áN3­Øgîèå7iQ+Q—ˆÔC›|n‚ýbƒr¸£:E›'&nõ€º£i]§‡‰h;ëĖȳ½§x•°Aж“œh‡Ó{¦ë'QÑwOä~Ãc¼ã¸6S\Ñ-°NiÏ›´L—§gLì?Fu_®ì¦KçŽÓÆÑ}ÎèžYgÞ‡q¬­ʨ©P[g¸Ê¢’³¨\š«¢ª	Ö¿Šã„mL‡üøŸ_þý‘—73äN9ëµJçgûœœVxAñb¯ûžÎs*ºGMÅ”i•¹ïe…Ô¶â7w4z<Ú¶èúŠBÂtL,\aêàsP˜Å­¹Š€öÿó‰ dçËDšeßf‘Kw×°?”Ùksò1—1ê£ò™ë$Á[æÝÀ#›USö²šI^ÒÞ>c1£…¦
+­k)Mí3ˆúѵÝÀ0Œ–Ã]OáN @y#ùõ‡r¬EJäqTøì03lÅäåÛ†ý‰1òXáCÃb«d;Ób^6exîÛ­ðöŒq}pªfîqÕNZÁ	NÜ>‡wËÄ„‰Mòâ+eZ¥Rybš„	.ç3Œ$|àW;ê6åWU…Ñ3
+É+ܶ³"½wؐ9/ð|º;¼.XœçwÔM_^Ijß+}<F-„Ѝäú<«ç®‘®Ox°Ä¿x+„Ÿ	·z|>R{›[ƒ…S“µW}Íò+Ê[Iï‰j`Læ)`S!&îÛÚÂ<´	‹8Ž¿¥y}ÀÙËx6	¥®øÞJO @Req|sJBV
Ú±ŒªÞ
+©3Û±>ö=J0|áªÀÔêŽWXÍøìÛqàÄZV¤¼¡@,^qÆ:Óð½•àl;À§ë„ ,ͯg£EñwjM at XO¸Á a•“„+µJxè#ÔÑL1ÊÏiôëÚÞ(Ym¨Ò*“›ÞHöFÙ£¿T©Îðr\(Þ
8RJ© Ÿr	l[nk-¡æ\ÿãØõ")Ñ°XWiþ¦Àmo à”œMž ”…•Ü&’—þàP#³ùíÓŸwL²Ýaà»F¾¾Q‡±oÍD$6‡Þ²ÌÈd‚Px¥1M«¾c£<y'Uîu¹lc®T8OU at hù!žˆÒ~˜*óSãïÄ%݉™ä5Ý>TÄ·%À瀪g7YâY“¨ÙóÎÿ®Þ‚KþvÄýI®^Ú_^õ±ù]÷¦{í'“ùwœ¥ÂÛÅ—9›@™O¨Û€ üŽxkÖy†ŽãùÓ+endstream
+endobj
+45 0 obj
+<<
+/F3 15 0 R
+/F4 18 0 R
+/F9 43 0 R
+/F5 21 0 R
+/F8 36 0 R
+/F6 24 0 R
+/F7 27 0 R
+>>
+endobj
+40 0 obj
+<<
+/ProcSet[/PDF/Text/ImageC]
+/Font 45 0 R
+>>
+endobj
+48 0 obj
+<<
+/Filter[/FlateDecode]
+/Length 2186
+>>
+stream
+xڍXÝsã¸
ï_áÙ'y&æJ$õÕÎ=ävm®{Ùtãö¦Swn‰±ÕÈ’OëÍtú¿ H}ÙéE/AAàG@—¹îb·Ðͧŏ›÷7r³8XlžaÀ‚x±
+8ýÅæã?»jÉCç´\	Áª«‘ÎfA¯&n©Ú%`€÷L̼ÁV:uW–y¹cËm~Z¸‹•ç±ØHVßÛåJúBÏŶÝ+"Ò"W%îÛځÄP§¼(ˆ:.W°ŸªŸªú@œZýÖ©¦5º¶%R«-}ék}šï/ËH8l¹Š\áÜwG:Ê
+5]y’År¬nZ‘FI^Â<ðg¾¿‰zãÉ€¹‰Ó?àÚëã±ÈÓ¤Ía±ž<X:ö˜/Íä´H-¬¸ÏSÜgO{¤IIü¦Mê–È„šv_«$£imE¼©Qôºüp,”9“™ŒõçÍæîɏœOë
ñÈŠÈFÁÈM¨ÙåßP9¸è†N’eµj3¥Ìˆ sVuö¹çü‚ÖV$ÏÜÀ·<3Œ¼¥ö”·ûÑV‘S&uî4OÚé¤tŠj·ÓŒcWÓÉ«F5WÈÉ™ppP;ZIéJRWŸS‹%9ÏÔíŽFšY'±_>üå᢬êi¬w õî#§wÁ?νçGŒG¯¸˜Ò§Àh‰þˆ¾DKÁZEë¶Î;šùîJ«Â]ó±ߝN'¶«ª]¡X¦`ˆ’Nä^‘ÌÐuùvù§¹Ë®|Gc-Tpú"9(݈oÍ„q'šsBœÐœhðn°vKò5\²Ë›VÕÔ£BY×Ã
	L«ƒÙR8Ouu°›¢¼Å=² sŠÀÙì5R’+@ƒ¦à.IýBìÄâ¹ú؁½{Š8•//Í$juLj¸êÙ°ODk¹úc¢àyWW]™]!‚Yx¤ðœ¤+ÞvRB;>©ÎN:æöªÝþÇT+„ÆðU[çGJ‹iKÁ-uKBÿÐüÿü`Š|£ÜàÂÀ—s€RèÙŸ,Ù:+& ð°ÁÈå%7FÔã’Òi+·Ëz•qAÎdɤØC-šAToè³tŽb
Blà;ÿVéïŒÑ‚ÁgþNqsõj½ÅŠ7y	þ¤ËJÄcÑã-qÑ<Ûâ‘ñÒKÍ®fÃÇ˜ªÉ45‚.?u‡8”—vÇFY	IK¼ä•”xÈÐ…ë<%y¡Ú3ëj¨²¬í„ÔðœÄ˜ùëäVxf·ùJf	Õ»	—Ò}ÌY“g;¹[²ÎíÆÚmÎ	̯ãP+šQGÃ)´Ü¥–+ΆæÒÁ=qI%=lêX˜ÑHç
%ÄMUm2!ïìaÀiúøz~ªÌ¥ìÂBL)9çÆ^Htã¼O/¸Ípû
+ÈÑ(Ê…¾#cnÏ:ºŒ~ÆÙè)8}t
+ÚÏl2µã¼rr#&å
+?f!T
+>„"÷± 8‹üžaŠWúãZça^SøLŒÄIÒe&nÌ5âî×_o¾|ýù¼¦ÀHÑ?€Gõqcñi¸¥i¦#†¿[Ã[þ¶×øxSÕ×åË—4ít&8<ȳ¬5¸lâ_­m~íOõ–_W×öϤdºö5
+p›½þbF2ú^ÒXŸÚéßyÁ¥ßy«3ì³yZ³ïÚ¬:•wU¦šËYZ3Ç«¸Vš›ÿÃÿ+endstream
+endobj
+49 0 obj
+<<
+/F4 18 0 R
+/F8 36 0 R
+>>
+endobj
+47 0 obj
+<<
+/ProcSet[/PDF/Text/ImageC]
+/Font 49 0 R
+>>
+endobj
+52 0 obj
+<<
+/Filter[/FlateDecode]
+/Length 1949
+>>
+stream
+xڍXKsÛ8¾ï¯PåDME	€¯lí!gã™L’ŠåÚÃj°Kœ¡H
–=Sóß·
Pe'>h4诟à,da8ÛÌÌçß³·ËWÄ,’LÈÙòv–D,g‹„ãgùþ¿œ/¢ˆÇÁŽÚí+=_ð8øëùBŠ4xSÜÍEè¶/»²ÞÀ¢ÌƒmYº&ÆN·wåÚîêúÅüËŸg\2>[D)Ë…9«*×ó…Hã`9ÏEд8‘A­{<ä0Ð~ǝ¯>ÈYÎòo¼ )<bYd¤,·pž#8|ÝÔÅB«®Ô]4h§2”ñbøU OIË¢èãtB®Q'\p[{:”£rF
\\Î3Žj ®!(±œ@E•à Ãœg¨ÒKP4ʃ›¡ÇAԍì°îsÛ´;¢¨7>à$ZýÇ+Ɛýjò/úX†]MûÀZÚWÁ›iø‹ÕüŸçŽpÿð¹)4ÉÚãôDº=ÑŠ5ì Ôð½xI,èÇ!·çøžçð¥"«Ô=&Bž#Ì):‚Äè¿ï5Zé”È`pH’0FÉ	—‡-JºAãÙ±"Ž1uÂø˜:a¢M*ëÛtIö„Á)™Kq#½„“Æ`ÆQïxkµÓSÛFqÂDfÄ]Œ)\2badY[Èâp¿-ŠäT*@4¤FJÊàë’Jw¾lfB7媕§Lä~kµñ¬	…S+(U³Ù î‡vo¨é4$ÅE’Èಧ5}OK ®#J¿U=	±5)…ÃTE$¯vAâ,+Kn‡šH`çGbb´FÕ¬UµmºþµÉGøRÁ¸ƒï%Å•4ˆAU×Í+Í®çïMG0`×êÓF_a8ŸÔY)£c¸Âòºª‚†æa¨JË¥Ã,
=v *ÓÒ‚|ÕU杈;ÇVýàÎcß•ë¡×žl“šÌÿ®•|ú³ã{8¸ÿ‡CžýáxÒbË-´+3Š+endstream
+endobj
+53 0 obj
+<<
+/F3 15 0 R
+/F4 18 0 R
+/F9 43 0 R
+/F8 36 0 R
+>>
+endobj
+51 0 obj
+<<
+/ProcSet[/PDF/Text/ImageC]
+/Font 53 0 R
+>>
+endobj
+56 0 obj
+<<
+/Filter[/FlateDecode]
+/Length 1880
+>>
+stream
+xÚµXYsÛ6~ï¯Ðä¥TÇbI¼ÒéC;I“&™Z™>ԝ,Â&©ò°ìüúîÅC’“™Î´/"°X,ö·Í?f·3ú¼š=_þxÍBíGz¶¼™%¡ŸÆ³E¢ð³|ù‡Ïa¨bïüÞlw;_DZyÑÓùBgÚ{VÜÍ£À³uëWÞò¢)¬V¶éé‘·vEaËùŸË_fJûj¶S?èÆÖwn…ÂÓØ«P$ÈU )ÚÛu×·âÕå<W^%¥mçQèíñ§ª?£ì/ô,÷óYð9*ô³ÎY®];³Ük×®F5sÐ~°&´Žß’ùn̪eJ»–+§u#ÔR÷kÐP¥Þz/’nj³µ{\B
i±ªÝ­+ÍfóÀ,kӐ[ðIèçìøëùöX‹Š„™wݹMëƒÛ£Ì@UÂüÖ¶ì_˜¬ª²qMÛð¬ºáo‹'Ãñø•»ºBÒ½³Ä™€ª¦å%SÛATiW­-D†lmûs% ':/çYÄÁQ9g8»þ|T¬áEŒø\%b`%"‰+f¤A5³YHnˆQyuÕ•Å¢­ÝŽyZ·¥t€½œÑ0M"=ú?ê¯mã“N€!¹>ÈCtœ	Lo`xn»Ú´®*bŸÃ0£ƳrÔCIÓÈ°Xô&0ÁÆME>«>‹÷™ò…·á}ʘ‘˜ž…±"®T¢ŽA…¾-ÂH<ÙÀ£r?ê™@×û‡ðXJ¤ý$Ž½k׃ù”puµCÉ°ªny=
‚-#ïòË·—<<eVþ±y‹TûAø¿Ø¨þ+ÃccøØx9Á§ýMÏ3H˜¦µeÃLR\žè€dðsÌ#RNvÔöÔVèFèý%Âqst8}äéƒËþ•t
+ý(kNý¡a5úþx1A>¾(¦º@>†—&¢ù¢¦0%jš¢vÆLηXÔ´öèFßÍIüI®eŸ;׎0ÌW¾:0›)Ž&%&MkjRÇnë6¦æ	A~ûm»ÚÞ¹ªk"ù
£à­帇¢˜Ò~Ázà¤À —Œ$à.E¸ÓI0)´>¸.ƒÊöŒéCV%áØQéDq:Âw„\JH`£<O”T) ©“2¥Å«ÁÀ#nÇÙ1˜“‡ré'`°¯]Û2oÞ»$g;Rm˜S ¬,vœ€nÃ@³Ätª	16XvËKk3fS¨Wz´®+ƒq×=äJŽáC=W2+Cw»ejËÒ¾¯K& €äp²—fÙh­¶©SI´’xÆ5¦¼O8ïq*Ǥ“‹9éᾂ(ø X¨$ñšŽÞ`7½5Òí6•)ð–©$Å+ÄÚ–…ýBeˆ,¡­=‚à\¾‚f±ÅÞ÷¡îH'õˆÁÕ–n¯K¯
+eâ‘~uÚ>3P»ÎðGª	+Õ	4*Î`ú’}À2ÚÔUҳĽø„@ÎÝ;,ôÈ2E	¦˜ò4ìv×Ê]×:õ^/—yôê|‰ñ¾ÎäH=i¾õ´ùþ¾á}Uéx\Ï¢¨<™Ó•ÐÈ›BdÞð×ðçk‡ð…¿Ë|5¼Qÿ<Ö‡c+endstream
+endobj
+57 0 obj
+<<
+/F3 15 0 R
+/F4 18 0 R
+/F9 43 0 R
+/F8 36 0 R
+>>
+endobj
+55 0 obj
+<<
+/ProcSet[/PDF/Text/ImageC]
+/Font 57 0 R
+>>
+endobj
+60 0 obj
+<<
+/Filter[/FlateDecode]
+/Length 1907
+>>
+stream
+xڍXÝoÛ6ß_aôIbE"õÙaiëlº$K¼Ã2ŒÄÄBeÉÓGœ`Øÿ¾û dÉr»<‰<ïx¿;Þ5slÇ™=ÎèóãìÝêô<šÅvÌV³ÀµC¶~Vþ´nuõ¤«³í6ÏÕde1_ø·j"óøþºÙ•Õ;©´j4ﻳÞ0ç›f
+Ç¿›?ÿkõóÌ™-\׎ž¦ÊŠGf*Ðs–¦•®ë‘ŠmU>¿¸ö£n®, d¥¨ðÍQÑïóLÍÄ„„Èß0÷tæìLžî„´	ÇŽÅPcä¯æºÂœéôÜëQ^øvÍ6¸´á|I«<%lJà„"´žæ"²tªÍ;$›1‘Ç
+?à+¤ ÕfõÚ¬°ðÀ"΀…ã,<.ŒÎ_3Ï=’^x½Yë¬brƒû^¶0mö|á¹±õi	DŠtT4ˆ¬ŒÍÒà¸F§¼˜™“‚¼ià‚œ¸¿*"F':Ĺ|‚+.™7ÛÍÜȵ…ˆy¶³
<[Æ=!ŸÝÂ=Q=Á×byùáâòDZ8ˆ8!<º¶cqCªw³üõ·åíê0el±1e·Î4<&ØÊ+¤í{Yj/ë8øÚPèF9ôA02ðUŠrDÜ’QðÃ`œÞB¿س†¿kUó`ˆ¸ÑÄŸJoóL_˜Ê ÿþT¢À.	z(
+ûr€»
+­Óaã„Ü”pé:=bò¯xÖ¬éRCLV	ˆ½A•]•€=}•àé~cØm¬Ë¶Jt}ÂC$p•«,T‘ax9ÊHáõ±?̾`Ÿ)÷G8ú2¾ãÁˆåκ›3ùþœžò׶mü;-U¨G8«JÓa|š“ÿ?ÚÑžäër¹_z½ÜaX›Š¼Gñ²D¬w}´o$ÄcQ7U›wb“Åa0̸…(2QÁ<&& 4I¹¡Ì,&¯Ô¼füÝ‹Ž""Dч]C³åÊQž
GaÝö	B©Sôµøá›Ï0Ú|ptý'0ºö–uÝ°Ü;Ë7à{¾ã8f`é×+AÍž°¼·z#Ï:KŸæҁPl²šãÜë2Hw]ZðIÁ„*tapÆ°S‚±˜Q5¡D‘ Wð%=xÕ@!\‹ûy ¬æ±y
À,û¹töe‚<+$"F~Eá¸ç
+«<Žto\Ä%‰]“PjTÅ‹M·ySv›9Ÿ1EÆ>µøHͺ'Jh•9—¥šeÕºi×n™]ñ‡,…v-ÜÆÕþ
Õ”¹°ê€ÕD„„“·Ü+JT܇
¬¥TÔ¦¬^xm<Üð+endstream
+endobj
+61 0 obj
+<<
+/F8 36 0 R
+/F4 18 0 R
+/F3 15 0 R
+>>
+endobj
+59 0 obj
+<<
+/ProcSet[/PDF/Text/ImageC]
+/Font 61 0 R
+>>
+endobj
+64 0 obj
+<<
+/Filter[/FlateDecode]
+/Length 1744
+>>
+stream
+xÚ½WKsÛ6¾÷Whr¢fL†@j§‡$mšig’™D·ºF„,Ž%R%);ίï¾(>$ÇNíÉ	Àb±Ø]ì~»˜…Aήf4ü1{½|ùv1[;[®g‰
ìbæ[$ñlùÛßÞ¥²jþÏòÏ—oÍ‘‰Æ¶?V‡ÖÕ̐4gŽš8¢©½´pÜífî«$ôVU9W‰×ÖÕ–	{R¯ª[\F^ªèIÆûôáÍ_ŸîcRÂôá#“#qèThæ'*ã™EÁ‚íÍÊ|îk­½¼¨y2K8l¶¦w›O?‚A=§ÛÔ3¹M?ÆmúA·é§¸Mÿ8·égr›yŒÛ̃n3Oq›yºÛ~+j·j«únê9m„8ÉQôpì6é±Ût¤F*G¨rŒnÓQÒ¹MGfè\“&tž;ӹͦÑßé¶ø‡¹M=—ÛìcÜft›=ã6ûX·Ù§»í“«oÐRHTß,ÀâMÖâ,õ¶EÓº²á¸ƒ¶* à5æi‹“BNÔî
+ÔNè™Ð7Ež»’çÍär¸zòxá0î+NPÅšSwÄßã7ÛÂñÓãÅâY1ÝÕëªÞ5üªµû÷àš¶a†CS”W<=Æ„¿¸íH«Ð+€ÝzáUx!xÅO= @Ç+P'a<4ÂÂÐòá?xœvÝ…’¥özl€ã=éò
+ÒøeeÐ86ÎÜm3ô<Îè§+úR£†&Qmbô+endstream
+endobj
+65 0 obj
+<<
+/F9 43 0 R
+/F4 18 0 R
+/F8 36 0 R
+>>
+endobj
+63 0 obj
+<<
+/ProcSet[/PDF/Text/ImageC]
+/Font 65 0 R
+>>
+endobj
+68 0 obj
+<<
+/Filter[/FlateDecode]
+/Length 1567
+>>
+stream
+xÚ¥WÝsÛ6ß_áëËä]¬ê[Öv»ž›Øk6×Îçú0ïrŠDۼʢKQqrûç#µ×í‰ 	‚À +´Z³×r.Y¦Š<ZšUc¤^(ÉV‘|’õŒþN[õ/5HˍaoGý€Ç×ð”Lm…ül"¾¬%T7’?ÐÜ™[óî/(âÕ“„Ò«˜ÁB1¹ÕH¥2×Êù¾G¾ñ}k#QïŒUÚ¡~€Ž…µÄc\;ñøVÅ×¼H%N”Àí–LöÄEM¼ì9Õôó¡­½–¦ÞžÁ[É»$«Ñq;S÷alNT*•j"+Œ?LþX?ú^SÂMÌé0¦ÄzþBªµ©–šÔàkʵÄW <<lÝ/s$š²ZâÔø‹Š¦j•*¤$3…eš‚räÞ*÷ diÞ,‹ä|U›{‘wh½³=Ë%6;ë]S¨tO€³Lßš*}u	™¥ÖbÍŒ‚ʆjEÖå*-—lËµ
+A7ºžHãé'„¢¶|‡kÓYœè	š嵦úÖèz2¤²\ÂÃHsªÈbÃ’XP+5¥v7}¢ÂyŠÃ¬Û÷¨Sôš´ñî(Î\·o‡á÷F@é ¦'
ó>`¬ —Ö
+i>ÒÑÐfo
+ýÝ؝-Zß­¯«C&=vŸØ¡Kj at dðì%ßµý>qèàs
+>—‚¯GÅ vì~²WÉšøMrLK&½&h¡Ùœ?dLqxäK$}­|L1ijÞî›Ï+Bñ…
¨¿oä­S-æ…4Å5®~¬Òæ…;«H›Å±ªÍ_2cJá¹Ó²ZáÏO‡¯~ÇÏïþo"é;¹æñ5#Jì¯($mjꥧ#^°‰P#ó€ê2>glƒá8¡µ5/@3š_Ž	cxÜõaªϤÊ×ß~©Ý °èRÏî#†‰YÎ~ø:YsÍ
+endstream
+endobj
+69 0 obj
+<<
+/F4 18 0 R
+/F8 36 0 R
+/F3 15 0 R
+/F9 43 0 R
+>>
+endobj
+67 0 obj
+<<
+/ProcSet[/PDF/Text/ImageC]
+/Font 69 0 R
+>>
+endobj
+8 0 obj
+<<
+/Type/FontDescriptor
+/CapHeight 850
+/Ascent 850
+/Descent -200
+/FontBBox[-33 -250 945 749]
+/FontName/XEEAJR+CMR17
+/ItalicAngle 0
+/StemV 53
+/FontFile 7 0 R
+/Flags 4
+>>
+endobj
+7 0 obj
+<<
+/Filter[/FlateDecode]
+/Length1 1095
+/Length2 4446
+/Length3 533
+/Length 5179
+>>
+stream
+xÚí”gXm—ÇiRBïE”©H½÷"Mš@HR0€TC&
+RD@šŠ4¥wé"Eº€PP¤#°ÑgßG÷y÷Ë^ûm¯ù2¿sÎý¿ÿsæÌ-Äoa%¡‰À¹!õpX?	iIi( mj)­+‡Õù!¡€´’’4 éï\¤å¡2ŠÐ²  ó	£<<ý+€&‰GÁaXÀæç‰ÄEà04`…ƒ£~A’+D",P~pOÀ†öEþŠ#±ˆš öí—ˆ®®¦‘%ø¯OúWÒ†ÂúYùü-û³úKÿfb{ð¨@ÀQJRJJšXH¼ÿõtù›ébá8
+K
+9y+ÇåÆVØû­0ž
+šŒó½ã
+JÝhoר{[¤ÜÐÆoïVôºÈ0+N:dt5DMRIô„3ýˆcJpm¤ÛWÁKÆQO¼R9[Ø͐‹ü:ºVº°ÀòDaŒœQfä{„t–¼‘	,KU°ˆÜÜðô¿»÷’î›÷›õ'ùá#lÔ­Æa×êjgʏi>Ñ”ÐÏ?Ç0¥Ò«yöY}ÄómE«›š‹‹CóX²d§æIæË5>¶Î~ȼ/—nlBµîxœÈ¾Þºº)<´CY+f½¾%q’Èo„TxìŽRÌÖ™	±sסmiS¸ˆ{™8üä(‘pväÞ¡îD%?ž5lm7rœbaâ݆ohú•ì¾\㜣‚ýÝ1ù]x.Õ=WÂÐcç•ã©Í åÛYá½Úu’U†!AYÞô»V8ûI[ð-$âTpðÅ´‚¸´Öj)Y3#'aä²~—iÞµd!Öäæ
+Þ+ÜšY0‚a0,õ³Íˆ
è¬+nßÖFÁ™EË1FU½›µã¼KŒÑ-C:±Ñ«3W‹#ÚEœh?y>„Ñ1 _ίzÊˆ#
¾#<¿ð„%jæ¦Ñ¼DÈsk¶w®býµ—z(8òòËw*¾m\¾ÃS"7èX—[™dÓ¯1)É]0X®åZläVÍü¾QÆ~¤÷XUÚx@Û»FŸE0–!/`œÅÉĶ7©$ìçxÊ
+,/¸Æ}uob«ÅC„ƒìë§;y1þ#_ùl¨ãqk	¹|I».åíÁ©çŒ\¡™m÷ÖRçæýàHŒsß^—Ú>‰„o°$®“Ê€Æ_œ[‡t—*i7|£¥'8Q»TÙ!Ì~:¾ç¥–=‚éqÍB~¶¾Z¦Ù$…Z)RnéSh‹i:mÉLßÕÛdÖœLjÿÎÕÜ»©Å¹ÂçOZŽI.«©² ¼
+|ÉçÌÄ!Lw¼mN)ñíÓñõ©yÄ©ŸfJÂ2ˆÙ¬KZrõç›9<QR¸#Ú˜9sÝ«³¤ø¦4¦/½¿µ9ë|8ÑéáÛ'Œöñ÷¨ãÂÁ·|©OÓå´y†Œ.’]²á9.^lç••bù'Ÿ}3K /~äá¸i©x‚ª¸æ§i¥èd<Ìé„мãNPPñU¥@ûVƒ÷˜—ÅM¾³åz_t{ܨOp·¿-°"{Û·›L²ÈH†äŽ…÷¨o•Øë¤ú’U,ªÞõR.}„J?¼c±Hå…Zw^B²K>áÕ_ÐfЯÖ{íŽ4¦yM²b׌‚DšÄõŽ²D¬r
+j8Œ8MA¿ž”ج‰Nݲե?=ŸíóYÅäQ"{•é”Â÷pn¡=ë¦7\‹äœwlÍÔ>d÷u-S”ú,Yô|›_m]
+ˆ4aˆ'yÕ"ÂaD¡8guKpqM±üöÕõª‚—¼2%ÐïSÏ^º®ù妅¸pÍF(Ÿ¶ýÁIwÉTÛÝ’jÆî3®»ÎwKìdFQH¤·Š!õÁéïQg¯LêQ#Ì-‡Àý&çoŒðpù<T€Ë³¾¥(>A<§)º6æšÎ\•”UžðØ$³vŸP41–ÝBhŠ©si滿¦Z?Êð-Î}¾½ÄÜi|åâG‹:ZÜŒÓns–È{ͪ-r•¾kœ–š±<Ɓú¹¬BM^ۑŃàdQ©ÃFk,ô†Ý̸Ú,_ÏÖÝ´õÁù©Éå`a嶌tQñWöUE‡ÆbÙ
BV7_äÅöÌMg›fp*í«é³/´¨œ'ul#'W–Tù<2•÷I¬?>òï0›x2c2–q†6ƒ^Ó õ#Dÿ%珔v됮›­S›gèNCWY2ò±¿u£ÑWPvfÏÒ\Ý;Ïtò–R¥É“þâU
•Ùu¦M5Ì£“‹uñ±™ ÓÕ,姨y}ìÇÄ92(‘µ×õtòJ[UÒ¾‘ÉœØ;Ci¥¾–Eÿ»‡PE—C^î»7­8]“)Z Ø 3BûAhÜßèÝØO«†´Ðá֐æ2ßÍýö£‚	ßÂjÿn$–‡‹P;*Îmäqdÿ;̍-Êh_Te­•³bz5ÎÌÁN§¼è¼@t»a›Ì’fÿ’`CáÂç0¶RZó˜oº{ËÜÒ¾àŒxÇmWù‰3’ÔŽ6j;©¢ë‚;J!Q çNŒá÷ýZÝ”×¾7?ÜÖB=YûÈv±pÞÚ’æ`] ×¥ÂÀüh\ßÇ_voã=Þ¾'ÓM%²gm¹õcUÁ…÷F_–j’DžŸÌ<æïVÆy6¶Nµ9
†v¯Oy&=Å&@4ê•€d_….×OxBÔÚÊA
öåÇ™cÆ?¨¥y$(—ót?]„N«½­,òqo®¿žùðÉædï´²
+X^RÀä,Ü“‰oPÔe¡¼Y»½/Tò~ܦló`¤üãzHz¯³ñ­€ôWêk5[fj’ʼ¶L\q$×õú᜽rH¢•ñ>ƒfúÒº:9²X½XÏý£mÜÍ[<váE‰¡Î&rÁiBuZÃNÝ¡×C…@Ëé§(é¾sù¨ñZ‡€$I­2—‡»v	ƒŸ/hq¼°kð5þ!•ÄÆùú5,ù–¹ëNB’‹¤ºRn¬³°ìr•nJècúÀÂÉ#ÿÞòPÖÀŽnïõüàqÍž—ít<jIèËfwlÜV¼‹†˹Ã8éá…®Ï×V’1ÉÝÂyêÅàF6áOÓ-,¢!ÓܯIYó’[·wé*Í8Ø“±bææ]jÆWÙr—)Ïz\&aÜe“yQI“…Lj³E>Ù=¸=kýêâu„º·éÙ…ÅÎBT׫y1ƒC†’ÑÁM‘Î&”g$_¤^SX̍"?õ^‰Z”÷4„`ÿŒð\U¬.w~̲jpÅ€º95FÍâ"4ó¥F'¸M$¡—a·¸œLÈiÄ›ÓXÔÉÍÁBùÛ>ÔÎÞ;Ïâå>¿¦(	EÒY%nb.´5MfÛ+!œ%ÚS¦E26°ÓŠðÔÉÀY»“Ò+^ëM®!báM;aŸo0[ØÒ}²û RØû£9'1‡äôU¶M0âùdÐmuöűUjjtïé§|*}ɵv Í¾ÏFU,©´†°…âJòñâVRñzÜi…’®ÏCíJ\2j{»v÷œò/Íغ,:¹ö¤Žð¼(§°]Ö‹¬h£¼ö¯t¼þÃÜóf×å9¨aw×0äo‹´Ú½–™UçÒ¿ÊrÔhswÝìË$‰½`w廬v?$¥âÝAçèw?—ÿÈMS"âAiƒïíóªGeš7Ÿ]ßè“Jò‘•x^-¹Ì#üüä˹–.ôöPtâûnjgYoXk¡Oé¬×bmïDISÆZ•¼'ô¼W³Úµ<€7!’°SdBn€yÅS2úv?½mãýÍWUh‰àŠºçù!-ò2Ö¼²	¹ò‰•ª7ÕE‘ÕRÇnv¦ÐÒ¶üg3߃LJ„Ñ‚·°
•L+endstream
+endobj
+11 0 obj
+<<
+/Type/FontDescriptor
+/CapHeight 850
+/Ascent 850
+/Descent -200
+/FontBBox[-34 -251 988 750]
+/FontName/NJEYML+CMR12
+/ItalicAngle 0
+/StemV 65
+/FontFile 10 0 R
+/Flags 4
+>>
+endobj
+10 0 obj
+<<
+/Filter[/FlateDecode]
+/Length1 1091
+/Length2 4694
+/Length3 533
+/Length 5429
+>>
+stream
+xÚí“g8œk·Ç‰h£GÔ(#!º1J0$ºˆD‹1ƒÁÌ0F2z¢“ H½—A0z‰!‰"щÞK™ì}Þ½sö{¾œë|;×yž/Ïﯵîÿ½îõðñèßU†¢Â4PHŒ(Xªê‚%€`1q+(ó§õpý×Ò#ڝà(@ð)$¸„¢.ž@(Ì+
+G†BúЍ¶õ¦ƒ@Ò@o0Ž„°@–à$†Da)@BS|ö(4à×}JIAv(Âö—ü§rr%\
+ú·$yÁШ¿iq óø7Ø
ƒý­HAî°G0ä_Ê
ÂN¶˜¿‘ üfô·0ᨠØo(	9ü†„TøoHÈuþ
	f]~C9 ñ7‚	Α¿!a#Ôo(A8ûoH°Œþ
é7$4êïó€	?%ûþû4¨¨ °Þ¢’R at Q	B«äde2Òâ¾ÿ5Î	wó€ÝQ#4W\\FNêÕ΍†!1ü~„AûÛÃ	c	ƒaav€´t&8±§ðþƒ½Ö‘«¦
£ú€¦ÜõéƒLæÁž-*R±1§°Pgý½qš“ïúQÉGÜŠá•~ºòq­Öžžì%SÌBöý³ûÒè%ÑŒ5æ@-°¨zœp¦´·5)Ýô4ãÚ¼ÝÅÃV{:Òí×<-1nîÇÍ?]ÀAìFÔk'¿´9¨ C¾W…š3²zVH0ú"‚$CÏŒw•Dý?Uk:šUåÔd¾»²7›“¹¤©© ?h5ïÓu+±²‚°ûƒð{'µŸ'§"óú³¯Æ,Ù<vK:MISVö;¹à›J
Š~%
Ư[fä-¢…¦“Ó–—™­#­BR·wÚö¡q\R×-¯—(†6õwwq¯Ï§³¦Þ;ZR	ݺ7ö!¸Œe•ÅÚºQ‰Ëá#{Æ*c:~ÏhÖ͘o¦ŠK…	³{'ÛÜ;¿úaJ~Æk€¥<k’Ç$˜þm2Žó&I‹’ö'ªòô¶tz³Æ%&’~ê3‘2þ¢áZ-Ÿë_<êÞ~iÖZ£èÓ†"°X¢’Ïj5Vôýí׼ϋÍ2åÒbØoSn,óJ…å%§ÿôè[Í	Øc(Œ‹D@Äüà2X	Ï’Tƒ¸e踬ÀÙFûªÀS˜çâÓuvçÊž”tÑLûÑ©ÇLš;“òØ!’Ù£šf>§µ34äd»	ùd—{³6§¿1Ûäˆ*Ø'Õ¼?Ö€ÝÔÉ9*3Ÿø{¾„_j4#ì!`ŽŒ*š3˜nLµX¤í¤ò´Úšn\ÝbJ­^ò@$yÿpÓ­ÕÞUŽ>>\ŸòNŒppìèûev>»téîLX=n·9ÐäõrçœØ&4S[çñÀåÔñ¶ë”êC´AR§Riin+ÕdšÙsV®+LæEßþ/¾óHµæÉ-@(ÉdººÎǝ†,§Â~iç–ÌC{¬öº–NpAÞɨ™ÖÒ?­ïÊеŒ§o´Ê8‘zCUg½±w¥KàYî§lõ$Ïö•Øæ‡|33ÅxË.#TÄûübaâœulÌþ;éWã_w˜k—¸5²¯ÓZ¼•²®¾ðhÛ†^:çûgc½¥D®¬[JÐu‘ìU¢`f”Bƒ‰Ä'ÿ{îâ[R4/öîºðÇ>§šû‚ҰϏî=À[Û9ŸGÉcóS_ÕMX/³¾ŠÍ›£oqÜ©üÂUÏr² $M”ü¢y¬òr%NÖ$ǵԕŽ„ø~¹ýþdøÀôö`g¡Ê¥ùüÌVªÖ'ñp¡Í'²”ÞhôÉ•o÷דËòµüC‰{ÜâÄO˜8鉧¹A]{,C®%Ÿx9ãR—me95Ò›ZÕ.©ô±³xØXvÂm+¦2›xñ~ØR©oS û(ß@
+щþ@9•weœÄ7û6sR)|ɦȝ’(îÊàLhù:–§¸„tãùgµ}Pcî zò@æq¼;µ&«l.SIZf g„ Î
¤	®FnEn‡Z÷-?hÚbG½È%ž³Fw–[ÿ¬ŒU½¤+I~‰Oÿ<\Ö1š·Ð2~›ÕXÝÊ¥§rÑX^C¥×åi«—+Þ·´þ4Š[2>¨,˜œ&Râæã…y
Ù#i" u%óVPƒ çNVâëž+‡s€¹'‚=ôW·4¾ˆºð+#
}©å'KÔôߐ,xººZ…ó#è
¢m+Gž§VÕ},ËÌ÷©Ôî²ß?Τ_ÂÞ+ÿGÛð)_%¶b¡]ÍnV°Ê#Z›¥Ð¤¦vªï)|Y%ïÉÔÄ6‡GkÏÖOŽ¼üØAbœ¤…K­=ŽÑÁE2õÂzŠ‡¿ïb§¹ÀÛ9æs/–ï÷å¿~ýaê„MÒ{["¥ùΛˆ7°âËøÔ²	ËŸesˆQ]ëT¦”—
Î!’Fò4ªj?¾çªãü$ËÀlE¬bò÷ã³Há·	
+ÙDL¶ŸŽÂ„qÁ|û¶y^̾––ɷ凂¢Þºƒd&|QJ>í#³ò°Ên)
¦hº¤¦È”÷:½Üî‰z‰#^¯Þ-mØÔ4€÷4&È´ûALc	ûÊK½éfê3ßv%
0ú¤n²îßò
^.xÉ¿uSwéÜKJº=¥·<AD¯S”Ñ
©#ªâ“¿58sQækîZáCËë{¹qZ
+h,­˜D¡ä?àŽ—B$;Ê/§ïÓÊ.^ПЪ¤rð¼4–£ü‚ïö4Û–ìÏJÂōŸãÌx¹DÍ{ zjâÜI¡Þ¯Ïk.7®„q<W £Ó™TM×d]¥œÞB31¸âIךƒŒ…@ƒE=-éAWT¯ìóPm~'ÖÒÔ+â\gnö”™<á³¾
+•ä´þá£æpìý	5uÑÚ +ô
çŸDòtÈJ•f<Øf­üˆy&µFžH™(-žÜÉ,ºUG@ºÓÙ_:üva×àœhì¢y¹A~ˆ'¶gýsÃœª½ˆëscjü5 M¶í¯÷%»µPGLda\³
+
<žB­ú˜j‘:´ží—«Íjõ­´„È0é«ì™¶Ûmùnߢ:[¥jf„¤j+Ðñâ{04™ÓT
+ºú³ Lzè2–!|kS…YW2¦ÓµYÓ[VÓ-[\àÄüäÍÒKÞ†)KõN«kµÐo Š3Tê¼@â›=éeêHÙ—íY÷v™£„}ŽžMK>©~MôTý´YôTÕ
ùqÆbG]D$Š5ÜÔ¤»š5æsò=ǹþ4ÆÈų͑¬)™†ôÎd$Ô‘	õw^ÊYê 6Õˆ§[÷”Wù^Ë~‡©µ5oA„nÆ
ýÂ捄Å_üîpŒŒéî0Æ®À[´’öãsäIÅIm¯š‹¼¬¡Ä¤å˜º~+`?/düüÔ½~n‹Wº4ýÃl«‡nÑÐYŽ$f
‘ÖJV@¤×™+ª¬wÚz¤ÝëáP>ÇÜZ!?Ÿ¿
+¸ XÑ&9(Ö_W­UÞJJëÆZD5Š¿«W—Uròª)RðwT$
+ó¹äçãZ|Ã|Z*~±äk¶?¯Ž„{€üEuÔÇÅç#W\õä`¬3î$“Hé2¡øÈ©ÓYí4©Â¸BêÉN+±·»gpÞ-W¨Þ{!ufâœx>G9jšÖ^¶̸lÙ}­~õµ}ÈâÒü‹+Ôòhb™gjSÚeûâ|[»‚]bù/6AüfTŽ­1…æ•ÄLv,»~By®o‹z)öñ5z—4,9È.¯o>ýVÃêÐ&=»œsb«o]aéöT©TÀ|G?ƒ{s at vÞ#ávíMçkÅGˆÙ¥žs_èÃ*Ý«;w¢œ)r8'CîVjÆ]p²èœÖfµ!G«ß„q~jœ1¼ØtËÞ,·”GðÆ@ñs¦»»4°êò—»œÚ#uÜ€Ï.ß8úµIlhå §¼-‰ŠóiÃ/ióGüŒ­*V®bN
+0{Q•N‘¢´Øæè+ȆғµQÕl¸áÚ“¤aZÅg뉌é^ôT½Ü7NÚ\_;«Üß9ÐøDÿJ²å»µžgý ¬ÚsX>‡ÒÐÈáy¦¹l£_×àQômc£ŒäÊž®|âŸåÊscWHámóª“ôLí\È$̈Œ—¸/³·v)z#+G{u„Rûþ2]éx.CÌ¿Î9Ú.Q“k/°²3šh.fÓì)rõ¨¬oûÁæbê^^4ä}7ÿÚ”Š»­6±‚ì8ßõR¥ù¥6ë„Ç_}Fr¦Û@gÑ•*L¯]Ù¨Z/›´RòL´Êö*©ð^(¾†ÀY벜\Wþ±ýò#	ž“êÀå ßE‡,òL°Ù* "ÂÔÄÚ_ye''ݞ͚[ü‰wçùÚ»íZz9–Go†\>\sc–ïó‹b
M\_%.ñ3°Z»ÝÌNêÂqdº½œ0)ÿà˜uêE![ÂÕøŸ<céÎIã>—z9˜XÅ9®ý(})d}+[‚9¦)#‰æîB”Kç²À=ø|Ï’p(8bĨut?­¶ÐbµãqÒ…æ]S‘˜Ø¦ðQ]¯4œöOÉ­Í®ˆëÿ
+Žç8ôj>+q<áPʏËZ‚œ‚"Uš´H‡“Asƒ.›æÍô)†>Ù¯%¡j†83ŽL	³;çë¬Å…ú[ýüŒ7óòçUQkÇ#@ïohã’&ªÖcs§Ùòø$„*±¤»=d,¦7ëú­ö{FF½AŠ€7?ºÚê=”°Fø»Ùø©LÑùÈáoŒúÆÞF‘ÃÉûW¤INôöN7©Ì­SÖ2ŽŽÜZniYã÷)é~_PV&k›-äN01æ/òg94Z­#ÝêTéL
+6€ìՍõN?BáÚnODvR
¿_Ù¹õ8‘ÃÏD–¯»ø˜ñ³2©ßv »`¨»	݃µ7	”¡±P=± åõˆ—ï÷p…™>‹÷eƒ“ÇFO3ïf®¨t)v‡S½3ȘQݼS¥á$Êáîê5õì"o5V^Ño£1JöÕ‘òælˆ)çjÇJi»mŸ[õ¥@Ø
+ÙÕýþ‰j©‚!Õ›ç•Ç+I=>µÕ‹{	“¦Šîþ•Zktý¥áÛ½7bàýÝcƒJ_MrH6òWJ±´·ëÛXýi\j†–69#¦´.x5¤‘õ᩺ÕLùú9}yõ\e†D‰2vÝÀ¥}}eþtéDâƒn‘òè~9Ž#aÝ{mÃo1¶Ëò1%âÉ dÕöx€Q ‡¿-'Ø¿•ñcÊ­ÞàÓÞ–…‹ÝÔ•Š?"¥¤íÄ‹—%(†‚Ì—@ea÷“£9„C®Ô¶q¿吧ñ~Já~…¾q):$§¼Ü¬Aœ«óyY:¬$õo˜ćÇ5+endstream
+endobj
+14 0 obj
+<<
+/Type/FontDescriptor
+/CapHeight 850
+/Ascent 850
+/Descent -200
+/FontBBox[-53 -251 1139 750]
+/FontName/SFIZFA+CMBX12
+/ItalicAngle 0
+/StemV 109
+/FontFile 13 0 R
+/Flags 4
+>>
+endobj
+13 0 obj
+<<
+/Filter[/FlateDecode]
+/Length1 1300
+/Length2 6348
+/Length3 533
+/Length 7162
+>>
+stream
+xÚí–eX”m×ïI¥»%†’r:DºD	ArfÈa†¡¤A:¤CAIénnABº‘Þ¹Ÿû}nÝÏ»¿ìcÛÇžëËüÖ¹®õÿŸk×u\ÜìzŠP¤5L‰@……e+ñŸ©F°¿Í=DÂÿ‡Œ:Ú
+nQDØÂa+…²ò"Âœ‰¼…ö(Ì+öG†+HX³mèˆÙ5ìÄ°ý1šv ƐýˆQuü1²ð?£ëô…1ºˆ?£‹ü1­vþ1º¨?ð¯¶þ˜Iýn Ɔûˆ±áñ1ïÐßóúŸ§ûáC¤§73d æ0‹JcF äû¿& ì]Ü`êʘó#$$‰ß_Qˆ
+C ÿõ:Á<9ÿf{Ìsƒy D™´öØ÷XN,[&8Œê¾Lê5ŸæÐ
õÃñ§ÂBõŽ§I/¶õ¢RÎyÙ^–½Ð–k95o÷òbú0GÇoÓß`º|"S½¡®2Á5ïÁ¢j-L+rĽÍñÉç@sÐÎM‡ìŒÕ—Ý/šf Lšz§+V"Vë±d `́,‹ÓO·ƒJPj¯O"Ô¾N¢!×G€~c•jŽëq”#'Í¥ÏØTkæe£}ÛwÓŒËõ)S*¨~
Ž‡úݏmÄ%ž¢¾™eùÖ¤×yΔR•½î¼æó(++ƒs’©%˜k€‹?*'J½ô+Ñ,­6íMæº^©ãÕ籊¨‹2ÆYx:ÈØ#ý† …›Z£+¹g dÅЕÑ%|øgŒ©E’¹¸´sEÂÐc"»ÃO¸fÍ++%W¬òzñjÒßV'F«‚6Þ$I‰î/«ó	ëŠ,7¦»Åò7¾¥pgØ;=”ÆßäûžsׯðÖ”KO¢^u¿`±¯$Ò<b^ö¡–éÔĤÚWëý¡-oþ^ËqŠsðÞÝÈr~3˜³ü`¬z¡ÍìeU‰Aãðtìù˜Ï僜ëÙBÙeUñÜ’Ü5[o`c~Q¶˜Üz`¦!	Ì$,uºêÕ*_h³éi±vÚ(¢Z§*Ðð~r~©š¤¡ÀzçeL¨l¹a"÷­{:eœ/U|òîAé½üP™:2‚£[.Pµ%&²ëðšcèRóüžŒÌ& X3¨+
ÿG\X‰‘íç®ÃÈýÂj™þz—®ãZøüÇÍ÷1J•ðr(ØÁ„Rm.¤žXÔ,]î|pge›÷™g¯Hg¤Ïò™ÄÞ-¬¥¬YHM÷g™2ÞœoùC÷€©ï„|âǘ5j°‰2yW${ù¤ôIfå/¿P$F_Oz,쌙àw`Š»ÓYf÷IôŸ¶ÆôziãQŸ-ôV\{dZÆß»ßu]-¤0'E”ÈY{®C„Þ¸ÝlÛTÚ¡Œâ–¨tø¶ˆ³<j\𔍭y¼W4ĝ²šÈñU˜ÙIÀÜ1ÙUá~=o÷ò—wš:9Å}Àj­‡£±ÝÕ1[Ú«1¾*_y&g+sãéý2™¡0¨®Ö7”Dñ"›’·1¹þÖ”ÙlЃóùÃýtëïl >ý™Ø\nߌ
KEŽ™:3p£ÌDÌåNŸ( ÜB°u®¹JÆG섆5v;^û¨ñBúA/=€&å°x«ãJæ¸!¼Rw—Ôwú&Aú7¥[ªMôØx}‡*9­„BÈ´—Ä°bn¯ùµh»ª+ÆH:]ÅÇ€ª}A¦RÐó|«ly®*rØtsT«!ÇïNó“Ü0PP™yDK¼š¢/¦>£©©ý ‘Ç}ˆÝ_»ˆ3Pp;´Y‡=¿ßp¾ÃààÒ´oMjÉ0®"=ªÃéúùW䐟â±p•'Ù¦ø“(+ûœ
G„ùШËKf¤ fÆhXE.%¸Ã;hŠGÒööª8Ú¯yI{óúÖ£ÑÔo¬9	äMôø²·S?˜c7g¶å:rŸyëO¥0žŒ¡7
+‡ÚdBhOxOÎûYŠ:Š)Eï2à*?J»UÎzÏtñâ\Lí9°DjoþµH[TÛ§©ÁxÙ§_óê™d‹Ò`½åœýw¢G%†ÌJW,3R©’Ø»eP:@¯åµîÇÆͯž+e³÷V¦ÝÃT£/£¯´YwÞ/Å(¨mJk[¾ªN`âéÅ”R5‹>²Vp'…ƒtRëbé­‡S’=¹s˜_\
]•70Ô•>å¤dw‹÷JßÇÅÏ­¢iXK¶L—-±k6¼°£7µï”èIT×ÙmŸŸ]ö+,EÏçíí¼xóÈ}Ñí@÷rËâ58ÌuTÏ㝉kC['{›æ“;›ê[ŸÚ¨¹’J•6v9LàßGÄ=ëGœ¥Ÿ!^FÒJÕ¯yyQ„G1|&ßæ[51ŠOØrOcSÍ•‘·ˆ]i¢Ž½6‚DëÈ iÞ{¥WEhîÒ/%”½Üÿ&cãïF,Ý)¹d ÆF?úºú†M•I{~“‚B¡pñL}žÿðÂòÂ}"€—4[a"ò/1Ó8Û]Ùma&ýþ½õgE*´"­H“Òø’ÍH-MYám
+Öá(ð-|tžÎòÑ4»ö:?fóšëWò5ö£`w²Í‡":u:µ^M cŸ[“itÇJ×ÆÇ©èZª¿æo¡\'ÅfU`h“‡”rŸc
+ÝÇ:àH¦d;ßg•@X®û½ùFÈÃ#þ÷®æG<\ñý¤üìÜ>¤”4.‰aòd|ú~\×養ç]E®½5ÞŸ÷.ôôzO¤Fõ4¯hÑT´¬}'š'¡µ[¥ßRïÔ畱šéšjCåü²:ʐ+ÖTñih×CÃÌyßñ&Z„pÍ}9‰¬´o«¿GuP<s9àäa_Îd’UÖ1Ó
Ïemk'+ãó1E‘°HÖD4	–׶·¸O`›“£z¬>;S[¿IvŠÿöÔ€cÈŠ
+¹C°ŽG *·´i’‹3|MLÎr[Z4/øtœ'o¨ääR‰ðŒú²,iWÒôà
 Ðbލínd•Ï§,ZÖíôj9±Øýeý¾?Ø 5qrh”HÈ™+¼«-Ac[ñêñצ$‡ûÛ!nŽÝ!ZšZþÇ°’W½úkª·y·'s̞ܳ8öuºs5®ÓoîŠÝ6¸Í}¨å÷Fõ♍eçnbÄ‘Å‹Gã$;ðÝ9ð¤Lë³ ŸEÿqžÉ¦ðž(ö…JÓþË(0{Î
þbäÓàÑC¶§…Sˆð׊0<iòNþçŠC+7™‡Ë°(ÅôDdËÛèZÈA,9/S¥®Ç°ðëÂ#zŸF̧ªæ-iò«M×Å­!c<‚ÀÊ$‹ICTÚÃd±z¢—Tíu6u>¹€Pó•×VhÀRîJlͺ%<p@Ó—²¨,brñ*óíëh³<Nà”>á},ÚtáûÉóÄž3Gg®…t+î¶Éã(,à¥ï™û’úÁË_R€¾æóäk<_
-çÀ?;#hnGõº?ìNÞæ·È“,ò«=öylF…‹¬Z3ËH¼>âc+Éýª[¹æá…s›1d²+Qí“ÙKˆéY¥Ms‡¡T÷Ÿ«±<S'¶K—½5Ô”Ÿ^>kO`Ë]˜(ÝoY­®>ä¡5©Î][ùå@9›{_Š)SâGUBN?¿âŽ¶yŸõibȉ¼*&Hõ û/>ˆœÂ•õ2Ô¬“}—”ÅÏ8éjˆ‚µýr0-G¦Սêò=´cZ}Ü]$‚e%XÆ3P羃7¬!÷Ž«.0m”lNbœ×zÚE·ÜKÿSݪ̲è|Üg±kŸFYÅÝçô§’— t¢*§|í—‚ø’݆ó»iKyúßäW¢ŸkÐ[ú‡P¶P1ˆôÔ&®¢õ‰ë¹˜ÿI¦Çóår§uN<_ø£&EVÈ!¤Ã‚{¶Ÿ‡V¬}þþNBÙñVÔy_ýk坫ÅÊf‚T~Þ¹ÖÔ>Vh-H£]5{Ne芳)i¨ÊÞE
–X:‹™T3ÃjOkÎÀ'N"ëq».zúÊÔÈéîã7¶í”¥£ãûhõuÞ¢Îk»ûpTD:ƒ˜ÈûÆ«9(fEŽtwÝ6R¼ôÅëxkÑÇ­F+ô¸!N½ýÓAoW	ҶȁëÒoÄòü\ñ½ÃZŠãžhI‡›V‘T“{`9Lù¥EnÝäÎó7'ŽÄ¨;®VÞ¦ ÛðÇ)Q¤V"bÄmåŒìÜ’rýåBWU¨ÍçK:G¥‡E=ú˜Hµ«óä¤þ²d5DôuRnõçbҍV¢÷r9„$¡aÖÃ,ás1-—Ur§hžõ¤¯T+)„G‹Öb–þØ°ZËžÐnœ˜hµzæÞïœ^/—ÄÊX€Ù"ПªßKÙò¿$éÝ»IiF[rª]î;=4Š»'ÿQœÿ±iåv7u"•ËÍöQ4ÿìàÚ«·Äœ*Ý“ï(¿¶“@4“_¼bþy¡_áã_͐ùÄ!_žÂGÌÅA©JµFMhéúàð¼Ýô°³Úpõ¢Tüg¯JL»pßÞo¬l¿N at pÎÄwõ¤YZ^8µb¢(%’üšÙ+giûŠ|‰†
+ß鳿È.ÑG˜¯Û‚l ç»OÎ%¿o)&@±,ò†
:ïlówN3­´}
™ùj‚~§{q§§‚R§ý|RQkÛ~´·#
+
+Ňó	z=\ØÑ4mõÄ1„s+¬€7©#§™»IŸ£=ÄäÖ»é3Šcušéz÷‚è•ê­Wá®ÐCo»­>Îpàcµ§Ã
+|×â(•€‘Œ*»XÆI­öæ €^
+†ôâ:œQGSä1G{õÚ³¶±Îˆ®Š'4õC¶¼6y	Ÿ>š€ÚNÞŽ­D¹ø}‹Ãï2Z¡Tâ7su…z¢]pɶâì#èþÀsÐþ­aӝ´þ¯×±ŒÚ†YÈ€Gf¦÷, ¼.ó±s­¨!B§dFâÔ©+ÿ1ð3q$‹ºÞÄÉÆ4Zº¡‚Bêi	MäÓVÞð@pt¾³ÔKQ|Šáól™}jn
+;*-°MC5ý»—­«„æÔ‰PÑÔ[¥I—[âY™ïö·z^—“„9i¼;%ÜŠ0¹dÄ50ìîù¬ýXQ¿íùbQ(Y’ŽªD+t²B9ýDY$
+endstream
+endobj
+17 0 obj
+<<
+/Type/FontDescriptor
+/CapHeight 850
+/Ascent 850
+/Descent -200
+/FontBBox[-251 -250 1009 969]
+/FontName/XCHVXE+CMR10
+/ItalicAngle 0
+/StemV 69
+/FontFile 16 0 R
+/Flags 4
+>>
+endobj
+16 0 obj
+<<
+/Filter[/FlateDecode]
+/Length1 1867
+/Length2 13928
+/Length3 533
+/Length 14981
+>>
+stream
+xÚíµeTœÏ¶¸‰»N qww
®ÁwwîNpÜÝÝ-ww‡à6üι÷$÷þçˬù6k€ÅêgW½{?µ«ÞjrbEezac[C¸­=33@Tî3€™‰Iœ\Ôt2·µ:x+þ³¿³ù_ÌùÏøŸ+dòW”ù¿¢ÿkò{v#[kkàŸÈ»™»ÈæOˆãýÙ÷*¶ÆB\+›8ébïTÛ5tbÞI˜½Þ†:xš|bºàv$·44R@ò2@GÜ„wÏYªV/1ã/ÌŒl3.Ž“æ6g^îU'Nâtâ8J–E”ögg»Zd´}PHüô¢‘yN·;;J¬Ù¬Ìé8j֍펎`Öù1•?#ŧق¡„mrk¯D²(Ëzíúq3·Ê­#9ä#„ªÅ·-­ÈËrñ§/	a`Žà*¨*²üR—.\Àñ¼FHÌ:›¥>ÆÒ£Ö¬	ZcJX)œ(Ç.“ó´Šâoñ¥b|pOáÈ§-f‡²=|Ý·ß"Pù|bºúG~Þå±ì?¨RyL’)gx‚=¨w*z¢!xN·,gíì@Šì’-ÑÛ1QÔ U®ÜúƒVü¬»ìUÉKbÑALÄ‚ÀïõÉ·)d‹LŽ£0–jéeº92"ídÐiQíÓ§áÅ锡×UnExªžƒ¢Ý4„`/ç/H_טEraºN7Û2ÕŒ…ÚND¨uŽÆge~né[f.<ö}æsÄvÝÜ+«ä,ÂŽBëÀ?3ÃÁ¼ž=‹"Cô:¬V˜¬œ…"¶¥X¡¢|BjÛÔ(DÌmZ_ÜìCÉÆl”0ôc$³ÒTsî…ÂSóÊwȉ(ß‚O|Yä†ëÙæ1ô˜®Nð­æ—ã¾)m2ù_­ç«OÃ^º*)ÓÌ£Ñ@ýàË-€}¶ø
ËLÑN¹`îTGñ;ìÍ°€7ø*|ÞXe†vÓÂÀà:w5W«Q¨Ï¦
+æî~•™”³n†Ýáù¼Ý©±æ>BÔ¤a@IœƒÆK{¹ üC{ö]§t+Vfú?Òǘ(B>[ÆÒÂMdûòCKZs§Yú¯¿|I¢•ßÜ¡Œ<ÚÀœ•›,Xé]jˆû6P?½xgXQÐøì9Ž‡SàF)ÝØ"#ú…2Dê;UE'‘è+kg¡ET#‚
+¿È™–¿ðò/—ó7 at n¾è²,ˆ”´G+zµ®…Z§Ì¾y¯uv›?±1'l­‹
Ϗç'®ô€ÌÁ‰›@æ¹àkÕlTáphuå ˜/7AZ#m+àCº›ª>A{\ß¹”D\<ƒ0ß8‘ÝЩ1ðûSöo¹‡Võn”
+ÜZƒé‚†9rˆ‡£³.ï¬t÷±	îª6	KÇŽÐ‘”$Ûd"pÓðw&à.ñø_©¾Â²äº‘SâÍ
+py4°Ý
+I‡õ1üŠêò)ÉAú +&t™áLû¢'7ïÍ0C9ÒEd§h©»®4	e¾"hÌPJ
+ç—\gš]˜yÎ陥Í1 |©Fˆ„2ŠxÆÛ–²Ê³ü-OksØUªÝf˜W‚Ž€‚ŠŠçË,ø}cúÓEd„ög«Þ~ʶ(¯Ñhù’XîJ0½¶Úßí*	,û>²KU%t¸,,þ*Ž{]N
+X”¨¾5‰/³æ#3ýcíäü	XEÁ¼ãÓûCèû¦ˆ%¶`bÒŸdŒáÚ³Ç2g‘u‰=ù*p„%›ÔÄš˜2ÌÊðFñäx®75úð£»îØDû‘â¾ øêÑç<·vß£§d»·»0±F•YGåÖ"þ6	OHSÇe<åýJ•6ªóRî12Ê7éáÎ÷½%j=•ŽŸÆåøôå&èêZl
+]^  dÞW“ì¬ͯöG²ÚYœªs;wƒ{u•ðmë˜Ê°Y‰’DVç®Â¾®À…ðÐ[ø´£Ç§ù4Q™u¹Á,h†ƒ›qîö«ÇŸ9:·×DöVÒž±¸
¡™b ì¦ò¡‡)¢ݵÎPÕ>r¹vsïø˜’aLŒ)Íuz;òÍ
^[1õ¡qtº‰‘èÅâ8<›Y³çWö*òKQD½uݵ%É“!tBA4X½îJ>î« p9™¬ìg¡«ì ñy‹^–ºóEâ¼GÖ9º†¯
+xnØqÏ1ËÅYÍpâw^ìß(•:X NPkYy³Ñwü¬‰Š”Öð]æ&?eëãy…kÏü\ÞÛÛï“èœ+VnÒ‹sgìÞÐ#¢öÝèxö­+ë‘ZvŽïrg³Et(>åèì””®^n":ºÙn(rÞœ›Åføv¶OÞ4µ÷~|¡Îu¹+àj¦8»a’˃}Bèž$ƒk®Qݾ€~èdÉâ6i†H@/šÙk͸Q
+Áàj ¦×øÈþËù[ŸKrGŸÕhÜ+E¤܇gµ/š²)È%Ðpîñ›>J¶–±(%†QG¡xbx«zì)“«|óÛm–Ž­›¥îCTñ‡äP¢iñ,´Ó„ÉØiÑ×þáMÕ®’õB!^CŸ|z·8òfGkà€˜¡IÕ°Lã—D÷lîùl(͆ŒkŽzŠ·ºnYÚΩ2½w`Â.ûݝøà.rÀiv´¾«ÚÁSaÔƒ>ãìÄd÷”›Ö	‚‹8? ¨[#›¬³ú¬ÃK
+1—HÕðF#\Ý°&ä•æ`Û†Òê#ì¿݄&ë].JÉL‚Ïߎž€,G8kÇ$9ÎҰ׆Xiã<"Bð›îi±ßFÀ!Z…DªÔËu¸nIaŠ];d¥h…·@66>R¼¥ŽQÐö'¥VÌs×þÆ2ejóDÏõ{Ÿ¬1<~¼¾=çÓÞg’+涾ÆffÏDzü–­sH‚9åÓF4C ;:ÚÄÕ¯ôe\©“.È—9mí…Ù‘°µK0Â`¦Op+H§ÝÊNÐl1UëYG¼ìڍ*X+“sé>v¨ÊTã§û³‚6Oû4´ÃòJû”VÓkÍ…!9˜ª‘~mV½õÝ­÷¨U
+w†dÕ°œX*ï“ÈÃ>÷Ôé‹ä”@ˆ\l–'Ð’`˜ªKüš0öÐBó)§¢|÷£sßúz&öø´°Ïá
ŠTs8ð{ÛàK°ú÷èo*& ¾¤éÉûEŽm:-­®PwýIØŸ\Ja-ÈÇÔÍÞÅŽbVìHµe3bäŽ!Ù(\[,UQpBRÈ?ˆhÓmPZ¶&s.=·§Ë#¿ï@¶AÄŸx«F:‡×ìñ¥d©Üw›ë¼J¤c¹AwO^ŽƒK«PE殤ȱ„)>ÆñšZ総ú( Ýbû=p²>Ü’¹wAoÇÛoX(ÃzÈA+±‡ž1ç$Nt¾ÖüÒÁÏJ\$Ô™»ùöyIf#v²æSf%ï;Žü®ƒÒE2­³VöóTž3æîC¼9aÜŽÚ£çó—.õ_ý‚O`0L´‘Õ¥€u¸î€C«…²Uó'$Zõšý>ÙÓö²5Ïqn%Oҍ±Ø>íi /þ«¸þ:oÁ_ïíâ5É+Í{¼Å¨?˜ø¥m›º—7Œ)­<¢Ü ¥ÚÆÞa79›[Á÷ôŠ‚â:@ñò›	ymÚÀûvQø\òÈ9%_!6-$IÀœJ[‡erX¥õaÝ…	p£´àÐ!	[
œÆï(/¾8³we—uÕâ–<fO͸	V¦þ©Ã&·³žìVñ-È«ÖÑtA¬½I?Af (kßF|ôiHd²ƒN͏F¸G§¸"¸‚å{b/Ïñá¶Î·šÛ^–o´¹.8`2Ƈý	ƒgºeÚR7S(oJ!KË·*ÇmsN(NýòUµ“YH.ܬZy_M-FÝ7?›Ù×&®’;{ü‡lu
ÎûQœáL^ëiÓ‚€ŸN>›ÐX5ÖŠrNl.a£ÂáÆ}Ò#Û4ÖõÂq6·tÝ{ÆU@Ñ‚ˆ3;ÊD*“¢;Ž©,4úŽ03Ñ‹‘å(dŽï¤'ì®Ñ…K´7¦né÷{ô3…šnJ(­´ú<OfB›¨.’‘ƒ·´5X%²š\öLçƒïò¥ŠûÕÿ´âŽÉRê¨)ÙSéî–67¯`t5Ô}´.ò-A:HÆöCmÔ*Ù*üfA¿‹¬$xš†{IJêñl5ê Ùéÿ¸JΫLF§?/.Eñ{SÙd³Õ¸£è©N{.ñŠÂ7œ9?¹ã†ƒ@ŽÖ:å“‘ÕÅìTÆ=žm´ÐÓvææñkñì–3…Ì>›C“•³êWVµŽô*Zb *C‚VnIffÖ-aôDK·ÈþíÀÝ4aï룶ü¬éqóNlàÙÆ“/žj…Ö è!ÌjÏJTÌÞñ+gQ\ëï †ßÇ¿Ü¿C³¢Né •+ÿ(hzQÚã5nGÏ7G“â¬àÑM3)åÊ÷w‰7ƒŽ¼¢’É?)§Á\ªcDº­òòö£Ý©ÓŽ|å×KîÐ F&+fÁ¬ˆÊ||s‚ILúJôúâ6CKëç8
cât²F²²Å,È©mo'G]$ëµó¸Qžc¥°Œ¢6…‹ó(wšü{摉L¥¼òSNK’kKxå­Ö¥öÜœ5ºSŽÙ,ÅW\â4É!\‰—£ú™à“'b¯Æ.	\•7›`Ì(þVã%÷—®žOzo?…š÷x
+œóɝKrÈN¬¹¾Ão?ˆlŒzU+ÔIœZ}ÐHdïÇÒVºKjy
+÷o}ûz:RLÐ<ùé fv&7a¼û¢nŒòG3¤{0ٔ½í£×Áïá²YÎOaÛæ§]hþì
+v¥îÇø>3UðyÇq-£™k+¾&ŠÉ;¢ú±ØQ€\G3yfÝ´µsE¸oªÓ`)Æ0“TU¯zü DÝóÛ¾º+.GFgÓ‚g	IC[h~ FÁÔ²T‹Y½{”Ž6@ôt ÜN˜?§@µX3ܘöƒ(ˆËÁ“¥dÝï[”`5Ù0¯'ŸèÎ…ã+PÓêaà¼5ŸKS4´X):Ö†á·yÍXn¶Kuäξˆ4çæ«åM1B_ÁYµß	Ahµ¿Š}ÁэÐô!92 7þÄ¡ÈcHY“~çèêÊž³±ñÛXiÈÅÚê3öÙK…¿gÿϪšÂj¹Oì)ó—)2H¨¯ µ e•B}‰@›õÞ
+2™jùÍAÕm›@e“é¬+š²(¤µ8>Û¯«‡}®•ù<€w6Ìq¤8:µwH~Ý-f#Ÿð‹/QâpV5Àýr¶Z¹½UÕõ“UZ7„A74·
+À•é?&g5bÅhñPÈËU1«*à(JobL4*8Ãà´Ðöé_µËZ–Ö	¥y[üRv>·±f›†Ì Oøº£žR¶#Ös$­ñ‰=€|É­NÀ„zBg>6Uzº~OIã"îæ/õímœ„kýž‚ÀP„Ƕ¼—ªÒÜ“ùTwQŸ6úêD«¹4ãðÂ*ù­¤·måÀ+I‘]•>#æGç)Ö[”ºžj!¸Ýßo¹ˆ‘§
㤭Ï~t…í0è‚Xüöúy`Ÿo×*NŸ]¥À»~iT½dÁžrh†¸¹nÓ——‹öæ'p²+ü+U
+ãw¡öæ—!e²ëïÚ"—ÂÏwZ³"z@@Ä	–DÔù•Wk(¸ fé íS‘+”k^¡%QS„MÇ…8WÝŽ/"ÃVßß~ɦ¸¿ZË1FR¨ÌŸÚzLâ©Vžº2ï“üF¦´Ç~2í5n¢…˜jsÜê¹PΆPsîÂN­$)hÈ¿ö“:GiKßå%`m H°‚PjjÀÝyg2Gr]+æ|ÀBqTåÀysŸíŒB–ìû.­ò15™ÍÖkÌÛÇVFéM=|û)Äå­IûßÑ5ëmã&á†$,䎘?ÓÏË”‰t'…d†¹Ç7v’âf¡{ Í•m¼ttW²µQ(¨xÑ&à{ˆô¹,B}]3°D“Ë
+yOJ’Îï¯ðÿÌ›-–,%k¬äÏà›w<{²¦é.	@¥Š1 at MÉbó×òäo›¼€.PLˆÿºÌ:1VПiÔ§Ÿùù1VYӁ±bòºRNÚ¡3}pTÚ<*¦S)Ò"¡²´FéXWŠ|æ¥Fn'Åeî
Œ©Õ£ÒtB¨FîV7ÝúŒYmƒÍ4_ÔØÉfÿ¡E#ZJr¡•X[p·$pìˆ*#`Ãç­ƒL”÷¡JdB—iS€~ÛÌ®%{}‡:	ì4:öÎ.'ßÀ;Ôöú…¹®yD¬=ätä6‹ÕW˜Ô@Š ³:åZR¥ ÄòÓèó®Ž!²Høi±›ˆ8í§s>÷%ÈÍÈlZ&:©Sf>$'ñÍÚ5ÜC![H*âÀžËº`„ËÇrRÓyq´Ø+?d#º'¨¯úqê\gLû2;dC¹ÒÔ–ßÆ2¬?›-DHý®[ÚõN  Ø_b?ð¨øv'ê¬,´}ã­¾8ؐða»Ìâc‰Êá¸ÄQwHMÁ³±0õ
’)Qlí£ÎÂl€Ÿ$–š²“,>¤õ¯ÜµÄÃíVq.®²Ù(Žåaʸ2¾„»ïÏ¿ëW,åùn‰•M„Õù#ìk×õ(c²ÁêѦIí
+ˆ(ƒ€©W¨ŸF¿ëö¡Ê^”ƒî+IZv¬iýÔ¢é~"Á¶ºÃ"8¨ÍШ”¤ZäN’{G	á-FÆÁ“4<h!u½±k­£˜tû7f˜—?PÝÁ5Án„Ý~²5ânF&,äÁÏŸï…ºó’Ø=Ëâ¡Jsû1/º à’®sÌÌ,Ë&|>øH1äczæ
+s"KlÅ´ÇBfüFð´°‚Ô¿Z«´ÌßT%æÊ‘”Noç>e<©×?Í“AA¨—ô¥‹¾DXÿB¢‘¤xÚê«-î?`ÔK]“±u![¶}Ô»Äã¦W;¢Úvþþ•ø¤X¿ÒHÃXê(Ix§'mš÷†4¦
+ƒóŠPÑpN8,TúA76óÅvhŒñ¯‰¹ªÐtÚh‡ø¾ŠzÁdšÉüoœ×éM—aîfOd‡{u[ˆê^ðš[¹áøºRBIIº­wž	SäÈ=ÏKu‹ƒÁÂpqPq%ð‹¬µ‘+J}êFNii|U1™ËÑz#ò~p+§é¼¤­véÌ's+l¢ç§2ÿ“ÿ‡[©-nßÒ_Éy·U)umÏøRá1OÐta‡?´c\°ë3!G…jäb(¤U“rNœ`•·V»À»ßKãã5Øo$k%णòÅ»JòkvÜ'Šg¤LQe!ÃëöûžOv°³ÓÛ5Qwrƾ¼Õ‡×n5¢ØÀý*.ªJO۹ߒӇ$¦3ÿ"TÓ°z ɨ„W%.º/ L‹ÂÌ7¸×oˆØ6@š­èFq‹ë%b½ìº$	!ÙÐòEŽøwPuÖË¿XÈ+Xpª2ãAÇ…¨”8ãksKr5Ü½…~lpðƒ&÷eU.Ædî-Þ§ï€ðãìkL7R=!‚0¥¥ó/F%mtL WZþ=žž˜@2LLQÉÏF–縩ƒÄþ¦W]:E
+Ìj(ØàTxò‹‰Hm돟ðæn<Õ»ºeËohc]?ˆuäžå-þ\¶é…Ô¹§–ÛÈYM,°ìÂeYi€ÏKÁ{Cú!.!p">ÒßïБßK°ŸpÉY+^©&¦9š¥ÃÑÇúö¤–™÷C3­"¤ž«BÊ\­„ÿNß©foŸ…”íq"Ûÿû¹Ho7C”ÉQD
ŽÄÔ5òUEHè̇[ø§ìL«†ë˜¹DÏR=¢½KB¥¢(šŸ|«*™Ú¯¾W–<îGQmð¼eŒµ+é2:
µö'^K¸0ÕOofzÎJ#ÊžÜ2¯"Ç‘TðW9&j‹¤_(L¤œ§ƒRRÆBäª&¿®4X3ò$•W/ÀJvþR¸;6Ð×3“‘È(ÆöA«»J@¢®Iù(d ÇªÐŸü©· –¬j–X®í7 at fz’²9ý2ט×òÙlò§–‘í³êÇúÚ;¶x²æ\Ü3<‘‚BFÍÐï5C×x43ÇÅH.š®A+Ê7¦Ã1žbY§$·ß° ¢c}4N–²z5íÇ"ûüHBå~<±l?7â,åkP$ß·YÉÓí ©µ˜'âñeaÍÕÚÕ‹Ö„ÎMÒŽÄÌùÁ”ód0·ÒëAåàUßÀÝÒ%Úré>pÈŒËîöƒÞ®<—‘°Õ¬ˆpìÖ
ï¦l
·÷Ž&	Y؁yËËÎSŠ;D~‰À x“£†×è—‚r䣵·%™Áso‹z´£·Àåõí=dJë ®}ž»iª¿H¨$Ë3tNæ›*[¼†à[œD…µ÷n‘…©Ö_é7VÁPzÕ0t˜¾~ü b„Ô´#Ûþí6ı@1)aéK½=Ûáâb§àTÅòÕé1ì‘óÇÌŠF7Ǎce2½è¯r+•’’åÕ
”–á9ór5j“Læ>ôÕ¿Ý=FÔ¿â)¹Å	8ÉWð}àƒcÿX*d¢˜ÖéĬeÚõªæ£{’J—Ë+ð½<¬›·¬H¡ëšìšû¶v¦ù]Š[[0§-ú¯P/ù5v!£»ù±©ÚˆVH ó]=‚ø®k„.9¼(—?”(Cî®é`û+}Ähöj5{µ´&„‚ùQIÜé5ÐU±‘(ý«™o¶tø¬¾¡1œOŸ³¯Úé:ŽUšÍJJ…uµºY©óBÄ7î—S(u–A¡û,ÿ×@j‘Tš­¼õ-=ņëq¥±eφÓÓ'=S<·;¸'/©»0¾ú× Ñs«ªñü½$;BëK¨FÞÊ`Qw8µ›lmFÂä]¿—SÏE/eÀlö¥ÐÈýÄx]ßÝñfÿjü·˜SåÇAáüÞ š¡çÌoç+”FÁSqÿS
+ØøÔc~í_Õý]%v–iªÃ´´L'*ª{Ÿ‘]rvB²R.äb(b¤ÑO×E+òRL5½jËGÇ'BýT–Ï4ÚRï¥w–0‹%p±ŒÉàfšˆ a¬ª=±všE×e°íŠ^ÚÕÍ2?§Þò¥Ý>Â÷l®¯Q††‰»!ß›|=Tùv› ­KAQrÌpüóÔø+¤oŸ âÍTM?}â¤ÞÄaت¥L†öÈ4ÔÅwâ¨sãT[®µìhYþä™dgÞCàØ9¸­ºo7 äNÞ[Á)8—@»@Hû¡
Ë/;ÔG„þÄ
+Ñ4þÚÚ7A_O¿ý°ÄÓ(vx ä#ä¿ÊÃ5ÍŽÿàqÁ¢OR ³8oëŸL€^hÈ€‰Cw¥®´(Ñœä<¡ÕÙ
+»uFÉå‚«¨Î/.Ú	YŸÆp˜˜ÕÙõ —ZK¿ÂÉ­V@CöDÀܞÙS—Lô®7Yáë_ÙAlƒ®¶Ý`!Ú’äãv­´Û[¹²oh!‡²ÄÊc2·’Ñ#©iŠl‚~)Øq-J©ØÒy•úÖù˜üZ%礖æÿãp
+·?¤
†Ð»[^FÎîã(w^´<äõ+endstream
+endobj
+20 0 obj
+<<
+/Type/FontDescriptor
+/CapHeight 850
+/Ascent 850
+/Descent -200
+/FontBBox[-27 -250 1122 750]
+/FontName/GCOOBE+CMR7
+/ItalicAngle 0
+/StemV 79
+/FontFile 19 0 R
+/Flags 4
+>>
+endobj
+19 0 obj
+<<
+/Filter[/FlateDecode]
+/Length1 845
+/Length2 2042
+/Length3 533
+/Length 2661
+>>
+stream
+xÚí’y<ÔëÇ•DƒJv•~YJd6Ƙ!ːB4¶™&³hf,CÆÉš
+¡$œÒ)-¶dÉ5¶ÂQJŠÊYêDö¥tFÝsϹûÏ}Ýÿîëþž~Ÿï÷ó|Ÿ÷ëó<Ú[ñ.ú–d†hà³õP°rpF(¢­mʼnl
+ƒnMdƒX+Èæ@À’Jœ—·°+[F²¥û2+íÊ	üçÔeó7øS‹ÂaRBO8GˆŒ¢õÇŸ×gí¦“d
+]ô$PF+ÀØ!Œ?5B¤ý™à_H+ýíK]†‚$HV¶"e…÷ÎÍzÓG¦ð5V·¿ÀCjsË@Íä(=y4.M•€v
ÀOuË.¼ÇŸ¹0§³Åü䮃ÉÙ3„G­ WI׷힧`[˜8¤ÿT¾Üc%ìþGðL•·g©^*œ ±®o‚œÃ¬ûÔ´Uõî"ÿ!·®‡\à¡W×Ò]:pj°FKâï±OÕûÙ{¦x5ý±ÙC^…SŒ” EÄ.¹MZè»Ev–íõ?dÁ¼Dטª[+ÕŒ?\—ÌÁ§M¸kÿSb×ú÷i4ì#³ƒÑ峃4#ì¡œm¹ÄLTÄÖÆ¡ðÑ
ê%ökVF¨¦)&½IÙa1²éÙ'ŽF©{l‚u‹%}'s~h•KE"¡wƒÉvì…Œ”üwZSWON&på‹wíÓŸ	i([²Øúæc¯žž–6íŒ(×ÌZœ-9§¡)8ù$ïy¹ÌñM˥ª(íbÐhÿZõ*œ¶æŠ;(5ÜFÖ•«Ï´I!Ø¥¢$³KT5xõÂ#eñ×\V~ÑøpÆ;Ÿ4hOˆq°Wn…}E7gѺt;vhðö­AÎ:Ìpª<åtè«BýöÑ[<™áÛÆ׊œ¥!ZžÒý™!/ZŒ†\d;*eomËXÐ	\pèÆ›é+—òa%K5k&JnË誤Š_ªS½~vMüÅ›îÂÎtD-EûŠëS⌊¤ÝË+Üõ¢#Oêaß½¦&×¹‹9ª­§L*cžM™r™ÑP|qd!ž£ÝqÊt5ØcaGoÔÙñÔD¬bì(¡‹@½í¬²©§æôB2ãRüRçN¹˜kf“ýeÊhµTðð™FVû«ÏÖ#Ùƒ¹Î‚·,Lõ‚l|…Œ]‘¸ÉÄŠ
?ÓŠïÔ¸ö÷‡ÖÈ¿?F¾}µöIH­ÒŠ±WªÂ‚襁Þ!Aë‹Y:-y©–Jóƒ	¯ïšŽÔ'ÿrÖDÀ#ãµG¾Ô*”£‹§ô Ù¡Å›pïv:Å#ËÊ¥Nd;Œªü”1åÎ
+iò¨Zë±Ùùò¾±¶[fïo¡‹Wm•8y¬–q¬ ö¬¾¸`Ää¿	¤f»éEÐ,êÐíÑSOœòš“¸âq¤´Ìp|tFëªDðvš–J6hã
?÷‚7mt¸ÜÆzŒiìZ÷f*ŸÛ£yëg6i>ÅÈîeWÄÒåã
+ö•iÂül½&°ííè¾¹!6'[߇k_g-ußÁÍ-Æ‚ ?¾âfߎ$™}õT··_c‹zµ-}óüp[ýñÖ€&êÅk%¦•åÁãŠñΛôTˆ?ò%eE›g°pÃ×çÇ#ïNýò䫍ÜA”¥Âó;éEè$¹æä;&åžµ›9¶ÒÅv€_NÓÁ¸Ú¦=W0jV̨n&ܰݾß3£!	Y…
+7Jè@ĬBZÒe´ÇúFË|',ä›o«*áüzß8ï]1wè£Â³ÓÛ.ÄœîæÿnSîc¥mà|>7«OA¶©r_d±Y¼xrÙã}Ýü)Ÿ÷m8']kéêáùWµ¹™iÕ7&Õ¬ÃúíÔnÇÍÙ%ô¥'L;«©ß#ŽrU0QKšª9í+–©W4E’j0Hó1ԝ³uT×êT)ÏœâE*Á7p­GÅ?¤kÉɨWÌÉúÏT‰¥%Ö	[zÉ
³Ù¯¸)˜l­#¡«iìã$E\9Þ-t£¹I}qîÉ
+é%7=#0Æ^}£é)Õk…PGÛÝ؉©stV‹³‰Ò¼Âƒâº’hï,‡­È¯Ñ-¾¸”Ÿ'ÓöØ»Š·š÷:Û­W¨f^ܤ~­¿Wsbz×DÍ÷VÓ¬ïtÞRpÿìdƧ¬>åóZ™a?­5”ySo©å^éÙMûåfuëÒ^T±ç+Ìu5üš–¸J{ŸyÃôÌõ¸ðîÞ/õhž¹:øaæðq‰¤¹—ÊpEÞŠs{äR¯?]õåcž~ât·ÕÍŽá«™û'$wmY¿·bó¦„Vf—‰¼£ðy¢LT²@ÝG_§Øšƒ{ÉûÔŒž×y!”ýœÂOÁÏ¨QL¹ƒŠ+†Ð‚ÏÜ6_‰
(„ ¶/8˜„ZÑ#ò&ÏÃÿËòÿÿHTÈd3hDf+endstream
+endobj
+23 0 obj
+<<
+/Type/FontDescriptor
+/CapHeight 850
+/Ascent 850
+/Descent -200
+/FontBBox[-20 -250 1193 750]
+/FontName/DMIVYN+CMR6
+/ItalicAngle 0
+/StemV 83
+/FontFile 22 0 R
+/Flags 4
+>>
+endobj
+22 0 obj
+<<
+/Filter[/FlateDecode]
+/Length1 845
+/Length2 2056
+/Length3 533
+/Length 2671
+>>
+stream
+xÚí’{8TûÇÝF5%9DB‘[æ†\wî·&w™3³5cŒ1C(‘¹%ŠP±©%j#¢Ø
+‘FIR¹ÄP[.gèì³÷iŸÎsþ;ÏYëŸõý¾ßõþ>Ïû{•]4M‰_ЊÑ5Ñ´`nç|+‡+æ*‹Fò ªæjë)]À4¤‘x°ÃÓÀ ^ž¸P$ÎB+šiõØüˆ­ó¸§÷äÛÜUìÁ•QÅÐÎÝ9‹ÉuÇŒ;§LO‘A·üI±nãðwƒ6"ºŠb%†úy›a[È–å%:l}N3@æŸOG¸<GV%‹
§þfò,ó3ÄÚS·øø†ä§µlS}Ôˆ³:–ü9ÇÌš}Ú¦¹W¶[Ž¤ö¿¯ìî;ü+F…¶©|³˜rÝÞn"¼úùiùœûê“¢þÅFÉžjY>Ñä…ºT¶â£é…D™^É	ïôi:rLÂ`„pjŠÕÌ·(¸Ê‡Â§q¶U±c–œïŠ{D"¤) Û(B½C 5¡ï>C*ðêì+ÒÍ;îÝ4(X[ÉMïc¸È\ÎËRw…$
ºUs°öS-›¹ÐT¥OVË÷§:ÂE®[	J«Œ<-<,w5M\iI*£kmטhŠh at M<¡î6¶¿c¸äSëöÑZúZN©÷õÛZC#×cOÉ4n…­dÀâ®I>Ì^N4nšß
U?¼§µƒ}ôDªHvŽÖÝàŸƒÂh%z¨ÿòÿ¿ÁÿDÄÓè” <-ÿ;ü|ví
+endstream
+endobj
+26 0 obj
+<<
+/Type/FontDescriptor
+/CapHeight 850
+/Ascent 850
+/Descent -200
+/FontBBox[-36 -250 1070 750]
+/FontName/MAMUVX+CMR8
+/ItalicAngle 0
+/StemV 76
+/FontFile 25 0 R
+/Flags 4
+>>
+endobj
+25 0 obj
+<<
+/Filter[/FlateDecode]
+/Length1 1289
+/Length2 6434
+/Length3 533
+/Length 7245
+>>
+stream
+xÚí”e\TíרiAºkŽa†.‘º¥†¡†RR¥»»Qîn	QZARD@)¥„w|ž÷ÿèyÞóåüηó;³¿Ìµîµ×ºö}¯½9Xuõòöî¶0ew7PP at P
++€	98 H…pwS„¢`R+}+¡7èDûÀÿ@tSÇ?ý\ˆ?-áü¢-\þ@´†ëoDk¸ýh
÷?­áñ¢û"ÿ@t_¯?}¨?­áý¢5|þ@´†ïoB÷õÿÿçP+(¸û=
+‹€B¢à_ˆ‹‚ÿ×DC7ú¼Õ¢`0X\òﱶóF¢Çõ×WýÂü‡è×óƒÙæäR!0­ù™øNmNzgï¿œ˜Ó%ìz¶Ÿ)ú½zjôÛm\y§ˆ0gÝ“â‹/º±gÜ,²‘õAZÒ‰½ß­üýé«ÞSó:Œwš¯ŸJUÇ}¾¡h6ÃõÀbÛ­Íù
+EYá’|8²/Dyt±Òµ\ÎŽu/ÚW™ñu-4nFou`HÙÍ*ùj$óåY¯Ãs›„Ø›QÐú×	Qº>»6<–¿mRu4i(m.|-Àp²^ZøIUõžô”åfÀ'¡Tz@•GY‰yd3„ŠŽ	‡­	Ïâ!„'"%Öy¿ZYmןãf Q
+"Öîh(É+í‰=ܱxÑÅnù¶ýŒÀØd¤l|?éZ÷f4ÛhÚïãôú‘m‘˜\¡+ͦöF¸‹ˆç¥Éõª™´/
Œ‘ºkž3Pò™¶§§7°k[ª/§©‹ñ³Uó5\ò6AVþ»yœç(È©å$MÕbzßéG¶±­¢Ø`6©ñð‰ù	
+	úOÚªjV#z¸’ÁÑqã÷Ûz+ç8bD8)2›ïÀGê_W#P¼±9„‹U¾£ÒjÂ}¹—_…lÒÕbM¯€À2!£lï7z0QŒ†# oKQõ
+°HCæÇ+ûN™ô_=“«Àm…Ò±–¥ø{ê!DÑÕ£óVª¯¶XÅ?Z`Ã-_(/]Í	Xؘ1øe¶½‘c䯢Ǽ[<ZVñ1ýÖÑÐQrJaN–©dò“î(\J½¨Ç­¾Ë™àmqfë`9`ãUºÎX¾3S~ÝË+Éîì« ΆRf8'Cãò¨‰â'÷ŠJýûkÎã*¡+†X-ñn}»PÒS!¼)ÆëR:c,»†Ð®Dw!³mô”Š“P¿ã^[ᯬ¬p
+Õìq…/Ž\‚Í4»m¡‰IŽÎîPG†‹³Ô8gx}gQÜ1–C¿jݪò„ò³A‹¤yĵ¥ŸåžüÔg*¯Û1ŸÙ>³6¼XØ
&®fr+X Úò|uV%ø2KÚ–®¼áƒ‚ÚeÄ9Ë­2ëöÄpõû`‡ ±æLb°´)ÉË%Ã>u!+#pc‰äupY¢ZjÛØR&Èq£Ì|‘Û&,/D¯{.®p[£¢7`
+sHõÇpúÏ½¾ÚNâ&Ö¡¢;Šío@o(¥¡¸û©ã><$óî/Î0ΝÕÔ/;GºoZxKÜ]a¦VUnŒ¶)e‰s›Ý öy¸RJ"×’–õŒð|M‘uÞ×ÙeLÊ1ìDÖ¾Ù$Xk“i> ?~–ÍÚ˹Tñåºò0
+¢†er»B•g¯^±´ÑžõΉ`æìë
+e-Û,Kn“NÖVC/ô<÷±*¦Bô&9Úµ,uÑ=ÏkÃÒg‚¹iq!¤€KDر]åý`Í¥–I°Á\l…³R^&Ï2føÎf4û"Äqg­¦2ãû>z(J½»sØŒáÏ«±›1¥pŽ›J2ø-!]Üq
+ã^JfEx+o•A?7ËÆRâÇQ1#1&AxˆŸ\
/ä’Í¡(aeÛiH_HðýÑfÙÊå©í]›Ú#NïdŽÅ·&’ÕõÞû›¥Æ£¢¾<ñµ@ímÝ48RÁO{ïÕÙË$…KîX|lu⺗!c(ó¹oa˜¦³­©ª–j—Y9™wV\¼U_÷óˆ?aÙbÀ.+Øpta.4î [NöÄ‹€ZÑyÆ·I¥È°vFvtΝ’‘EGùðî*."ô"bc–
£ÕºJúuCȪg¿Ü=¨¶Ÿ‰o›BüÞ6~ÏcQzU%µÎ>ô‚ŽË<
+«wÅö'¬gsIñ¹fWùD]·]x;Šçy<Œ#O´Þlã>fábÖ×t«/öý¨‰oä)HÊ¡”ÙH/……t™— öGâ›ÝæG\>Æ.Œòªé;ú!|«Uîç—/,-ô:”ðõFÞ¯àx½Ë*˜a艰¼d6áòƒ¼º>†ÆñE²¡BÎ)S;Á’þp©#XvRlSÜB*›è§ëÙ3å*j£º7~:‘É+TÚ±=Çz‚©‡ðöÚ¨×ÕëÛ×/CP¬µç¢YÝ¥fkªR,‘T<EëÒŠ¶×UváHšFòàºû®Àl‡|ÃêT_ü°žFbu'ÝL±“䄨
+àmj-²ÓÕ„)êžÑõ¹ÃÄðá³Üoî{¾•ð&VÝÞQÈ%}+¤á›OòMé¢Jï€u{'q¦cS®kGÙ¹<ý©®JG˜ÑTkÔÚ'z`†dš{NÁ^
+D©“^´N0<~¼þ¹ØrY‘°ìt˜}ºx3A’à§ßµÉdÏtÉÉ|d¤1WÏܶÍ!Âs>G\NµÛϺ·xÍ:ÌþÃÛ4ÎŒ¡rœ++ at .·á1À¿Pûµ¯ÃvÇÇ–Õés÷´I‡©cíº”¸·V“»Á癘´-ß²”m£V58ßÈÛJ…®Éüùͧ7|JZf<¾D¦Y¦ëð‰orlCÏŽÁ—â^·‹æžµÔ|’ÓÌǐŸØž,<9Yc,™ "òH{•,¡ßKµ/’›Cú3©ÿ€“’<Q}b5–×$Z5öB"z#¡VN¼¤Œmµôö]{Þ£äŸ+Oktat¢Í­”ÑüÐ=°x§éÐÓûð¢H‚gf,ëV—5¬5Û‡ÈOÒ’¾û¶þ˜̽oUìE½³°‚(‘êFcV†¾(!V§³‘QlºÊQ©Àã¿Sv»©W¨å‚¬»ý­¸Wd!ÄL¥=Yëâš›pUããÅ£ÅÒrЧûj9.¦8Kô’æ·|7¬ÚLåÇ7	JìpÙ(µR;A‚;ÒÅf6knb>R=h˜k·Zò%Èo)‘éÈí‹âñ²­¶l¸¥éõ"ŸÆSëµ#樗:ÙR™'RAxMmúS[Ùß¿¦)m¨°-æZóI`úÊXÊïîš	è‡
+Câo9ê/(RÁemÃN÷î??¦Íp|W%‹¹†µ°yÐÐÓíĈÙÆ= ¥qXg6÷Áñü&_5j¢dDŒcS ”µ`‹)K¨ÍÞ}¹Z=J³g÷…[:i%r,z[e…®°5µQþnþJѽqì/iåØ”Ùx95‡Mõ9OàÌÛ·—WŽë$¼¯WÚý
†«‡‘¾`¶$lñ<;6"'PU‘Kio)叏ÓÕoµmç6üMĬ+i_aÑ,Šã´°¡(Š¬3®ÃÊ€ÏX3êy=9¾R*/‚H‡n‹fR……«h×^¹büš½‚XñûÆVdw§„¯Â±íý^Úƒ›XàZ_ïtMîª8t£*ð|$Ùʇš5F¹”µèÅ,õ·ÕLB–é
qށˆF%«¼z@‰Aç[ísçIçÖ³¹k÷-•­µâ7Îódšôƒóø;ZÓô\¥ÍXàÄ6ÚêÔáj^|¶O»J8¡ð‰fM]B„àøØýÃÑaPªôÊ—½QyÉpP	Q£%.œý²¬’€+ª½ä¼Ó._X½A„R´.µÈR¦~Ͳé”ìρXá’àyÞïOƒ—ž³ÍáËýøhœÓ¿½ecÙê™ZaHÊ¢ÇUx·B01µô½…þSùÕ—«|L§¸¤Ï‡[8ÎP÷Mj‹.ÎwŒSÀ¯V°„”wŒ½…
1FQ¡|ò3£-vÖV‡”݁¥*º?î_–çU¿ôűR]f)“¶#ÉÌÓNì³lœç1?ùÚ~!ÆT˜ûcððòŽ(Y ÁQÿÊ`;ð
àë¨Ûî´FóJ2Ã4í¬þ8Çé:È¡
+AIUí:3"‘sýT›U÷NCˆÀù>ŒöÄ €?hᵤy	M…¥¡j–j?AêHJÕt¹Ï<çœ"·÷è7Ä	NûbtǃâæZ3¡êÏ—&‚y¦8}zŽŽúeȦUµ²,ô‰0æì%„^Öa‘˜?;³ÞrêmÑ«ºëôx¡»4ieåÚÔgNáÜøS+ÿ½ñÍ{‘ÎotÖ±Á\Å}ÇúÚOæ›r¨.^_½SÊ	dÛŠÔ"¡QbeÖ‰žÎöm‚:ÛšªIÌ+P&Qã¢üøfX6žLŒÑ	²Ê%'çÓèNŸœ¡”
ÏG
xZ‚F¿*—/Þ3Zæ/o¹ôiMé6åã
‘ì Ô{ì/GÉ°叉»·ˆËæ•0[”m;pIQõá¾N³õ—p•ß›Ã¥†Þ f1¾’âŸCò¥ß„¨6¢{#¿['
+'éŽíá%(ª¿ _>ä'q3Ü6V郮Ã.zàçmÊÂ¥´3»ØühIGŠ%Ê/pÆçN?;**,4Y™Ô{¬KÔÖ]eÒãá:µ`ûÄ]-ñ žFÞŒ),™£¿eàð
+ÚXùÞ2(u¬öédrä7,WÖ3ñQ¢¸)ñ3MÁR̤»½~äw?µâôy¨–—lDŠ¿ê"Oíú©qªÒc-y^É•^GD³´/:ˆ…%ÌйêcFecLÝUA<ã¾¹kG‡ {×ÐÈ;Ži½·~8Vx at 6Ôù†!uñÀ0®²=òàµÚ:ÞØœØJž‚Ô®2”Û/ao¢FUôÔqn}Ü5ÞÓN
+Êy‘¥ù‚ªÒ¹PwôF¡5ÅÀ¸E¯âÕ›­bG—OÃÛœ<\‚>™êÛ¾•s&Ϩ|‚½z]6=Š¥0 ‹õ¶e|GÙ)™ý©Æº¦gɝ:s!#RivTÿ§àô’[Ú®q$žÀ3é$néWðEîôÃÊ7ô"Ð’qÙ4Ú×äåkŒû­Á;•.úÚ̏Òšà (çꊓÖµ^åç?ÌVi’á+®º‘¥40%0°,¼Sqöù¾™g&EèÞCß·úeŠÑÅ^ÚABR0ã4FH\샡­Ö,Î[+žN,G¬+å“<_Uô®i1ý6š7§wdöq˜Ÿ˜6‰œ4䔪׹eÝ$9{yì|Šÿ„|å“¡ù`ïò­Ë,®ÜTXAðË‹–’Pw+*ԏȬ“†\¿ûúA¶µÄ•”øÕbi“ E)؏8êRºûT…‰Wj2%ÛZmâ;±yÞ…™?†ÂÔEÂà-rça_<µou‹t¾Y¤XSš\מN)Ûù½ø©3‡Â5UÂ…ÁÈ­njþÖ’@=aÄÓwÓ–˜¥‰E:/çY|ç‡Ôᝧvy)uµQò–”úR1k&˜4W;Kí‘‚‚Ø&›·hË´
+Žc#jwñ1û„²¸éƒÉwíŒòÓÎ`†ŒÅïR•}û@õ¥yÏß›«I¯¯%†ªJöxèuÁ2]SÇ7¨4^7>H”#ÆÊ©*ðÀÈÜŠom=¤tcÄ–+‰<8“Í­:5[°õ .Å«ò&à}É P«øp&Òb_
Ëy
tMìè2s䳿ÅÛu²ìQ>LÌ[óe¹¿Û»ã¤Ù_æ+uDl>.U|rÑò¤ž…*¹k‡[´"^M¼5×y6¾#«kÄC^~­qUÚÅ4n½×¸ØÒƒ&XCr–8_®~’–´ÉŒT:n^;>þH$éokcSêOÛL™×l83×ä‰ó´AIa‡µ_“þëÕÝpd_5Æq02ÙÙ$IK»šv¤%B.A]Õž—”:LwLžkQfc.më[s’'ûuõȍïµ[fV›i3-h…›Q¯oþ$¨>NlÒÇ‘%H½9¼.)»ËdU¢— ¼wJMVÏ¥´ÅÂ.R¶öx›8ºLØz04³|r
¸¼*á·ŸöQ¥Ó¯ˆÐ1VçC>“ÀRY”0îÂz
Ý„ž
¼¸“[o’Æ´–˜÷€Çj+gR˜ÅqK_êÒ5 dŠŒQW?¬¤¾lv[CTÉ«?8Åtνf9ñééZFˆ›%QÞ‘Âtã'«âÚ§©c%¦ÄŸãoïx„_éUUG—Í-ÚIùúQ|õYéЫ¬qá°#+‰ㆀ{ÏC-~ˆÈ	âKÝkn=!Ю5‹§ÿÑ=|{~¸”æÛà¿`wÓòtùL9\…ÃÍÖbÏeРôY*têá‹™æ6q—sŠ5W8éfïJ§rc"γŒ\!íÛ @“®0Ýe¢•!Ò°TV.‡Zló–}¿_/SOd•°Ž%Í…%v¥:qÀ˝ã^ñ‰3
+HMƵÉØs§`~é1e+îâÖyƒò‚ûb¡Cqó'È·½ î+[µ›ôOÛÌù´r¨´ö‡½z,E9V<!wãÕNÇô‡Þ	Õɘ)0wðÿåðÿø¢€ŠD¹»B‘΄„ÿ`‹9
+endstream
+endobj
+35 0 obj
+<<
+/Type/FontDescriptor
+/CapHeight 850
+/Ascent 850
+/Descent -200
+/FontBBox[-4 -235 731 800]
+/FontName/PQEIII+CMTT10
+/ItalicAngle 0
+/StemV 69
+/FontFile 34 0 R
+/Flags 4
+>>
+endobj
+34 0 obj
+<<
+/Filter[/FlateDecode]
+/Length1 1874
+/Length2 12070
+/Length3 533
+/Length 13138
+>>
+stream
+xÚí·UXÜͶ¸‰»ChÜÝ	îîîÖ—ÆÝÝÝ%Xp÷àîÁ58	$¸3ýí}ÎNÎùÏÍ<s7Ï+æ@ýÿžªü·œÐÜÊÕîÊ¸˜ØZ™‰Ø²Xþ²r–´ò+ôþshÿB°•Ë_¶rýÁVn!ØÊý²­<þB°•ç_Ö0™˜ÿÇõÀÊÆù_á?÷Ãÿy1ŠŠ:xx3r+¨ü:¯˜‚OgûÍ@Ëj(—F^âÊàð!P",5™J<Âç؏¥^Ó­œ’»?—¯u+gyHÓ0.ݝ߳t‡>cö¸‰4‹¬
+qì0±:tcŠlùžµêW
þ ÙÕΪ1pžzÑŠ×}:ãXYÿxÛJi¢l3p¼^øE7ì]RK"›µám( ͺG蕐|<ä/çãÌÈöCºZFÜQ·û©a…£ÙËÿâ…<†.w=˜µú:}šñ´ÇËkÅJEá£ôÞ¶nA@ÎMNbeË`s•)+&‚(œ.²B–e”¨Ô5ßéo’CŽ¯í~éÒ“U'+Ë	l6¼íj뿼õ`»OÎbg*Qž–Q0S.“õJ¤*ï·†ÆG|U{1kCzQ³Í2˜Ë9Æ‰í¤è…æ;mÌê\–>c
+‰8žpMëò|aZìJY!hZ»ˆëýÑi¥I¶M÷kBÑNþ¤&_bšøfÄÌE8Õ.•)dÁÙ²!C‘¹¡…M¸OvLŠZƒÝ­¥öWÛŸP°•‹Y¼ÜÆ…¥ƒéGª™Ž¬ÍËg7çG/ÍÒ+3ŸûìL†áT|HG%ߌºE
++žw´TžTþ>ÇÌ2Ù½},:{g­
—ûà9Ö|\D5TdcÞË.à-,¹Ä%ITxîhÃ]®öàP³De?E­v2ÐÜ;Ù:€7Ȫo¯N- Y64éwò“²G§@HÖYpòÎx"™íóq-Tõ7’Ê iWëÄ:rµæîâ‚Ù¿©‘¥º÷%ráŸè´–ƒ‡ívf‰‘p;¯(Y¬ÈFXfi8ܽ†]ÑC±?¸×Ý|4µàŸÿ—£þˆ ß9Í­dÔ°Kù“a‡)Æ“YϏ\2b®"ÞÔKÒ‘=ÏÜ,ó8ETÔìGs¾€I±¸Œ	q¹‡.àéûGÒ¬9I°íƒöd±ÈÒnI
Ë#Þf†du>¹±¼g6nyºçóÄgqäΑ@ÜC­¦Æ„M4‹$l7Œ¦Ä$Ð{⯤F”îGcÖ3þÌ’.èûtC$?Êñlùñ5›9ß>,É•à¨)l8÷©?-=4½$`<klÑqÆìZ8B_i4þ0ôJi¦à˜C“§*k±5À¿fŽÊ²êÊ›ÈE;”õõN.÷"ûlcl]\'…â žÈPs*ÁÍæÁþˆý¬ÿádwE¬Ã€á—ïÇtF¶
+‚dzWTÛQ^ç§ötƐOHCãC3ÍúÛ¤)ÉÓ´5	®°Ñuž?Gg\a>¾8G&ö´!ï‡-—>ÞçŸVãТG„œ<†ú‘õ“½Gi™‘Þh¡QĆ 9+t¥³ªcŸƒ¨Îô>˜ÝìÆþæñqäAa»ß¼w¥€øØÀå’+êeÏôÒ%»m%VÞ°_` _Rô{Ϋ¨¸m-Ú²é	x	¾"Ÿß‹²79£¢ÔºWpU‚ôI@”X_ü®ÓdsâKÒ½áè™;È)òa÷÷>¥zñ„ðw¾ªFԽ–ä0(Ô{œM7©,%­Œ:û¡Y¶*t<Â]LSÅô{D[$²Ÿ
¸	¹åxGÈbú1á³Ð‚ÂÍ6Jï\u£zñƒÝ`Öó”Þ+ôŸàk]ü¸vŸ†+8ñ€²™mpÃ`°4}g+ƒÖ¯ó¦yÏ'º²õC(
Ë'ª	ÚïõŽâŸ¦úWSYqsq€Ž.]m2YÜ–‚¦s²ÉRU²G0ÿ¥!D¿ÀàEê@<ØÔ–ù¤éÀÖ£¡Ú:›­Fê‰Ð#÷w΂ã8ı¼žn¦Úuƒízô2‹ˆ{½*–~­°°d>ïmBT,S…‚†Ìfÿ}£^ò1Qn‚ËüµuGöv-×°Jö(ʧ(–dSf€žÂÏZh¿ÜŽÞ‘5֐åHÒKD0æ
+.gÑ6ZŒÐTìÅîVf¿)@%å—oQñZ_‡ù3;xÇDîû–L°U*ÎÙš/$J¶Ûi!²i‘J¶¢g­X‹R+(‡»&Š×ç_d·v;%±ŠGEìUÏû>³#íÞ¦‰CíkŸ’îp—ÜOxNò>Ë;îÀ£‚¤éá½_‰­A†{‰­ö´£Üm_à`p–J·fÒ+!£é%9íA™â‹
ßÁ”3ÉòÚÓ7-IƁ˜ð­@Í#6‰f1UÐȃï8½ohm5¹Â®L%ó6:··Y¿þî2ˆ$Oˆ¤*µ"ÎÁ©éƒ©(t‡È%Š¯ÊA¨!*1ðõC€¡ÚÈôñåpÂøÓŽ¯–vn]Fž”eÁg4Qÿ6,£ºn¦ò^+¦¢Àßò]ØÏã	ËÏ·é1+1§ü µ7‹9É4R½0K{ÏŠÆ„ü[îGÌÅÏ©iZÚ͐óf„ÄìlÛò'怖åB÷wÈ~:ƒŒ—<ýBÖýŽ}ÙT„Y(t(PÔ$ˆo¯\X’Ü®åú›8ã‹Ë‡Õí™ÐåP1'ÄÍòHI©Måì§g'f¼Ÿ8í3mHoóéž’NHÁ_¦îts–0ì~7;ï>V[;6f@~„ÿåÊ£4SŸ!ã+ë¡l\#—l z‰VƒH'¶
+É!]1˜í)Ëæ<²¨ÝÝÃ<‹>UÕÇÜ2¬A¹+z>@ÔÚêå"°QrõEZ”Ë¯?…´o„êjֻʣׅ.å·þ¹…+HPÐ¥éßž= ȾNºÃ¼°zŸsèuªÇw@>4"DÔŠ² švÀ­´é%RW¤Ö¹…[®g|#ڝ|S
WýN8ïªòÝ`oÂ/`CI/†ÖÓßÛ@$Ь`…@‰ö.‹àH™¾`u„àÎàWO.EPÏ –ð†XÏBÚ0ê™Ì‚Sñ;ùãz}”»Z…]¦5¼|q¶gl8Š¡WÎÅû¼%ÆÑ»+jRÞ½ÖÎi/"§íö¤Ä†£!.×QØŶÓ•Dr¦[AëŸHzÉ+¯jÏ\äL¾8{—zjË€b,,Ž×ÆûïQ2RՐ0L<åtúvä]–.dÁ
+–÷;åߟ"‚S¢¥6"]¾ö£ÊK~oÌÏ‚´~ÏS`ô“d‡<2 ·áH¿¥ž¿Ðr]éð^“V%Ùû4À8Í›òã˜gÔ%ÿ~øzˆ¨Š“¬#„òM™›zzc褌.+‰iJ|®lm[îD6l¯]e!C€ømú
+ù^J=ªHµòw4mІÞB÷?ýǧÚ[óóTP®õº)yÎëy	Ô3¶[Üï[vGêk?B©Hü¹¢÷K•Åz¼bdÖû’W³¡rg™1WqŒf¬#’Ä‘©¼¢÷V×v.<Ýù;÷·–hã‹ØFä•°vµ	’P)’'š_…¾Eúb2JFD¡‡6Ì«@¡à¶Ý$˜§œ–‰@±J]WÍÊøXq7Œ|‚b– S²2ØgÒÒþ„ó	II·’?ÜÁÙ^cQå’Ô;–žÊ`ªk#áAY÷k&wáõ>œë—‹A/=[EÁìmª¤>[+^+_¢Ï`òÄ(™Ô”¦ÂgÓ’"ÂÆéæÕ*âñ]ddÜK³p¿ÏÁÓ8J%ˆv(é®ÄW'íŠ5¡«`¦ó;SO&ð¯¼:>âiÖ!#7Ó ÚZu´p=“šß3`)öća?fVÙJa¤)6…>›½ðÒ¡ˆ;
+ÝNíÕ+c
ÓÅŒW”VÑÎäFzÉën|&!áëÜ>þñ%,ՏS“ÉðBdØavƒÁðœŠ);Æl{Œ!
åkÃJWƒE470ýᾶ'›ä*ÃZœ5õ¬…6;‘#{<ÞpÐ!ÞKÞ3­Çvd*Ëþ1Ø&þ¦œÉ2h´Å§¨ƒl;]W8Zí\Zž¼ŲٌgsÞo‡Dô,u´†§õ¨joÖ„žƒ{¨Í	t¿wÐ]Ø“ {ZÙ”]š8ù4&àÃq×&aÛç¤Ãe¾w®ó¤ÊÀtÒHÄM³-¢3Ó‡DͨubºŠÆªÍ<KDs¨”“[K‰NùÚR@ÙIJ|Þ;1N4pmLä,/ä*/°ª/þ)€ÿƒ³^–l7©à‹=,©W~õqÄž|+8ŸêŽÃ_ÿ­ËÚOýÔ±ÜwŸ0÷Ùdæç¶\<m†"ö>ÜÉ»S8šwI‰%öÅ‘ïßun¼FýÖÜ>z63ô‹Û¶[~û¢Ê[Y
+‰ËûàÖì0éÉõ—^ÎC›äL•©†Ÿð§îY¶;bo«´œØ¤œþ"és®
?…©`±{³¹þÍR«€¦†Ü/ÖÂ1=Š<ï‘
_&РQ¢¥VTÇà?'O±<²Ú´G¼Z¥¿;Ï´51Tz+s´ºÁÖèn"Yèno†×àâF¦Ò>ìPX»#\¼“m÷Ív S.3Ȧ¥ÐúŽšŠÉÍž3Èø»
+÷³¬Q¦qhÑ\?/üg6Ôâ+tE²CY<4Z̤_Xй½¤·é-`*{¤®Óü*ûîbëõ¸²ÄìŽwNHZ¶OìѸU>ðÂ5ÖÛWrM.8 þÒÆX(«üÀâ Ãìï5;J”èµFdÁ¡»ÀàHÑQ,Q}ƒˆ\ìÖg1á¨ìßÖ|jqÎaòÓ¬ç:(v»Q’V¶õŠƒÝ«úÆåaM3 ïv_jáNqé!Ù¼p“ÒÜ7<ã´i~
+[#Ž2Hã-¥é×/³MånX-±tìc’¯„•[Úߌá ±7åLë;ò©%­SE•é8_ôÏÕ¨µCQœ½ <"U~rä«Ðœ1øýÆ‘ ¯?,XºXÎ	ÖGBÊXuH}’¾=Õ=
O_`·mYÄXT™åIÒ®j¢«yš:FQÜ5{Ó¼
¢õ¤í­ÅñÊêEeÔÐ1J}ôE´l—T¢ ;ôÍß^%g3?ãõ`À9šmåñ}‡=¼KpŠÄÅ>=؍³„š4ÒSTUºÐRçO%2‘¬+RÍýB1DËöÕ3ÛIôµ<Iâ‹ô¥ûï
vjxþÇ"6€kš¢’/+,ÐJ®Å]ÇÂëá¡Ð×éä’DÍnа˜ÚÞV8Zº}Ùl+¯u5ŠV	/ÜF]‚¬x—ÝË÷»,òs
Áï}§zP#4æÑÉùnŠ¶Ñ#Ϊa¹ý?ž4Š(Q9Çnº›gæi¸¥Ž’9WwBÀ㏠=¬>wN ¨JEÄKæ§r1ž/.œãä¿~~y`².!ÚpBÓ?¨Ç|/N.ÓôCØ-+ôÛI»4ÖÓ¡óøQ}€žè¨Yn?ËZ.ž  pyì‘W½/ÿ̻޲t'ƒÜ]µn©ðXý…ÑGSb¥‰Ùnºud½Ì£?r¸»´1¶)î@â“@ë$¢f„×Ö	\¹ÍŸÞ(£‰È¹T6¤Ü§ÒjºÀ„;w7~É–Bo »ZÏI¤Wìù¹ $(Wpa“üœòY¡¦9ü¡ê¼JƒR‹žùü>»¨®§ÝôãW,"JQl³”òqÁå"k¤˜|’•9²3ùzÎHJGŒƒIF“=ùTÑÌiö€Ø¼æùüƒ´+ìŸÉ2͉=<ŸtñÕ¸4_ŒÑÔnù¿tÁg)ï!úmj‰£ÎÌI°î/Œ^IÐH–ÝU(FÛ+·7ùß”ÒnÙÞ–·šxü̃jêW%Ò (üj Y
+„à¤|ÎÐöÖKÄöÙB™áõE©8|èšxµ-Œý°”0¦›9W³È *TÔFԁ!‘N‹¹ËÛgd	»±‹b{¿ŽZ2»i¢a²œ†€ù’1ìŸÄPÅŒ~ñb¯Ó7:åDªë*éÐã‘tLóä=
+Vø¥zHë¶s!TMD‰„è=tIpï7D¿s¢Ì*´¶¡•~·SEÞUDˆÈ–¨ÓˆX5Óüêè™Qò`¶EX§ÄF.O“5øPØ­PþpDúÄêè^ÿ#ÀêºÇSê7vúh}ÿvÿi©¡g›Í¦³@±"n?_Ú<O½û姉å{Z)ôðv¶‹Œ´âONc+²ÐŸºlË΢®gn$?âZeó)
Ä BÅFGB'“¦OV‰.^ÒàèvV$•Z$
ɨÑA9K)ab¥©=Æ?2òút+#ûJ7ÇÞ+X¤Kô:À½§ÉäÃON'ÜTYÿ”&¢R’‡UûsÝ=Ú…[m‹3×,ŠúzÄfJd…P¶ý}+·i/4Eý€Úéá¦ÍHPénáS
ÚLƒ"RÌ1é>ŒÊÅrßíÂ>6lœºJ±ìDÖ¾ùHfÐhdª'ÝÄX³ûü‡å2EEùt|jt‰ä÷ãURJº8b÷ö°®„)ß^?'´ú'Á
+÷…C(]>09âr69LÕ³dØ£³…yâ™)‹ìR5lÜ™žY_QÜM¸âāú )L›­Ž™¥ùÃ'ÔW}V@¤»úûòÓüæ æ­{8ñ°¯þUNrta÷ñJõwVt(q­ëUS’,aÖ.>-]¾GÞÜMm_½W+ÞÃç$k™O»-ú6#Å}ÎÇZ>)¹K	Û‚[GîZ6©áwñC_ne%j “Æô[oK)Wÿ‘´³Tø¾Å®“x±U"Ãᆴ
¥{2uÏQ¼ŸZÑ÷¶š4ú!E]€13ÂÓñ>/Áº5SJ¡g«K[ýlj¶ĝâHOZj&c9†õ`uÜÔŒyÿB"GkHLòÁÓû
+³N3
ù(~;¦þ
+k„$÷Çû¯å˜TMÌh6IºÈ1«ÈÕ¿¥5z\Ú¼;v0e!^hsŽ"›óÞ¡\OõCV¬¥€”c×ãh"Ñg¯‡ñ˜Ä|ˤ««7í&ò§9Ù˜Ýw({ƒ>ÉÝtÁVM¬ì4¦–ð…¹phähÍgއǧÅâbÜ®äjò5Açü *@°9‰c»åOx?òR/Wà‘
+s—žG²<ä_/W-O¯Œ
adžOõü4ßÝX¤MF‚ä½î“œy®xÇ°Ök}*qÒ¹™ÆôJ ?E `4’½‡He®“a9‰pðícñ+*áw¬.fÅ$¸‹´Æçï‚Fí—²½Çß“Ö$0(H—#Ÿ‡v=ÒŠ$dêÞ	ˆ<™ùÛd-%˜ÿn½ËÁíѾ+ê‘ØÐ[“ªï+¿áÌÉ9ch¡7Êx_´©¿ C@\õü!ˆÝs©ü18+©Ÿ(c„ù™à
ÕèòŽ†ýëy…I+N„`» cz¹äûmêšè~êð1×zEQ¹¼¯µμõ=F÷$ÖàԐI}I 1ÙÖê{òýk<3yD;ÝñxaÛ
+ð*¿HTòMc7a»¨Pý*å*Hºã/¼Â{¸Æ:]¨Ü'~LçF1…Î EŸVùÌK¹›”ýByv¥ïh,zÔá­‹}\–Æñ¾ùž™çyòÛŠ1×Lê‚ûÅfŠæ¾_vQ2ai»R\›§ßŠä›kˆÀðW%JØïXÑd+7H¤©®—‰½9
+c¯Ì82Qôÿá6+endstream
+endobj
+42 0 obj
+<<
+/Type/FontDescriptor
+/CapHeight 850
+/Ascent 850
+/Descent -200
+/FontBBox[-29 -960 1116 775]
+/FontName/NENDNC+CMSY10
+/ItalicAngle -14.035
+/StemV 85
+/FontFile 41 0 R
+/Flags 68
+>>
+endobj
+41 0 obj
+<<
+/Filter[/FlateDecode]
+/Length1 777
+/Length2 594
+/Length3 533
+/Length 1156
+>>
+stream
+xÚSUÖuLÉOJuËÏ+Ñ5Ô3´Rpö
Ž44P0Ô3àRUu.JM,ÉÌÏsI,IµR0´´4Tp,MW04U00·22°25çâRUpÎ/¨,ÊLÏ(QÐpÖ©2WpÌM-ÊLNÌSðM,ÉHÍ’œ˜£œŸœ™ZR©§ à˜“£ÒR¬”ZœZT–š¢ÇÅeh¨’™\¢”šž™Ç¥r”g^Z¾‚9D8¥´+ at W¦äçåT*¤¤¦qéûåmKº…dgaqºán¥99~‰¹ ãÁ…!Ÿ˜›™S	U‘Ÿ[PZ’Z¤à›Ÿ’Z”‡®4<â8ßÔ”ÌÒ\tYÏ’ÄœÌdǼôœT]C=cSˆDf±[fEjJ at fIr†BZbNq*X<5/Ý)Àà;DßÏÕÏÅÏYµÙ€Ä̼’Ê‚T„r0ßÁSQf…B´ž!P!ÂX±h¶¹æ%ç§dæ¥+™š)$%VrSgªPm¨™—’Z¡Zt²¾^^~	P‹0ljÒò‹¸@ñjhn¡ ŸÜԐ¦Wœœò+ªu,t-Í€&š)˜››Ö¢*ÍË,,MõtQ0500°0‚x-¹´¨(5¯œ†€Áã§eƒ65µ"5™köÑLÆxYí¯	_\U
+ßuâÜÍ
³Ô£6JúošÄÚ”ï7=öܹãÚ:ŸvÌ<sOþ¦é…¹Öœ9ÇvØÜ÷WÞ¹Œw‹ü̲5ÆFÉ'9î¼?»×§…é-Ãâ¼’öd_ŽùW¢¾,î
+[â¨cͽþ×ëÂê÷æÿ®jIØï•.±á¸M¸ ëµ©EeÁ¬‹~¿?vâ[APE_:óî¸'SÛ¹OÉZÝ0¸:}‡Ó›9NÖÕmï}^ê5QËÅþ¬Ë÷ÃËþY†mX%WáµîØŸ½ëxÊnÍå²Û¾³+Éøæˇ?ðŸÉÖÉš½Èêsç³ûÿýþ¾ÇzM='û‰xÍSózÕi|_Þ6o™Í¸£øó2ËsŸÃ”f§”/hœlÿÞzŸöÜÿË¿‡¦oüå[<»\¿7Ý/¬ÔäØÑÝÏ'íSØØRô}UľÅܽ÷Ïpµm»aس¬ÿ‘N×GëëáùONEUýdˆ[zÆïvåÕi}þo¼ë£æmýðä@ÇÙƾ3þ¯½Þt_š!ýÉ8¡d›3ãákûe»/[S·}ÎTeÿ¶¬×æw\¿ðÎ@ñ¸÷ï„m“\ê'Ïå[µ,úW$cïM¦ÉgÏ>6Vé|È)5]fkÚÓí"Y61|QÍŸÁ{BÁRÐIJçM¤}•Ém‡K®ñûk¾ØMW(úÄWÁ°,‹æš•-¢éYÅœ¿ƒÏ-VÉŽeÐ==kÌnÏ…
:~§ö;]Žê¸sææ÷Úν·WoÜÚ÷hetº–7#[HÃ$ÆÒŠK±S¡ÆAßÇóZü
™þO‚åÛô¢ò×
+g…¼<þi@!à5`Xœ“šXT’Ÿ›X”ÍÅ+endstream
+endobj
+1 0 obj
+<<
+/Creator( TeX output 2007.04.23:1418)
+/Producer(dvipdfm 0.13.2c, Copyright \251 1998, by Mark A. Wicks)
+/CreationDate(D:20070423141904+01'00')
+>>
+endobj
+5 0 obj
+<<
+/Type/Page
+/Resources 6 0 R
+/Contents[28 0 R 4 0 R 29 0 R 30 0 R]
+/Parent 70 0 R
+>>
+endobj
+32 0 obj
+<<
+/Type/Page
+/Resources 33 0 R
+/Contents[28 0 R 4 0 R 37 0 R 30 0 R]
+/Parent 70 0 R
+>>
+endobj
+70 0 obj
+<<
+/Type/Pages
+/Count 2
+/Kids[5 0 R 32 0 R]
+/Parent 3 0 R
+>>
+endobj
+39 0 obj
+<<
+/Type/Page
+/Resources 40 0 R
+/Contents[28 0 R 4 0 R 44 0 R 30 0 R]
+/Parent 71 0 R
+>>
+endobj
+46 0 obj
+<<
+/Type/Page
+/Resources 47 0 R
+/Contents[28 0 R 4 0 R 48 0 R 30 0 R]
+/Parent 71 0 R
+>>
+endobj
+71 0 obj
+<<
+/Type/Pages
+/Count 2
+/Kids[39 0 R 46 0 R]
+/Parent 3 0 R
+>>
+endobj
+50 0 obj
+<<
+/Type/Page
+/Resources 51 0 R
+/Contents[28 0 R 4 0 R 52 0 R 30 0 R]
+/Parent 72 0 R
+>>
+endobj
+54 0 obj
+<<
+/Type/Page
+/Resources 55 0 R
+/Contents[28 0 R 4 0 R 56 0 R 30 0 R]
+/Parent 72 0 R
+>>
+endobj
+72 0 obj
+<<
+/Type/Pages
+/Count 2
+/Kids[50 0 R 54 0 R]
+/Parent 3 0 R
+>>
+endobj
+58 0 obj
+<<
+/Type/Page
+/Resources 59 0 R
+/Contents[28 0 R 4 0 R 60 0 R 30 0 R]
+/Parent 73 0 R
+>>
+endobj
+62 0 obj
+<<
+/Type/Page
+/Resources 63 0 R
+/Contents[28 0 R 4 0 R 64 0 R 30 0 R]
+/Parent 73 0 R
+>>
+endobj
+66 0 obj
+<<
+/Type/Page
+/Resources 67 0 R
+/Contents[28 0 R 4 0 R 68 0 R 30 0 R]
+/Parent 73 0 R
+>>
+endobj
+73 0 obj
+<<
+/Type/Pages
+/Count 3
+/Kids[58 0 R 62 0 R 66 0 R]
+/Parent 3 0 R
+>>
+endobj
+3 0 obj
+<<
+/Type/Pages
+/Count 9
+/Kids[70 0 R 71 0 R 72 0 R 73 0 R]
+/MediaBox[0 0 595 842]
+>>
+endobj
+28 0 obj
+<<
+/Length 1
+>>
+stream
+
+endstream
+endobj
+30 0 obj
+<<
+/Length 1
+>>
+stream
+
+endstream
+endobj
+4 0 obj
+<<
+/Length 33
+>>
+stream
+1.00028 0 0 1.00028 72 769.82 cm
+endstream
+endobj
+74 0 obj
+<<
+>>
+endobj
+75 0 obj
+null
+endobj
+76 0 obj
+<<
+>>
+endobj
+2 0 obj
+<<
+/Type/Catalog
+/Pages 3 0 R
+/Outlines 74 0 R
+/Threads 75 0 R
+/Names 76 0 R
+>>
+endobj
+xref
+0 77
+0000000000 65535 f 
+0000090280 00000 n 
+0000092043 00000 n 
+0000091696 00000 n 
+0000091896 00000 n 
+0000090444 00000 n 
+0000008923 00000 n 
+0000028122 00000 n 
+0000027938 00000 n 
+0000000009 00000 n 
+0000033601 00000 n 
+0000033415 00000 n 
+0000000985 00000 n 
+0000039334 00000 n 
+0000039145 00000 n 
+0000001886 00000 n 
+0000046799 00000 n 
+0000046611 00000 n 
+0000002816 00000 n 
+0000062083 00000 n 
+0000061897 00000 n 
+0000003717 00000 n 
+0000065044 00000 n 
+0000064858 00000 n 
+0000004665 00000 n 
+0000068015 00000 n 
+0000067829 00000 n 
+0000005649 00000 n 
+0000091796 00000 n 
+0000006633 00000 n 
+0000091846 00000 n 
+0000008825 00000 n 
+0000090546 00000 n 
+0000012202 00000 n 
+0000075561 00000 n 
+0000075375 00000 n 
+0000008984 00000 n 
+0000009711 00000 n 
+0000012114 00000 n 
+0000090727 00000 n 
+0000015475 00000 n 
+0000089011 00000 n 
+0000088816 00000 n 
+0000012264 00000 n 
+0000013198 00000 n 
+0000015376 00000 n 
+0000090831 00000 n 
+0000017841 00000 n 
+0000015537 00000 n 
+0000017797 00000 n 
+0000091013 00000 n 
+0000019992 00000 n 
+0000017903 00000 n 
+0000019926 00000 n 
+0000091117 00000 n 
+0000022074 00000 n 
+0000020054 00000 n 
+0000022008 00000 n 
+0000091299 00000 n 
+0000024172 00000 n 
+0000022136 00000 n 
+0000024117 00000 n 
+0000091403 00000 n 
+0000026107 00000 n 
+0000024234 00000 n 
+0000026052 00000 n 
+0000091507 00000 n 
+0000027876 00000 n 
+0000026169 00000 n 
+0000027810 00000 n 
+0000090650 00000 n 
+0000090935 00000 n 
+0000091221 00000 n 
+0000091611 00000 n 
+0000091978 00000 n 
+0000092000 00000 n 
+0000092021 00000 n 
+trailer
+<<
+/Size 77
+/Root 2 0 R
+/Info 1 0 R
+>>
+startxref
+92138
+%%EOF


Property changes on: puppetor/trunk/doc/howto.pdf
___________________________________________________________________
Name: svn:executable
   + *

Added: puppetor/trunk/doc/howto.tex
===================================================================
--- puppetor/trunk/doc/howto.tex	2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/doc/howto.tex	2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,521 @@
+\documentclass{article}%
+\begin{document}%
+%
+\author{karsten.loesing at gmx.net}%
+\title{PuppeTor\\A Java-based Tor Simulator\\User's Guide}%
+\maketitle%
+%
+
+\section{Introduction}
+
+PuppeTor\footnote{The name signifies the metaphor of a puppeteer playing with
+puppets like this framework plays with Tor processes. This is a working title.
+Please feel free to suggest a better name for it.} is a Java framework that
+facilitates the
+configuration of a set of local Tor processes and the execution of automatic
+tests based on these processes. The intention is to make it easier for
+developers to
+analyze Tor's behavior in arbitrary network settings and to measure the
+effects of changes to the Tor source code. Due to the automation of
+configuration and execution, these experiments can be done in an unsupervised 
+batch fashion.
+
+An application that makes use of this framework starts with setting up a set of
+pre-defined Tor processes: proxy, router, and directory. Though these
+configurations should work in most settings, they can be altered by adding or
+removing configuration entries.\footnote{This is not implemented yet, but it 
+should not be too hard to do it.} After deciding whether the processes shall
+either create a private Tor network, or connect to the public Tor network,
+processes are started. Now the application can start clients and servers and
+perform requests using the local processes. In doing so, it can measure time
+intervals between events originating from Tor processes and it can synchronize 
+with such events. Further, the application can re-configure processes during
+their execution using the Tor controller.\footnote{This is not implemented yet,
+but we already make heavy use of the controller, so that this addition is not a
+real problem.}
+
+There are two typical situations in which this framework can be useful:%
+\begin{enumerate}%
+\item Developers need to oversee the effects of their changes to the source code.
+Therefore, it is useful to have a clean setting of Tor nodes in a private
+network, so that all nodes are under full control of the developer.
+\item Developers might want to measure the real-world performance of certain Tor
+operations. Hence, they can set up nodes at the edge of the public Tor network
+and conduct performance measurements, maybe in a batch of some hundreds or
+thousands of runs.
+\end{enumerate}
+
+Of course, the applications described here are possible without this framework.
+But this framework has certain advantages over writing own configuration files
+and test scripts:%
+\begin{enumerate}
+\item It provides developers with pre-defined configurations of nodes.
+Especially the configuration of nodes in a private network with own directory
+nodes is not a trivial task.
+\item It takes away the need to implement synchronization of a test application
+with events created by Tor processes. This, too, is a non-trivial task and can,
+if not done properly, lead to deadlocks or inconsistent states (yes, this
+happened during development of the framework, too).
+\item It relieves the developer from the task to collect and merge log files.
+Typically, every Tor process produces its own log file, so that all files might
+need to be merged in chronological order to identify causal dependencies.
+\end{enumerate}
+
+Originally this framework has been designed to perform experiments on Tor hidden
+services. But it should be feasible for experiments on onion routing and other
+Tor services, too. If you have found an alternative usage for it, and maybe
+have changed or extended it to support your own development, please feel free to
+contribute your additions. And please report bugs.
+
+\section{Installation}
+
+PuppeTor requires a Tor executable and the Java Tor Controller API which can
+both be downloaded from the Subversion repository on the Tor
+homepage.\footnote{At the moment the Tor Controller API is included, because we
+had to make some changes to the Tor Controller code, so that the framework does
+not run with the current Tor Controller code. Maybe we should make these changes
+(bugs?) to the Tor Controller API before?}
+
+After downloading, you need to put the Tor executable to the base directory of
+this framework and make sure that it has the name \texttt{tor.exe} or
+\texttt{tor.sh}, respectively.\footnote{This is going to be configurable in the
+future.} Further, you need to have a Java Runtime Environment version 5 or
+higher installed on your machine.
+
+You can then start the examples with one of the following commands:
+
+\begin{verbatim}
+java -cp bin
+    de.uniba.wiai.lspi.puppetor.examples.AccessingPublicWebServerOverTor
+java -cp bin
+    de.uniba.wiai.lspi.puppetor.examples.AdvertisingHiddenServiceToPublicTorNetwork
+java -cp bin
+    de.uniba.wiai.lspi.puppetor.examples.AdvertisingAndAccessingHiddenServiceOverPublicTorNetwork
+java -cp bin
+    de.uniba.wiai.lspi.puppetor.examples.AdvertisingAndAccessingHiddenServiceOverPrivateTorNetwork
+\end{verbatim}
+
+\section{Example 1: Accessing public Web server over Tor}
+
+The easiest way to use Tor is to anonymously access a public Web server. This is
+what we do in this first example.
+
+The network configuration consists only of one node and looks like this:
+\begin{itemize}
+\item Proxy with control port\footnote{Every node needs to open its control
+port, so that we can communicate with it.} 7001 and SOCKS port 7002.
+\end{itemize}
+
+%%%%% picture of network setting? %%%%%
+
+In the following, we will walk through the code that is necessary to configure
+this ``network'' and perform the request to a public Web server.
+
+We start with obtaining a \texttt{Network} instance which is the central place
+for each test run:
+
+\begin{verbatim}
+Network network = NetworkFactory.createNetwork("example1");
+\end{verbatim}
+
+Now we are ready to create the Tor proxy that we need to perform our request. As
+stated above it should listen on port 7001 for us as controller and on 7002 for
+us as SOCKS client. The name is given for logging purposes only:
+
+\begin{verbatim}
+network.createProxy("proxy", 7001, 7002);
+\end{verbatim}
+
+As we are fine with the pre-defined configuration (at least for the moment), we
+can write the configurations of all nodes (which is only our proxy in this case)
+to \texttt{torrc} files in the nodes' working directories:
+
+\begin{verbatim}
+network.writeConfigurations();
+\end{verbatim}
+
+Next we start the nodes of our network. (The separation of this call from the
+previous one is the possible investigation of the working directory content, if
+required. Starting nodes can fail for some reason and block our application
+forever. Therefore, we can provide a timeout in milliseconds after which we
+consider the operation as failed. Starting nodes is considered to be complete as
+soon as Tor opens its control port and we have connected to it:
+
+\begin{verbatim}
+network.startNodes(5000);
+\end{verbatim}
+
+From time to time, Tor cannot create a circuit without some kind of launching
+assistance. Especially in a private-network setting nodes need to reboot in
+order to refresh their directory information and be able to build circuits.
+Hence, we send the nodes a ``HUP'' signal in regular intervals until it tells us
+that it has opened a circuit. In the following operation we can configure how
+often we want to retry and how long we want to wait in between retries:
+
+\begin{verbatim}
+network.hupUntilUp(5, 10000);
+\end{verbatim}
+
+Now our Tor network is running.
+
+Next is the client that will perform requests using our proxy. PuppeTor contains
+a \texttt{ClientApplication} class which can start a thread to perform simple
+HTTP GET requests to a given address and port. We provide it with a name for
+logging purposes, the address and port to look up, and the SOCKS port of our
+proxy:
+
+\begin{verbatim}
+ClientApplication client = network.createClient("client",
+    "www.google.de", 80, 7002);
+\end{verbatim}
+
+Before starting the request we want to register for events coming from this
+client. This is necessary as requests are performed in a separate thread in the
+background, which allows more complex applications to perform multiple requests
+in parallel. Therefore, we implement the interface \texttt{EventListener}
+and its method \texttt{handleEvent(Event)}. In this method we are interested in
+the two events \texttt{Event.APPLICATION\_SENDING\_REQUEST} and
+\texttt{Event.APPLICATION\_REPLY\_RECEIVED}. What we do with these events is
+application-specific. In our case we record the time of the first event and
+subtract it from the time of the second event to obtain the round-trip time of
+our request:
+
+\begin{verbatim}
+EventListener clientEventListener =
+    new EventListener() { // ... };
+\end{verbatim}
+
+Next we obtain a reference on the \texttt{EventManager} which handles all
+asynchronous events coming from the Tor processes:
+
+\begin{verbatim}
+EventManager manager = network.getEventManager();
+\end{verbatim}
+
+We add our event handler to the event manager with the client as event subject:
+
+\begin{verbatim}
+manager.addEventListener(client, clientEventListener);
+\end{verbatim}
+
+Finally, we can perform the requests to the public server. Just in case that a
+request fails or times out, we state that we are willing to make three tries
+with a timeout of 20 seconds each, and that further requests should be aborted
+as soon as one request succeeds (last parameter \texttt{true}).
+
+\begin{verbatim}
+client.performRequest(3, 20000, true);
+\end{verbatim}
+
+Due to the asynchronous performing of requests, we need to explicitly wait for
+the requests to be completed. We can do this by invoking a method on the event
+manager that blocks the invoking thread until a certain event is received. In
+this case we want to be blocked until the event
+\texttt{Event.APPLICATION\_REQUESTS\_PERFORMED} is received from our client:
+
+\begin{verbatim}
+manager.waitForAnyOccurence(client,
+    Event.APPLICATION_REQUESTS_PERFORMED);
+\end{verbatim}
+
+At last we shut down the proxy:
+
+\begin{verbatim}
+network.shutdownNodes();
+\end{verbatim}
+
+\section{Example 2: Advertising hidden service to public Tor network}
+
+The second-easiest example is to advertise a hidden service to the public Tor
+network, but not perform any requests to it. In fact this can be a useful test 
+to measure publication times of rendezvous service descriptors.
+
+The network configuration again consists only of one proxy node:
+\begin{itemize}
+\item Proxy with control port 7001 and SOCKS port 7002.
+\end{itemize}
+
+%%%%% picture of network setting? %%%%%
+
+We start again with creating a network and a proxy node. But this time we keep a
+reference on the proxy node:
+
+\begin{verbatim}
+Network network = NetworkFactory.createNetwork("example2");
+ProxyNode proxy = network.createProxy("proxy", 7001, 7002);
+\end{verbatim}
+
+Next we extend the configuration of the proxy by a hidden service entry. The
+hidden service gets the name \texttt{hidServ} which is used for its working directory
+and for logging purposes. It expects that the real service will run on
+\texttt{localhost:7001}, but announces to Tor that it runs on the virtual port
+80:
+
+\begin{verbatim}
+proxy.addHiddenService("hidServ", 7005, 80);
+\end{verbatim}
+
+The next step stays the same. We write the configuration of our proxy to its
+\texttt{torrc} file.
+
+\begin{verbatim}
+network.writeConfigurations();
+\end{verbatim}
+
+Before starting the proxy, we create and register an event listener that informs
+us about two events: when Tor opens a circuit
+(\texttt{Event.NODE\_CIRCUIT\_OPENED}) and when it has published a rendezvous
+service descriptor (\texttt{Event.NODE\_RSD\_PUBLISHED}).
+
+\begin{verbatim}
+EventListener proxyEventListener = new EventListener() { // ... }
+\end{verbatim}
+
+Again we register this event listener at the event manager, but this time with
+the proxy as event subject.
+
+\begin{verbatim}
+EventManager manager = network.getEventManager();
+manager.addEventListener(proxy, proxyEventListener);
+\end{verbatim}
+
+The next steps are similar to the first example. We need to start the proxy and
+maybe send ``HUP'' signals until it has built a circuit.
+
+\begin{verbatim}
+network.startNodes(5000);
+network.hupUntilUp(5, 10000));
+\end{verbatim}
+
+As there is no event we could wait for, we fall asleep for five minutes to
+observe publication of rendezvous service descriptors.
+
+\begin{verbatim}
+Thread.sleep(5L * 60L * 1000L);
+\end{verbatim}
+
+At the end we shut down the proxy.
+
+\begin{verbatim}
+network.shutdownNodes();
+\end{verbatim}
+
+\section{Example 3: Advertising and accessing hidden service over public Tor
+network}
+
+This third example is in fact the first for which this framework originally has
+been built. The setting consists of two proxies that are connected to the
+public Tor network, a server that is connected as hidden server to the first
+proxy, and a client that performs requests using the second proxy. This scenario
+can be used to measure real-world round-trip times for requests to hidden
+services.
+
+The network configuration consists of two proxy nodes and looks like this:
+\begin{itemize}
+\item Proxy \texttt{proxy1} with control port 7001 and SOCKS port 7002.
+\item Proxy \texttt{proxy2} with control port 7011 and SOCKS port 7012.
+\item Server that listens on port 7005 and is registered as hidden server at 
+\texttt{proxy1}.
+\item Client that performs requests using SOCKS port 7012, i.e. over
+\texttt{proxy2}.
+\end{itemize}
+
+%%%%% picture of network setting? %%%%%
+
+The example starts similar to the previous example with creating and configuring
+two proxy nodes. A hidden service is configured at the first proxy. Then
+configurations are written and nodes started until all of them have built a
+circuit.
+
+\begin{verbatim}
+Network network = NetworkFactory.createNetwork("example3");
+ProxyNode proxy1 = network.createProxy("proxy1", 7001, 7002);
+network.createProxy("proxy2", 7011, 7012);
+proxy1.addHiddenService("hidServ", 7005, 80);
+network.writeConfigurations();
+network.startNodes(5000);
+network.hupUntilUp(5, 10000);
+\end{verbatim}
+
+In contrast to the previous example we want to wait until the first proxy has
+successfully uploaded a rendezvous service descriptor to the directory before
+performing a request. From experience we can say that hidden service
+initialization takes most part of the time. We want to wait for this event from
+the first proxy as source for at most 3 minutes:
+
+\begin{verbatim}
+EventManager manager = network.getEventManager();
+manager.waitForAnyOccurence(proxy1, Event.NODE_RSD_PUBLISHED,
+    3L * 60L * 1000L);
+\end{verbatim}
+
+As soon as the first descriptor has been published, we can initialize both, 
+server and client. In contrast to the first example the client will perform an
+empty HTTP GET on the hidden server's onion address instead of a public Web
+server. The server will answer with an empty HTTP OK:
+
+\begin{verbatim}
+ServerApplication server = network.createServer("server", 7005);
+String onionAddress = proxy1.getOnionAddress("hidServ");
+ClientApplication client = network.createClient("client", onionAddress,
+    80, 7012);
+\end{verbatim}
+
+For convenience we create a single event listener for both, client and server.
+It can distinguish client and server events by their type. We are interested in
+the events \texttt{Event.APPLICATION\_SENDING\_REQUEST} which is fired from the
+client before sending a request, \texttt{Event.APPLICATION\_REQUEST\_RECEIVED}
+which is fired by the server when a request is received, and
+\texttt{Event.APPLICATION\_REPLY\_RECEIVED}
+which is again fired by the client when it has received a reply. However, we
+need to register this event listener for both event sources, client and server.
+
+\begin{verbatim}
+EventListener clientAndServerEventListener = new EventListener() { // ... }
+manager.addEventListener(client, clientAndServerEventListener);
+manager.addEventListener(server, clientAndServerEventListener);
+\end{verbatim}
+
+Now we instruct the server to listen for incoming requests and the client to
+perform such requests.
+
+\begin{verbatim}
+server.listen();
+client.performRequest(5, 45000, true);
+\end{verbatim}
+
+We wait for the client to complete all requests.
+
+\begin{verbatim}
+manager.waitForAnyOccurence(client,
+    Event.APPLICATION_REQUESTS_PERFORMED);
+\end{verbatim}
+
+Finally, we shut down all nodes.
+
+\begin{verbatim}
+network.shutdownNodes();
+\end{verbatim}
+
+\section{Example 4: Advertising and accessing hidden service over private Tor
+network}
+
+The last example is by far the most complex. It involves setting up a private
+Tor network including own directory nodes. The overall task stays the same as in
+the previous example: perform a request to a hidden service and see what
+happens. In this case performance does not mean too much, because there is
+no network latency on the local host. But in contrast to a public-network
+setting one could potentially observe what happens on all routers and directory
+nodes during the request.
+
+The network configuration now consists of three router nodes, which are used for
+performing onion routing and misused as proxies, two directory nodes, a client,
+and a server:
+\begin{itemize}
+\item Router \texttt{router1} with control port 7021, SOCKS port 7022, OR port
+7023, and dir port 7024.
+\item Router \texttt{router2} with control port 7031, SOCKS port 7032, OR port
+7033, and dir port 7034.
+\item Router \texttt{router3} with control port 7041, SOCKS port 7042, OR port
+7043, and dir port 7044.
+\item Directory \texttt{dir1} with control port 7051, SOCKS port 7052, OR port
+7053, and dir port 7054.
+\item Directory \texttt{dir2} with control port 7061, SOCKS port 7062, OR port
+7063, and dir port 7064.
+\item Server that listens on port 7025 and is registered as hidden server at
+\texttt{router1}.
+\item Client that performs requests using SOCKS port 7042, i.e. over
+\texttt{router3}.
+\end{itemize}
+
+%%%%% picture of network setting? %%%%%
+
+Creating and configuring these nodes is done similar to all previous examples
+and requires no further explanation:
+
+\begin{verbatim}
+Network network = NetworkFactory.createNetwork("example4");
+RouterNode router1 = network.createRouter("router1", 7021, 7022, 7023, 7024);
+network.createRouter("router2", 7031, 7032, 7033, 7034);
+network.createRouter("router3", 7041, 7042, 7043, 7044);
+network.createDirectory("dir1", 7051, 7052, 7053, 7054);
+network.createDirectory("dir2", 7061, 7062, 7063, 7064);
+router1.addHiddenService("hidServ", 7025, 80);
+\end{verbatim}
+
+However, the next step does require further explanation. The main difficulty
+lies in the fact that all nodes need to be configured so that they accept our
+own directory nodes instead of the pre-configured directory nodes from the
+public Tor network. This configuration requires the fingerprints of all
+directory nodes. These fingerprints are written to disk by the directory nodes
+as soon as they are started. But the directories need to be configured before
+being started, too, in order to prevent them from becoming part of the public
+Tor network. And now we have the chicken or the egg dilemma.
+
+The non-trivial solution is to configure the directory nodes with a fake
+directory configuration and start them using the \texttt{--list-fingerprint}
+option. Hence they write a \texttt{fingerprint} file to disk and shut down
+immediately. This fingerprint can be read, and all nodes can be
+configured to use the directory using this fingerprint.
+
+A second, non-trivial task is to authorize routers and directory nodes.
+Therefore, an authoritative directory needs to know all fingerprints of
+authorized nodes. They are stored in the \texttt{approved-routers} file in the
+working directory of the directory node.
+
+The complete task is encapsulated in the following method for convenience.
+However, all operations could also be performed directly on the nodes, if
+required.
+
+\begin{verbatim}
+network.configureAsPrivateNetwork();
+\end{verbatim}
+
+Afterwards, the process is more or less similar to the previous example:
+
+\begin{verbatim}
+network.writeConfigurations();
+network.startNodes(5000);
+network.hupUntilUp(8, 10000);
+EventManager manager = network.getEventManager();
+manager.waitForAnyOccurence(router1, Event.NODE_RSD_PUBLISHED,
+    3L * 60L * 1000L);
+ServerApplication server = network.createServer("server", 7025);
+String onionAddress = router1.getOnionAddress("hidServ");
+ClientApplication client = network.createClient("client", onionAddress,
+    80, 7042);
+EventListener clientAndServerEventListener = new EventListener() {
+manager.addEventListener(client, clientAndServerEventListener);
+manager.addEventListener(server, clientAndServerEventListener);
+server.listen();
+client.performRequest(5, 45000, true);
+manager.waitForAnyOccurence(client,
+    Event.APPLICATION_REQUESTS_PERFORMED);
+network.shutdownNodes();
+\end{verbatim}
+
+\section{Known issues}
+
+There is already a small list of problems and open questiosn that require more
+investigation:
+
+\begin{itemize}
+\item When \texttt{ProxyNode}s are used in a private-network setting, the
+application does not succeed. What configuration entry is missing in
+\texttt{ProxyNode} that is present in \texttt{RouterNode}? Or is it a Tor
+problem?
+%
+\item When logging to stdout is set to something lower than \texttt{notice}, the
+application does not succeed. This is not a big problem, because we can parse
+the output that we get from the controller. But maybe it's a problem of Tor tat
+we should fix?
+%
+\item From time to time there is a FileNotFoundException when starting the
+private-network example.
+
+\item Fight the TODOs\ldots
+
+\item Complete logging and documentation for the impl package.
+\end{itemize}
+
+\end{document} 
\ No newline at end of file


Property changes on: puppetor/trunk/doc/howto.tex
___________________________________________________________________
Name: svn:executable
   + *

Added: puppetor/trunk/doc/logging
===================================================================
--- puppetor/trunk/doc/logging	2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/doc/logging	2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,49 @@
+
+
+Notice: Logging has not been implemented consistently over all classes!
+
+
+
+=======================
+= Logging Conventions =
+=======================
+
+Logger names
+============
+
+Logger names consist of (1) the fully-qualified class name of the class that
+contains the logging statement and (2) the system component in the network that
+executes the given class.
+
+This leads to two possibilities for logger names:
+1. <fully-qualified class name>.<system component>
+2. <system component>.<fully-qualified class name>
+
+We decided to use the second schema to facilitate observing the operations of a
+single node or application in the network.
+
+
+Log Levels
+==========
+
+The following log levels are used for the given occurences:
+
+SEVERE
+- an error has occured that requires the test to be aborted immediately
+
+WARNING
+- an error has occured, but the test may continue
+
+INFO
+- an important change has occured in the test (e.g. all nodes started), mostly
+concerning Network
+
+FINE
+- some state, i.e. variable value, has changed
+
+FINER
+- entering and exiting a method, throwing an exception
+- some intermediate place in a method
+
+FINEST
+- redirected automatic output, e.g. from Tor


Property changes on: puppetor/trunk/doc/logging
___________________________________________________________________
Name: svn:executable
   + *

Added: puppetor/trunk/license
===================================================================
--- puppetor/trunk/license	2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/license	2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,66 @@
+===============================================================================
+PuppeTor - A Java-based Tor Simulator - is distributed under this license:
+
+Copyright (c) 2007, Karsten Loesing
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+    * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+
+    * Neither the names of the copyright owners nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+===============================================================================
+The Tor controller demonstration code is distributed under this license:
+
+Copyright (c) 2005, Nick Mathewson, Roger Dingledine
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+    * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+
+    * Neither the names of the copyright owners nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Added: puppetor/trunk/logging.properties
===================================================================
--- puppetor/trunk/logging.properties	2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/logging.properties	2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,4 @@
+handlers= java.util.logging.ConsoleHandler
+java.util.logging.ConsoleHandler.level = FINEST
+java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
+.level= FINEST
\ No newline at end of file


Property changes on: puppetor/trunk/logging.properties
___________________________________________________________________
Name: svn:executable
   + *

Added: puppetor/trunk/readme
===================================================================
--- puppetor/trunk/readme	2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/readme	2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,8 @@
+This is a Java framework that facilitates the configuration of a set of local
+Tor processes and the execution of automatic tests based on these processes. The
+intention is to make it easier for developers to analyze Tor's behavior in
+arbitrary network settings and to measure the effects of changes to the Tor
+source code. Due to the automation of configuration and execution, these
+experiments can be done in an unsupervised batch fashion.
+
+For more information, read the how-to document in doc/howto.pdf .

Added: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/ClientApplication.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/ClientApplication.java	2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/ClientApplication.java	2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,55 @@
+package de.uniba.wiai.lspi.puppetor;
+
+/**
+ * The <code>ClientApplication</code> can be used to simulate simple
+ * <code>HTTP GET</code> requests by a virtual local client. Therefore, an
+ * address and a port are given wo which the client shall connect. Requests are
+ * performed by a background thread, so that multiple requests could be
+ * performed at the same time.
+ * 
+ * @author kloesing
+ */
+public interface ClientApplication extends EventSource {
+
+	/**
+	 * <p>
+	 * Arranges for the requests being performed by a thread in the background
+	 * and returns immediately. This thread will try for <code>retries</code>
+	 * times to make the request with a timeout of
+	 * <code>timeoutForEachRetry</code> millis each. If an attempt is not
+	 * successful, the thread nevertheless waits for the timeout to expire. If
+	 * <code>stopOnSuccess</code> is set to <code>true</code>, the thread
+	 * will quit performing requests after the first successful request.
+	 * </p>
+	 * 
+	 * <p>
+	 * For each sent request the application fires an
+	 * <event>Event.APPLICATION_SENDING_REQUEST</code> event. On receiving a
+	 * reply it fires an event of type <code>Event.APPLICATION_REPLY_RECEIVED</code>,
+	 * if a request is not successful or times out, an <code>Event.APPLICATION_GAVE_UP_REQUEST</code>
+	 * event is fired. After all requests have been performed (either
+	 * successfully, or not) an event of type <code>Event.APPLICATION_REQUESTS_PERFORMED</code>
+	 * is fired.
+	 * </p>
+	 * 
+	 * @param retries
+	 *            The number of retries that this client will perform. Must be
+	 *            <code>1</code> or greater.
+	 * @param timeoutForEachRetry
+	 *            The timeout for each request. If a request is not successful,
+	 *            the thread nevertheless waits for the timeout to expire. Must
+	 *            not be negative.
+	 * @param stopOnSuccess
+	 *            If set to <code>true</code>, the client quits performing
+	 *            requests after the first successful request, if <code>false</code>
+	 *            it continues until all <code>retries</code> have been
+	 *            processed.
+	 * @throws IllegalArgumentException
+	 *             Thrown if an invalid value is given for either of the
+	 *             parameters.
+	 */
+	public void performRequest(int retries, long timeoutForEachRetry,
+			boolean stopOnSuccess);
+
+	// TODO we need some way to interrupt performing requests
+}


Property changes on: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/ClientApplication.java
___________________________________________________________________
Name: svn:executable
   + *

Added: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/DirectoryNode.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/DirectoryNode.java	2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/DirectoryNode.java	2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,43 @@
+package de.uniba.wiai.lspi.puppetor;
+
+import java.util.Set;
+
+/**
+ * A DirectoryNode represents a Tor process that acts as RouterNode and is
+ * further a directory authoritative server for the (private) Tor network. It
+ * inherits most of the configuration and behavior from RouterNode and adds some
+ * directory-specific configurations and behavior.
+ * 
+ * @author kloesing
+ */
+public interface DirectoryNode extends RouterNode {
+
+	/**
+	 * Combines the node's fingerprint to a DirServer string that can be used to
+	 * configure this or other nodes to use this node as directory server.
+	 * 
+	 * @return DirServer string to configure a node to use this node as
+	 *         directory server.
+	 * @throws TorProcessException
+	 *             Thrown if a problem occurs when determining the node's
+	 *             fingerprint.
+	 */
+	public abstract String determineDirServerString()
+			throws TorProcessException;
+
+	/**
+	 * Writes the given set of routers (either directories or routers) to the
+	 * <code>approved-routers</code> file. This will confirm to directory
+	 * clients, that the given routers can be trusted.
+	 * 
+	 * @param approvedRouters
+	 *            The set of approved routers to be written.
+	 * @throws IllegalArgumentException
+	 *             Thrown if <code>null</code> is passed as parameter.
+	 * @throws TorProcessException
+	 *             Thrown if the <code>approved-routers</code> file cannot be
+	 *             written to disk.
+	 */
+	public void writeApprovedRouters(Set<String> approvedRouters)
+			throws TorProcessException;
+}


Property changes on: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/DirectoryNode.java
___________________________________________________________________
Name: svn:executable
   + *

Added: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/Event.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/Event.java	2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/Event.java	2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,87 @@
+package de.uniba.wiai.lspi.puppetor;
+
+/**
+ * An Event is created for every state change of an asynchronous system
+ * component, e.g. a Tor process or a client/server application running as
+ * thread in the background. In contrast to <code>NodeState</code> or
+ * <code>NetworkState</code> an <code>Event</code> cannot be a pre- or
+ * postconditions for a method invocation. There is no prescribed order in which
+ * events are fired by a certain process or application. Some events can be
+ * fired only once, others possibly multiple times. All management operations
+ * for events are contained in the <code>EventManager</code>.
+ * 
+ * TODO change from enum to hierarchie of interfaces/classes, because enums do
+ * not support a type hierarchie which would be very useful!
+ * 
+ * TODO add methods and attributes to events to include additional information
+ * (e.g. time of occurence)!
+ * 
+ * @author kloesing
+ */
+public enum Event {
+
+	/**
+	 * The node which fires this event has successfully opened a circuit. This
+	 * event can only be fired once.
+	 */
+	NODE_CIRCUIT_OPENED,
+
+	/**
+	 * The node which fires this event has opened its control port. This event
+	 * can only be fired once.
+	 */
+	NODE_CONTROL_PORT_OPENED,
+
+	/**
+	 * The node which fires this event has successfully published a rendezvous
+	 * service descriptor. This event can be fired multiple times.
+	 */
+	NODE_RSD_PUBLISHED,
+
+	/**
+	 * The network fires this event when all nodes have opened their control
+	 * ports. This event can only be fired once.
+	 */
+	NETWORK_CONTROL_PORTS_OPENED,
+
+	/**
+	 * The network fires this event when all nodes have successfully opened a
+	 * circuit. This event can only be fired once.
+	 */
+	NETWORK_CIRCUITS_ESTABLISHED,
+
+	/**
+	 * The client application which fires this event has received a reply to a
+	 * previously sent request. This event can be fired multiple times.
+	 */
+	APPLICATION_REPLY_RECEIVED,
+
+	/**
+	 * The client application which fires this event has sent a request. This
+	 * event can be fired multiple times.
+	 */
+	APPLICATION_SENDING_REQUEST,
+
+	/**
+	 * The server application which fires this event has received a request and
+	 * sent a reply to it. This event can be fired multiple times.
+	 */
+	APPLICATION_REQUEST_RECEIVED,
+
+	/**
+	 * The client application which fires this event has given up waiting for
+	 * the reply to a previously sent request. This event can be fired multiple
+	 * times.
+	 */
+	APPLICATION_GAVE_UP_REQUEST,
+
+	/**
+	 * The client application which fires this event has completed a series of
+	 * requests, whether they were successful or not. This event can be fired
+	 * multiple times. TODO Can it really be fired multiple times? Is it
+	 * possible to send multiple request series from the same client? Should it
+	 * be possible?
+	 */
+	APPLICATION_REQUESTS_PERFORMED
+
+}


Property changes on: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/Event.java
___________________________________________________________________
Name: svn:executable
   + *

Added: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/EventListener.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/EventListener.java	2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/EventListener.java	2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,20 @@
+package de.uniba.wiai.lspi.puppetor;
+
+/**
+ * This interface must be implemented by any object in a test application that
+ * shall be registered as event listener.
+ * 
+ * @author kloesing
+ */
+public interface EventListener {
+
+	/**
+	 * Is invoked when an asynchronous event is fired by the source (or one of
+	 * the sources) for which this listener was registered.
+	 * 
+	 * @param event
+	 *            The event that was fired.
+	 */
+	public void handleEvent(Event event);
+
+}


Property changes on: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/EventListener.java
___________________________________________________________________
Name: svn:executable
   + *

Added: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/EventManager.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/EventManager.java	2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/EventManager.java	2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,190 @@
+package de.uniba.wiai.lspi.puppetor;
+
+import java.util.List;
+
+/**
+ * The EventManager is the central place for a test run to manage asynchronous
+ * events by Tor processes and client/server applications running as threads in
+ * the background. A test application can either register event listeners to be
+ * notified asynchronously about events when they occur, or synchronize with an
+ * event by being blocked until the certain event occurs.
+ * 
+ * @author kloesing
+ */
+
+public interface EventManager {
+
+	/**
+	 * Registers the given <code>listener</code> as event listener for events
+	 * originating from the given <code>source</code>. This method returns a
+	 * list of all previously fired events by this source, so that each event
+	 * fired by this source is either included in the returned list or
+	 * signalized in a later invocation on the event listener, but not in both.
+	 * This prevents race conditions by eliminating the gap between registration
+	 * of an event handler and asking if an event has been fired before
+	 * registering. This method can be invoked in any node or network state.
+	 * 
+	 * @param source
+	 *            The source of events that the listener is interested in. May
+	 *            not be <code>null</code>.
+	 * @param listener
+	 *            The listener that wants to be notified about events from the
+	 *            given <code>source</code>. If the <code>listener</code>
+	 *            is already registered for the same <code>source</code>,
+	 *            nothing happens, i.e. the <code>listener</code> will not
+	 *            receive multiple invocations for the same event. May not be
+	 *            <code>null</code>.
+	 * @return A list of all previously fired events for the given
+	 *         <code>source</code>. If no event has been fired before, an
+	 *         empty list is returned instead of <code>null</code>.
+	 * @throws IllegalArgumentException
+	 *             Thrown if <code>null</code> is passed for either of the
+	 *             parameters.
+	 */
+	public abstract List<Event> addEventListener(EventSource source,
+			EventListener listener);
+
+	/**
+	 * Returns the list of all previously observed events from the given
+	 * <code>source</code>.
+	 * 
+	 * @param source
+	 *            The source of the events that the invoking thread is
+	 *            interested in. May not be <code>null</code>.
+	 * @throws IllegalArgumentException
+	 *             Thrown if <code>null</code> is passed as parameter.
+	 * @return List of all previously observed events from the given
+	 *         <code>source</code>.
+	 */
+	public abstract List<Event> getEventHistory(EventSource source);
+
+	/**
+	 * Returns whether the given <code>event</code> has been observed from the
+	 * given <code>source</code> before, or not.
+	 * 
+	 * @param source
+	 *            The source of the event that the invoking thread is interested
+	 *            in. May not be <code>null</code>.
+	 * @param event
+	 *            The event that the invoking thread is interested int. May not
+	 *            be <code>null</code>.
+	 * @throws IllegalArgumentException
+	 *             Thrown if <code>null</code> is passed for either of the
+	 *             parameters.
+	 * @return <code>true</code> if the event has been observed from the
+	 *         source before, <code>false</code> otherwise.
+	 */
+	public abstract boolean hasEventOccured(EventSource source, Event event);
+
+	/**
+	 * Removes the given <code>listener</code> as event listener from all
+	 * previously registered sources. If this listener is not registered for any
+	 * source, nothing happens. This method can be invoked in any node or
+	 * network state.
+	 * 
+	 * TODO should we include the source as parameter, too, to have even more
+	 * control over removing listeners from sources? Or should we overload the
+	 * method?
+	 * 
+	 * @param listener
+	 *            The listener that shall be removed from the list of registered
+	 *            listeners. May not be <code>null</code>.
+	 * @throws IllegalArgumentException
+	 *             Thrown if <code>null</code> is passed as parameter.
+	 */
+	public abstract void removeEventListener(EventListener listener);
+
+	/**
+	 * Checks if the given <code>event</code> has been observed from the given
+	 * <code>source</code> before; if not, blocks the invoking thread until
+	 * the next event is fired from that source. Note that this method does not
+	 * restrict waiting to a timeout, so that it could potentially block
+	 * forever! This method can be invoked in any node or network state.
+	 * 
+	 * @param source
+	 *            The source of the event that the invoking thread is willing to
+	 *            wait for. May not be <code>null</code>.
+	 * @param event
+	 *            The event that the invoking thread is willing to wait for from
+	 *            the given <code>source</code>. May not be <code>null</code>.
+	 * @throws IllegalArgumentException
+	 *             Thrown if <code>null</code> is passed for either of the
+	 *             parameters.
+	 */
+	public abstract void waitForAnyOccurence(EventSource source, Event event);
+
+	/**
+	 * Checks if the given <code>event</code> has been observed from the given
+	 * <code>source</code> before; if not, blocks the invoking thread until
+	 * the next event is fired from that source or the given timeout of
+	 * <code>maximumTimeToWaitInMillis</code> millis has expired. This method
+	 * can be invoked in any node or network state.
+	 * 
+	 * @param source
+	 *            The source of the event that the invoking thread is willing to
+	 *            wait for. May not be <code>null</code>.
+	 * @param event
+	 *            The event that the invoking thread is willing to wait for from
+	 *            the given <code>source</code>. May not be <code>null</code>.
+	 * @param maximumTimeToWaitInMillis
+	 *            The maximum time to wait in milliseconds. A positive value or
+	 *            zero restricts waiting to this time. If this value is
+	 *            negative, we will wait potentially forever.
+	 * @return <code>true</code> if an event of the given type has been fired
+	 *         by the source within the given timeout, <code>false</code>
+	 *         otherwise.
+	 * @throws IllegalArgumentException
+	 *             Thrown if an invalid value is passed for either of the
+	 *             parameters.
+	 */
+	public abstract boolean waitForAnyOccurence(EventSource source,
+			Event event, long maximumTimeToWaitInMillis);
+
+	/**
+	 * Blocks the invoking thread until the next <code>event</code> is fired
+	 * from the given <code>source</code>. This method only waits for the
+	 * next occurence of an event, regardless of previous occurrences. Note that
+	 * this method does not restrict waiting to a timeout, so that it could
+	 * potentially block forever! This method can be invoked in any node or
+	 * network state.
+	 * 
+	 * @param source
+	 *            The source of the event that the invoking thread is willing to
+	 *            wait for. May not be <code>null</code>.
+	 * @param event
+	 *            The event that the invoking thread is willing to wait for from
+	 *            the given <code>source</code>. May not be <code>null</code>.
+	 * @throws IllegalArgumentException
+	 *             Thrown if <code>null</code> is passed for either of the
+	 *             parameters.
+	 */
+	public abstract void waitForNextOccurence(EventSource source, Event event);
+
+	/**
+	 * Blocks the invoking thread until the next <code>event</code> is fired
+	 * from the given <code>source</code> or the given timeout of
+	 * <code>maximumTimeToWaitInMillis</code> millis has expired. This method
+	 * only waits for the next occurence of an event, regardless of previous
+	 * occurrences. This method can be invoked in any node or network state.
+	 * 
+	 * @param source
+	 *            The source of the event that the invoking thread is willing to
+	 *            wait for. May not be <code>null</code>.
+	 * @param event
+	 *            The event that the invoking thread is willing to wait for from
+	 *            the given <code>source</code>. May not be <code>null</code>.
+	 * @param maximumTimeToWaitInMillis
+	 *            The maximum time to wait in milliseconds. A positive value or
+	 *            zero restricts waiting to this time. If this value is
+	 *            negative, we will wait potentially forever.
+	 * @return <code>true</code> if an event of the given type has been fired
+	 *         by the source within the given timeout, <code>false</code>
+	 *         otherwise.
+	 * @throws IllegalArgumentException
+	 *             Thrown if an invalid value is passed for either of the
+	 *             parameters.
+	 */
+	public abstract boolean waitForNextOccurence(EventSource source,
+			Event event, long maximumTimeToWaitInMillis);
+
+}


Property changes on: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/EventManager.java
___________________________________________________________________
Name: svn:executable
   + *

Added: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/EventSource.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/EventSource.java	2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/EventSource.java	2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,11 @@
+package de.uniba.wiai.lspi.puppetor;
+
+/**
+ * This marker interface is the super-interface of those interfaces that can be
+ * the source for events.
+ * 
+ * @author kloesing
+ */
+public interface EventSource {
+	// this interface contains no methods, because it is a marker interface.
+}


Property changes on: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/EventSource.java
___________________________________________________________________
Name: svn:executable
   + *

Added: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/Network.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/Network.java	2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/Network.java	2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,324 @@
+package de.uniba.wiai.lspi.puppetor;
+
+/**
+ * A Network instance constitutes the central object of any test run and is
+ * aware of the node configuration. It creates all nodes for this configuration
+ * and is able to perform common operations on these nodes. Apart from the
+ * factory methods, all other operations could also be performed manually by an
+ * application using the appropriate interfaces.
+ * 
+ * @author kloesing
+ */
+public interface Network extends EventSource {
+
+	/**
+	 * <p>
+	 * Configures the nodes in this network so that they can run in a private
+	 * network and don't require public directory servers or onion routers from
+	 * the Internet. This configuration should be done after configuring the
+	 * nodes and before writing configurations to disk.
+	 * </p>
+	 * 
+	 * <p>
+	 * The main requirement for this method lies in the fact that all nodes need
+	 * to be configured so that they accept our own directory nodes instead of
+	 * the pre-configured directory nodes from the public Tor network. This
+	 * configuration requires the fingerprints of all directory nodes. These
+	 * fingerprints are written to disk by the directory nodes as soon as they
+	 * are started. But the directories need to be configured before being
+	 * started, too, in order to prevent them from becoming part of the public
+	 * Tor network. And now we have the chicken or the egg dilemma.
+	 * </p>
+	 * 
+	 * <p>
+	 * The non-trivial solution is to configure the directory nodes with a fake
+	 * directory configuration and start them using the
+	 * <code>--list-fingerprint</code> option. Hence they write a
+	 * <code>fingerprint</code> file to disk and shut down immediately. This
+	 * fingerprint can be read, and all nodes can be configured to use the
+	 * directory using this fingerprint.
+	 * </p>
+	 * 
+	 * <p>
+	 * A second, non-trivial task is to authorize routers and directory nodes.
+	 * Therefore, an authoritative directory needs to know all fingerprints of
+	 * authorized nodes. They are stored in the \texttt{approved-routers} file
+	 * in the working directory of the directory node.
+	 * </p>
+	 * 
+	 * <p>
+	 * The complete task is encapsulated in this method for convenience.
+	 * However, all operations could also be performed directly on the nodes, if
+	 * required.
+	 * </p>
+	 * 
+	 * TODO check if we have enough directory and router nodes to build a
+	 * private network? How many are required? 2 dirs and 3 routers?
+	 * 
+	 * TODO allow invocation of this method only in correct state
+	 * 
+	 * @throws TorProcessException
+	 *             Thrown if an I/O problem occurs while starting nodes with the
+	 *             <code>--list-fingerprint</code> option, reading files from
+	 *             the nodes' working directories, or writing the
+	 *             <code>approved-routers</code> files.
+	 */
+	public abstract void configureAsPrivateNetwork() throws TorProcessException;
+
+	/**
+	 * Creates a new client application, but does not yet perform a request.
+	 * 
+	 * TODO allow invocation of this method only in correct state
+	 * 
+	 * @param clientApplicationName
+	 *            The name for this client application, which is used for
+	 *            logging purposes only. May neither be null or a zero-length
+	 *            string.
+	 * @param targetAddress
+	 *            The target for requests sent by this client application. Can
+	 *            be an IP address, a domain name, or an onion address. May
+	 *            neither be null or a zero-length string.
+	 * @param targetPort
+	 *            The TCP port for requests sent by this client application. If
+	 *            the target address is an onion address, this port is the
+	 *            virtual port that the hidden service has announced. May not be
+	 *            negative or greater than 65535.
+	 * @param socksPort
+	 *            The TCP port on which a local Tor process is waiting for
+	 *            incoming SOCKS requests. May not be negative or greater than
+	 *            65535.
+	 * @return Reference to the created client application.
+	 * @throws IllegalArgumentException
+	 *             Thrown if an invalid value is given for either of the
+	 *             parameters.
+	 */
+	public abstract ClientApplication createClient(
+			String clientApplicationName, String targetAddress, int targetPort,
+			int socksPort);
+
+	/**
+	 * Creates a new directory node and adds it to the network, but does not yet
+	 * write its configuration to disk or start the corresponding Tor process.
+	 * 
+	 * TODO allow invocation of this method only in correct state
+	 * 
+	 * @param nodeName
+	 *            The name for this node, which is used as name for the working
+	 *            directory, for logging purposes, and as node nickname. May
+	 *            neither be <code>null</code> or have zero or more than 19
+	 *            alpha-numeric characters.
+	 * @param controlPort
+	 *            The TCP port on which the corresponding Tor process will wait
+	 *            for a controller. May not be negative or greater than 65535.
+	 * @param socksPort
+	 *            The TCP port on which the corresponding Tor process will wait
+	 *            for incoming SOCKS requests. May not be negative or greater
+	 *            than 65535.
+	 * @param orPort
+	 *            The TCP port on which the corresponding Tor process will wait
+	 *            for incoming requests from other onion routers. May not be
+	 *            negative or greater than 65535.
+	 * @param dirPort
+	 *            The TCP port on which the corresponding Tor process will wait
+	 *            for incoming directory requests. May not be negative or
+	 *            greater than 65535.
+	 * @return Reference to the created directory node.
+	 * @throws IllegalArgumentException
+	 *             Thrown if an invalid value is given for either of the
+	 *             parameters.
+	 */
+	public abstract DirectoryNode createDirectory(String nodeName,
+			int controlPort, int socksPort, int orPort, int dirPort);
+
+	/**
+	 * Creates a new proxy node and adds it to the network, but does not yet
+	 * write its configuration to disk or start the corresponding Tor process.
+	 * 
+	 * TODO allow invocation of this method only in correct state
+	 * 
+	 * @param nodeName
+	 *            The name for this node, which is only used as name for the
+	 *            working directory and for logging purposes. May neither be
+	 *            <code>null</code> or have zero or more than 19 alpha-numeric
+	 *            characters.
+	 * @param controlPort
+	 *            The TCP port on which the corresponding Tor process will wait
+	 *            for a controller. May not be negative or greater than 65535.
+	 * @param socksPort
+	 *            The TCP port on which the corresponding Tor process will wait
+	 *            for incoming SOCKS requests. May not be negative or greater
+	 *            than 65535.
+	 * @return Reference to the created proxy node.
+	 * @throws IllegalArgumentException
+	 *             Thrown if an invalid value is given for either of the
+	 *             parameters.
+	 */
+	public abstract ProxyNode createProxy(String nodeName, int controlPort,
+			int socksPort);
+
+	/**
+	 * Creates a new router node and adds it to the network, but does not yet
+	 * write its configuration to disk or start the corresponding Tor process.
+	 * 
+	 * TODO allow invocation of this method only in correct state
+	 * 
+	 * @param nodeName
+	 *            The name for this node, which is used as name for the working
+	 *            directory, for logging purposes, and as node nickname. May
+	 *            neither be <code>null</code> or have zero or more than 19
+	 *            alpha-numeric characters.
+	 * @param controlPort
+	 *            The TCP port on which the corresponding Tor process will wait
+	 *            for a controller. May not be negative or greater than 65535.
+	 * @param socksPort
+	 *            The TCP port on which the corresponding Tor process will wait
+	 *            for incoming SOCKS requests. May not be negative or greater
+	 *            than 65535.
+	 * @param orPort
+	 *            The TCP port on which the corresponding Tor process will wait
+	 *            for incoming requests from other onion routers. May not be
+	 *            negative or greater than 65535.
+	 * @param dirPort
+	 *            The TCP port on which the corresponding Tor process will wait
+	 *            for incoming directory requests which in fact are requests for
+	 *            the mirrored directory. May not be negative or greater than
+	 *            65535.
+	 * @return Reference to the created router node.
+	 * @throws IllegalArgumentException
+	 *             Thrown if an invalid value is given for either of the
+	 *             parameters.
+	 */
+	public abstract RouterNode createRouter(String nodeName, int controlPort,
+			int socksPort, int orPort, int dirPort);
+
+	/**
+	 * Creates a new server application, but does not start listening for
+	 * incoming requests.
+	 * 
+	 * TODO allow invocation of this method only in correct state
+	 * 
+	 * @param serverApplicationName
+	 *            The name for this server application, which is used for
+	 *            logging purposes only. May neither be null or a zero-length
+	 *            string.
+	 * @param serverPort
+	 *            The TCP port on which the server will wait for incoming
+	 *            requests. May not be negative or greater than 65535.
+	 * @return Reference to the created server application.
+	 * @throws IllegalArgumentException
+	 *             Thrown if an invalid value is given for either of the
+	 *             parameters.
+	 */
+	public abstract ServerApplication createServer(
+			String serverApplicationName, int serverPort);
+
+	/**
+	 * Returns a reference on the (single) event manager for this network.
+	 * 
+	 * TODO allow invocation of this method only in correct state
+	 * 
+	 * @return Reference on the (single) event manager for this network.
+	 */
+	public abstract EventManager getEventManager();
+
+	/**
+	 * Returns the current network state.
+	 * 
+	 * @return Current network state.
+	 */
+	public abstract NetworkState getNetworkState();
+
+	/**
+	 * <p>
+	 * Sends a HUP signal to all nodes in the network in regular intervals and
+	 * blocks the invoking thread until all nodes have reported to have
+	 * successfully opened a circuit.
+	 * </p>
+	 * 
+	 * <p>
+	 * First, the method waits for <code>hupInterval</code> millis for the
+	 * nodes to have successfully opened a circuit. If they do not succeed
+	 * within this time, a HUP signal is sent to all nodes and the method waits
+	 * for another <code>hupInterval</code> millis. In total, the method sends
+	 * at most <code>tries</code> HUP signals before giving up and returning
+	 * with <code>false</code>. Thus, the maximum waiting time is
+	 * <code>(tries + 1)</code> times <code>hupInterval</code>. As soon as
+	 * all nodes have successfully opened circuits, the method returns with
+	 * <code>true</code>.
+	 * </p>
+	 * 
+	 * TODO allow invocation of this method only in correct state
+	 * 
+	 * @param tries
+	 *            The maximum number of HUP signals that are sent to the Tor
+	 *            processes. Negative values are not allowed. A value of zero
+	 *            means to wait only for the given time of
+	 *            <code>hupInterval</code> millis without sending a HUP
+	 *            signal. Typical values depend on the network being a public or
+	 *            private Tor network and range about 3 to 5 tries.
+	 * @param hupInterval
+	 *            The time in millis that the method will wait between sending
+	 *            HUP signals. Negative values are not allowed. Typically,
+	 *            values should not be smaller than 10 seconds to permit Tor to
+	 *            stabilize.
+	 * @throws IllegalArgumentException
+	 *             Thrown if a negative value is given for either
+	 *             <code>tries</code> or <code>hupInterval</code>.
+	 * @throws TorProcessException
+	 *             Thrown if an I/O problem occurs while sending HUP signals.
+	 * @return <code>true</code> if all nodes have reported to have
+	 *         successfully opened a circuit, <code>false</code> otherwise.
+	 */
+	public abstract boolean hupUntilUp(int tries, long hupInterval)
+			throws TorProcessException;
+
+	/**
+	 * Attempts to shut down all nodes. The method blocks until all shutdown
+	 * requests have been sent and either returns, or throws the first exception
+	 * that has been observed when shutting down nodes. The method can be
+	 * assumed to return very quickly.
+	 * 
+	 * TODO allow invocation of this method only in correct state
+	 * 
+	 * @throws TorProcessException
+	 *             Thrown if an I/O problem occurs while shutting down the
+	 *             nodes.
+	 */
+	public abstract void shutdownNodes() throws TorProcessException;
+
+	/**
+	 * Attempts to start all nodes within a given timeout of
+	 * <code>maximumTimeToWaitInMillis</code> millis. The method returns as
+	 * soon as all nodes have started and opened their control port so that we
+	 * can connect to them. It returns a boolean that states whether the
+	 * operation was either successful or has timed out.
+	 * 
+	 * TODO allow invocation of this method only in correct state
+	 * 
+	 * @param maximumTimeToWaitInMillis
+	 *            The maximum time to wait in milliseconds. A positive value or
+	 *            zero restricts waiting to this time. Negative values are not
+	 *            allowed. Typical values are in the range of a few seconds.
+	 * @return <code>true</code> if all nodes could be started successfully,
+	 *         <code>false</code> if a timeout has occured.
+	 * @throws IllegalArgumentException
+	 *             Thrown if a negative value is given for
+	 *             <code>maximumTimeToWaitInMillis</code>.
+	 * @throws TorProcessException
+	 *             Thrown if an I/O problem occurs while startint the nodes.
+	 */
+	public abstract boolean startNodes(long maximumTimeToWaitInMillis)
+			throws TorProcessException;
+
+	/**
+	 * Writes the configurations of all nodes in this network to disk. This
+	 * method is assumed to return very quickly.
+	 * 
+	 * TODO allow invocation of this method only in correct state
+	 * 
+	 * @throws TorProcessException
+	 *             Thrown if an I/O problem occurs while writing to the nodes'
+	 *             working directories.
+	 */
+	public abstract void writeConfigurations() throws TorProcessException;
+}


Property changes on: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/Network.java
___________________________________________________________________
Name: svn:executable
   + *

Added: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/NetworkFactory.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/NetworkFactory.java	2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/NetworkFactory.java	2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,32 @@
+package de.uniba.wiai.lspi.puppetor;
+
+import de.uniba.wiai.lspi.puppetor.impl.NetworkImpl;
+
+/**
+ * The <code>NetworkFactory</code> is an abstract factory that can create
+ * <code>Network</code> instances.
+ * 
+ * TODO At the moment, this class uses the concrete class NetworkImpl to
+ * implement its only factory method. If we want to make this a real abstract
+ * factory, we need to replace the concrete constructor by reading the class
+ * name of the class implementing Network from a property file and invoking its
+ * constructor using reflection. Currently, this is the only place where we
+ * reference a class from the impl package.
+ * 
+ * @author karsten
+ */
+public abstract class NetworkFactory {
+
+	/**
+	 * Creates a new network that is required for a test run. The new network is
+	 * initially unpopulated and creates its own working directory at
+	 * test-env/randomTestID/.
+	 * 
+	 * @param networkName
+	 *            Name of this network configuration.
+	 * @return A new network instance.
+	 */
+	public static Network createNetwork(String networkName) {
+		return new NetworkImpl(networkName);
+	}
+}


Property changes on: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/NetworkFactory.java
___________________________________________________________________
Name: svn:executable
   + *

Added: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/NetworkState.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/NetworkState.java	2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/NetworkState.java	2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,41 @@
+package de.uniba.wiai.lspi.puppetor;
+
+/**
+ * The <code>NetworkState</code> constitutes the single state of a network
+ * that is the result of the <code>NodeState</code>s of all nodes in the
+ * network. In contrast to <code>Event</code> the network (and node) states
+ * depend only on the methods that have been invoked on these objects, and not
+ * on asynchronous state changes. Most operations of <code>Network</code>
+ * require a certain <code>NetworkState</code> as precondition and may ensure
+ * another <code>NetworkState</code> as postcondition. There is a prescribed
+ * order of states.
+ * 
+ * @author kloesing
+ */
+public enum NetworkState {
+	/**
+	 * The configurations of the nodes in the network have not been written to
+	 * disk and can be changed. This is the initial state of a
+	 * <code>Network</code>.
+	 */
+	CONFIGURING_NODES,
+
+	/**
+	 * The configurations of all nodes in the network have been written to disk
+	 * and cannot be changed anymore, but the Tor processes have not been
+	 * started, yet. This state could be useful to review the configurations
+	 * that have been written to disk.
+	 */
+	CONFIGURATIONS_WRITTEN,
+
+	/**
+	 * The nodes in the network have been started and are running.
+	 */
+	NODES_STARTED,
+
+	/**
+	 * The nodes in the network had been started and shut down.
+	 */
+	NODES_SHUT_DOWN
+
+}


Property changes on: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/NetworkState.java
___________________________________________________________________
Name: svn:executable
   + *

Added: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/NodeState.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/NodeState.java	2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/NodeState.java	2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,39 @@
+package de.uniba.wiai.lspi.puppetor;
+
+/**
+ * The <code>NodeState</code> constitutes the state of a single Tor node. In
+ * contrast to <code>Event</code> the node (and network) states depend only on
+ * the methods that have been invoked on these objects, and not on asynchronous
+ * state changes. Most operations of <code>ProxyNode</code> and its subclasses
+ * require a certain <code>NodeState</code> as precondition and may ensure
+ * another <code>NodeState</code> as postcondition. There is a prescribed
+ * order of states.
+ * 
+ * @author kloesing
+ */
+public enum NodeState {
+
+	/**
+	 * The node's configuration has not been written to disk and can be changed.
+	 * This is the initial state of a <code>ProxyNode</code> or one of its
+	 * subclasses.
+	 */
+	CONFIGURING,
+
+	/**
+	 * The node's configuration has been written to disk and cannot be changed
+	 * anymore, but the Tor process has not been started, yet. This state could
+	 * be useful to review the configuration that has been written to disk.
+	 */
+	CONFIGURATION_WRITTEN,
+
+	/**
+	 * The node has been started and is running.
+	 */
+	RUNNING,
+
+	/**
+	 * The node had been started and shut down.
+	 */
+	SHUT_DOWN
+}


Property changes on: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/NodeState.java
___________________________________________________________________
Name: svn:executable
   + *

Added: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/ProxyNode.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/ProxyNode.java	2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/ProxyNode.java	2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,164 @@
+package de.uniba.wiai.lspi.puppetor;
+
+import java.util.Set;
+
+/**
+ * A ProxyNode represents a Tor process that is configured as onion proxy, i.e.
+ * to relay traffic from a local application to the Tor network and vice versa,
+ * and does not route traffic on behalf of remote applications. It is the
+ * superclass for other node types that extend the configuration of a ProxyNode.
+ * 
+ * @author kloesing
+ */
+public interface ProxyNode extends EventSource {
+
+	/**
+	 * Adds hidden service entries to this node's configuration. This method can
+	 * only be invoked while the node is in state
+	 * <code>NodeState.CONFIGURING</code>. TODO Should this operation also be
+	 * possible while the process is running? We could easily change the
+	 * configuration via the controller.
+	 * 
+	 * @param serviceName
+	 *            Name of the hidden service that will be used as name for the
+	 *            hidden service directory. May neither be <code>null</code>
+	 *            or a zero-length string.
+	 * @param servicePort
+	 *            The TCP port on which the service will be available for
+	 *            requests. This can be different for the virtual port that is
+	 *            announced to clients. May not be negative or greater than
+	 *            65535.
+	 * @param virtualPort
+	 *            The virtual TCP port that this hidden service runs on as it is
+	 *            announced to clients. May not be negative or greater than
+	 *            65535.
+	 * @throws IllegalStateException
+	 *             Thrown if node is not in state
+	 *             <code>NodeState.CONFIGURING</code>.
+	 * @throws IllegalArgumentException
+	 *             Thrown if an invalid value is given for either of the
+	 *             parameters.
+	 */
+	public abstract void addHiddenService(String serviceName, int servicePort,
+			int virtualPort);
+
+	/**
+	 * Adds the given set of DirServer configuration entries to this node's
+	 * configuration. Note that as soon as one DirServer is configured, the node
+	 * does not connect to an outside directory server of the public network any
+	 * more!
+	 * 
+	 * TODO allow invocation of this method only in correct state
+	 * 
+	 * @param authorizedDirServerStrings
+	 *            A set of DirServer configuration entries that each contain one
+	 *            directory server that this node shall connect to. May not be
+	 *            <code>null</code>.
+	 * @throws IllegalArgumentException
+	 *             Thrown if <code>null</code> is passed as parameter.
+	 */
+	public abstract void configureDirServers(
+			Set<String> authorizedDirServerStrings);
+
+	/**
+	 * Returns the node's name.
+	 * 
+	 * @return The node's name.
+	 */
+	public abstract String getNodeName();
+
+	/**
+	 * Returns the node's state.
+	 * 
+	 * @return The node's state.
+	 */
+	public abstract NodeState getNodeState();
+
+	/**
+	 * Determines the onion address for the previously added hidden service with
+	 * name <code>serviceName</code>. Requires that the node has been
+	 * started, i.e. is in state <code>NodeState.RUNNING</code>.
+	 * 
+	 * @param serviceName
+	 *            Name of the hidden service that has been used before to add
+	 *            the hidden service. May neither be <code>null</code> or a
+	 *            zero-length string.
+	 * @return The onion address string consisting of 16 base32 chars plus
+	 *         ".onion".
+	 * @throws IllegalArgumentException
+	 *             Thrown if <code>null</code> or a zero-length string is
+	 *             passed as parameter.
+	 * @throws TorProcessException
+	 *             Thrown if either there does not exist a hidden service with
+	 *             the given <code>serviceName</code> as directory or if the
+	 *             <code>hostname</code> file could not be read.
+	 */
+	public abstract String getOnionAddress(String serviceName)
+			throws TorProcessException;
+
+	/**
+	 * Sends a HUP command to the process via its control port to restart it;
+	 * can only be done if node has already been started!
+	 * 
+	 * @throws TorProcessException
+	 *             Thrown if an I/O problem occurs while sending the HUP signal.
+	 * @throws IllegalStateException
+	 *             Thrown if node is not in state <code>NodeState.RUNNING</code>.
+	 */
+	public abstract void hup() throws TorProcessException;
+
+	/**
+	 * Shuts down Tor process corresponding to this node immediately. This is
+	 * done by sending the <code>SHUTDOWN</code> signal twice, so that nodes
+	 * extending ProxyNode and have opened their OR port shutdown immediately,
+	 * too.
+	 * 
+	 * @throws IllegalStateException
+	 *             Thrown if node is not in state <code>NodeState.RUNNING</code>.
+	 * @throws TorProcessException
+	 *             Thrown if an I/O problem occurs while sending the
+	 *             <code>SHUTDOWN</code> signal.
+	 */
+	public abstract void shutdown() throws TorProcessException;
+
+	/**
+	 * Starts the Tor process for this node and connects to the control port as
+	 * soon as it is opened. <b>In order for this method to succeed it is
+	 * absolutely necessary, that logging on the console is not changed in the
+	 * node's configuration to a higher level than NOTICE, because the output is
+	 * parsed to see when the control port is opened.</b>
+	 * 
+	 * @param maximumTimeToWaitInMillis
+	 *            Maximum time in millis we will wait for the Tor process to be
+	 *            started and the control port being opened. If this value is
+	 *            negative or zero, we will wait potentially forever. TODO
+	 *            should we normalize behavior for negative timeouts to either
+	 *            throw an exception or wait forever consistently for the whole
+	 *            framework?
+	 * @return <code>true</code> if the node could be started successfully,
+	 *         <code>false</code> otherwise.
+	 * @throws IllegalStateException
+	 *             Thrown if node is not in state
+	 *             <code>NodeState.CONFIGURATION_WRITTEN</code>, i.e. if
+	 *             either configuration has not been written or the process has
+	 *             already been started.
+	 * @throws TorProcessException
+	 *             Thrown if either the process could not be started, or the
+	 *             connection to the control port could not be established.
+	 */
+	public abstract boolean startNode(long maximumTimeToWaitInMillis)
+			throws TorProcessException;
+
+	/**
+	 * Writes the node's configuration to the <code>torrc</code> file in the
+	 * node's working directory and changes the state to
+	 * <code>NodeState.CONFIGURATION_WRITTEN</code>.
+	 * 
+	 * @throws IllegalStateException
+	 *             Thrown if not invoked in state
+	 *             <code>NodeState.CONFIGURING</code>.
+	 * @throws TorProcessException
+	 *             Thrown if the configuration file cannot be written to disk.
+	 */
+	public abstract void writeConfiguration() throws TorProcessException;
+}


Property changes on: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/ProxyNode.java
___________________________________________________________________
Name: svn:executable
   + *

Added: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/RouterNode.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/RouterNode.java	2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/RouterNode.java	2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,55 @@
+package de.uniba.wiai.lspi.puppetor;
+
+/**
+ * A RouterNode represents a Tor process that is configured to both, relay
+ * traffic from a local application to the Tor network and to route traffic on
+ * behalf of remote applications. It inherits most of its configuration and
+ * behavior from its superclass ProxyNode and adds some router-specific
+ * configurations and behavior.
+ * 
+ * @author kloesing
+ */
+public interface RouterNode extends ProxyNode {
+
+	/**
+	 * <p>
+	 * Determines the directory node's fingerprint. If the Tor process has not
+	 * been started before, it is started to determine the node's fingerprint.
+	 * This is done using a temporary configuration file and with the
+	 * command-line option <code>--list-fingerprint</code>. Tor then
+	 * generates a new onion key and writes its fingerprint to the
+	 * <code>fingerprint</code> file in its working directory, but does not
+	 * start routing traffic.
+	 * </p>
+	 * 
+	 * <p>
+	 * The temporary <code>torrc</code> file contains a fake entry as
+	 * DirServer, so that the node thinks that it is in a private network. The
+	 * reason for this is that some configuration entries might only work in a
+	 * private network, but the node cannot be configured with valid DirServer
+	 * entries that would make it believe to be in a private network, because
+	 * they can only be generated after invoking this method. Therefore, a
+	 * single DirServer entry is configured with this node as directory server
+	 * having a fingerprint of
+	 * <code>0000 0000 0000 0000 0000 0000 0000 0000 0000 0000</code>.
+	 * </p>
+	 * 
+	 * <p>
+	 * The fingerprint is returned and locally stored so that further
+	 * invocations of this method can be answered immediately. This method can
+	 * only be invoked in state <code>NodeState.CONFIGURING</code> and does
+	 * not change this state. It should be invoked after most of the other
+	 * configurations for this node have been completed, but can be invoked
+	 * before specifying directory servers for this node.
+	 * </p>
+	 * 
+	 * @return The content of the node's fingerprint file.
+	 * @throws TorProcessException
+	 *             Thrown if either the temporary <code>torrc</code>
+	 *             configuration file cannot be written, the Tor process cannot
+	 *             be started temporarily, or the fingerprint file cannot be
+	 *             read.
+	 */
+	public abstract String determineFingerprint() throws TorProcessException;
+
+}


Property changes on: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/RouterNode.java
___________________________________________________________________
Name: svn:executable
   + *

Added: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/ServerApplication.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/ServerApplication.java	2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/ServerApplication.java	2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,21 @@
+package de.uniba.wiai.lspi.puppetor;
+
+/**
+ * The <code>ServerApplication</code> can be used as simple HTTP server that
+ * answers all <code>HTTP GET</code> requests by empty <code>HTTP OK</code>
+ * replies. Therefore, a thread will be started to listen for incoming requests
+ * in the background.
+ * 
+ * @author kloesing
+ */
+public interface ServerApplication extends EventSource {
+
+	/**
+	 * Starts listening for incoming <code>HTTP GET</code> requests by
+	 * clients. Any incoming request is answered by an empty
+	 * <code>HTTP OK</code> reply.
+	 */
+	public abstract void listen();
+
+	// TODO we need some way to interrupt listening for requests
+}


Property changes on: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/ServerApplication.java
___________________________________________________________________
Name: svn:executable
   + *

Added: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/TorProcessException.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/TorProcessException.java	2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/TorProcessException.java	2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,59 @@
+package de.uniba.wiai.lspi.puppetor;
+
+/**
+ * The <code>TorProcessException</code> comprises all kinds of checked
+ * exceptions that occur when interacting with the JVM-external Tor processes or
+ * with the local file system. Any occurence of this exception denotes either a
+ * configuration problem that can only be solved outside of the JVM, or an
+ * unexpected problem. In contrast to this, all kinds of programming errors of a
+ * test application (invoking a method with wrong parameter values, in wrong
+ * state, etc.) will instead cause runtime exceptions from the Java API.
+ * 
+ * @author kloesing
+ */
+ at SuppressWarnings("serial")
+public class TorProcessException extends Exception {
+
+	/**
+	 * Creates a TorProcessException without a detail message or cause.
+	 */
+	public TorProcessException() {
+		super();
+	}
+
+	/**
+	 * Creates a TorProcessException with the given detail <code>message</code>
+	 * and <code>cause</code>.
+	 * 
+	 * @param message
+	 *            The detail message of this exception.
+	 * @param cause
+	 *            The cause for this exception.
+	 */
+	public TorProcessException(String message, Throwable cause) {
+		super(message, cause);
+	}
+
+	/**
+	 * Creates a TorProcessException with the given detail <code>message</code>,
+	 * but without a cause.
+	 * 
+	 * @param message
+	 *            The detail message of this exception.
+	 */
+	public TorProcessException(String message) {
+		super(message);
+	}
+
+	/**
+	 * Creates a TorProcessException with the given <code>cause</code>, but
+	 * without a detail message.
+	 * 
+	 * @param cause
+	 *            The cause for this exception.
+	 */
+	public TorProcessException(Throwable cause) {
+		super(cause);
+	}
+
+}


Property changes on: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/TorProcessException.java
___________________________________________________________________
Name: svn:executable
   + *

Added: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/AccessingPublicWebServerOverTor.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/AccessingPublicWebServerOverTor.java	2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/AccessingPublicWebServerOverTor.java	2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,100 @@
+package de.uniba.wiai.lspi.puppetor.examples;
+
+import de.uniba.wiai.lspi.puppetor.ClientApplication;
+import de.uniba.wiai.lspi.puppetor.Event;
+import de.uniba.wiai.lspi.puppetor.EventListener;
+import de.uniba.wiai.lspi.puppetor.EventManager;
+import de.uniba.wiai.lspi.puppetor.Network;
+import de.uniba.wiai.lspi.puppetor.NetworkFactory;
+import de.uniba.wiai.lspi.puppetor.TorProcessException;
+
+/**
+ * Example for accessing a public Web server (www.google.com) over Tor to
+ * measure access times.
+ * 
+ * @author kloesing
+ */
+public class AccessingPublicWebServerOverTor {
+
+	/**
+	 * Sets up and runs the test.
+	 * 
+	 * @param args
+	 *            Command-line arguments are ignored.
+	 * 
+	 * @throws TorProcessException
+	 *             Thrown if there is a problem with the JVM-external Tor
+	 *             processes that we cannot handle.
+	 */
+	public static void main(String[] args) throws TorProcessException {
+
+		// though we only need one proxy, we always need to create a network
+		// to initialize a test case
+		Network network = NetworkFactory.createNetwork("example1");
+
+		// create a single proxy node with name "proxy", control port 7001,
+		// and SOCKS port 7002
+		network.createProxy("proxy", 7001, 7002);
+
+		// write configuration of proxy node
+		network.writeConfigurations();
+
+		// start proxy node and wait until it has opened a circuit with a
+		// timeout of 5 seconds
+		if (!network.startNodes(5000)) {
+
+			// failed to start the proxy
+			return;
+		}
+
+		// hup until proxy has built circuits (5 retries, 10 seconds timeout
+		// each)
+		if (!network.hupUntilUp(5, 10000)) {
+
+			// failed to build circuits
+			return;
+		}
+
+		// create client application
+		ClientApplication client = network.createClient("client",
+				"www.google.de", 80, 7002);
+
+		// create event listener to listen for client application events
+		EventListener clientEventListener = new EventListener() {
+
+			// remember time when request was sent
+			private long before;
+
+			public void handleEvent(Event event) {
+				switch (event) {
+				case APPLICATION_SENDING_REQUEST:
+					before = System.currentTimeMillis();
+					break;
+				case APPLICATION_REPLY_RECEIVED:
+					System.out
+							.println("Request took "
+									+ (System.currentTimeMillis() - before)
+									+ " millis");
+				}
+			}
+		};
+
+		// obtain reference to event manager to be able to respond to events
+		EventManager manager = network.getEventManager();
+
+		// register event handler for client application events
+		manager.addEventListener(client, clientEventListener);
+
+		// perform at most three request with a timeout of 20 seconds each
+		client.performRequest(3, 20000, true);
+
+		// block this thread as long as client requests are running
+		manager.waitForAnyOccurence(client,
+				Event.APPLICATION_REQUESTS_PERFORMED);
+
+		// shut down proxy
+		network.shutdownNodes();
+
+		System.out.println("Exiting...");
+	}
+}


Property changes on: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/AccessingPublicWebServerOverTor.java
___________________________________________________________________
Name: svn:executable
   + *

Added: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/AdvertisingAndAccessingHiddenServiceOverPrivateTorNetwork.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/AdvertisingAndAccessingHiddenServiceOverPrivateTorNetwork.java	2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/AdvertisingAndAccessingHiddenServiceOverPrivateTorNetwork.java	2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,154 @@
+package de.uniba.wiai.lspi.puppetor.examples;
+
+import de.uniba.wiai.lspi.puppetor.ClientApplication;
+import de.uniba.wiai.lspi.puppetor.Event;
+import de.uniba.wiai.lspi.puppetor.EventListener;
+import de.uniba.wiai.lspi.puppetor.EventManager;
+import de.uniba.wiai.lspi.puppetor.Network;
+import de.uniba.wiai.lspi.puppetor.NetworkFactory;
+import de.uniba.wiai.lspi.puppetor.RouterNode;
+import de.uniba.wiai.lspi.puppetor.ServerApplication;
+import de.uniba.wiai.lspi.puppetor.TorProcessException;
+
+/**
+ * Example for advertising and accessing a hidden service over a private Tor
+ * network.
+ * 
+ * @author kloesing
+ */
+public class AdvertisingAndAccessingHiddenServiceOverPrivateTorNetwork {
+
+	/**
+	 * Sets up and runs the test.
+	 * 
+	 * @param args
+	 *            Command-line arguments are ignored.
+	 * 
+	 * @throws TorProcessException
+	 *             Thrown if there is a problem with the JVM-external Tor
+	 *             processes that we cannot handle.
+	 */
+	public static void main(String[] args) throws TorProcessException {
+
+		// create a network to initialize a test case
+		Network network = NetworkFactory.createNetwork("example4");
+
+		// create two proxy nodes with names "proxy1"/"proxy2", control ports
+		// 7001/7011, and SOCKS ports 7002/7012
+		// TODO for some reason proxies do not work in a private-network
+		// setting...
+		// ProxyNode proxy1 = network.createProxy("proxy1", 7001, 7002);
+		// network.createProxy("proxy2", 7011, 7012);
+
+		// create three router nodes with parameters (router name, control port,
+		// SOCKS port, OR port, dir mirror port)
+		RouterNode router1 = network.createRouter("router1", 7021, 7022, 7023,
+				7024);
+		network.createRouter("router2", 7031, 7032, 7033, 7034);
+		network.createRouter("router3", 7041, 7042, 7043, 7044);
+
+		// create two directory nodes with parameters (router name, control
+		// port, SOCKS port, OR port, dir port)
+		network.createDirectory("dir1", 7051, 7052, 7053, 7054);
+		network.createDirectory("dir2", 7061, 7062, 7063, 7064);
+
+		// add hidden service to the configuration of proxy1
+		router1.addHiddenService("hidServ", 7025, 80);
+
+		// configure nodes of this network to be part of a private network
+		network.configureAsPrivateNetwork();
+
+		// write configuration of proxy node
+		network.writeConfigurations();
+
+		// start proxy node and wait until it has opened a circuit with a
+		// timeout of 5 seconds
+		if (!network.startNodes(5000)) {
+
+			// failed to start the proxy
+			System.out.println("Failed to start nodes!");
+			return;
+		}
+
+		// hup until proxy has built circuits (8 retries, 10 seconds timeout
+		// each)
+		if (!network.hupUntilUp(8, 10000)) {
+
+			// failed to build circuits
+			System.out.println("Failed to build circuits!");
+			return;
+		}
+
+		// obtain reference to event manager to be able to respond to events
+		EventManager manager = network.getEventManager();
+
+		// wait for 3 minutes that the proxy has published its first RSD
+		if (!manager.waitForAnyOccurence(router1, Event.NODE_RSD_PUBLISHED,
+				3L * 60L * 1000L)) {
+
+			// failed to publish an RSD
+			System.out.println("Failed to publish an RSD!");
+			return;
+		}
+
+		// determine onion address for hidden service
+		String onionAddress = router1.getOnionAddress("hidServ");
+
+		// create server application
+		ServerApplication server = network.createServer("server", 7025);
+
+		// create client application
+		ClientApplication client = network.createClient("client", onionAddress,
+				80, 7042);
+
+		// create event listener to listen for client and server application
+		// events
+		EventListener clientAndServerEventListener = new EventListener() {
+
+			private long requestReceivedAtServer;
+
+			// remember time when request was sent and when it was received
+			private long requestSentFromClient;
+
+			public void handleEvent(Event event) {
+				switch (event) {
+				case APPLICATION_SENDING_REQUEST:
+					requestSentFromClient = System.currentTimeMillis();
+					break;
+				case APPLICATION_REQUEST_RECEIVED:
+					requestReceivedAtServer = System.currentTimeMillis();
+					System.out.println("Request took "
+							+ (requestReceivedAtServer - requestSentFromClient)
+							+ " millis from client to server!");
+					break;
+				case APPLICATION_REPLY_RECEIVED:
+					System.out
+							.println("Request took "
+									+ (System.currentTimeMillis() - requestSentFromClient)
+									+ " millis for the rount-trip and "
+									+ (System.currentTimeMillis() - requestReceivedAtServer)
+									+ " millis from server to client!");
+				}
+			}
+		};
+
+		// register event handler for client and server application events
+		manager.addEventListener(client, clientAndServerEventListener);
+		manager.addEventListener(server, clientAndServerEventListener);
+
+		// start server
+		server.listen();
+
+		// perform at most five request with a timeout of 45 seconds each
+		client.performRequest(5, 45000, true);
+
+		// block this thread as long as client requests are running
+		manager.waitForAnyOccurence(client,
+				Event.APPLICATION_REQUESTS_PERFORMED);
+
+		// shut down proxy
+		network.shutdownNodes();
+
+		System.out.println("Exiting...");
+	}
+}


Property changes on: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/AdvertisingAndAccessingHiddenServiceOverPrivateTorNetwork.java
___________________________________________________________________
Name: svn:executable
   + *

Added: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/AdvertisingAndAccessingHiddenServiceOverPublicTorNetwork.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/AdvertisingAndAccessingHiddenServiceOverPublicTorNetwork.java	2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/AdvertisingAndAccessingHiddenServiceOverPublicTorNetwork.java	2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,137 @@
+package de.uniba.wiai.lspi.puppetor.examples;
+
+import de.uniba.wiai.lspi.puppetor.ClientApplication;
+import de.uniba.wiai.lspi.puppetor.Event;
+import de.uniba.wiai.lspi.puppetor.EventListener;
+import de.uniba.wiai.lspi.puppetor.EventManager;
+import de.uniba.wiai.lspi.puppetor.Network;
+import de.uniba.wiai.lspi.puppetor.NetworkFactory;
+import de.uniba.wiai.lspi.puppetor.ProxyNode;
+import de.uniba.wiai.lspi.puppetor.ServerApplication;
+import de.uniba.wiai.lspi.puppetor.TorProcessException;
+
+/**
+ * Example for advertising and accessing a hidden service over the public Tor
+ * network.
+ * 
+ * @author kloesing
+ */
+public class AdvertisingAndAccessingHiddenServiceOverPublicTorNetwork {
+
+	/**
+	 * Sets up and runs the test.
+	 * 
+	 * @param args
+	 *            Command-line arguments are ignored.
+	 * 
+	 * @throws TorProcessException
+	 *             Thrown if there is a problem with the JVM-external Tor
+	 *             processes that we cannot handle.
+	 */
+	public static void main(String[] args) throws TorProcessException {
+
+		// create a network to initialize a test case
+		Network network = NetworkFactory.createNetwork("example3");
+
+		// create two proxy nodes with names "proxy1"/"proxy2", control ports
+		// 7001/7011, and SOCKS ports 7002/7012
+		ProxyNode proxy1 = network.createProxy("proxy1", 7001, 7002);
+		network.createProxy("proxy2", 7011, 7012);
+
+		// add hidden service to the configuration of proxy1
+		proxy1.addHiddenService("hidServ", 7005, 80);
+
+		// write configuration of proxy node
+		network.writeConfigurations();
+
+		// start proxy node and wait until it has opened a circuit with a
+		// timeout of 5 seconds
+		if (!network.startNodes(5000)) {
+
+			// failed to start the proxy
+			System.out.println("Failed to start the proxy!");
+			return;
+		}
+
+		// hup until proxy has built circuits (5 retries, 10 seconds timeout
+		// each)
+		if (!network.hupUntilUp(5, 10000)) {
+
+			// failed to build circuits
+			System.out.println("Failed to build circuits!");
+			return;
+		}
+
+		// obtain reference to event manager to be able to respond to events
+		EventManager manager = network.getEventManager();
+
+		// wait for 3 minutes that the proxy has published its first RSD
+		if (!manager.waitForAnyOccurence(proxy1, Event.NODE_RSD_PUBLISHED,
+				3L * 60L * 1000L)) {
+
+			// failed to publish an RSD
+			System.out.println("Failed to publish an RSD!");
+			return;
+		}
+
+		// create server application
+		ServerApplication server = network.createServer("server", 7005);
+
+		// determine onion address for hidden service
+		String onionAddress = proxy1.getOnionAddress("hidServ");
+
+		// create client application
+		ClientApplication client = network.createClient("client", onionAddress,
+				80, 7012);
+
+		// create event listener to listen for client and server application
+		// events
+		EventListener clientAndServerEventListener = new EventListener() {
+
+			private long requestReceivedAtServer;
+
+			// remember time when request was sent and when it was received
+			private long requestSentFromClient;
+
+			public void handleEvent(Event event) {
+				switch (event) {
+				case APPLICATION_SENDING_REQUEST:
+					requestSentFromClient = System.currentTimeMillis();
+					break;
+				case APPLICATION_REQUEST_RECEIVED:
+					requestReceivedAtServer = System.currentTimeMillis();
+					System.out.println("Request took "
+							+ (requestReceivedAtServer - requestSentFromClient)
+							+ " millis from client to server!");
+					break;
+				case APPLICATION_REPLY_RECEIVED:
+					System.out
+							.println("Request took "
+									+ (System.currentTimeMillis() - requestSentFromClient)
+									+ " millis for the rount-trip and "
+									+ (System.currentTimeMillis() - requestReceivedAtServer)
+									+ " millis from server to client!");
+				}
+			}
+		};
+
+		// register event handler for client and server application events
+		manager.addEventListener(client, clientAndServerEventListener);
+		manager.addEventListener(server, clientAndServerEventListener);
+
+		// start server
+		server.listen();
+
+		// perform at most five request with a timeout of 45 seconds each
+		client.performRequest(5, 45000, true);
+
+		// block this thread as long as client requests are running
+		manager.waitForAnyOccurence(client,
+				Event.APPLICATION_REQUESTS_PERFORMED);
+
+		// shut down proxy
+		network.shutdownNodes();
+
+		System.out.println("Exiting...");
+	}
+}


Property changes on: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/AdvertisingAndAccessingHiddenServiceOverPublicTorNetwork.java
___________________________________________________________________
Name: svn:executable
   + *

Added: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/AdvertisingHiddenServiceToPublicTorNetwork.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/AdvertisingHiddenServiceToPublicTorNetwork.java	2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/AdvertisingHiddenServiceToPublicTorNetwork.java	2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,105 @@
+package de.uniba.wiai.lspi.puppetor.examples;
+
+import de.uniba.wiai.lspi.puppetor.Event;
+import de.uniba.wiai.lspi.puppetor.EventListener;
+import de.uniba.wiai.lspi.puppetor.EventManager;
+import de.uniba.wiai.lspi.puppetor.Network;
+import de.uniba.wiai.lspi.puppetor.NetworkFactory;
+import de.uniba.wiai.lspi.puppetor.ProxyNode;
+import de.uniba.wiai.lspi.puppetor.TorProcessException;
+
+/**
+ * Example for advertising a hidden service to the public Tor network and
+ * observe publication of rendezvous service descriptors.
+ * 
+ * @author kloesing
+ */
+public class AdvertisingHiddenServiceToPublicTorNetwork {
+
+	/**
+	 * Sets up and runs the test.
+	 * 
+	 * @param args
+	 *            Command-line arguments are ignored.
+	 * 
+	 * @throws TorProcessException
+	 *             Thrown if there is a problem with the JVM-external Tor
+	 *             processes that we cannot handle.
+	 */
+	public static void main(String[] args) throws TorProcessException {
+
+		// though we only need one proxy, we always need to create a network
+		// to initialize a test case
+		Network network = NetworkFactory.createNetwork("example2");
+
+		// create a single proxy node with name "proxy", control port 7001,
+		// and SOCKS port 7002
+		ProxyNode proxy = network.createProxy("proxy", 7001, 7002);
+
+		// add hidden service to the configuration
+		proxy.addHiddenService("hidServ", 7005, 80);
+
+		// write configuration of proxy node
+		network.writeConfigurations();
+
+		// create event listener to listen for events from our proxy
+		EventListener proxyEventListener = new EventListener() {
+
+			// remember time when request was sent
+			private long circuitOpened = -1;
+
+			public void handleEvent(Event event) {
+				switch (event) {
+				case NODE_CIRCUIT_OPENED:
+					if (circuitOpened == -1) {
+						circuitOpened = System.currentTimeMillis();
+					}
+					break;
+				case NODE_RSD_PUBLISHED:
+					System.out.println("RSD published "
+							+ (System.currentTimeMillis() - circuitOpened)
+							+ " millis after first circuit was opened");
+				}
+			}
+		};
+
+		// obtain reference to event manager to be able to respond to events
+		EventManager manager = network.getEventManager();
+
+		// register event handler for proxy events
+		manager.addEventListener(proxy, proxyEventListener);
+
+		// start proxy node and wait until it has opened a circuit with a
+		// timeout of 5 seconds
+		if (!network.startNodes(5000)) {
+
+			// failed to start the proxy
+			System.out.println("Failed to start the proxy!");
+			return;
+		}
+
+		// hup until proxy has built circuits (5 retries, 10 seconds timeout
+		// each)
+		if (!network.hupUntilUp(5, 10000)) {
+
+			// failed to build circuits
+			System.out.println("Failed to build circuits!");
+			return;
+		}
+
+		// let it run for 5 minutes and observe when RSDs are published...
+		System.out
+				.println("Waiting for 5 minutes and observing RSD publications...");
+
+		try {
+			Thread.sleep(5L * 60L * 1000L);
+		} catch (InterruptedException e) {
+			// do nothing
+		}
+
+		// shut down proxy
+		network.shutdownNodes();
+
+		System.out.println("Exiting...");
+	}
+}


Property changes on: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/examples/AdvertisingHiddenServiceToPublicTorNetwork.java
___________________________________________________________________
Name: svn:executable
   + *

Added: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/ClientApplicationImpl.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/ClientApplicationImpl.java	2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/ClientApplicationImpl.java	2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,390 @@
+/*
+ * NOTICE: This file is still work in progress. As you can see the Java
+ * documentation is written partly in German and in English and logging and
+ * documentation still need some work. Sorry for any inconvenience!  
+ */
+package de.uniba.wiai.lspi.puppetor.impl;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintStream;
+import java.net.InetSocketAddress;
+import java.net.Proxy;
+import java.net.Socket;
+import java.net.SocketTimeoutException;
+import java.net.Proxy.Type;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import de.uniba.wiai.lspi.puppetor.ClientApplication;
+import de.uniba.wiai.lspi.puppetor.Event;
+
+/**
+ * Implementation of ClientApplication.
+ * 
+ * @author kloesing
+ */
+public class ClientApplicationImpl implements ClientApplication {
+
+	/**
+	 * Thread that performs the requests in the background
+	 */
+	private RequestThread clientThread;
+
+	/**
+	 * Logger for this client
+	 */
+	private Logger logger;
+
+	/**
+	 * Target address for requests; can be either a server address or an onion
+	 * address.
+	 */
+	private String targetAddress;
+
+	/**
+	 * Target port for requests; can be either a server port or a virtual port
+	 * of a hidden service.
+	 */
+	private int targetPort;
+
+	/**
+	 * SOCKS port of the local Tor node.
+	 */
+	private int socksPort;
+
+	/**
+	 * 
+	 */
+	private EventManagerImpl eventManager;
+
+	/**
+	 * 
+	 */
+	private String clientApplicationName;
+
+	/**
+	 * (logging finished)
+	 * 
+	 * erzeugt neue client app innerhalb der jvm, started aber noch keine
+	 * requests.
+	 * 
+	 * @param network
+	 * 
+	 * @param clientApplicationName
+	 *            für logging
+	 * @param targetAddress
+	 *            Target address for requests; can be either a server address or
+	 *            an onion address.
+	 * @param targetPort
+	 *            Target port for requests; can be either a server port or a
+	 *            virtual port of a hidden service.
+	 * @param socksPort
+	 *            SOCKS port of the local Tor node.
+	 */
+	ClientApplicationImpl(NetworkImpl network, String clientApplicationName,
+			String targetAddress, int targetPort, int socksPort) {
+
+		// TODO make sure that name is loggable!!
+
+		// create logger
+		this.logger = Logger.getLogger("application." + clientApplicationName);
+
+		// log entering
+		this.logger.entering(this.getClass().getName(),
+				"ClientApplicationImpl", new Object[] { clientApplicationName,
+						targetAddress, targetPort, socksPort });
+
+		// check parameters
+		if (clientApplicationName == null
+				|| clientApplicationName.length() == 0 || targetAddress == null
+				|| targetAddress.length() == 0 || targetPort < 0
+				|| targetPort > 65535 || socksPort < 0 || socksPort > 65535) {
+
+			IllegalArgumentException e = new IllegalArgumentException("bla");
+			this.logger.throwing(this.getClass().getName(),
+					"ClientApplicationImpl", e);
+			throw e;
+		}
+
+		// remember parameters
+		this.clientApplicationName = clientApplicationName;
+		this.targetAddress = targetAddress;
+		this.targetPort = targetPort;
+		this.socksPort = socksPort;
+
+		// obtain reference on event manager
+		this.eventManager = network.getEventManagerImpl();
+
+		// log exiting
+		this.logger.exiting(this.getClass().getName(), "ClientApplicationImpl");
+	}
+
+	/**
+	 * bricht alle laufenden requests ab
+	 * 
+	 */
+	public void stopRequest() {
+
+		// log entering
+		this.logger.entering(this.getClass().getName(), "stopRequest");
+
+		// check if a request is running
+		if (this.clientThread == null) {
+			throw new IllegalStateException("No request has been started!");
+		}
+
+		// log this event
+		this.logger.log(Level.FINE, "Shutting down client");
+
+		// interrupt thread
+		this.clientThread.stopRequest();
+
+		// log exiting
+		this.logger.exiting(this.getClass().getName(), "stopRequest");
+	}
+
+	/**
+	 * (logging done)
+	 * 
+	 * startet einen oder mehrere aufeinander folgende aufrufe an die im
+	 * konstruktor übergebene adresse und port.
+	 */
+	public void performRequest(int retries, long timeoutForEachRetry,
+			boolean stopOnSuccess) {
+
+		// log entering
+		this.logger.entering(this.getClass().getName(), "performRequest",
+				new Object[] { retries, timeoutForEachRetry, stopOnSuccess });
+
+		// check parameters
+		if (retries <= 0 || timeoutForEachRetry < 0) {
+			throw new IllegalArgumentException();
+		}
+
+		// check if we already have started a request (TODO change this to allow
+		// multiple requests in parallel? would be possible)
+		if (this.clientThread != null) {
+			throw new IllegalStateException(
+					"Another request has already been started!");
+		}
+
+		// create a thread that performs requests in the background
+		this.clientThread = new RequestThread(retries, timeoutForEachRetry,
+				stopOnSuccess);
+		this.clientThread.setName("Request Thread");
+		this.clientThread.setDaemon(true);
+		this.clientThread.start();
+
+		// log exiting
+		this.logger.exiting(this.getClass().getName(), "performRequest");
+	}
+
+	/**
+	 * (logging done)
+	 * 
+	 * @author kloesing
+	 * 
+	 */
+	private class RequestThread extends Thread {
+
+		private int retries;
+
+		private long timeoutForEachRetry;
+
+		private boolean connected;
+
+		private boolean stopOnSuccess;
+
+		RequestThread(int retries, long timeoutForEachRetry,
+				boolean stopOnSuccess) {
+
+			// log entering
+			logger
+					.entering(this.getClass().getName(), "RequestThread",
+							new Object[] { retries, timeoutForEachRetry,
+									stopOnSuccess });
+
+			// remember parameters
+			this.retries = retries;
+			this.timeoutForEachRetry = timeoutForEachRetry;
+			this.stopOnSuccess = stopOnSuccess;
+
+			// start connected
+			this.connected = true;
+
+			// log exiting
+			logger.exiting(this.getClass().getName(), "RequestThread");
+		}
+
+		/**
+		 * stoppt diesen request egal wo er gerade steht
+		 * 
+		 */
+		public void stopRequest() {
+
+			// log entering
+			logger.entering(this.getClass().getName(), "stopRequest");
+
+			// change connected state to false and interrupt thread
+			this.connected = false;
+			this.interrupt();
+
+			// log exiting
+			logger.exiting(this.getClass().getName(), "stopRequest");
+		}
+
+		/**
+		 * (logging done)
+		 */
+		@Override
+		public void run() {
+
+			// log entering
+			logger.entering(this.getClass().getName(), "run");
+
+			try {
+
+				// set Tor as proxy
+				InetSocketAddress isa = new InetSocketAddress("127.0.0.1",
+						socksPort);
+				Proxy p = new Proxy(Type.SOCKS, isa);
+
+				// create target address for socket
+				InetSocketAddress hs = new InetSocketAddress(targetAddress,
+						targetPort);
+
+				// start loop
+				for (int i = 0; connected && i < retries; i++) {
+
+					// log this try
+					logger.log(Level.FINE, "Trying to perform request");
+
+					// remember when we started
+					long timeBeforeConnectionAttempt = System
+							.currentTimeMillis();
+
+					// send event to event manager
+					eventManager.observeEvent(ClientApplicationImpl.this,
+							Event.APPLICATION_SENDING_REQUEST);
+
+					Socket s = null;
+					try {
+
+						// create new socket using Tor as proxy
+						s = new Socket(p);
+
+						// try to connect to remote server
+						s.connect(hs, (int) timeoutForEachRetry);
+
+						// open output stream to write request
+						PrintStream out = new PrintStream(s.getOutputStream());
+						out.print("GET / HTTP/1.0\r\n\r\n");
+
+						// open input stream to read reply
+						BufferedReader in = new BufferedReader(
+								new InputStreamReader(s.getInputStream()));
+
+						// only read the first char in the response; this method
+						// blocks until there is a response
+						in.read();
+
+						// send event to event manager
+						eventManager.observeEvent(ClientApplicationImpl.this,
+								Event.APPLICATION_REPLY_RECEIVED);
+
+						// if we should stop on success, stop further connection
+						// attempts
+						if (this.stopOnSuccess) {
+							this.connected = false;
+						}
+
+						// clean up socket
+						in.close();
+						out.close();
+						s.close();
+
+					} catch (SocketTimeoutException e) {
+
+						// log warning
+						logger.log(Level.WARNING,
+								"Connection to remote server timed out!", e);
+
+						// send event to event manager
+						eventManager.observeEvent(ClientApplicationImpl.this,
+								Event.APPLICATION_GAVE_UP_REQUEST);
+
+						// try again immediately, if there are retries left
+
+					} catch (IOException e) {
+
+						// log warning
+						logger.log(Level.WARNING,
+								"Connection to remote server could not be "
+										+ "established!", e);
+
+						// send event to event manager
+						eventManager.observeEvent(ClientApplicationImpl.this,
+								Event.APPLICATION_GAVE_UP_REQUEST);
+
+						// wait for the rest of the timeout
+						long timeOfTimeoutLeft = timeBeforeConnectionAttempt
+								+ timeoutForEachRetry
+								- System.currentTimeMillis();
+						if (timeOfTimeoutLeft > 0) {
+							try {
+								Thread.sleep(timeOfTimeoutLeft);
+							} catch (InterruptedException ex) {
+								// do nothing
+							}
+						}
+
+					} finally {
+
+						// close connection
+						try {
+
+							// try to close socket
+							logger.log(Level.FINER, "Trying to close socket.");
+							s.close();
+							logger.log(Level.FINE, "Socket closed.");
+
+						} catch (Exception e1) {
+
+							// log warning
+							logger.log(Level.WARNING,
+									"Exception when trying to close socket!",
+									e1);
+						}
+					}
+				}
+
+			} catch (Exception e) {
+
+				// log that we have been interrupted
+				logger.log(Level.WARNING, "Client has been interrupted!", e);
+
+			} finally {
+
+				// we are done here
+				logger.log(Level.FINE, "Requests performed!");
+
+				// send event to event manager
+				eventManager.observeEvent(ClientApplicationImpl.this,
+						Event.APPLICATION_REQUESTS_PERFORMED);
+
+				// log exiting
+				logger.exiting(this.getClass().getName(), "run");
+			}
+		}
+	}
+
+	@Override
+	public String toString() {
+		return this.getClass().getSimpleName() + ": clientApplicationName=\""
+				+ this.clientApplicationName + "\", targetAddress=\""
+				+ this.targetAddress + "\", targetPort=" + this.targetPort
+				+ ", socksPort=" + this.socksPort;
+	}
+}


Property changes on: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/ClientApplicationImpl.java
___________________________________________________________________
Name: svn:executable
   + *

Added: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/DirectoryNodeImpl.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/DirectoryNodeImpl.java	2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/DirectoryNodeImpl.java	2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,84 @@
+/*
+ * NOTICE: This file is still work in progress. As you can see the Java
+ * documentation is written partly in German and in English and logging and
+ * documentation still need some work. Sorry for any inconvenience!  
+ */
+package de.uniba.wiai.lspi.puppetor.impl;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+import de.uniba.wiai.lspi.puppetor.DirectoryNode;
+import de.uniba.wiai.lspi.puppetor.TorProcessException;
+
+public class DirectoryNodeImpl extends RouterNodeImpl implements DirectoryNode {
+	/**
+	 * Initializes this Tor node, but does not start it, yet.
+	 */
+	public DirectoryNodeImpl(NetworkImpl network, String nodeName,
+			int controlPort, int socksPort, int orPort, int dirPort) {
+		super(network, nodeName, controlPort, socksPort, orPort, dirPort);
+
+		// configure this node as an authoritative directory
+		this.configuration.add("AuthoritativeDirectory 1");
+
+		// TODO make this a little bit more configurable---same as to
+		// location of tor.exe?
+		this.configuration
+				.add("RecommendedVersions 0.1.2.12-rc,0.1.2.7-alpha-dev");
+
+		this.configuration.add("VersioningAuthoritativeDirectory 1");
+
+		this.configuration.add("DirAllowPrivateAddresses 1");
+
+		this.configuration.add("NamingAuthoritativeDirectory 1");
+
+		this.configuration.add("V1AuthoritativeDirectory 1");
+
+		// TODO this only works since Tor 0.1.2.x!!!
+		this.configuration.add("HSAuthoritativeDir 1");
+
+	}
+
+	public void writeApprovedRouters(Set<String> approvedRouters)
+			throws TorProcessException {
+		
+		// check param!
+		
+		try {
+			File approvedRoutersFile = new File(this.workingDir
+					.getAbsolutePath()
+					+ File.separator + "approved-routers");
+			BufferedWriter bw = new BufferedWriter(new FileWriter(
+					approvedRoutersFile));
+			SortedSet<String> sortedApprovedRouters = new TreeSet<String>(
+					approvedRouters);
+			for (String approvedRouter : sortedApprovedRouters) {
+				bw.write(approvedRouter + "\n");
+			}
+			bw.close();
+		} catch (IOException e) {
+			throw new TorProcessException(e);
+		}
+	}
+
+	public synchronized String determineDirServerString()
+			throws TorProcessException {
+
+		// determine fingerprint
+		String fingerprint = determineFingerprint();
+
+		// cut off router nickname
+		fingerprint = fingerprint.substring(fingerprint.indexOf(" ") + 1);
+
+		// put together everything
+		String dirServerString = "DirServer " + this.nodeName
+				+ " hs 127.0.0.1:" + this.dirPort + " " + fingerprint;
+		return dirServerString;
+	}
+}


Property changes on: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/DirectoryNodeImpl.java
___________________________________________________________________
Name: svn:executable
   + *

Added: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/EventManagerImpl.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/EventManagerImpl.java	2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/EventManagerImpl.java	2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,348 @@
+/*
+ * NOTICE: This file is still work in progress. As you can see the Java
+ * documentation is written partly in German and in English and logging and
+ * documentation still need some work. Sorry for any inconvenience!  
+ */
+package de.uniba.wiai.lspi.puppetor.impl;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import de.uniba.wiai.lspi.puppetor.Event;
+import de.uniba.wiai.lspi.puppetor.EventListener;
+import de.uniba.wiai.lspi.puppetor.EventManager;
+import de.uniba.wiai.lspi.puppetor.EventSource;
+
+public class EventManagerImpl implements EventManager {
+
+	private Logger logger;
+
+	EventManagerImpl(String networkName) {
+		
+//		 TODO make sure that name is loggable!!
+		
+		this.logger = Logger.getLogger("event." + networkName);
+		this.observedEvents = new HashMap<EventSource, List<Event>>();
+		this.eventHandlers = new HashMap<EventSource, Set<EventListener>>();
+	}
+
+	private Map<EventSource, List<Event>> observedEvents;
+
+	private Map<EventSource, Set<EventListener>> eventHandlers;
+
+	public synchronized List<Event> addEventListener(EventSource source,
+			EventListener listener) {
+
+		// log entering
+		this.logger.entering(this.getClass().getName(), "addEventListener",
+				new Object[] { source, listener });
+
+		// check parameters
+		if (source == null || listener == null) {
+			throw new IllegalArgumentException();
+		}
+
+		// if necessary, create new event listener set for source
+		if (!this.eventHandlers.containsKey(source)) {
+			this.eventHandlers.put(source, new HashSet<EventListener>());
+		}
+
+		// add listener
+		this.eventHandlers.get(source).add(listener);
+
+		// log change
+		this.logger
+				.log(Level.FINE, "Added event listener for source " + source);
+
+		// log exiting and return
+		List<Event> result = getEventHistory(source);
+		this.logger.exiting(this.getClass().getName(), "addEventListener",
+				result);
+		return result;
+	}
+
+	public synchronized List<Event> getEventHistory(EventSource source) {
+
+		// log entering
+		this.logger.entering(this.getClass().getName(), "getNodeEventHistory",
+				source);
+
+		// check parameter
+		if (source == null) {
+			throw new IllegalArgumentException();
+		}
+
+		// prepare result
+		List<Event> result = new ArrayList<Event>();
+
+		// did we already observe events for this source?
+		if (this.observedEvents.containsKey(source)) {
+			// yes, add all events to result list
+			result.addAll(this.observedEvents.get(source));
+		}
+
+		// log exiting and return result
+		this.logger.exiting(this.getClass().getName(), "getNodeEventHistory",
+				result);
+		return result;
+
+	}
+
+	public synchronized boolean hasEventOccured(EventSource source, Event event) {
+
+		// log entering
+		this.logger.entering(this.getClass().getName(), "hasEventOccured",
+				new Object[] { source, event });
+
+		// check parameters
+		if (source == null || event == null) {
+			throw new IllegalArgumentException();
+		}
+
+		// determine result
+		boolean result = this.observedEvents.containsKey(source)
+				&& this.observedEvents.get(source).contains(event);
+
+		// log exiting and return result
+		this.logger.exiting(this.getClass().getName(), "hasEventOccured",
+				result);
+		return result;
+	}
+
+	public synchronized void removeEventListener(EventListener eventListener) {
+
+		// log entering
+		this.logger.entering(this.getClass().getName(), "removeEventListener",
+				eventListener);
+
+		// check parameters
+		if (eventListener == null) {
+			throw new IllegalArgumentException();
+		}
+
+		// don't know to which source this listener has been added (may to more
+		// than one), so remove it from all possible sets
+		for (Set<EventListener> set : eventHandlers.values()) {
+			if (set.remove(eventListener)) {
+				logger.log(Level.FINE, "Removed event listener!");
+			}
+		}
+
+		// log exiting
+		this.logger.exiting(this.getClass().getName(), "removeEventListener");
+	}
+
+	public synchronized void waitForAnyOccurence(EventSource source, Event event) {
+
+		// log entering
+		this.logger.entering(this.getClass().getName(), "waitForAnyOccurence",
+				new Object[] { source, event });
+
+		// check parameters
+		if (source == null || event == null) {
+			throw new IllegalArgumentException();
+		}
+
+		// invoke overloaded method with maximumTimeToWaitInMillis of -1L which
+		// means to wait forever
+		waitForAnyOccurence(source, event, -1L);
+
+		// log exiting
+		this.logger.exiting(this.getClass().getName(), "waitForAnyOccurence");
+
+	}
+
+	public synchronized boolean waitForAnyOccurence(EventSource source,
+			Event event, long maximumTimeToWaitInMillis) {
+
+		// log entering
+		this.logger.entering(this.getClass().getName(), "waitForAnyOccurence",
+				new Object[] { source, event, maximumTimeToWaitInMillis });
+
+		// check parameters
+		if (source == null || event == null) {
+			throw new IllegalArgumentException();
+		}
+
+		// check if we have already observed the event
+		if (this.hasEventOccured(source, event)) {
+
+			this.logger.log(Level.FINE, "Waiting for any occurence of event "
+					+ event + " returned immediately!");
+
+			this.logger.exiting(this.getClass().getName(),
+					"waitForAnyOccurence", true);
+			return true;
+		}
+
+		// invoke method that waits for next occurence of the event
+		boolean result = waitForNextOccurence(source, event,
+				maximumTimeToWaitInMillis);
+
+		// log exiting and return result
+		this.logger.exiting(this.getClass().getName(), "waitForAnyOccurence",
+				result);
+		return result;
+
+	}
+
+	public synchronized void waitForNextOccurence(EventSource source,
+			Event event) {
+
+		// log entering
+		this.logger.entering(this.getClass().getName(), "waitForNextOccurence",
+				new Object[] { source, event });
+
+		// check parameters
+		if (source == null || event == null) {
+			throw new IllegalArgumentException();
+		}
+
+		// invoke overloaded method with maximumTimeToWaitInMillis of -1L which
+		// means to wait forever
+		waitForNextOccurence(source, event, -1L);
+
+		// log exiting
+		this.logger.exiting(this.getClass().getName(), "waitForNextOccurence");
+
+	}
+
+	public synchronized boolean waitForNextOccurence(EventSource source,
+			Event event, long maximumTimeToWaitInMillis) {
+
+		// log entering
+		this.logger.entering(this.getClass().getName(), "waitForNextOccurence",
+				new Object[] { source, event, maximumTimeToWaitInMillis });
+
+		// check parameters
+		if (source == null || event == null) {
+			throw new IllegalArgumentException();
+		}
+
+		// distinguish between negative waiting time (wait forever) and zero or
+		// positive waiting time
+		if (maximumTimeToWaitInMillis < 0) {
+
+			// wait forever
+			while (!this.hasEventOccured(source, event)) {
+
+				this.logger.log(Level.FINEST,
+						"We will wait infinetely for the next occurence of "
+								+ "event " + event + " from source " + source
+								+ "...");
+				try {
+					wait();
+				} catch (InterruptedException e) {
+					// don't handle
+				}
+
+				this.logger.log(Level.FINEST,
+						"We have been notified about an observed event while "
+								+ "waiting for events of type " + event
+								+ " from source " + source
+								+ "; need to check whether the observed event "
+								+ "is what we are looking for...");
+			}
+
+			this.logger.log(Level.FINE, "Waiting for occurence of event "
+					+ event + " succeeded!");
+
+			// log exiting and return result
+			this.logger.exiting(this.getClass().getName(),
+					"waitForNextOccurence", true);
+			return true;
+
+		} else {
+
+			// wait for the given time at most
+			long endOfTime = System.currentTimeMillis()
+					+ maximumTimeToWaitInMillis;
+			long timeLeft = 0;
+			while (!this.hasEventOccured(source, event)
+					&& (timeLeft = endOfTime - System.currentTimeMillis()) > 0) {
+
+				this.logger.log(Level.FINEST, "We will wait for " + timeLeft
+						+ " millis for the next occurence of event " + event
+						+ " from source " + source + "...");
+
+				try {
+					wait(timeLeft);
+				} catch (InterruptedException e) {
+					// don't handle
+				}
+
+				this.logger.log(Level.FINEST,
+						"We have been notified about an observed event while "
+								+ "waiting for events of type " + event
+								+ " from source " + source
+								+ "; need to check whether the observed event "
+								+ "is what we are looking for...");
+			}
+
+			// determine result
+			boolean result = this.hasEventOccured(source, event);
+
+			this.logger.log(Level.FINE, "Waiting for next occurence of event "
+					+ event + " from source " + source
+					+ (result ? " succeeded!" : " did not succeed!"));
+
+			// log exiting and return result
+			this.logger.exiting(this.getClass().getName(),
+					"waitForNextOccurence", result);
+			return result;
+		}
+	}
+
+	/**
+	 * wird lokal aufgerufen; speichert event in history und meldet es an alle
+	 * registrierten event handler weiter
+	 * 
+	 */
+	synchronized void observeEvent(EventSource source, Event event) {
+
+		// log entering
+		this.logger.entering(this.getClass().getName(), "observeEvent",
+				new Object[] { source, event });
+
+		this.logger.log(Level.FINE, "Observed event " + event + " from source "
+				+ source + "!");
+
+		// remember observed event
+		if (!this.observedEvents.containsKey(source)) {
+			this.observedEvents.put(source, new ArrayList<Event>());
+		}
+		this.observedEvents.get(source).add(event);
+
+		// notify waiting threads
+		notifyAll();
+
+		// inform event listeners
+		if (this.eventHandlers.containsKey(source)) {
+
+			// make a copy of the event handler set, because some event handlers
+			// might want to remove itself from this set while handling the
+			// event
+
+			Set<EventListener> copyOfEventHandlers = new HashSet<EventListener>(
+					this.eventHandlers.get(source));
+
+			for (EventListener eventHandler : copyOfEventHandlers) {
+
+				this.logger.log(Level.FINE, "Informing event listener "
+						+ eventHandler + " about recently observed event "
+						+ event + " from source " + source + "!");
+				eventHandler.handleEvent(event);
+			}
+		}
+
+		// log exiting
+		this.logger.exiting(this.getClass().getName(), "observeEvent");
+	}
+
+}


Property changes on: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/EventManagerImpl.java
___________________________________________________________________
Name: svn:executable
   + *

Added: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/NetworkImpl.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/NetworkImpl.java	2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/NetworkImpl.java	2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,461 @@
+/*
+ * NOTICE: This file is still work in progress. As you can see the Java
+ * documentation is written partly in German and in English and logging and
+ * documentation still need some work. Sorry for any inconvenience!  
+ */
+package de.uniba.wiai.lspi.puppetor.impl;
+
+import java.io.File;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import de.uniba.wiai.lspi.puppetor.ClientApplication;
+import de.uniba.wiai.lspi.puppetor.DirectoryNode;
+import de.uniba.wiai.lspi.puppetor.Event;
+import de.uniba.wiai.lspi.puppetor.EventListener;
+import de.uniba.wiai.lspi.puppetor.EventManager;
+import de.uniba.wiai.lspi.puppetor.Network;
+import de.uniba.wiai.lspi.puppetor.NetworkState;
+import de.uniba.wiai.lspi.puppetor.NodeState;
+import de.uniba.wiai.lspi.puppetor.ProxyNode;
+import de.uniba.wiai.lspi.puppetor.RouterNode;
+import de.uniba.wiai.lspi.puppetor.ServerApplication;
+import de.uniba.wiai.lspi.puppetor.TorProcessException;
+
+public class NetworkImpl implements Network {
+
+	private EventManagerImpl eventManager;
+
+	@Override
+	public String toString() {
+		return this.getClass().getSimpleName() + ": networkName=\""
+				+ this.networkName + "\", networkState="
+				+ this.networkState.toString() + ", nodes.size()="
+				+ this.nodes.size();
+	}
+
+	private Logger logger;
+
+	/**
+	 * Contains the name of this node configuration which is the String
+	 * conversion of System.currentTimeMillis().
+	 */
+	protected String networkName;
+
+	/**
+	 * Creates an initially unpopulated Tor network and creates a new working
+	 * directory for it at test-env/randomTestID/.
+	 * 
+	 * @param networkName
+	 *            Name of this network configuration.
+	 * @param logToConsole
+	 *            Whether logging statements shall be written to the console or
+	 *            to a file.
+	 */
+	public NetworkImpl(String networkName) {
+		
+//		 TODO make sure that name is loggable!!
+		
+		this.logger = Logger.getLogger("network." + networkName);
+		this.logger.setLevel(Level.ALL);
+		this.networkName = networkName;
+		this.workingDir = new File("test-env/" + System.currentTimeMillis());
+		this.workingDir.mkdirs();
+
+		// TODO if we want to log to file, set this... somehow...
+		// this.logFile = new File(this.workingDir.getAbsolutePath()
+		// + "\\events.log");
+
+		this.eventManager = new EventManagerImpl(this.networkName);
+	}
+
+	public NetworkState getNetworkState() {
+		// TODO Auto-generated method stub
+		throw new UnsupportedOperationException(
+				"Auto-generated method stub in NetworkImpl.getNetworkState");
+	}
+
+	private boolean allNodesUp() {
+
+		// check if all nodes are up
+		for (ProxyNode node : this.nodes) {
+			if (!eventManager.hasEventOccured(node, Event.NODE_CIRCUIT_OPENED)) {
+				return false;
+			}
+		}
+		return true;
+	}
+
+	/**
+	 * check, (wait, check, hup)*
+	 * 
+	 * if all nodes get up during sleeping, we will wake up and return
+	 * immediately
+	 * 
+	 * @param retries
+	 * @param hupInterval
+	 * @throws Exception
+	 */
+	public boolean hupUntilUp(int retries, long hupInterval)
+			throws TorProcessException {
+
+		if (allNodesUp()) {
+			return true;
+		}
+
+		// register event handlers
+		final Thread sleepingThread = Thread.currentThread();
+		for (ProxyNode node : this.nodes) {
+			eventManager.addEventListener(node, new EventListener() {
+
+				public void handleEvent(Event event) {
+					if (event == Event.NODE_CIRCUIT_OPENED) {
+						sleepingThread.interrupt();
+						eventManager.removeEventListener(this);
+					}
+				}
+			});
+		}
+
+		for (int i = 0; i < retries; i++) {
+
+			// determine how long to sleep
+			long endOfSleeping = System.currentTimeMillis() + hupInterval;
+			long now;
+
+			while ((now = System.currentTimeMillis()) < endOfSleeping) {
+
+				// sleep
+				try {
+					Thread.sleep(endOfSleeping - now);
+				} catch (InterruptedException e) {
+				}
+
+				// check
+				if (allNodesUp()) {
+					return true;
+				}
+			}
+
+			// send a HUP signal to all nodes
+			for (ProxyNode node : this.nodes) {
+				node.hup();
+			}
+
+			// continue in loop
+		}
+
+		// no retries left and not all nodes are up; return failure
+		return false;
+
+	}
+
+	public void shutdownNodes() throws TorProcessException {
+		if (this.networkState != NetworkState.NODES_STARTED) {
+			throw new IllegalStateException();
+		}
+		TorProcessException firstCaughtException = null;
+		for (ProxyNode node : this.nodes) {
+			try {
+				node.shutdown();
+			} catch (TorProcessException e) {
+				if (firstCaughtException != null) {
+					firstCaughtException = e;
+				}
+			}
+		}
+		this.networkState = NetworkState.NODES_SHUT_DOWN;
+		if (firstCaughtException != null) {
+			throw firstCaughtException;
+		}
+	}
+
+	private static class NodeStarter extends Thread {
+
+		TorProcessException caughtException;
+
+		private long maximumTimeToWaitInMillis;
+
+		private ProxyNode node;
+
+		boolean success = false;
+
+		NodeStarter(ProxyNode node, long maximumTimeToWaitInMillis) {
+			this.node = node;
+			this.maximumTimeToWaitInMillis = maximumTimeToWaitInMillis;
+		}
+
+		@Override
+		public void run() {
+			try {
+				this.success = this.node
+						.startNode(this.maximumTimeToWaitInMillis);
+			} catch (TorProcessException e) {
+				this.caughtException = e;
+			}
+		}
+	}
+
+	public boolean startNodes(long maximumTimeToWaitInMillis)
+			throws TorProcessException {
+		if (this.networkState != NetworkState.CONFIGURATIONS_WRITTEN) {
+			throw new IllegalStateException();
+		}
+
+		if (maximumTimeToWaitInMillis < 0) {
+			throw new IllegalArgumentException();
+		}
+
+		// remember time when we begin starting the nodes
+		long before = System.currentTimeMillis();
+
+		// start nodes in parallel
+		Set<NodeStarter> allNodeStarters = new HashSet<NodeStarter>();
+		for (ProxyNode node : this.nodes) {
+			NodeStarter nodeStarter = new NodeStarter(node,
+					maximumTimeToWaitInMillis);
+			allNodeStarters.add(nodeStarter);
+			nodeStarter.start();
+		}
+
+		for (NodeStarter nodeStarter : allNodeStarters) {
+			try {
+				nodeStarter.join();
+			} catch (InterruptedException e) {
+				// we have some kind of problem here!
+				return false;
+			}
+			if (nodeStarter.caughtException != null) {
+				this.networkState = NetworkState.NODES_SHUT_DOWN;
+				throw nodeStarter.caughtException;
+			}
+			if (!nodeStarter.success) {
+				this.logger.log(Level.WARNING,
+						"Starting nodes was not successful in "
+								+ (maximumTimeToWaitInMillis / 1000)
+								+ " seconds.", this.networkName);
+				return false;
+			}
+		}
+
+		// check how long we took to start all nodes
+		long after = System.currentTimeMillis();
+		this.logger.log(Level.FINE, "Starting nodes was successful and took "
+				+ ((after - before) / 1000) + " seconds.", this.networkName);
+
+		// change state
+		this.networkState = NetworkState.NODES_STARTED;
+
+		return true;
+	}
+
+	public ClientApplication createClient(String clientApplicationName,
+			String targetAddress, int targetPort, int socksPort) {
+		return new ClientApplicationImpl(this, clientApplicationName,
+				targetAddress, targetPort, socksPort);
+	}
+
+	public ProxyNode createProxy(String nodeName, int controlPort, int socksPort) {
+
+		// TODO check parms
+
+		ProxyNodeImpl proxy = new ProxyNodeImpl(this, nodeName, controlPort,
+				socksPort);
+
+		// add to nodes
+		this.nodes.add(proxy);
+
+		return proxy;
+	}
+
+	public DirectoryNode createDirectory(String nodeName, int controlPort,
+			int socksPort, int orPort, int dirPort) {
+
+		DirectoryNode dir = new DirectoryNodeImpl(this, nodeName, controlPort,
+				socksPort, orPort, dirPort);
+
+		this.nodes.add(dir);
+
+		return dir;
+	}
+
+	public RouterNode createRouter(String nodeName, int controlPort,
+			int socksPort, int orPort, int dirPort) {
+		RouterNode router = new RouterNodeImpl(this, nodeName, controlPort,
+				socksPort, orPort, dirPort);
+
+		this.nodes.add(router);
+
+		return router;
+	}
+
+	public ServerApplication createServer(String serverApplicationName,
+			int serverPort) {
+		return new ServerApplicationImpl(this, serverApplicationName,
+				serverPort);
+	}
+
+	public EventManager getEventManager() {
+		return this.eventManager;
+	}
+
+	public EventManagerImpl getEventManagerImpl() {
+		return this.eventManager;
+	}
+
+	private NetworkState networkState = NetworkState.CONFIGURING_NODES;
+
+	/**
+	 * Writes configurations for all nodes in the network, including torrc and
+	 * approved-routers files. Directory nodes are configured first in order to
+	 * obtain their fingerprints for all torrc files. Next are routers to obtain
+	 * their fingerprints for the directories' approved-routers files. Proxies
+	 * are configured at the end.
+	 * 
+	 * This operation can only be invoked, if network status is CONFIGURABLE.
+	 * 
+	 * @throws IllegalStateException
+	 *             If method is invoked in network status other than
+	 *             CONFIGURABLE.
+	 */
+	public void writeConfigurations() throws TorProcessException {
+
+		// check state
+		if (this.networkState != NetworkState.CONFIGURING_NODES) {
+			throw new IllegalStateException();
+		}
+
+		// TODO don't we need to configure the nodes as private nodes, if we
+		// have a directory node here?!
+
+		for (ProxyNode node : this.nodes) {
+			if (node.getNodeState() == NodeState.CONFIGURING) {
+				node.writeConfiguration();
+			}
+		}
+
+		// change state
+		this.networkState = NetworkState.CONFIGURATIONS_WRITTEN;
+	}
+
+	/**
+	 * Directory that contains status information of all nodes contained in this
+	 * network, plus the common log file, if one is created.
+	 */
+	protected File workingDir;
+
+	/**
+	 * Returns the working directory of this network configuration which is in
+	 * test-env/networkName/.
+	 * 
+	 * @return Working directory of this network.
+	 */
+	File getWorkingDir() {
+		return workingDir;
+	}
+
+	/**
+	 * All nodes contained in this network. It is important that we store only
+	 * interface types to assure that all operations could also be performed by
+	 * the application itself.
+	 */
+	protected Set<ProxyNode> nodes = new HashSet<ProxyNode>();
+
+	/**
+	 * Internal class that is used to determine fingerprints in parallel.
+	 * 
+	 * 
+	 */
+	private static class FingerprintThread extends Thread {
+
+		private TorProcessException caughtException = null;
+
+		private RouterNode node = null;
+
+		FingerprintThread(RouterNode node) {
+			this.node = node;
+		}
+
+		public void run() {
+			try {
+				node.determineFingerprint();
+			} catch (TorProcessException e) {
+				this.caughtException = e;
+			}
+		}
+	}
+
+	private HashSet<String> approvedRoutersFingerprints;
+
+	Set<String> authorizedDirectoriesFingerprints = new HashSet<String>();
+
+	public void configureAsPrivateNetwork() throws TorProcessException {
+
+		// determine fingerprints for all directories and routers (can be done
+		// in parallel)
+		Set<FingerprintThread> fingerprintThreads = new HashSet<FingerprintThread>();
+		for (ProxyNode node : nodes) {
+			if (node instanceof RouterNode) {
+				RouterNode dirOrRouterNode = (RouterNode) node;
+				FingerprintThread fingerprintThread = new FingerprintThread(
+						dirOrRouterNode);
+				fingerprintThread.setName(node.getNodeName()
+						+ " Fingerprint Resolver");
+				fingerprintThreads.add(fingerprintThread);
+				fingerprintThread.start();
+			}
+		}
+		for (FingerprintThread fingerprintThread : fingerprintThreads) {
+			try {
+				fingerprintThread.join();
+			} catch (InterruptedException e) {
+				// ignore
+				logger.log(Level.WARNING, "fingerprint thread was interrupted.");
+			}
+			if (fingerprintThread.caughtException != null) {
+				throw fingerprintThread.caughtException;
+			}
+		}
+
+		// read DirServer strings for all directories from memory; they should
+		// have been read from disk before, so that this will perform really
+		// fast
+		for (ProxyNode node : this.nodes) {
+			if (node instanceof DirectoryNode) {
+				DirectoryNode dirNode = (DirectoryNode) node;
+				this.authorizedDirectoriesFingerprints.add(dirNode
+						.determineDirServerString());
+			}
+
+		}
+
+		// configure nodes
+		for (ProxyNode node : this.nodes) {
+			if (node.getNodeState() == NodeState.CONFIGURING) {
+				node
+						.configureDirServers(this.authorizedDirectoriesFingerprints);
+			}
+		}
+
+		// read fingerprints for all directories and routers from memory and
+		// write them to approved-routers file; they should have been read from
+		// disk before, so that this will perform really fast
+		this.approvedRoutersFingerprints = new HashSet<String>();
+		for (ProxyNode node : this.nodes) {
+			if (node instanceof RouterNode) {
+				RouterNode routerOrDirNode = (RouterNode) node;
+				this.approvedRoutersFingerprints.add(routerOrDirNode
+						.determineFingerprint());
+			}
+
+		}
+		for (ProxyNode node : this.nodes) {
+			if (node instanceof DirectoryNode) {
+				DirectoryNode dirNode = (DirectoryNode) node;
+				dirNode.writeApprovedRouters(this.approvedRoutersFingerprints);
+			}
+		}
+	}
+}


Property changes on: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/NetworkImpl.java
___________________________________________________________________
Name: svn:executable
   + *

Added: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/ProxyNodeImpl.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/ProxyNodeImpl.java	2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/ProxyNodeImpl.java	2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,730 @@
+/*
+ * NOTICE: This file is still work in progress. As you can see the Java
+ * documentation is written partly in German and in English or needs some work
+ * to refine it. This which will be the first thing to change in the next
+ * version. Sorry for any inconvenience!  
+ */
+package de.uniba.wiai.lspi.puppetor.impl;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.Socket;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import net.freehaven.tor.control.EventHandler;
+import net.freehaven.tor.control.TorControlConnection;
+import de.uniba.wiai.lspi.puppetor.Event;
+import de.uniba.wiai.lspi.puppetor.NodeState;
+import de.uniba.wiai.lspi.puppetor.ProxyNode;
+import de.uniba.wiai.lspi.puppetor.TorProcessException;
+
+/**
+ * Abstract Java proxy that represents one Tor node (i.e. Tor process) in the
+ * testbed network. Can be a Tor proxy, a Tor router, or a Tor directory.
+ * 
+ * @author kloesing
+ */
+public class ProxyNodeImpl implements ProxyNode {
+
+	// TODO make this configurable
+	protected static final File torExecutable = new File("tor");
+
+	/**
+	 * Logger for this Node instance which is called "node." plus the node's
+	 * name.
+	 */
+	protected Logger logger;
+
+	/**
+	 * Collects all configuration strings for this node during configuration
+	 * phase in the order they are added.
+	 */
+	protected List<String> configuration;
+
+	public synchronized NodeState getNodeState() {
+		return this.nodeState;
+	}
+
+	@Override
+	public String toString() {
+		return this.getClass().getSimpleName() + ": nodeName=\""
+				+ this.nodeName + "\", controlPort=" + this.controlPort
+				+ ", socksPort=" + this.socksPort;
+	}
+
+	/**
+	 * Port on which node will be listening for SOCKS connections.
+	 */
+	protected int socksPort;
+
+	private EventManagerImpl eventManager;
+
+	/**
+	 * LOGGING OK
+	 * 
+	 * Creates a new node and adds it to the given network. Does not yet create
+	 * a new Tor process.
+	 * 
+	 * @param network
+	 *            Network configuration to which this node belongs.
+	 * @param nodeName
+	 *            The node's name which may only consist of between 1 and 19
+	 *            alpha-numeric characters.
+	 * @param controlPort
+	 *            Port on which the Tor process will be listening for us as its
+	 *            controller.
+	 * 
+	 * @throws IllegalArgumentException
+	 *             If at least one of the parameters is null or has an invalid
+	 *             value.
+	 */
+	public ProxyNodeImpl(NetworkImpl network, String nodeName, int controlPort,
+			int socksPort) {
+
+		// make sure that name is a valid logger name
+		if (nodeName == null || nodeName.length() < 1 || nodeName.length() > 19
+				|| !nodeName.matches("[a-zA-Z0-9]*")) {
+
+			// prepare and throw exception
+			String reason = "\"" + nodeName + "\" is not a valid node name!";
+			IllegalArgumentException e = new IllegalArgumentException(reason);
+			throw e;
+		}
+
+		// create logger
+		this.logger = Logger.getLogger(nodeName + "."
+				+ this.getClass().getName());
+
+		// log entering
+		this.logger.entering(this.getClass().getName(), "ProxyNodeImpl",
+				new Object[] { network, nodeName, controlPort, socksPort });
+
+		// check remaining parameters
+		if (network == null || controlPort < 0 || controlPort > 65535
+				|| socksPort < 0 || socksPort > 65535) {
+
+			// prepare and throw exception
+			String reason = "Invalid parameter values: network=" + network
+					+ ", controlPort=" + controlPort + ", socksPort="
+					+ socksPort;
+			IllegalArgumentException e = new IllegalArgumentException(reason);
+			this.logger.log(Level.SEVERE, reason, e);
+			this.logger.throwing(this.getClass().getName(), "ProxyNodeImpl", e);
+			throw e;
+		}
+
+		// store parameter values
+		this.network = network;
+		this.nodeName = nodeName;
+		this.controlPort = controlPort;
+		this.socksPort = socksPort;
+
+		// obtain reference on event manager from network
+		this.eventManager = network.getEventManagerImpl();
+
+		// determine working directory
+		this.workingDir = new File(this.network.getWorkingDir()
+				.getAbsolutePath()
+				+ File.separator + nodeName + File.separator);
+
+		// create working directory
+		this.logger.log(Level.FINER, "Creating working directory \""
+				+ this.workingDir + "\"...");
+		this.workingDir.mkdirs();
+		this.logger.log(Level.FINE, "Created working directory \""
+				+ this.workingDir + "\"!");
+
+		// create reference on config file
+		this.configFile = new File(this.workingDir.getAbsolutePath()
+				+ File.separator + "torrc");
+
+		// initialize configuration with general-purpose configurations
+		this.configuration = new ArrayList<String>();
+		this.configuration.add("DataDirectory .");
+		this.configuration.add("SafeLogging 0");
+		this.configuration.add("UseEntryGuards 0");
+		this.configuration.add("ControlPort " + controlPort);
+		this.configuration.add("SocksPort " + socksPort);
+
+		// TODO why is this not the standard????
+		this.configuration.add("RunAsDaemon 0");
+
+		// TODO setting log level to lower level than NOTICE leads to error when
+		// connecting via control port! why?
+		this.configuration.add("Log notice stdout");
+
+		// allow two nodes on the same circuit to be in the same /16 net
+		// TODO this depends in private or public network setting!!!
+		// TODO even more important: this only works since Tor 0.1.2.x!!!
+		this.configuration.add("EnforceDistinctSubnets 0");
+
+		// don't rely on node verification, yet... TODO change?
+		this.configuration
+				.add("AllowInvalidNodes middle,rendezvous,exit,entry,introduction");
+
+		// initialize state
+		this.nodeState = NodeState.CONFIGURING;
+
+		// log exiting
+		this.logger.exiting(this.getClass().getName(), "ProxyNodeImpl");
+
+	}
+
+	/**
+	 * LOGGING OK
+	 * 
+	 * @see de.uniba.wiai.lspi.puppetor.ProxyNode#configureDirServers(java.util.Set)
+	 */
+	public synchronized void configureDirServers(
+			Set<String> authorizedDirServerStrings) {
+
+		// log entering
+		this.logger.entering(this.getClass().getName(), "configureDirServers",
+				authorizedDirServerStrings);
+
+		// check parameter
+		if (authorizedDirServerStrings == null) {
+
+			// prepare and throw exception
+			String reason = "Parameter may not be null!";
+			IllegalArgumentException e = new IllegalArgumentException(reason);
+			this.logger.log(Level.SEVERE, reason, e);
+			this.logger.throwing(this.getClass().getName(),
+					"configureDirServers", e);
+			throw e;
+		}
+
+		// add to configuration
+		this.configuration.addAll(authorizedDirServerStrings);
+		this.logger.log(Level.FINE, "Added "
+				+ authorizedDirServerStrings.size()
+				+ " DirServer entries to configuration!");
+
+		// log exiting
+		this.logger.exiting(this.getClass().getName(), "configureDirServers");
+
+	}
+
+	/**
+	 * Network to which this node belongs.
+	 */
+	protected NetworkImpl network;
+
+	/**
+	 * Name of this node that is used as part of the node's working directory
+	 * and logger.
+	 */
+	protected String nodeName;
+
+	/**
+	 * Port on which the process will be listening for us as its controller.
+	 */
+	protected int controlPort;
+
+	/**
+	 * Directory in which all information concerning this node is stored.
+	 */
+	protected File workingDir;
+
+	/**
+	 * LOGGING OK
+	 * 
+	 * @param maximumTimeToWaitInMillis <
+	 *            0 bedeutet unendlich lange
+	 * 
+	 */
+	public synchronized boolean startNode(long maximumTimeToWaitInMillis)
+			throws TorProcessException {
+
+		// log entering
+		this.logger.entering(this.getClass().getName(), "startNode",
+				maximumTimeToWaitInMillis);
+
+		// check state
+		if (this.nodeState != NodeState.CONFIGURATION_WRITTEN) {
+
+			// prepare and throw exception
+			String reason = "Node is not in state "
+					+ "NodeState.CONFIGURATION_WRITTEN!";
+			IllegalStateException e = new IllegalStateException(reason);
+			this.logger.log(Level.SEVERE, reason, e);
+			this.logger.throwing(this.getClass().getName(), "startNode", e);
+			throw e;
+		}
+
+		// start process
+		ProcessBuilder processBuilder = new ProcessBuilder(torExecutable
+				.getPath(), "-f", "torrc");
+		processBuilder.directory(this.workingDir);
+		processBuilder.redirectErrorStream(true);
+		try {
+			this.torProcess = processBuilder.start();
+			this.logger.log(Level.FINE, "Started Tor process successfully!");
+		} catch (IOException e) {
+
+			// prepare and throw exception
+			String reason = "Could not start Tor process!";
+			TorProcessException e1 = new TorProcessException(reason, e);
+			this.logger.log(Level.SEVERE, reason, e1);
+			this.logger.throwing(this.getClass().getName(), "startNode", e1);
+			throw e1;
+		}
+
+		// start thread to parse output
+		final BufferedReader br = new BufferedReader(new InputStreamReader(
+				this.torProcess.getInputStream()));
+		Thread outputThread = new Thread() {
+			@Override
+			public void run() {
+
+				// log entering
+				logger.entering(this.getClass().getName(), "run");
+
+				String line = null;
+				boolean waitingForControlPort = true;
+
+				// read output from Tor
+				try {
+
+					while ((line = br.readLine()) != null) {
+						logger.log(Level.FINEST, line);
+
+						// if control port has not been opened, check log
+						// statement for Tor to say this
+						if (waitingForControlPort
+								&& line.contains("Opening Control listener on")) {
+							logger.log(Level.FINE, "Observed event of type "
+									+ "Event.NODE_CONTROL_PORT_OPENED");
+							eventManager.observeEvent(ProxyNodeImpl.this,
+									Event.NODE_CONTROL_PORT_OPENED);
+							waitingForControlPort = false;
+						}
+					}
+				} catch (IOException e) {
+
+					// don't throw this exception, nobody will catch it!
+					String reason = "IOException when reading output from Tor "
+							+ "process!";
+					logger.log(Level.WARNING, reason, e);
+				}
+
+				// log exiting
+				logger.exiting(this.getClass().getName(), "run");
+			}
+		};
+		outputThread.setDaemon(true);
+		outputThread.setName(this.nodeName + " Output Parser");
+		outputThread.start();
+		this.logger.log(Level.FINE, "Started thread to parse output!");
+
+		// add shutdown hook that kills the process on JVM exit
+		final Process p = this.torProcess;
+		Runtime.getRuntime().addShutdownHook(new Thread() {
+			@Override
+			public void run() {
+
+				// log entering
+				logger.entering(this.getClass().getName(), "run");
+
+				// destroy Tor process
+				p.destroy();
+
+				// log exiting
+				logger.exiting(this.getClass().getName(), "run");
+			}
+		});
+		this.logger.log(Level.FINER,
+				"Started shutdown hook that will destroy the Tor process on "
+						+ "JVM exit!");
+
+		// wait for Tor to open the control port
+		this.logger.log(Level.FINER,
+				"Waiting for Tor to open its control port...");
+		if (!this.eventManager.waitForAnyOccurence(this,
+				Event.NODE_CONTROL_PORT_OPENED, maximumTimeToWaitInMillis)) {
+
+			// Tor did not open its control port
+			this.logger.log(Level.WARNING,
+					"Tor did not manage to open its control port within "
+							+ maximumTimeToWaitInMillis + " millis!");
+
+			// log exiting
+			this.logger.exiting(this.getClass().getName(), "startNode", false);
+			return false;
+		}
+		this.logger.log(Level.FINE,
+				"Tor has successfully opened its control port and told us "
+						+ "about that!");
+
+		// connect to the controller
+		this.logger.log(Level.FINER, "Connecting to control port...");
+		try {
+			Socket controlSocket = new java.net.Socket("127.0.0.1", controlPort);
+			this.conn = TorControlConnection.getConnection(controlSocket);
+			this.conn.authenticate(new byte[0]);
+		} catch (IOException e) {
+
+			// prepare and throw exception
+			String reason = "Could not connect to control port!";
+			TorProcessException e1 = new TorProcessException(reason, e);
+			this.logger.log(Level.SEVERE, reason, e1);
+			this.logger.throwing(this.getClass().getName(), "startNode", e1);
+			throw e1;
+		}
+		this.logger.log(Level.FINE, "Connected to control port successfully!");
+
+		// register event handler at Tor process
+		EventHandler handler = new EventHandler() {
+
+			public void bandwidthUsed(long read, long written) {
+				logger.log(Level.FINEST, "bandwidthUsed(read, written) = "
+						+ read + ", " + written);
+			}
+
+			public void circuitStatus(String status, String circID, String path) {
+				logger.log(Level.FINEST,
+						"circuitStatus(status, circID, path) = " + status
+								+ ", " + circID + ", " + path);
+			}
+
+			public void message(String severity, String msg) {
+				logger.log(Level.FINEST, "message(severity, msg) = " + severity
+						+ ", " + msg);
+				if (msg.contains("Uploading rendezvous descriptor")) {
+					logger.log(Level.FINER, "Observed event of type "
+							+ "NODE_RSD_PUBLISHED");
+					eventManager.observeEvent(ProxyNodeImpl.this,
+							Event.NODE_RSD_PUBLISHED);
+				} else if (msg
+						.contains("Tor has successfully opened a circuit.")) {
+					logger.log(Level.FINER, "Observed event of type "
+							+ "NODE_CIRCUIT_OPENED");
+					eventManager.observeEvent(ProxyNodeImpl.this,
+							Event.NODE_CIRCUIT_OPENED);
+				} else {
+					// log statement did not contain anything of interest for
+					// us...
+				}
+			}
+
+			public void newDescriptors(List orList) {
+				StringBuilder sb = new StringBuilder(
+						"newDescriptors(orList) =\n");
+				for (Object o : orList) {
+					sb.append(o + "\n");
+				}
+				logger.log(Level.FINEST, sb.toString());
+			}
+
+			public void orConnStatus(String status, String orName) {
+				logger.log(Level.FINEST, "orConnStatus(status, orName) = "
+						+ status + ", " + orName);
+			}
+
+			public void streamStatus(String status, String streamID,
+					String target) {
+				logger.log(Level.FINEST,
+						"streamStatus(status, streamID, target) = " + status
+								+ ", " + streamID + ", " + target);
+			}
+
+			public void unrecognized(String type, String msg) {
+				logger.log(Level.FINEST, "unrecognized(type, msg) = " + type
+						+ ", " + msg);
+			}
+		};
+		conn.setEventHandler(handler);
+		
+		// register for logging events of level INFO and higher
+		List<String> events = new ArrayList<String>();
+		events.add("INFO");
+		events.add("NOTICE");
+		events.add("WARN");
+		events.add("ERR");
+		try {
+			
+			// register for events using controller
+			this.conn.setEvents(events);
+		} catch (IOException e) {
+
+			// prepare and throw exception
+			String reason = "Could not register event handler at Tor process!";
+			TorProcessException e1 = new TorProcessException(reason, e);
+			this.logger.log(Level.SEVERE, reason, e1);
+			this.logger.throwing(this.getClass().getName(), "startNode", e1);
+			throw e1;
+		}
+		this.logger.log(Level.FINE, "Registered event handler at Tor process!");
+
+		// set state to RUNNING
+		this.nodeState = NodeState.RUNNING;
+
+		// log exiting and return with success
+		this.logger.exiting(this.getClass().getName(), "startNode", true);
+		return true;
+	}
+
+	/**
+	 * Handle on running Tor process.
+	 */
+	protected Process torProcess;
+
+	public synchronized void addHiddenService(String serviceName,
+			int servicePort, int virtualPort) {
+
+		// check state
+		if (this.nodeState != NodeState.CONFIGURING) {
+			this.logger.log(Level.SEVERE,
+					"Hidden service can only be added when node is in state "
+							+ "CONFIGURING!");
+			throw new IllegalStateException(
+					"Hidden service can only be added when node is in state "
+							+ "CONFIGURING!");
+		}
+
+		// check params
+		if (serviceName == null || serviceName.length() == 0 || servicePort < 0
+				|| servicePort > 65535 || virtualPort < 0
+				|| virtualPort > 65535) {
+			this.logger.log(Level.SEVERE,
+					"Illegal argument when adding hidden service!");
+			throw new IllegalArgumentException();
+		}
+
+		// add hidden service using Tor controller
+		this.configuration.add("HiddenServiceDir "
+				+ workingDir.getAbsolutePath() + "/" + serviceName);
+		this.configuration.add("HiddenServicePort " + virtualPort
+				+ " 127.0.0.1:" + servicePort);
+
+	}
+
+	public synchronized String getOnionAddress(String serviceName)
+			throws TorProcessException {
+
+		// check parameter
+		if (serviceName == null || serviceName.length() == 0) {
+			this.logger.log(Level.SEVERE, "Illegal argument!");
+			throw new IllegalArgumentException();
+		}
+
+		// check if hidden service directory exists
+		File hiddenServiceFile = new File(workingDir.getAbsolutePath()
+				+ File.separator + serviceName + File.separator + "hostname");
+		if (!hiddenServiceFile.exists()) {
+			this.logger.log(Level.SEVERE,
+					"Hidden service directory does not exist: "
+							+ hiddenServiceFile.getAbsolutePath());
+			throw new TorProcessException(
+					"Hidden service directory does not exist: "
+							+ hiddenServiceFile.getAbsolutePath());
+		}
+
+		// read hostname from file
+		String address = null;
+		try {
+			// TODO use FileReader
+			FileInputStream fis = new FileInputStream(hiddenServiceFile);
+			BufferedInputStream bis = new BufferedInputStream(fis);
+			byte[] bytes = new byte[16];
+			bis.read(bytes);
+			address = new String(bytes) + ".onion";
+			bis.close();
+		} catch (IOException e) {
+			throw new TorProcessException("Could not read hostname file!", e);
+		}
+
+		return address;
+	}
+
+	/**
+	 * Connection via Tor controller.
+	 */
+	protected TorControlConnection conn;
+
+	protected NodeState nodeState = NodeState.CONFIGURING;
+
+	protected File configFile;
+
+	public synchronized void writeConfiguration() throws TorProcessException {
+
+		// check state
+		if (this.nodeState != NodeState.CONFIGURING) {
+			throw new IllegalStateException();
+		}
+
+		// write config file
+		try {
+			BufferedWriter bw = new BufferedWriter(new FileWriter(configFile));
+			for (String c : this.configuration) {
+				bw.write(c + "\n");
+			}
+			bw.close();
+		} catch (IOException e) {
+			throw new TorProcessException("Could not write configuration!", e);
+		}
+
+		// change state
+		this.nodeState = NodeState.CONFIGURATION_WRITTEN;
+	}
+
+	/**
+	 * @see de.uniba.wiai.lspi.puppetor.ProxyNode#shutdown()
+	 */
+	public synchronized void shutdown() throws TorProcessException {
+
+		// check state
+		if (this.nodeState != NodeState.RUNNING) {
+			throw new IllegalStateException();
+		}
+
+		// we cannot simply kill the tor.exe process, because we have
+		// established a controller connection to it which would interpret a
+		// closed socket as failure and throw a RuntimeException
+		// TODO who cares?!
+		try {
+			this.conn.stopThread();
+			this.conn.shutdownTor("SHUTDOWN");
+			this.conn.shutdownTor("SHUTDOWN");
+		} catch (IOException e) {
+			this.logger.log(Level.SEVERE,
+					"Could not send shutdown command to Tor process!", e);
+			throw new TorProcessException(
+					"Could not send shutdown command to Tor process!", e);
+		}
+
+		// change state
+		this.nodeState = NodeState.SHUT_DOWN;
+	}
+
+	// /**
+	// * Flags that denotes whether certain events have been observed in the
+	// log.
+	// * In general, this requires logging to be done at least on notice level!
+	// */
+	// protected Set<Event> observedEvents;
+
+	// public synchronized boolean hasEventOccured(Event event) {
+	// return observedEvents.contains(event);
+	// }
+
+	public synchronized void hup() throws TorProcessException {
+
+		// check state
+		if (this.nodeState != NodeState.RUNNING) {
+			this.logger.log(Level.SEVERE,
+					"Cannot hup a process when it's not running!");
+			throw new IllegalStateException(
+					"Cannot hup a process when it's not running!");
+		}
+
+		// should we check whether conn is null?
+		if (this.conn == null) {
+			System.out.println("WARNING! conn is null!");
+		}
+		try {
+			// TODO sometimes, this throws a NullPointerException...
+			this.conn.signal("HUP");
+		} catch (IOException e) {
+			this.logger.log(Level.SEVERE,
+					"Could not send the command HUP to the Tor process!", e);
+			throw new TorProcessException(
+					"Could not send the command HUP to the Tor process!", e);
+		}
+	}
+
+	// public synchronized void addConfig(String newConfiguration) {
+	// if (this.nodeStatus != Status.CONFIGURING) {
+	// throw new IllegalStateException();
+	// }
+	// // TODO should we check the new config?!
+	// this.configuration.add(newConfiguration);
+	// }
+
+	// public String getConfiguration() {
+	// StringBuilder result = new StringBuilder("Node configuration: ");
+	// for (String next : this.configuration) {
+	// result.append(next + "\n");
+	// }
+	// return result.toString();
+	// }
+
+	/**
+	 * Adds a hidden service configuration to this node while the node is
+	 * running.
+	 * 
+	 * TODO this should be possible both, during configuration and when running
+	 * a network...
+	 */
+	public synchronized String addHiddenServiceUsingController(
+			String serviceName, int servicePort, int virtualPort) {
+		if (1 != 2)
+			throw new UnsupportedOperationException();
+		// TODO check state
+
+		// check params
+		if (serviceName == null || serviceName.length() == 0 || servicePort < 0
+				|| servicePort > 65535 || virtualPort < 0
+				|| virtualPort > 65535) {
+			throw new IllegalArgumentException();
+		}
+
+		// String serverName = server.getServerName();
+		// int serverPort = server.getServerPort();
+
+		// add hidden service using Tor controller
+		this.logger.log(Level.FINE,
+				"Adding hidden service using Tor controller.");
+
+		List<String> configs = new ArrayList<String>();
+		configs.add("HiddenServiceDir " + workingDir.getAbsolutePath() + "/"
+				+ serviceName);
+		configs.add("HiddenServicePort " + virtualPort + " 127.0.0.1:"
+				+ servicePort);
+		try {
+			conn.setConf(configs);
+		} catch (IOException e) {
+			throw new RuntimeException(
+					"IOException when trying to register a new "
+							+ "hidden service", e);
+		}
+		this.logger.log(Level.FINE,
+				"Hidden service successfully registered at Tor proxy.");
+
+		// 
+		File hiddenServiceFile = new File(workingDir.getAbsolutePath() + "\\"
+				+ serviceName + "\\hostname");
+		if (!hiddenServiceFile.exists()) {
+			throw new RuntimeException();
+		}
+		// read hostname from file
+		String address = null;
+		try {
+			// TODO use FileReader
+			FileInputStream fis = new FileInputStream(hiddenServiceFile);
+			BufferedInputStream bis = new BufferedInputStream(fis);
+			byte[] bytes = new byte[16];
+			bis.read(bytes);
+			address = new String(bytes) + ".onion";
+		} catch (IOException e) {
+			throw new RuntimeException(e);
+		}
+
+		return address;
+	}
+
+	public synchronized String getNodeName() {
+		return this.nodeName;
+	}
+
+}


Property changes on: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/ProxyNodeImpl.java
___________________________________________________________________
Name: svn:executable
   + *

Added: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/RouterNodeImpl.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/RouterNodeImpl.java	2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/RouterNodeImpl.java	2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,189 @@
+/*
+ * NOTICE: This file is still work in progress. As you can see the Java
+ * documentation is written partly in German and in English and logging and
+ * documentation still need some work. Sorry for any inconvenience!  
+ */
+package de.uniba.wiai.lspi.puppetor.impl;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.logging.Level;
+
+import de.uniba.wiai.lspi.puppetor.RouterNode;
+import de.uniba.wiai.lspi.puppetor.TorProcessException;
+
+public class RouterNodeImpl extends ProxyNodeImpl implements RouterNode {
+
+	/**
+	 * OR port of this router.
+	 */
+	protected int orPort;
+
+	/**
+	 * Directory port of this router.
+	 */
+	protected int dirPort;
+
+	/**
+	 * This node's fingerprint that is determined as hash value of this node's
+	 * onion routing key.
+	 */
+	protected String fingerprint;
+
+	@Override
+	public String toString() {
+		return super.toString() + ", orPort=" + this.orPort + ", dirPort="
+				+ this.dirPort;
+	}
+
+	public RouterNodeImpl(NetworkImpl network, String nodeName,
+			int controlPort, int socksPort, int orPort, int dirPort) {
+		super(network, nodeName, controlPort, socksPort);
+
+		// log entering
+		this.logger.entering(this.getClass().getName(), "RouterNodeImpl",
+				new Object[] { network, nodeName, controlPort, socksPort,
+						orPort, dirPort });
+
+		// check parameters
+		if (orPort < 0 || orPort > 65535 || dirPort < 0 || dirPort > 65535) {
+			throw new IllegalArgumentException();
+		}
+
+		// remember parameters
+		this.orPort = orPort;
+		this.dirPort = dirPort;
+
+		// add further configuration to make this node a router node
+		this.configuration.add("ORPort " + orPort);
+		this.configuration.add("Nickname " + nodeName);
+		this.configuration.add("ContactInfo wont at reply.org");
+
+		// all routers mirror the directory
+		this.configuration.add("DirPort " + dirPort);
+
+		// this node's address should be localhost and not guessed by Tor
+		this.configuration.add("Address 127.0.0.1");
+
+		// the OR port may only be contacted locally
+		this.configuration.add("ORListenAddress 127.0.0.1");
+
+		// offer directory only locally (either by being an authority, or by
+		// mirroring it)
+		this.configuration.add("DirListenAddress 127.0.0.1");
+
+		// allow exit to private network and everything else (node will only
+		// be used by other nodes in the private network, so no worry)
+		this.configuration.add("ExitPolicyRejectPrivate 0");
+		this.configuration.add("ExitPolicy accept *:*");
+
+		// bypass testing if we are reachable
+		this.configuration.add("AssumeReachable 1");
+		
+		// log exiting
+		this.logger.exiting(this.getClass().getName(), "RouterNodeImpl");
+
+	}
+
+	/**
+	 * @see de.uniba.wiai.lspi.puppetor.RouterNode#determineFingerprint()
+	 */
+	public synchronized String determineFingerprint()
+			throws TorProcessException {
+		if (this.fingerprint != null) {
+
+			// we can answer the request immediately
+			return this.fingerprint;
+		}
+
+
+		// write modified config file, including a DirServer option with
+		// false fingerprint; this is necessary, because otherwise Tor
+		// would not accept that this router node has a private IP
+		// address, but connects to the public directory servers; just a
+		// workaround...
+		Set<String> copyOfConfig = new HashSet<String>(this.configuration);
+		// TODO when working dir exists, but Tor did not manage to write
+		// a fingerprint file, we get a NullPointerException...
+		String fakeDirServerString = "DirServer " + this.nodeName
+				+ " 127.0.0.1:" + this.dirPort
+				+ " 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000";
+		copyOfConfig.add(fakeDirServerString);
+		try {
+			BufferedWriter bw = new BufferedWriter(new FileWriter(configFile));
+			for (String c : copyOfConfig) {
+				bw.write(c + "\n");
+			}
+			bw.close();
+		} catch (IOException e) {
+			throw new TorProcessException(
+					"Could not write temporary config file!", e);
+		}
+
+		// start process with option --list-fingerprint
+		ProcessBuilder processBuilder = new ProcessBuilder(torExecutable
+				.getPath(), "--list-fingerprint", "-f", "torrc");
+		processBuilder.directory(this.workingDir);
+		processBuilder.redirectErrorStream(true);
+		Process tmpProcess = null;
+
+		try {
+			tmpProcess = processBuilder.start();
+		} catch (IOException e) {
+			throw new TorProcessException(
+					"Could not start Tor process temporarily with "
+							+ "--list-fingerprint option!", e);
+		}
+
+		// start thread to parse output
+		final BufferedReader br = new BufferedReader(new InputStreamReader(
+				tmpProcess.getInputStream()));
+		Thread outputThread = new Thread(new Runnable() {
+			public void run() {
+				try {
+					// Read output from tor
+					String line = null;
+					while ((line = br.readLine()) != null) {
+						// TODO discard output?
+						logger.log(Level.FINER, line);
+					}
+				} catch (IOException e) {
+					// TODO handle?
+				}
+			}
+		});
+		outputThread.setDaemon(true);
+		outputThread.start();
+
+		// wait for process to terminate
+		// TODO should limit waiting to a certain time?!
+		try {
+			tmpProcess.waitFor();
+		} catch (InterruptedException e) {
+			// TODO handle?
+		}
+
+		// read fingerprint from file
+
+		File fingerprintFile = new File(this.workingDir.getAbsolutePath()
+				+ File.separator + "fingerprint");
+		try {
+			BufferedReader br2 = new BufferedReader(new FileReader(
+					fingerprintFile));
+			this.fingerprint = br2.readLine();
+			br2.close();
+		} catch (IOException e) {
+			throw new TorProcessException(
+					"Could not read fingerprint from file!", e);
+		}
+		
+		return this.fingerprint;
+	}
+}


Property changes on: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/RouterNodeImpl.java
___________________________________________________________________
Name: svn:executable
   + *

Added: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/ServerApplicationImpl.java
===================================================================
--- puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/ServerApplicationImpl.java	2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/ServerApplicationImpl.java	2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,222 @@
+/*
+ * NOTICE: This file is still work in progress. As you can see the Java
+ * documentation is written partly in German and in English and logging and
+ * documentation still need some work. Sorry for any inconvenience!  
+ */
+package de.uniba.wiai.lspi.puppetor.impl;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintStream;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import de.uniba.wiai.lspi.puppetor.Event;
+import de.uniba.wiai.lspi.puppetor.ServerApplication;
+
+public class ServerApplicationImpl implements ServerApplication {
+
+	/**
+	 * logger für diesen server
+	 */
+	private Logger logger;
+
+	private EventManagerImpl eventManager;
+
+	private int serverPort;
+
+	/**
+	 * erzeugt neue server app innerhalb der jvm, fängt aber noch nicht an mit
+	 * listening.
+	 * 
+	 */
+	ServerApplicationImpl(NetworkImpl network, String serverApplicationName,
+			int serverPort) {
+
+		// TODO make sure that name is loggable!!
+
+		// create logger
+		this.logger = Logger.getLogger("application." + serverApplicationName);
+
+		// log entering
+		this.logger.entering(this.getClass().getName(),
+				"ServerApplicationImpl", new Object[] { serverApplicationName,
+						serverPort });
+
+		// check parameters
+		if (serverApplicationName == null
+				|| serverApplicationName.length() == 0 || serverPort < 0
+				|| serverPort > 65535) {
+			throw new IllegalArgumentException();
+		}
+
+		// remember parameters
+		this.serverPort = serverPort;
+
+		// obtain reference on event manager
+		this.eventManager = network.getEventManagerImpl();
+
+		// log exiting
+		this.logger.exiting(this.getClass().getName(), "ClientApplicationImpl");
+	}
+
+	private Thread serverThread;
+
+	public void stopListening() {
+
+		// log entering
+		this.logger.entering(this.getClass().getName(), "stopListening");
+
+		// check if a request is running
+		if (this.serverThread == null) {
+			throw new IllegalStateException(
+					"We did not start listening before!");
+		}
+
+		// log this event
+		this.logger.log(Level.FINE, "Shutting down server");
+
+		// interrupt thread
+		this.serverThread.interrupt();
+
+		// log exiting
+		this.logger.exiting(this.getClass().getName(), "stopListening");
+
+	}
+
+	public void listen() {
+
+		// log entering
+		this.logger.entering(this.getClass().getName(), "listen");
+
+		// check if we are already listening
+		if (this.serverThread != null) {
+			throw new IllegalStateException("We are already listening!");
+		}
+
+		// create a thread that listens in the background
+		this.serverThread = new ListenThread();
+		this.serverThread.setName("Reply Thread");
+		this.serverThread.setDaemon(true);
+		this.serverThread.start();
+
+		// log exiting
+		this.logger.exiting(this.getClass().getName(), "listen");
+	}
+
+	private class ListenThread extends Thread {
+
+		private boolean connected;
+
+		ListenThread() {
+
+			// log entering
+			logger.entering(this.getClass().getName(), "ListenThread");
+
+			// start connected
+			this.connected = true;
+
+			// log exiting
+			logger.exiting(this.getClass().getName(), "ListenThread");
+		}
+
+		/**
+		 * stoppt listening
+		 * 
+		 */
+		public void stopListening() {
+
+			// log entering
+			logger.entering(this.getClass().getName(), "stopListening");
+
+			// change connected state to false and interrupt thread
+			this.connected = false;
+			this.interrupt();
+
+			// log exiting
+			logger.exiting(this.getClass().getName(), "stopListening");
+		}
+
+		@Override
+		public void run() {
+
+			// log entering
+			logger.entering(this.getClass().getName(), "run");
+
+			try {
+
+				ServerSocket serverSocket = null;
+
+				try {
+					serverSocket = new ServerSocket(serverPort);
+				} catch (IOException ioe) {
+					System.out.println("Can't open server socket on port "
+							+ serverPort + "!");
+				}
+				System.out.println("Listening on port " + serverPort + "...");
+				while (connected) {
+					try {
+						Socket incomingConnection = serverSocket.accept();
+						HandlerThread handler = new HandlerThread(
+								incomingConnection);
+						new Thread(handler).start();
+					} catch (Exception e) {
+					}
+				}
+
+			} catch (Exception e) {
+
+				// log that we have been interrupted
+				logger.log(Level.WARNING, "Server has been interrupted!", e);
+
+				// log exiting
+				logger.exiting(this.getClass().getName(), "run");
+			}
+
+		}
+
+	}
+
+	private class HandlerThread implements Runnable {
+
+		private Socket handleSocket = null;
+
+		public HandlerThread(Socket requestSocket) {
+			handleSocket = requestSocket;
+		}
+
+		public void run() {
+
+			try {
+
+				// wait for request (don't mind content)
+				BufferedReader in = new BufferedReader(new InputStreamReader(
+						handleSocket.getInputStream()));
+				in.read();
+
+				// send event to event manager
+				eventManager.observeEvent(ServerApplicationImpl.this,
+						Event.APPLICATION_REQUEST_RECEIVED);
+
+				// write response
+				PrintStream out = new PrintStream(handleSocket
+						.getOutputStream());
+				out.print("HTTP/1.0 200 OK\r\n");
+
+			} catch (Exception e) {
+				e.printStackTrace();
+			}
+
+			try {
+				// handleSocket.getInputStream().close();
+				// handleSocket.getOutputStream().flush();
+				// handleSocket.getOutputStream().close();
+				handleSocket.close();
+			} catch (Exception e) {
+			}
+		}
+	}
+}


Property changes on: puppetor/trunk/src/de/uniba/wiai/lspi/puppetor/impl/ServerApplicationImpl.java
___________________________________________________________________
Name: svn:executable
   + *

Added: puppetor/trunk/src/net/freehaven/tor/control/.cvsignore
===================================================================
--- puppetor/trunk/src/net/freehaven/tor/control/.cvsignore	2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/net/freehaven/tor/control/.cvsignore	2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1 @@
+*.class


Property changes on: puppetor/trunk/src/net/freehaven/tor/control/.cvsignore
___________________________________________________________________
Name: svn:executable
   + *

Added: puppetor/trunk/src/net/freehaven/tor/control/Bytes.java
===================================================================
--- puppetor/trunk/src/net/freehaven/tor/control/Bytes.java	2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/net/freehaven/tor/control/Bytes.java	2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,115 @@
+// $Id: Bytes.java 8419 2006-09-19 14:57:31Z nickm $
+// Copyright 2005 Nick Mathewson, Roger Dingledine
+// See LICENSE file for copying information
+package net.freehaven.tor.control;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Static class to do bytewise structure manipulation in Java.
+ */
+/* XXXX There must be a better way to do most of this.
+ * XXXX The string logic here uses default encoding, which is stupid.
+ */
+final class Bytes {
+
+    /** Write the two-byte value in 's' into the byte array 'ba', starting at
+     * the index 'pos'. */
+    public static void setU16(byte[] ba, int pos, short s) {
+        ba[pos]   = (byte)((s >> 8) & 0xff);
+        ba[pos+1] = (byte)((s     ) & 0xff);
+    }
+
+    /** Write the four-byte value in 'i' into the byte array 'ba', starting at
+     * the index 'pos'. */
+    public static void setU32(byte[] ba, int pos, int i) {
+        ba[pos]   = (byte)((i >> 24) & 0xff);
+        ba[pos+1] = (byte)((i >> 16) & 0xff);
+        ba[pos+2] = (byte)((i >>  8) & 0xff);
+        ba[pos+3] = (byte)((i      ) & 0xff);
+    }
+
+    /** Return the four-byte value starting at index 'pos' within 'ba' */
+    public static int getU32(byte[] ba, int pos) {
+        return
+            ((ba[pos  ]&0xff)<<24) |
+            ((ba[pos+1]&0xff)<<16) |
+            ((ba[pos+2]&0xff)<< 8)  |
+            ((ba[pos+3]&0xff));
+    }
+
+    public static String getU32S(byte[] ba, int pos) {
+        return String.valueOf( ((long)getU32(ba,pos))&0xffffffffL );
+    }
+
+    /** Return the two-byte value starting at index 'pos' within 'ba' */
+    public static int getU16(byte[] ba, int pos) {
+        return
+            ((ba[pos  ]&0xff)<<8) |
+            ((ba[pos+1]&0xff));
+    }
+
+    /** Return the string starting at position 'pos' of ba and extending
+     * until a zero byte or the end of the string. */
+    public static String getNulTerminatedStr(byte[] ba, int pos) {
+        int len, maxlen = ba.length-pos;
+        for (len=0; len<maxlen; ++len) {
+            if (ba[pos+len] == 0)
+                break;
+        }
+        return new String(ba, pos, len);
+    }
+
+    /**
+     * Read bytes from 'ba' starting at 'pos', dividing them into strings
+     * along the character in 'split' and writing them into 'lst'
+     */
+    public static void splitStr(List lst, byte[] ba, int pos, byte split) {
+        while (pos < ba.length && ba[pos] != 0) {
+            int len;
+            for (len=0; pos+len < ba.length; ++len) {
+                if (ba[pos+len] == 0 || ba[pos+len] == split)
+                    break;
+            }
+            if (len>0)
+                lst.add(new String(ba, pos, len));
+            pos += len;
+            if (ba[pos] == split)
+                ++pos;
+        }
+    }
+
+    /**
+     * Read bytes from 'ba' starting at 'pos', dividing them into strings
+     * along the character in 'split' and writing them into 'lst'
+     */
+    public static List splitStr(List lst, String str) {
+        // split string on spaces, include trailing/leading
+        String[] tokenArray = str.split(" ", -1);
+        if (lst == null) {
+            lst = Arrays.asList( tokenArray );
+        } else {
+            lst.addAll( Arrays.asList( tokenArray ) );
+        }
+        return lst;
+    }
+
+    private static final char[] NYBBLES = {
+        '0', '1', '2', '3', '4', '5', '6', '7',
+        '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
+    };
+
+    public static final String hex(byte[] ba) {
+        StringBuffer buf = new StringBuffer();
+        for (int i = 0; i < ba.length; ++i) {
+            int b = ((int)ba[i]) & 0xff;
+            buf.append(NYBBLES[b >> 4]);
+            buf.append(NYBBLES[b&0x0f]);
+        }
+        return buf.toString();
+    }
+
+    private Bytes() {};
+}
+


Property changes on: puppetor/trunk/src/net/freehaven/tor/control/Bytes.java
___________________________________________________________________
Name: svn:executable
   + *

Added: puppetor/trunk/src/net/freehaven/tor/control/ConfigEntry.java
===================================================================
--- puppetor/trunk/src/net/freehaven/tor/control/ConfigEntry.java	2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/net/freehaven/tor/control/ConfigEntry.java	2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,14 @@
+// $Id: ConfigEntry.java 6843 2005-07-14 20:26:11Z nickm $
+// Copyright 2005 Nick Mathewson, Roger Dingledine
+// See LICENSE file for copying information
+package net.freehaven.tor.control;
+
+/** A single key-value pair from Tor's configuration. */
+public class ConfigEntry {
+    public ConfigEntry(String k, String v) {
+        key = k;
+        value = v;
+    }
+    public final String key;
+    public final String value;
+}


Property changes on: puppetor/trunk/src/net/freehaven/tor/control/ConfigEntry.java
___________________________________________________________________
Name: svn:executable
   + *

Added: puppetor/trunk/src/net/freehaven/tor/control/EventHandler.java
===================================================================
--- puppetor/trunk/src/net/freehaven/tor/control/EventHandler.java	2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/net/freehaven/tor/control/EventHandler.java	2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,76 @@
+// $Id: EventHandler.java 6853 2005-07-23 01:17:27Z nickm $
+// Copyright 2005 Nick Mathewson, Roger Dingledine
+// See LICENSE file for copying information
+package net.freehaven.tor.control;
+
+/**
+ * Abstract interface whose methods are invoked when Tor sends us an event.
+ *
+ * @see TorControlConnection#setEventHandler
+ * @see TorControlConnection#setEvents
+ */
+public interface EventHandler {
+    /**
+     * Invoked when a circuit's status has changed.
+     * Possible values for <b>status</b> are:
+     * <ul>
+     *   <li>"LAUNCHED" :  circuit ID assigned to new circuit</li>
+     *   <li>"BUILT"    :  all hops finished, can now accept streams</li>
+     *   <li>"EXTENDED" :  one more hop has been completed</li>
+     *   <li>"FAILED"   :  circuit closed (was not built)</li>
+     *   <li>"CLOSED"   :  circuit closed (was built)</li>
+     *	</ul>
+     * 
+     * <b>circID</b> is the alphanumeric identifier of the affected circuit,
+     * and <b>path</b> is a comma-separated list of alphanumeric ServerIDs.
+     */
+    public void circuitStatus(String status, String circID, String path);
+    /**
+     * Invoked when a stream's status has changed.
+     * Possible values for <b>status</b> are:
+     * <ul>
+     *   <li>"NEW"         :  New request to connect</li>
+     *   <li>"NEWRESOLVE"  :  New request to resolve an address</li>
+     *   <li>"SENTCONNECT" :  Sent a connect cell along a circuit</li>
+     *   <li>"SENTRESOLVE" :  Sent a resolve cell along a circuit</li>
+     *   <li>"SUCCEEDED"   :  Received a reply; stream established</li>
+     *   <li>"FAILED"      :  Stream failed and not retriable.</li>
+     *   <li>"CLOSED"      :  Stream closed</li>
+     *   <li>"DETACHED"    :  Detached from circuit; still retriable.</li>
+     *	</ul>
+     *
+     * <b>streamID</b> is the alphanumeric identifier of the affected stream,
+     * and its <b>target</b> is specified as address:port.
+     */
+    public void streamStatus(String status, String streamID, String target);
+    /**
+     * Invoked when the status of a connection to an OR has changed.
+     * Possible values for <b>status</b> are ["LAUNCHED" | "CONNECTED" | "FAILED" | "CLOSED"].
+     * <b>orName</b> is the alphanumeric identifier of the OR affected.
+     */
+    public void orConnStatus(String status, String orName);
+    /**
+     * Invoked once per second. <b>read</b> and <b>written</b> are
+     * the number of bytes read and written, respectively, in
+     * the last second.
+     */
+    public void bandwidthUsed(long read, long written);
+    /**
+     * Invoked whenever Tor learns about new ORs.  The <b>orList</b> object
+     * contains the alphanumeric ServerIDs associated with the new ORs.
+     */
+    public void newDescriptors(java.util.List orList);
+    /**
+     * Invoked when Tor logs a message.
+     * <b>severity</b> is one of ["DEBUG" | "INFO" | "NOTICE" | "WARN" | "ERR"],
+     * and <b>msg</b> is the message string.
+     */
+    public void message(String severity, String msg);
+    /**
+     * Invoked when an unspecified message is received.
+     * <type> is the message type, and <msg> is the message string.
+     */
+    public void unrecognized(String type, String msg);
+
+}
+


Property changes on: puppetor/trunk/src/net/freehaven/tor/control/EventHandler.java
___________________________________________________________________
Name: svn:executable
   + *

Added: puppetor/trunk/src/net/freehaven/tor/control/NullEventHandler.java
===================================================================
--- puppetor/trunk/src/net/freehaven/tor/control/NullEventHandler.java	2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/net/freehaven/tor/control/NullEventHandler.java	2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,19 @@
+// $Id: NullEventHandler.java 6833 2005-07-11 19:14:18Z nickm $
+// Copyright 2005 Nick Mathewson, Roger Dingledine
+// See LICENSE file for copying information
+package net.freehaven.tor.control;
+
+/**
+ * Implementation of EventHandler that ignores all events.  Useful
+ * when you only want to override one method.
+ */
+public class NullEventHandler implements EventHandler {
+    public void circuitStatus(String status, String circID, String path) {}
+    public void streamStatus(String status, String streamID, String target) {}
+    public void orConnStatus(String status, String orName) {}
+    public void bandwidthUsed(long read, long written) {}
+    public void newDescriptors(java.util.List orList) {}
+    public void message(String severity, String msg) {}
+    public void unrecognized(String type, String msg) {}
+}
+


Property changes on: puppetor/trunk/src/net/freehaven/tor/control/NullEventHandler.java
___________________________________________________________________
Name: svn:executable
   + *

Added: puppetor/trunk/src/net/freehaven/tor/control/PasswordDigest.java
===================================================================
--- puppetor/trunk/src/net/freehaven/tor/control/PasswordDigest.java	2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/net/freehaven/tor/control/PasswordDigest.java	2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,103 @@
+// $Id: PasswordDigest.java 6831 2005-06-24 18:03:27Z nickm $
+// Copyright 2005 Nick Mathewson, Roger Dingledine
+// See LICENSE file for copying information
+package net.freehaven.tor.control;
+
+import java.security.SecureRandom;
+import java.security.MessageDigest;
+
+/**
+ * A hashed digest of a secret password (used to set control connection
+ * security.)
+ *
+ * For the actual hashing algorithm, see RFC2440's secret-to-key conversion.
+ */
+public class PasswordDigest {
+
+    byte[] secret;
+    String hashedKey;
+
+    /** Return a new password digest with a random secret and salt. */
+    public static PasswordDigest generateDigest() {
+        byte[] secret = new byte[20];
+        SecureRandom rng = new SecureRandom();
+        rng.nextBytes(secret);
+        return new PasswordDigest(secret);
+    }
+
+    /** Construct a new password digest with a given secret and random salt */
+    public PasswordDigest(byte[] secret) {
+        this(secret, null);
+    }
+
+    /** Construct a new password digest with a given secret and random salt.
+     * Note that the 9th byte of the specifier determines the number of hash
+     * iterations as in RFC2440.
+     */
+    public PasswordDigest(byte[] secret, byte[] specifier) {
+        this.secret = secret;
+        if (specifier == null) {
+            specifier = new byte[9];
+            SecureRandom rng = new SecureRandom();
+            rng.nextBytes(specifier);
+            specifier[8] = 96;
+        }
+        hashedKey = "16:"+encodeBytes(secretToKey(secret, specifier));
+    }
+
+    /** Return the secret used to generate this password hash.
+     */
+    public byte[] getSecret() {
+        return secret;
+    }
+
+    /** Return the hashed password in the format used by Tor. */
+    public String getHashedPassword() {
+        return hashedKey;
+    }
+
+    /** Parameter used by RFC2440's s2k algorithm. */
+    private static final int EXPBIAS = 6;
+
+    /** Implement rfc2440 s2k */
+    public static byte[] secretToKey(byte[] secret, byte[] specifier) {
+        MessageDigest d;
+        try {
+            d = MessageDigest.getInstance("SHA-1");
+        } catch (java.security.NoSuchAlgorithmException ex) {
+            throw new RuntimeException("Can't run without sha-1.");
+        }
+        int c = ((int)specifier[8])&0xff;
+        int count = (16 + (c&15)) << ((c>>4) + EXPBIAS);
+
+        byte[] tmp = new byte[8+secret.length];
+        System.arraycopy(specifier, 0, tmp, 0, 8);
+        System.arraycopy(secret, 0, tmp, 8, secret.length);
+        while (count > 0) {
+            if (count >= tmp.length) {
+                d.update(tmp);
+                count -= tmp.length;
+            } else {
+                d.update(tmp, 0, count);
+                count = 0;
+            }
+        }
+        byte[] key = new byte[20+9];
+        System.arraycopy(d.digest(), 0, key, 9, 20);
+        System.arraycopy(specifier, 0, key, 0, 9);
+        return key;
+    }
+
+    private static final char[] NYBBLES = {
+        '0', '1', '2', '3', '4', '5', '6', '7',
+        '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
+    };
+
+    /** Return a hexadecimal encoding of a byte array. */
+    // XXX There must be a better way to do this in Java.
+    private static final String encodeBytes(byte[] ba) {
+        return Bytes.hex(ba);
+    }
+
+}
+


Property changes on: puppetor/trunk/src/net/freehaven/tor/control/PasswordDigest.java
___________________________________________________________________
Name: svn:executable
   + *

Added: puppetor/trunk/src/net/freehaven/tor/control/TorControlCommands.java
===================================================================
--- puppetor/trunk/src/net/freehaven/tor/control/TorControlCommands.java	2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/net/freehaven/tor/control/TorControlCommands.java	2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,149 @@
+// $Id: TorControlCommands.java 6849 2005-07-20 22:48:35Z nickm $
+// Copyright 2005 Nick Mathewson, Roger Dingledine
+// See LICENSE file for copying information
+package net.freehaven.tor.control;
+
+/** Interface defining constants used by the Tor controller protocol.
+ */
+// XXXX Take documentation for these from control-spec.txt
+public interface TorControlCommands {
+
+    public static final short CMD_ERROR = 0x0000;
+    public static final short CMD_DONE = 0x0001;
+    public static final short CMD_SETCONF = 0x0002;
+    public static final short CMD_GETCONF = 0x0003;
+    public static final short CMD_CONFVALUE = 0x0004;
+    public static final short CMD_SETEVENTS = 0x0005;
+    public static final short CMD_EVENT = 0x0006;
+    public static final short CMD_AUTH = 0x0007;
+    public static final short CMD_SAVECONF = 0x0008;
+    public static final short CMD_SIGNAL = 0x0009;
+    public static final short CMD_MAPADDRESS = 0x000A;
+    public static final short CMD_GETINFO = 0x000B;
+    public static final short CMD_INFOVALUE = 0x000C;
+    public static final short CMD_EXTENDCIRCUIT = 0x000D;
+    public static final short CMD_ATTACHSTREAM = 0x000E;
+    public static final short CMD_POSTDESCRIPTOR = 0x000F;
+    public static final short CMD_FRAGMENTHEADER = 0x0010;
+    public static final short CMD_FRAGMENT = 0x0011;
+    public static final short CMD_REDIRECTSTREAM = 0x0012;
+    public static final short CMD_CLOSESTREAM = 0x0013;
+    public static final short CMD_CLOSECIRCUIT = 0x0014;
+
+    public static final String[] CMD_NAMES = {
+        "ERROR",
+        "DONE",
+        "SETCONF",
+        "GETCONF",
+        "CONFVALUE",
+        "SETEVENTS",
+        "EVENT",
+        "AUTH",
+        "SAVECONF",
+        "SIGNAL",
+        "MAPADDRESS",
+        "GETINFO",
+        "INFOVALUE",
+        "EXTENDCIRCUIT",
+        "ATTACHSTREAM",
+        "POSTDESCRIPTOR",
+        "FRAGMENTHEADER",
+        "FRAGMENT",
+        "REDIRECTSTREAM",
+        "CLOSESTREAM",
+        "CLOSECIRCUIT",
+    };
+
+    public static final short EVENT_CIRCSTATUS = 0x0001;
+    public static final short EVENT_STREAMSTATUS = 0x0002;
+    public static final short EVENT_ORCONNSTATUS = 0x0003;
+    public static final short EVENT_BANDWIDTH = 0x0004;
+    public static final short EVENT_NEWDESCRIPTOR = 0x0006;
+    public static final short EVENT_MSG_DEBUG = 0x0007;
+    public static final short EVENT_MSG_INFO = 0x0008;
+    public static final short EVENT_MSG_NOTICE = 0x0009;
+    public static final short EVENT_MSG_WARN = 0x000A;
+    public static final short EVENT_MSG_ERROR = 0x000B;
+
+    public static final String[] EVENT_NAMES = {
+        "(0)",
+        "CIRC",
+        "STREAM",
+        "ORCONN",
+        "BW",
+        "OLDLOG",
+        "NEWDESC",
+        "DEBUG",
+        "INFO",
+        "NOTICE",
+        "WARN",
+        "ERR",
+    };
+
+    public static final byte CIRC_STATUS_LAUNCHED = 0x01;
+    public static final byte CIRC_STATUS_BUILT = 0x02;
+    public static final byte CIRC_STATUS_EXTENDED = 0x03;
+    public static final byte CIRC_STATUS_FAILED = 0x04;
+    public static final byte CIRC_STATUS_CLOSED = 0x05;
+
+    public static final String[] CIRC_STATUS_NAMES = {
+        "LAUNCHED",
+        "BUILT",
+        "EXTENDED",
+        "FAILED",
+        "CLOSED",
+    };
+
+    public static final byte STREAM_STATUS_SENT_CONNECT = 0x00;
+    public static final byte STREAM_STATUS_SENT_RESOLVE = 0x01;
+    public static final byte STREAM_STATUS_SUCCEEDED = 0x02;
+    public static final byte STREAM_STATUS_FAILED = 0x03;
+    public static final byte STREAM_STATUS_CLOSED = 0x04;
+    public static final byte STREAM_STATUS_NEW_CONNECT = 0x05;
+    public static final byte STREAM_STATUS_NEW_RESOLVE = 0x06;
+    public static final byte STREAM_STATUS_DETACHED = 0x07;
+
+    public static final String[] STREAM_STATUS_NAMES = {
+        "SENT_CONNECT",
+        "SENT_RESOLVE",
+        "SUCCEEDED",
+        "FAILED",
+        "CLOSED",
+        "NEW_CONNECT",
+        "NEW_RESOLVE",
+        "DETACHED"
+    };
+
+    public static final byte OR_CONN_STATUS_LAUNCHED = 0x00;
+    public static final byte OR_CONN_STATUS_CONNECTED = 0x01;
+    public static final byte OR_CONN_STATUS_FAILED = 0x02;
+    public static final byte OR_CONN_STATUS_CLOSED = 0x03;
+
+    public static final String[] OR_CONN_STATUS_NAMES = {
+        "LAUNCHED","CONNECTED","FAILED","CLOSED"
+    };
+
+    public static final byte SIGNAL_HUP = 0x01;
+    public static final byte SIGNAL_INT = 0x02;
+    public static final byte SIGNAL_USR1 = 0x0A;
+    public static final byte SIGNAL_USR2 = 0x0C;
+    public static final byte SIGNAL_TERM = 0x0F;
+
+    public static final String ERROR_MSGS[] = {
+        "Unspecified error",
+        "Internal error",
+        "Unrecognized message type",
+        "Syntax error",
+        "Unrecognized configuration key",
+        "Invalid configuration value",
+        "Unrecognized byte code",
+        "Unauthorized",
+        "Failed authentication attempt",
+        "Resource exhausted",
+        "No such stream",
+        "No such circuit",
+        "No such OR",
+    };
+
+}
+


Property changes on: puppetor/trunk/src/net/freehaven/tor/control/TorControlCommands.java
___________________________________________________________________
Name: svn:executable
   + *

Added: puppetor/trunk/src/net/freehaven/tor/control/TorControlConnection.java
===================================================================
--- puppetor/trunk/src/net/freehaven/tor/control/TorControlConnection.java	2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/net/freehaven/tor/control/TorControlConnection.java	2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,249 @@
+// $Id: TorControlConnection.java 9193 2006-12-25 04:37:53Z nickm $
+// Copyright 2005 Nick Mathewson, Roger Dingledine
+// See LICENSE file for copying information
+package net.freehaven.tor.control;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+/** A connection to a running Tor process. */
+public abstract class TorControlConnection// implements TorControlCommands {
+{
+
+    protected EventHandler handler;
+
+    protected LinkedList waiters;
+
+    protected ControlParseThread thread;
+
+    static class Waiter {
+        Object response;
+        public synchronized Object getResponse() {
+            try {
+                while (response == null) {
+                    wait();
+                }
+            } catch (InterruptedException ex) {
+                return null;
+            }
+            return response;
+        }
+        public synchronized void setResponse(Object response) {
+            this.response = response;
+            notifyAll();
+        }
+    }
+
+    protected static int detectVersion(java.io.InputStream input,
+                                       java.io.OutputStream output)
+        throws IOException
+    {
+        java.io.DataInputStream dInput = new java.io.DataInputStream(input);
+        byte out[] = { 0, 0, 13, 10 };
+        output.write(out);
+
+        int len = dInput.readUnsignedShort();
+        int tp = dInput.readUnsignedShort();
+        if (tp == 0) {
+            byte err[] = new byte[len];
+            dInput.readFully(err);
+            return 0;
+        } else if ((len & 0xff00) != 0x0a00 &&
+                   (len & 0x00ff) != 0x000a &&
+                   (tp  & 0xff00) != 0x0a00 &&
+                   (tp  & 0x00ff) != 0x000a) {
+            while (input.read() != '\n')
+                ;
+        }
+        return 1;
+    }
+
+    public static TorControlConnection getConnection(java.net.Socket sock)
+        throws IOException
+    {
+        int version = detectVersion(sock.getInputStream(),
+                                    sock.getOutputStream());
+        if (version == 0)
+            return new TorControlConnection0(sock);
+        else
+            return new TorControlConnection1(sock);
+    }
+
+    protected TorControlConnection() {
+        this.waiters = new LinkedList();
+    }
+
+    /** Set the EventHandler object that will be notified of any
+     * events Tor delivers to this connection.  To make Tor send us
+     * events, call setEvents(). */
+    public void setEventHandler(EventHandler handler) {
+        this.handler = handler;
+    }
+
+    /**
+     * Start a thread to react to Tor's responses in the background.
+     * This is necessary to handle asynchronous events and synchronous
+     * responses that arrive independantly over the same socket.
+     */
+    public Thread launchThread(boolean daemon) {
+    	ControlParseThread th = new ControlParseThread(this);
+        if (daemon)
+            th.setDaemon(true);
+        th.setName("Tor Control Parser");
+        th.start();
+        this.thread = th;
+        return th;
+    }
+
+    private static class ControlParseThread extends Thread {
+    	TorControlConnection parent;
+    	ControlParseThread(TorControlConnection parent) {
+    		this.parent = parent;
+    	}
+    	boolean stopped = false;
+        public void run() {
+            try {
+                parent.react();
+            } catch (IOException ex) {
+            	if (stopped) return; // TODO THIS is imporant!
+                throw new RuntimeException(ex);
+            }
+        }
+    };
+    
+    protected final void checkThread() {
+        if (thread == null)
+            launchThread(true);
+    }
+
+    public void stopThread() {
+    	if (this.thread != null) {
+    		this.thread.stopped = true;
+    		this.thread.interrupt();
+    	}
+    }
+    
+    protected abstract void react() throws IOException;
+
+    /** Change the value of the configuration option 'key' to 'val'.
+     */
+    public void setConf(String key, String value) throws IOException {
+        List lst = new ArrayList();
+        lst.add(key+" "+value);
+        setConf(lst);
+    }
+
+    /** Change the values of the configuration options stored in kvMap. */
+    public void setConf(Map kvMap) throws IOException {
+        List lst = new ArrayList();
+        for (Iterator it = kvMap.entrySet().iterator(); it.hasNext(); ) {
+            Map.Entry ent = (Map.Entry) it.next();
+            lst.add(ent.getKey()+" "+ent.getValue()+"\n");
+        }
+        setConf(lst);
+    }
+
+    /** Change the values of the configuration options stored in
+     * 'kvList'.  (The format is "key value"). */
+    public abstract void setConf(Collection kvList) throws IOException;
+
+    /** Try to reset the values listed in the collection 'keys' to their
+     * default values.
+     **/
+    public abstract void resetConf(Collection keys) throws IOException;
+
+    /** Return the value of the configuration option 'key' */
+    public List getConf(String key) throws IOException {
+        List lst = new ArrayList();
+        lst.add(key);
+        return getConf(lst);
+    }
+
+    /** Return a key-value map for the configuration options in 'keys' */
+    public abstract List getConf(Collection keys) throws IOException;
+
+    /** Tell Tor to begin sending us events of the types listed in 'events'.
+     * Elements must be one of the EVENT_* values from TorControlCommands */
+    public abstract void setEvents(List events) throws IOException;
+
+    /** Send Tor an authentication sequence 'auth' */
+    // XXXX more info about how to set this up securely.
+    public abstract void authenticate(byte[] auth) throws IOException;
+
+    /** Tell Tor to save the value of its configuration to disk. */
+    public abstract void saveConf() throws IOException;
+
+    /** Send a signal to the Tor process. */
+    public abstract void signal(String signal) throws IOException;
+
+    /** Send a signal to the Tor process to shut it down or halt it.
+     * Does not wait for a response. */
+        public abstract void shutdownTor(String signal) throws IOException;
+
+    /** Tell Tor to replace incoming addresses with those as listed in 'kvLines'.
+     */
+    public abstract Map mapAddresses(Collection kvLines) throws IOException;
+
+    public Map mapAddresses(Map addresses) throws IOException {
+        List kvList = new ArrayList();
+        for (Iterator it = addresses.entrySet().iterator(); it.hasNext(); ) {
+            Map.Entry e = (Map.Entry) it.next();
+            kvList.add(e.getKey()+" "+e.getValue());
+        }
+        return mapAddresses(kvList);
+    }
+
+    public String mapAddress(String fromAddr, String toAddr) throws IOException {
+        List lst = new ArrayList();
+        lst.add(fromAddr+" "+toAddr+"\n");
+        Map m = mapAddresses(lst);
+        return (String) m.get(fromAddr);
+    }
+
+    /** Look up the information values listed in keys. */
+    public abstract Map getInfo(Collection keys) throws IOException;
+
+    /** Return the value of the information field 'key' */
+    public String getInfo(String key) throws IOException {
+        List lst = new ArrayList();
+        lst.add(key);
+        Map m = getInfo(lst);
+        return (String) m.get(key);
+    }
+
+    /**
+     * Tell Tor to extend the circuit identified by 'circID' through the
+     * servers named in the list 'path'.
+     */
+    public abstract String extendCircuit(String circID, String path) throws IOException;
+
+    /**
+     * Tell Tor to attach the stream identified by 'streamID' to the circuit
+     * identified by 'circID'.
+     */
+    public abstract void attachStream(String streamID, String circID) throws IOException;
+
+    /** Tell Tor about the server descriptor in 'desc' */
+    public abstract String postDescriptor(String desc) throws IOException;
+
+    /** Tell Tor to change the target of the stream identified by 'streamID'
+     * to 'address'.
+     */
+    public abstract void redirectStream(String streamID, String address) throws IOException;
+
+    /** Tell Tor to close the stream identified by 'streamID'.
+     */
+    public abstract void closeStream(String streamID, byte reason)
+        throws IOException;
+
+    /** Tell Tor to close the circuit identified by 'streamID'.
+     */
+    public abstract void closeCircuit(String circID, boolean ifUnused) throws IOException;
+
+}
+


Property changes on: puppetor/trunk/src/net/freehaven/tor/control/TorControlConnection.java
___________________________________________________________________
Name: svn:executable
   + *

Added: puppetor/trunk/src/net/freehaven/tor/control/TorControlConnection0.java
===================================================================
--- puppetor/trunk/src/net/freehaven/tor/control/TorControlConnection0.java	2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/net/freehaven/tor/control/TorControlConnection0.java	2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,431 @@
+// $Id: TorControlConnection0.java 9193 2006-12-25 04:37:53Z nickm $
+// Copyright 2005 Nick Mathewson, Roger Dingledine
+// See LICENSE file for copying information
+package net.freehaven.tor.control;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+/** DOCDOC */
+public class TorControlConnection0 extends TorControlConnection
+    implements TorControlCommands
+{
+    protected java.io.DataOutputStream outStream;
+    protected java.io.DataInputStream inStream;
+
+    static class Cmd {
+        public int type;
+        public byte[] body;
+
+        Cmd(int t, byte[] b) { type = t; body = b; }
+        Cmd(int t, int l) { type = t; body = new byte[l]; };
+    }
+
+    /** Create a new TorControlConnection to communicate with Tor over
+     * a given socket.  After calling this constructor, it is typical to
+     * call launchThread and authenticate. */
+    public TorControlConnection0(java.net.Socket connection)
+        throws IOException {
+        this(connection.getInputStream(), connection.getOutputStream());
+    }
+
+    /** Create a new TorControlConnection to communicate with Tor over
+     * an arbitrary pair of data streams.
+     */
+    public TorControlConnection0(java.io.InputStream i, java.io.OutputStream o)
+        throws IOException {
+        this.outStream = new java.io.DataOutputStream(o);
+        this.inStream = new java.io.DataInputStream(i);
+        this.waiters = new LinkedList();
+    }
+
+    /** helper: sends a single (unfragmentable) command to Tor */
+    protected final void sendCommand0(int type, byte[] cmd)
+        throws IOException {
+        int length = cmd == null ? 0 : cmd.length;
+        outStream.writeShort((short)length);
+        outStream.writeShort(type);
+        if (cmd != null)
+            outStream.write(cmd);
+    }
+
+    /** helper: sends a single (possibly fragmented) command to Tor */
+    protected void sendCommand(short type, byte[] cmd) throws IOException {
+        synchronized(this.outStream) {
+            if (cmd == null || cmd.length <= 65535) {
+                sendCommand0(type, cmd);
+                return;
+            }
+            int length = cmd.length;
+            outStream.writeShort(65535);
+            outStream.writeShort(CMD_FRAGMENTHEADER);
+            outStream.writeShort(type);
+            outStream.writeInt(length);
+            outStream.write(cmd, 0, 65535);
+            for (int pos=65535; pos < length; pos += 65535) {
+                int flen = length-pos < 65535 ? length-pos : 65535;
+                outStream.writeShort(flen);
+                outStream.writeShort(CMD_FRAGMENT);
+                this.outStream.write(cmd, pos, flen);
+            }
+        }
+    }
+
+    /** helper: read a possibly fragmented command from Tor */
+    protected final Cmd readCommand0() throws IOException {
+        int len = this.inStream.readUnsignedShort();
+        int cmd = this.inStream.readUnsignedShort();
+        byte[] result = new byte[len];
+        this.inStream.readFully(result);
+        return new Cmd(cmd, result);
+    }
+
+    /** Read a command from Tor, defragmenting as necessary */
+    protected Cmd readCommand() throws IOException {
+        synchronized (inStream) {
+            Cmd c = readCommand0();
+            if (c.type != CMD_FRAGMENT && c.type != CMD_FRAGMENTHEADER)
+                return c;
+
+            if (c.type == CMD_FRAGMENT)
+                throw new TorControlSyntaxError("Fragment without header");
+
+            int realType = Bytes.getU16(c.body, 0);
+            int realLen = Bytes.getU32(c.body, 2);
+
+            Cmd out = new Cmd(realType, realLen);
+            System.arraycopy(c.body, 6, out.body, 0, c.body.length-6);
+            int pos = c.body.length-6;
+            while (pos < realLen) {
+                c = readCommand0();
+                if (c.type != CMD_FRAGMENT)
+                    throw new TorControlSyntaxError("Incomplete fragmented message");
+                System.arraycopy(c.body, 0, out.body, pos, c.body.length);
+                pos += c.body.length;
+            }
+            return out;
+        }
+    }
+
+    /** helper: implement the main background loop. */
+    protected void react() throws IOException {
+        while (true) {
+            Cmd c = readCommand();
+            if (c.type == CMD_EVENT)
+                handleEvent(c);
+            else {
+                Waiter w;
+                synchronized (waiters) {
+                    w = (Waiter) waiters.removeFirst();
+                }
+                w.setResponse(c);
+            }
+        }
+    }
+
+    /** helper: Send a command and wait for the next reponse type command
+     * to be received (in order) */
+    protected synchronized Cmd _sendAndWaitForResponse(short type, byte[] cmd)
+        throws IOException {
+        checkThread();
+        Waiter w = new Waiter();
+        synchronized (waiters) {
+            sendCommand(type, cmd);
+            waiters.addLast(w);
+        }
+        return (Cmd) w.getResponse();
+    }
+
+    /** Send a message to Tor, and wait for a respose.
+     *
+     * @throw TorControlError if Tor tells us about an error
+     * @throw TorControlSyntaxError if the response type wasn't exType1...4
+     **/
+    protected Cmd sendAndWaitForResponse(short type, byte[] cmd,
+                   short exType1, short exType2, short exType3, short exType4)
+        throws IOException {
+
+        Cmd c = _sendAndWaitForResponse(type, cmd);
+        if (c.type == CMD_ERROR)
+            throw new TorControlError(Bytes.getU16(c.body, 0),
+                                      Bytes.getNulTerminatedStr(c.body, 2));
+        if (c.type == exType1 || c.type == exType2 || c.type == exType3 ||
+            c.type == exType4)
+            return c;
+
+        throw new TorControlSyntaxError("Unexpected reply type: "+c.type);
+    }
+
+    protected Cmd sendAndWaitForResponse(short type, byte[] cmd)
+        throws IOException {
+        return sendAndWaitForResponse(type, cmd, CMD_DONE, CMD_DONE, CMD_DONE, CMD_DONE);
+    }
+
+    protected Cmd sendAndWaitForResponse(short type, byte[] cmd, short exType1)
+        throws IOException {
+        return sendAndWaitForResponse(type, cmd, exType1, exType1, exType1,
+                                      exType1);
+    }
+
+    protected Cmd sendAndWaitForResponse(short type, byte[] cmd,
+                                    short exType1, short exType2)
+        throws IOException {
+        return sendAndWaitForResponse(type, cmd, exType1, exType2, exType2,
+                                      exType2);
+    }
+
+    protected Cmd sendAndWaitForResponse(short type, byte[] cmd,
+                                   short exType1, short exType2, short exType3)
+        throws IOException {
+        return sendAndWaitForResponse(type, cmd, exType1, exType2, exType3,
+                                      exType3);
+    }
+
+    /** Helper: decode a CMD_EVENT command and dispatch it to our
+     * EventHandler (if any). */
+    protected void handleEvent(Cmd c) {
+        if (handler == null)
+            return;
+        int type = Bytes.getU16(c.body, 0);
+
+        switch (type) {
+          case EVENT_CIRCSTATUS:
+              handler.circuitStatus(CIRC_STATUS_NAMES[c.body[2]],
+                                    Bytes.getU32S(c.body, 3),
+                                    Bytes.getNulTerminatedStr(c.body, 7));
+              break;
+          case EVENT_STREAMSTATUS:
+              handler.streamStatus(STREAM_STATUS_NAMES[c.body[2]],
+                                   Bytes.getU32S(c.body, 3),
+                                   Bytes.getNulTerminatedStr(c.body, 7));
+              break;
+          case EVENT_ORCONNSTATUS:
+              handler.orConnStatus(OR_CONN_STATUS_NAMES[c.body[2]],
+                                   Bytes.getNulTerminatedStr(c.body, 3));
+              break;
+          case EVENT_BANDWIDTH:
+              handler.bandwidthUsed(Bytes.getU32(c.body, 2),
+                                    Bytes.getU32(c.body, 6));
+              break;
+          case EVENT_NEWDESCRIPTOR:
+              List lst = new ArrayList();
+              Bytes.splitStr(lst, c.body, 2, (byte)',');
+              handler.newDescriptors(lst);
+              break;
+          case EVENT_MSG_DEBUG:
+              handler.message("DEBUG", Bytes.getNulTerminatedStr(c.body, 2));
+              break;
+          case EVENT_MSG_INFO:
+              handler.message("INFO", Bytes.getNulTerminatedStr(c.body, 2));
+              break;
+          case EVENT_MSG_NOTICE:
+              handler.message("NOTICE", Bytes.getNulTerminatedStr(c.body, 2));
+              break;
+          case EVENT_MSG_WARN:
+              handler.message("WARN", Bytes.getNulTerminatedStr(c.body, 2));
+              break;
+          case EVENT_MSG_ERROR:
+              handler.message("ERR", Bytes.getNulTerminatedStr(c.body, 2));
+              break;
+          default:
+              throw new TorControlSyntaxError("Unrecognized event type.");
+        }
+    }
+
+    /** Change the values of the configuration options stored in
+     * 'kvList'.  (The format is "key value"). */
+    public void setConf(Collection kvList) throws IOException {
+        StringBuffer b = new StringBuffer();
+        for (Iterator it = kvList.iterator(); it.hasNext(); ) {
+            String kv = (String) it.next();
+            b.append(kv).append("\n");
+        }
+        sendAndWaitForResponse(CMD_SETCONF, b.toString().getBytes());
+    }
+
+    public void resetConf(Collection keylist) throws IOException {
+        setConf(keylist);
+    }
+
+    public List getConf(Collection keys) throws IOException {
+        StringBuffer s = new StringBuffer();
+        for (Iterator it = keys.iterator(); it.hasNext(); ) {
+            String key = (String) it.next();
+            s.append(key).append("\n");
+        }
+        Cmd c = sendAndWaitForResponse(CMD_GETCONF, s.toString().getBytes(),
+                                       CMD_CONFVALUE);
+        List lines = new ArrayList();
+        Bytes.splitStr(lines, c.body, 0, (byte)'\n');
+        List result = new ArrayList();
+        for (Iterator it = lines.iterator(); it.hasNext(); ) {
+            String kv = (String) it.next();
+            int idx = kv.indexOf(' ');
+            result.add(new ConfigEntry(kv.substring(0, idx),
+                                       kv.substring(idx+1)));
+        }
+        return result;
+    }
+
+    public void setEvents(List events) throws IOException {
+        byte[] ba = new byte[events.size() * 2];
+        int i;
+        Iterator it;
+        for(i=0, it = events.iterator(); it.hasNext(); i += 2) {
+            Object event = it.next();
+            short e = -1;
+            if (event instanceof Number) {
+                e = ((Number)event).shortValue();
+            } else {
+                String s = ((String) event).toUpperCase();
+                for (int j = 0; i < EVENT_NAMES.length; ++i) {
+                    if (EVENT_NAMES[j].equals(s)) {
+                        e = (short)j;
+                        break;
+                    }
+                }
+                if (e < 0)
+                    throw new TorControlError("Unknown v0 code for event '"+s+"'");
+            }
+            Bytes.setU16(ba, i, e);
+        }
+        sendAndWaitForResponse(CMD_SETEVENTS, ba);
+        System.out.println("OK");
+    }
+
+    public void authenticate(byte[] auth) throws IOException {
+        if (auth == null)
+            auth = new byte[0];
+        sendAndWaitForResponse(CMD_AUTH, auth);
+    }
+
+    public void saveConf() throws IOException {
+        sendAndWaitForResponse(CMD_SAVECONF, new byte[0]);
+    }
+
+    public void signal(String signal) throws IOException {
+        int sig;
+        signal = signal.toUpperCase();
+        if (signal.equals("HUP") || signal.equals("RELOAD"))
+            sig = SIGNAL_HUP;
+        else if (signal.equals("INT") || signal.equals("SHUTDOWN"))
+            sig = SIGNAL_INT;
+        else if (signal.equals("USR1") || signal.equals("DUMP"))
+            sig = SIGNAL_USR1;
+        else if (signal.equals("USR2") || signal.equals("DEBUG"))
+            sig = SIGNAL_USR2;
+        else if (signal.equals("TERM") || signal.equals("HALT"))
+            sig = SIGNAL_TERM;
+        else
+            throw new TorControlError("Unrecognized value for signal()");
+        byte[] ba = { (byte)sig };
+        sendAndWaitForResponse(CMD_SIGNAL, ba);
+    }
+    
+    /** Send a signal to the Tor process to shut it down or halt it.
+     * Does not wait for a response. */
+        public void shutdownTor(String signal) throws IOException {
+                throw new RuntimeException("Method is not implemented!");
+        }
+
+    public Map mapAddresses(Collection kvLines) throws IOException {
+        StringBuffer sb = new StringBuffer();
+        for (Iterator it = kvLines.iterator(); it.hasNext(); ) {
+            sb.append((String)it.next()).append("\n");
+        }
+        Cmd c = sendAndWaitForResponse(CMD_MAPADDRESS, sb.toString().getBytes());
+        Map result = new HashMap();
+        List lst = new ArrayList();
+        Bytes.splitStr(lst, c.body, 0, (byte)'\n');
+        for (Iterator it = lst.iterator(); it.hasNext(); ) {
+            String kv = (String) it.next();
+            int idx = kv.indexOf(' ');
+            result.put(kv.substring(0, idx),
+                       kv.substring(idx+1));
+        }
+        return result;
+    }
+
+    public Map getInfo(Collection keys) throws IOException {
+        StringBuffer sb = new StringBuffer();
+        for (Iterator it = keys.iterator(); it.hasNext(); ) {
+            sb.append(((String)it.next())+"\n");
+        }
+        Cmd c = sendAndWaitForResponse(CMD_GETINFO, sb.toString().getBytes(),
+                                       CMD_INFOVALUE);
+        Map m = new HashMap();
+        List lst = new ArrayList();
+        Bytes.splitStr(lst, c.body, 0, (byte)0);
+        if ((lst.size() % 2) != 0)
+            throw new TorControlSyntaxError(
+                                     "Odd number of substrings from GETINFO");
+        for (Iterator it = lst.iterator(); it.hasNext(); ) {
+            Object k = it.next();
+            Object v = it.next();
+            m.put(k, v);
+        }
+        return m;
+    }
+
+    public String extendCircuit(String circID, String path) throws IOException {
+        byte[] p = path.getBytes();
+        byte[] ba = new byte[p.length+4];
+        Bytes.setU32(ba, 0, (int)Long.parseLong(circID));
+        System.arraycopy(p, 0, ba, 4, p.length);
+        Cmd c = sendAndWaitForResponse(CMD_EXTENDCIRCUIT, ba);
+        return Integer.toString(Bytes.getU32(c.body, 0));
+    }
+
+    public void attachStream(String streamID, String circID)
+        throws IOException {
+        byte[] ba = new byte[8];
+        Bytes.setU32(ba, 0, (int)Long.parseLong(streamID));
+        Bytes.setU32(ba, 4, (int)Long.parseLong(circID));
+        sendAndWaitForResponse(CMD_ATTACHSTREAM, ba);
+    }
+
+    /** Tell Tor about the server descriptor in 'desc' */
+    public String postDescriptor(String desc) throws IOException {
+        return new String(
+             sendAndWaitForResponse(CMD_POSTDESCRIPTOR, desc.getBytes()).body);
+    }
+
+    /** Tell Tor to change the target of the stream identified by 'streamID'
+     * to 'address'.
+     */
+    public void redirectStream(String streamID, String address) throws IOException {
+        byte[] addr = address.getBytes();
+        byte[] ba = new byte[addr.length+4];
+        Bytes.setU32(ba, 0, (int)Long.parseLong(streamID));
+        System.arraycopy(addr, 0, ba, 4, addr.length);
+        sendAndWaitForResponse(CMD_REDIRECTSTREAM, ba);
+    }
+
+    /** Tell Tor to close the stream identified by 'streamID'.
+     */
+    public void closeStream(String streamID, byte reason)
+        throws IOException {
+        byte[] ba = new byte[6];
+        Bytes.setU32(ba, 0, (int)Long.parseLong(streamID));
+        ba[4] = reason;
+        ba[5] = (byte)0;
+        sendAndWaitForResponse(CMD_CLOSESTREAM, ba);
+    }
+
+    /** Tell Tor to close the circuit identified by 'streamID'.
+     */
+    public void closeCircuit(String circID, boolean ifUnused) throws IOException {
+        byte[] ba = new byte[5];
+        Bytes.setU32(ba, 0, (int)Long.parseLong(circID));
+        ba[4] = (byte)(ifUnused? 1 : 0);
+        sendAndWaitForResponse(CMD_CLOSECIRCUIT, ba);
+    }
+
+}
+


Property changes on: puppetor/trunk/src/net/freehaven/tor/control/TorControlConnection0.java
___________________________________________________________________
Name: svn:executable
   + *

Added: puppetor/trunk/src/net/freehaven/tor/control/TorControlConnection1.java
===================================================================
--- puppetor/trunk/src/net/freehaven/tor/control/TorControlConnection1.java	2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/net/freehaven/tor/control/TorControlConnection1.java	2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,625 @@
+// $Id: TorControlConnection1.java 9193 2006-12-25 04:37:53Z nickm $
+// Copyright 2005 Nick Mathewson, Roger Dingledine
+// See LICENSE file for copying information
+package net.freehaven.tor.control;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.StringTokenizer;
+
+/** Extends the TorControlConnection class to implement Version 1
+* of the TC protocol, as specified in control-spec.txt.
+*/
+public class TorControlConnection1 extends TorControlConnection
+    implements TorControlCommands
+{
+    protected java.io.BufferedReader input;
+    protected java.io.Writer output;
+    protected java.io.PrintWriter debugOutput;
+
+    static class ReplyLine {
+        public String status;
+        public String msg;
+        public String rest;
+
+        ReplyLine(String status, String msg, String rest) {
+            this.status = status; this.msg = msg; this.rest = rest;
+        }
+        
+        public String toString() {
+        	return status + " " + msg + " " + rest;
+        }
+    }
+
+    /** Create a new TorControlConnection to communicate with Tor over
+     * a given socket.  After calling this constructor, it is typical to
+     * call launchThread and authenticate. */
+    public TorControlConnection1(java.net.Socket connection)
+        throws IOException {
+        this(connection.getInputStream(), connection.getOutputStream());
+    }
+
+    /** Create a new TorControlConnection to communicate with Tor over
+     * an arbitrary pair of data streams.
+     */
+    public TorControlConnection1(java.io.InputStream i, java.io.OutputStream o)
+        throws IOException {
+        this(new java.io.InputStreamReader(i),
+             new java.io.OutputStreamWriter(o));
+    }
+
+    public TorControlConnection1(java.io.Reader i, java.io.Writer o)
+        throws IOException {
+        this.output = o;
+        if (i instanceof java.io.BufferedReader)
+            this.input = (java.io.BufferedReader) i;
+        else
+            this.input = new java.io.BufferedReader(i);
+
+        this.waiters = new LinkedList();
+    }
+
+    protected final void writeEscaped(String s) throws IOException {
+        StringTokenizer st = new StringTokenizer(s, "\n");
+        while (st.hasMoreTokens()) {
+            String line = st.nextToken();
+            if (line.startsWith("."))
+                line = "."+line;
+            if (line.endsWith("\r"))
+                line += "\n";
+            else
+                line += "\r\n";
+            if (debugOutput != null)
+                debugOutput.print(">> "+line);
+            output.write(line);
+        }
+        output.write(".\r\n");
+        if (debugOutput != null)
+            debugOutput.print(">> .\n");
+    }
+
+    protected static final String quote(String s) {
+        StringBuffer sb = new StringBuffer("\"");
+        for (int i = 0; i < s.length(); ++i) {
+            char c = s.charAt(i);
+            switch (c)
+                {
+                case '\r':
+                case '\n':
+                case '\\':
+                case '\"':
+                    sb.append('\\');
+                }
+            sb.append(c);
+        }
+        sb.append('\"');
+        return sb.toString();
+    }
+
+    protected final ArrayList readReply() throws IOException {
+        ArrayList reply = new ArrayList();
+        char c;
+        do {
+            String line = input.readLine();
+            if (line == null) {
+                // if line is null, the end of the stream has been reached, i.e.
+                // the connection to Tor has been closed!
+                if (reply.isEmpty()) {
+                        // nothing received so far, can exit cleanly
+                        return reply;
+                } else {
+                        // received half of a reply before the connection broke down
+                        throw new TorControlSyntaxError("Connection to Tor " +
+                                        " broke down while receiving reply!");
+                }
+            }
+            if (debugOutput != null)
+                debugOutput.println("<< "+line);
+            if (line.length() < 4)
+                throw new TorControlSyntaxError("Line (\""+line+"\") too short");
+            String status = line.substring(0,3);
+            c = line.charAt(3);
+            String msg = line.substring(4);
+            String rest = null;
+            if (c == '+') {
+                StringBuffer data = new StringBuffer();
+                while (true) {
+                    line = input.readLine();
+                    if (debugOutput != null)
+                        debugOutput.print("<< "+line);
+                    if (line.equals("."))
+                        break;
+                    else if (line.startsWith("."))
+                        line = line.substring(1);
+                    data.append(line).append('\n');
+                }
+                rest = data.toString();
+            }
+            reply.add(new ReplyLine(status, msg, rest));
+        } while (c != ' ');
+
+        return reply;
+    }
+
+    /** helper: implement the main background loop. */
+    protected void react() throws IOException {
+        while (true) {
+            ArrayList lst = readReply();
+            if (lst.isEmpty()) {
+                // connection has been closed remotely! end the loop!
+                return;
+            }
+            if (((ReplyLine)lst.get(0)).status.startsWith("6"))
+                handleEvent(lst);
+            else {
+                Waiter w;
+                synchronized (waiters) {
+                	// waiters may be empty after shutting down Tor...
+                	if (waiters.isEmpty()) {
+                		System.err.println("Attempt to remove a waiter from the waiter list failed! Event status: " + ((ReplyLine)lst.get(0)).status);
+                		break; // TODO is this correct?!
+                	}
+                    w = (Waiter) waiters.removeFirst();
+                }
+                w.setResponse(lst);
+            }
+        }
+    }
+
+    protected synchronized ArrayList sendAndWaitForResponse(String s,String rest)
+        throws IOException {
+        checkThread();
+        Waiter w = new Waiter();
+        if (debugOutput != null)
+            debugOutput.print(">> "+s);
+        synchronized (waiters) {
+            output.write(s);
+            output.flush();
+            if (rest != null)
+                writeEscaped(rest);
+            waiters.addLast(w);
+        }
+        ArrayList lst = (ArrayList) w.getResponse();
+        for (Iterator i = lst.iterator(); i.hasNext(); ) {
+            ReplyLine c = (ReplyLine) i.next();
+            if (! c.status.startsWith("2"))
+                throw new TorControlError("Error reply: "+c.msg);
+        }
+        return lst;
+    }
+
+    /** Helper: decode a CMD_EVENT command and dispatch it to our
+     * EventHandler (if any). */
+    protected void handleEvent(ArrayList events) {
+        if (handler == null)
+            return;
+
+        for (Iterator i = events.iterator(); i.hasNext(); ) {
+            ReplyLine line = (ReplyLine) i.next();
+            int idx = line.msg.indexOf(' ');
+            // value of idx is -1. check this first!
+            if (idx < 0) {
+            	System.err.println("Cannot parse event format: " + events.toString());
+            	continue;
+            }
+            String tp = line.msg.substring(0, idx).toUpperCase();
+            String rest = line.msg.substring(idx+1);
+            if (tp.equals("CIRC")) {
+                List lst = Bytes.splitStr(null, rest);
+                handler.circuitStatus((String)lst.get(1),
+                                      (String)lst.get(0),
+                                      (String)lst.get(2));
+            } else if (tp.equals("STREAM")) {
+                List lst = Bytes.splitStr(null, rest);
+                handler.streamStatus((String)lst.get(1),
+                                     (String)lst.get(0),
+                                     (String)lst.get(3));
+                // XXXX circID.
+            } else if (tp.equals("ORCONN")) {
+                List lst = Bytes.splitStr(null, rest);
+                handler.orConnStatus((String)lst.get(1), (String)lst.get(0));
+            } else if (tp.equals("BW")) {
+                List lst = Bytes.splitStr(null, rest);
+                handler.bandwidthUsed(Integer.parseInt((String)lst.get(0)),
+                                      Integer.parseInt((String)lst.get(1)));
+            } else if (tp.equals("NEWDESC")) {
+                List lst = Bytes.splitStr(null, rest);
+                handler.newDescriptors(lst);
+            } else if (tp.equals("DEBUG") ||
+                       tp.equals("INFO") ||
+                       tp.equals("NOTICE") ||
+                       tp.equals("WARN") ||
+                       tp.equals("ERR")) {
+                handler.message(tp, rest);
+            } else {
+                handler.unrecognized(tp, rest);
+            }
+        }
+    }
+
+    /** Changes the values of the configuration options stored in
+     * <b>kvList</b>.  Each list element in <b>kvList</b> is expected to be
+     * String of the format "key value".
+     *
+     * Tor behaves as though it had just read each of the key-value pairs
+     * from its configuration file.  Keywords with no corresponding values have
+     * their configuration values reset to their defaults.  setConf is
+     * all-or-nothing: if there is an error in any of the configuration settings,
+     * Tor sets none of them.
+     *
+     * When a configuration option takes multiple values, or when multiple
+     * configuration keys form a context-sensitive group (see getConf below), then
+     * setting any of the options in a setConf command is taken to reset all of
+     * the others.  For example, if two ORBindAddress values are configured, and a
+     * command arrives containing a single ORBindAddress value, the new
+     * command's value replaces the two old values.
+     * 
+     * To remove all settings for a given option entirely (and go back to its
+     * default value), include a String in <b>kvList</b> containing the key and no value.
+     */
+    public void setConf(Collection kvList) throws IOException {
+        if (kvList.size() == 0)
+            return;
+        StringBuffer b = new StringBuffer("SETCONF");
+        for (Iterator it = kvList.iterator(); it.hasNext(); ) {
+            String kv = (String) it.next();
+            int i = kv.indexOf(' ');
+            if (i == -1)
+                b.append(" ").append(kv);
+            b.append(" ").append(kv.substring(0,i)).append("=")
+                .append(quote(kv.substring(i+1)));
+        }
+        b.append("\r\n");
+        sendAndWaitForResponse(b.toString(), null);
+    }
+
+    public void resetConf(Collection keys) throws IOException {
+        if (keys.size() == 0)
+            return;
+        StringBuffer b = new StringBuffer("RESETCONF");
+        for (Iterator it = keys.iterator(); it.hasNext(); ) {
+            String key = (String) it.next();
+            b.append(" ").append(key);
+        }
+        b.append("\r\n");
+        sendAndWaitForResponse(b.toString(), null);
+    }
+
+    /** Sets <b>w</b> as the PrintWriter for debugging output, 
+    * which writes out all messages passed between Tor and the controller.  
+    * Outgoing messages are preceded by "\>\>" and incoming messages are preceded
+    * by "\<\<"
+    */
+    public void setDebugging(java.io.PrintWriter w) {
+        if (w instanceof java.io.PrintWriter)
+            debugOutput = (java.io.PrintWriter) w;
+        else
+            debugOutput = new java.io.PrintWriter(w, true);
+    }
+    
+    /** Sets <b>s</b> as the PrintStream for debugging output, 
+    * which writes out all messages passed between Tor and the controller.  
+    * Outgoing messages are preceded by "\>\>" and incoming messages are preceded
+    * by "\<\<"
+    */
+    public void setDebugging(java.io.PrintStream s) {
+        debugOutput = new java.io.PrintWriter(s, true);
+    }
+    
+    /** Requests the values of the configuration variables listed in <b>keys</b>.
+    * Results are returned as a list of ConfigEntry objects.
+    * 
+    * If an option appears multiple times in the configuration, all of its
+    * key-value pairs are returned in order.
+    *
+    * Some options are context-sensitive, and depend on other options with
+    * different keywords.  These cannot be fetched directly.  Currently there
+    * is only one such option: clients should use the "HiddenServiceOptions"
+    * virtual keyword to get all HiddenServiceDir, HiddenServicePort,
+    * HiddenServiceNodes, and HiddenServiceExcludeNodes option settings.
+    */
+    public List getConf(Collection keys) throws IOException {
+        StringBuffer sb = new StringBuffer("GETCONF");
+        for (Iterator it = keys.iterator(); it.hasNext(); ) {
+            String key = (String) it.next();
+            sb.append(" ").append(key);
+        }
+        sb.append("\r\n");
+        ArrayList lst = sendAndWaitForResponse(sb.toString(), null);
+        ArrayList result = new ArrayList();
+        for (Iterator it = lst.iterator(); it.hasNext(); ) {
+            String kv = ((ReplyLine) it.next()).msg;
+            int idx = kv.indexOf('=');
+            if (idx != -1) {
+            	result.add(new ConfigEntry(kv.substring(0, idx),
+                                       kv.substring(idx+1)));
+            }
+        }
+        return result;
+    }
+    
+    /** Request that the server inform the client about interesting events.
+    * Each element of <b>events</b> is one of the following Strings: 
+    * ["CIRC" | "STREAM" | "ORCONN" | "BW" | "DEBUG" |
+    *  "INFO" | "NOTICE" | "WARN" | "ERR" | "NEWDESC" | "ADDRMAP"] .
+    * 
+    * Any events not listed in the <b>events</b> are turned off; thus, calling
+    * setEvents with an empty <b>events</b> argument turns off all event reporting.
+    */
+    public void setEvents(List events) throws IOException {
+        StringBuffer sb = new StringBuffer("SETEVENTS");
+        for (Iterator it = events.iterator(); it.hasNext(); ) {
+            Object event = it.next();
+            if (event instanceof String) {
+                sb.append(" ").append((String)event);
+            } else {
+                int i = ((Number) event).intValue();
+                sb.append(" ").append(EVENT_NAMES[i]);
+            }
+        }
+        sb.append("\r\n");
+        sendAndWaitForResponse(sb.toString(), null);
+    }
+    
+    
+    /** Authenticates the controller to the Tor server.
+    *
+    * By default, the current Tor implementation trusts all local users, and 
+    * the controller can authenticate itself by calling authenticate(new byte[0]).
+    *
+    * If the 'CookieAuthentication' option is true, Tor writes a "magic cookie"
+    * file named "control_auth_cookie" into its data directory.  To authenticate,
+    * the controller must send the contents of this file in <b>auth</b>.
+    * 
+    * If the 'HashedControlPassword' option is set, <b>auth</b> must contain the salted
+    * hash of a secret password.  The salted hash is computed according to the
+    * S2K algorithm in RFC 2440 (OpenPGP), and prefixed with the s2k specifier.
+    * This is then encoded in hexadecimal, prefixed by the indicator sequence
+    * "16:".
+    *
+    * You can generate the salt of a password by calling
+    *       'tor --hash-password <password>'
+    * or by using the provided PasswordDigest class.
+    * To authenticate under this scheme, the controller sends Tor the original
+    * secret that was used to generate the password.
+    */
+    public void authenticate(byte[] auth) throws IOException {
+        String cmd = "AUTHENTICATE " + Bytes.hex(auth) + "\r\n";
+        sendAndWaitForResponse(cmd, null);
+    }
+
+    /** Instructs the server to write out its configuration options into its torrc.
+    */
+    public void saveConf() throws IOException {
+        sendAndWaitForResponse("SAVECONF\r\n", null);
+    }
+
+    /** Sends a signal from the controller to the Tor server.
+    * <b>signal</b> is one of the following Strings:
+    * <ul>
+    * <li>"RELOAD" or "HUP" :  Reload config items, refetch directory</li>
+    * <li>"SHUTDOWN" or "INT" : Controlled shutdown: if server is an OP, exit immediately.
+    *     If it's an OR, close listeners and exit after 30 seconds</li>
+    * <li>"DUMP" or "USR1" : Dump stats: log information about open connections and circuits</li>
+    * <li>"DEBUG" or "USR2" : Debug: switch all open logs to loglevel debug</li>
+    * <li>"HALT" or "TERM" : Immediate shutdown: clean up and exit now</li>
+    * </ul>
+    */
+    public void signal(String signal) throws IOException {
+        String cmd = "SIGNAL " + signal + "\r\n";
+        sendAndWaitForResponse(cmd, null);
+    }
+    
+    /** Send a signal to the Tor process to shut it down or halt it.
+     * Does not wait for a response. */
+        public void shutdownTor(String signal) throws IOException {
+                String s = "SIGNAL " + signal + "\r\n";
+        if (debugOutput != null)
+            debugOutput.print(">> "+s);
+        synchronized (waiters) {
+            output.write(s);
+            output.flush();
+        }        
+        }
+    
+    /** Tells the Tor server that future SOCKS requests for connections to a set of original
+    * addresses should be replaced with connections to the specified replacement
+    * addresses.  Each element of <b>kvLines</b> is a String of the form
+    * "old-address new-address".  This function returns the new address mapping.
+    *
+    * The client may decline to provide a body for the original address, and
+    * instead send a special null address ("0.0.0.0" for IPv4, "::0" for IPv6, or
+    * "." for hostname), signifying that the server should choose the original
+    * address itself, and return that address in the reply.  The server
+    * should ensure that it returns an element of address space that is unlikely
+    * to be in actual use.  If there is already an address mapped to the
+    * destination address, the server may reuse that mapping.
+    * 
+    * If the original address is already mapped to a different address, the old
+    * mapping is removed.  If the original address and the destination address
+    * are the same, the server removes any mapping in place for the original
+    * address.
+    *
+    * Mappings set by the controller last until the Tor process exits:
+    * they never expire. If the controller wants the mapping to last only
+    * a certain time, then it must explicitly un-map the address when that
+    * time has elapsed.
+    */
+    public Map mapAddresses(Collection kvLines) throws IOException {
+        StringBuffer sb = new StringBuffer("MAPADDRESS");
+        for (Iterator it = kvLines.iterator(); it.hasNext(); ) {
+            String kv = (String) it.next();
+            int i = kv.indexOf(' ');
+            sb.append(" ").append(kv.substring(0,i)).append("=")
+                .append(quote(kv.substring(i+1)));
+        }
+        sb.append("\r\n");
+        ArrayList lst = sendAndWaitForResponse(sb.toString(), null);
+        Map result = new HashMap();
+        for (Iterator it = lst.iterator(); it.hasNext(); ) {
+            String kv = ((ReplyLine) it.next()).msg;
+            int idx = kv.indexOf('=');
+            result.put(kv.substring(0, idx),
+                       kv.substring(idx+1));
+        }
+        return result;
+    }
+    
+    /** Queries the Tor server for keyed values that are not stored in the torrc
+    * configuration file.  Returns a map of keys to values.
+    *
+    * Recognized keys include:
+    * <ul>
+    * <li>"version" : The version of the server's software, including the name
+    *  of the software. (example: "Tor 0.0.9.4")</li>
+    * <li>"desc/id/<OR identity>" or "desc/name/<OR nickname>" : the latest server
+    * descriptor for a given OR, NUL-terminated.  If no such OR is known, the
+    * corresponding value is an empty string.</li>
+    * <li>"network-status" : a space-separated list of all known OR identities.
+    * This is in the same format as the router-status line in directories;
+    * see tor-spec.txt for details.</li>
+    * <li>"addr-mappings/all"</li>
+    * <li>"addr-mappings/config"</li>
+    * <li>"addr-mappings/cache"</li>
+    * <li>"addr-mappings/control" : a space-separated list of address mappings, each
+    * in the form of "from-address=to-address".  The 'config' key
+    * returns those address mappings set in the configuration; the 'cache'
+    * key returns the mappings in the client-side DNS cache; the 'control'
+    * key returns the mappings set via the control interface; the 'all'
+    * target returns the mappings set through any mechanism.</li>
+    * <li>"circuit-status" : A series of lines as for a circuit status event. Each line is of the form:
+    * "CircuitID CircStatus Path"</li>
+    * <li>"stream-status" : A series of lines as for a stream status event.  Each is of the form:
+    * "StreamID StreamStatus CircID Target"</li>
+    * <li>"orconn-status" : A series of lines as for an OR connection status event.  Each is of the
+    * form: "ServerID ORStatus"</li>
+    * </ul>
+    */
+    public Map getInfo(Collection keys) throws IOException {
+        StringBuffer sb = new StringBuffer("GETINFO");
+        for (Iterator it = keys.iterator(); it.hasNext(); ) {
+            sb.append(" ").append((String)it.next());
+        }
+        sb.append("\r\n");
+        ArrayList lst = sendAndWaitForResponse(sb.toString(), null);
+        Map m = new HashMap();
+        for (Iterator it = lst.iterator(); it.hasNext(); ) {
+            ReplyLine line = (ReplyLine) it.next();
+            int idx = line.msg.indexOf('=');
+            if (idx<0)
+                break;
+            String k = line.msg.substring(0,idx);
+            Object v;
+            if (line.rest != null) {
+                v = line.rest;
+            } else {
+                v = line.msg.substring(idx+1);
+            }
+            m.put(k, v);
+        }
+        return m;
+    }
+    
+    /** An extendCircuit request takes one of two forms: either the <b>circID</b> is zero, in
+    * which case it is a request for the server to build a new circuit according
+    * to the specified path, or the <b>circID</b> is nonzero, in which case it is a
+    * request for the server to extend an existing circuit with that ID according
+    * to the specified <b>path</b>.
+    *
+    * If successful, returns the Circuit ID of the (maybe newly created) circuit.
+    */
+    public String extendCircuit(String circID, String path) throws IOException {
+        ArrayList lst = sendAndWaitForResponse(
+                          "EXTENDCIRCUIT "+circID+" "+path+"\r\n", null);
+        return ((ReplyLine)lst.get(0)).msg;
+    }
+    
+    /** Informs the Tor server that the stream specified by <b>streamID</b> should be
+    * associated with the circuit specified by <b>circID</b>.  
+    * 
+    * Each stream may be associated with
+    * at most one circuit, and multiple streams may share the same circuit.
+    * Streams can only be attached to completed circuits (that is, circuits that
+    * have sent a circuit status "BUILT" event or are listed as built in a
+    * getInfo circuit-status request).
+    * 
+    * If <b>circID</b> is 0, responsibility for attaching the given stream is
+    * returned to Tor.
+    * 
+    * By default, Tor automatically attaches streams to
+    * circuits itself, unless the configuration variable
+    * "__LeaveStreamsUnattached" is set to "1".  Attempting to attach streams
+    * via TC when "__LeaveStreamsUnattached" is false may cause a race between
+    * Tor and the controller, as both attempt to attach streams to circuits.
+    */
+    public void attachStream(String streamID, String circID)
+        throws IOException {
+        sendAndWaitForResponse("ATTACHSTREAM "+streamID+" "+circID+"\r\n", null);
+    }
+
+    /** Tells Tor about the server descriptor in <b>desc</b>.
+    * 
+    * The descriptor, when parsed, must contain a number of well-specified
+    * fields, including fields for its nickname and identity.
+    */
+    // More documentation here on format of desc?
+    // No need for return value?  control-spec.txt says reply is merely "250 OK" on success...
+    public String postDescriptor(String desc) throws IOException {
+        ArrayList lst = sendAndWaitForResponse("+POSTDESCRIPTOR\r\n", desc);
+        return ((ReplyLine)lst.get(0)).msg;
+    }
+
+    /** Tells Tor to change the exit address of the stream identified by <b>streamID</b>
+     * to <b>address</b>. No remapping is performed on the new provided address.
+     * 
+     * To be sure that the modified address will be used, this event must be sent
+     * after a new stream event is received, and before attaching this stream to
+     * a circuit.
+     */
+    public void redirectStream(String streamID, String address) throws IOException {
+        sendAndWaitForResponse("REDIRECTSTREAM "+streamID+" "+address+"\r\n",
+                               null);
+    }
+
+    /** Tells Tor to close the stream identified by <b>streamID</b>.
+    * <b>reason</b> should be one of the Tor RELAY_END reasons given in tor-spec.txt, as a decimal:
+    * <ul>
+    * <li>1 -- REASON_MISC           (catch-all for unlisted reasons)</li>
+    * <li>2 -- REASON_RESOLVEFAILED  (couldn't look up hostname)</li>
+    * <li>3 -- REASON_CONNECTREFUSED (remote host refused connection)</li>
+    * <li>4 -- REASON_EXITPOLICY     (OR refuses to connect to host or port)</li>
+    * <li>5 -- REASON_DESTROY        (Circuit is being destroyed)</li>
+    * <li>6 -- REASON_DONE           (Anonymized TCP connection was closed)</li>
+    * <li>7 -- REASON_TIMEOUT        (Connection timed out, or OR timed out while connecting)</li>
+    * <li>8 -- (unallocated)</li>
+    * <li>9 -- REASON_HIBERNATING    (OR is temporarily hibernating)</li>
+    * <li>10 -- REASON_INTERNAL       (Internal error at the OR)</li>
+    * <li>11 -- REASON_RESOURCELIMIT  (OR has no resources to fulfill request)</li>
+    * <li>12 -- REASON_CONNRESET      (Connection was unexpectedly reset)</li>
+    * <li>13 -- REASON_TORPROTOCOL    (Sent when closing connection because of Tor protocol violations)</li>
+    * </ul>
+    *
+    * Tor may hold the stream open for a while to flush any data that is pending.
+    */
+    public void closeStream(String streamID, byte reason)
+        throws IOException {
+        sendAndWaitForResponse("CLOSESTREAM "+streamID+" "+reason+"\r\n",null);
+    }
+
+    /** Tells Tor to close the circuit identified by <b>circID</b>.
+    * If <b>ifUnused</b> is true, do not close the circuit unless it is unused.
+    */
+    public void closeCircuit(String circID, boolean ifUnused) throws IOException {
+        sendAndWaitForResponse("CLOSECIRCUIT "+circID+
+                               (ifUnused?" IFUNUSED":"")+"\r\n", null);
+    }
+
+}
+


Property changes on: puppetor/trunk/src/net/freehaven/tor/control/TorControlConnection1.java
___________________________________________________________________
Name: svn:executable
   + *

Added: puppetor/trunk/src/net/freehaven/tor/control/TorControlError.java
===================================================================
--- puppetor/trunk/src/net/freehaven/tor/control/TorControlError.java	2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/net/freehaven/tor/control/TorControlError.java	2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,31 @@
+// $Id: TorControlError.java 6831 2005-06-24 18:03:27Z nickm $
+// Copyright 2005 Nick Mathewson, Roger Dingledine
+// See LICENSE file for copying information
+package net.freehaven.tor.control;
+
+/**
+ * An exception raised when Tor tells us about an error.
+ */
+public class TorControlError extends RuntimeException {
+    int errorType;
+    public TorControlError(int type, String s) {
+        super(s);
+        errorType = type;
+    }
+    public TorControlError(String s) {
+        this(-1, s);
+    }
+    public int getErrorType() {
+        return errorType;
+    }
+    public String getErrorMsg() {
+        try {
+            if (errorType == -1)
+                return null;
+            return TorControlCommands.ERROR_MSGS[errorType];
+        } catch (ArrayIndexOutOfBoundsException ex) {
+            return "Unrecongized error #"+errorType;
+        }
+    }
+}
+


Property changes on: puppetor/trunk/src/net/freehaven/tor/control/TorControlError.java
___________________________________________________________________
Name: svn:executable
   + *

Added: puppetor/trunk/src/net/freehaven/tor/control/TorControlSyntaxError.java
===================================================================
--- puppetor/trunk/src/net/freehaven/tor/control/TorControlSyntaxError.java	2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/net/freehaven/tor/control/TorControlSyntaxError.java	2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,9 @@
+package net.freehaven.tor.control;
+
+/**
+ * An exception raised when Tor behaves in an unexpected way.
+ */
+public class TorControlSyntaxError extends RuntimeException {
+    public TorControlSyntaxError(String s) { super(s); }
+}
+


Property changes on: puppetor/trunk/src/net/freehaven/tor/control/TorControlSyntaxError.java
___________________________________________________________________
Name: svn:executable
   + *

Added: puppetor/trunk/src/net/freehaven/tor/control/examples/.cvsignore
===================================================================
--- puppetor/trunk/src/net/freehaven/tor/control/examples/.cvsignore	2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/net/freehaven/tor/control/examples/.cvsignore	2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1 @@
+*.class


Property changes on: puppetor/trunk/src/net/freehaven/tor/control/examples/.cvsignore
___________________________________________________________________
Name: svn:executable
   + *

Added: puppetor/trunk/src/net/freehaven/tor/control/examples/DebuggingEventHandler.java
===================================================================
--- puppetor/trunk/src/net/freehaven/tor/control/examples/DebuggingEventHandler.java	2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/net/freehaven/tor/control/examples/DebuggingEventHandler.java	2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,45 @@
+// $Id: DebuggingEventHandler.java 6831 2005-06-24 18:03:27Z nickm $
+// Copyright 2005 Nick Mathewson, Roger Dingledine
+// See LICENSE file for copying information
+package net.freehaven.tor.control.examples;
+
+import java.io.PrintWriter;
+import java.util.Iterator;
+import net.freehaven.tor.control.EventHandler;
+
+public class DebuggingEventHandler implements EventHandler {
+
+    protected PrintWriter out;
+
+    public DebuggingEventHandler(PrintWriter p) {
+        out = p;
+    }
+
+    public void circuitStatus(String status, String circID, String path) {
+        out.println("Circuit "+circID+" is now "+status+" (path="+path+")");
+    }
+    public void streamStatus(String status, String streamID, String target) {
+        out.println("Stream "+streamID+" is now "+status+" (target="+target+")");
+    }
+    public void orConnStatus(String status, String orName) {
+        out.println("OR connection to "+orName+" is now "+status);
+    }
+    public void bandwidthUsed(long read, long written) {
+        out.println("Bandwidth usage: "+read+" bytes read; "+
+                    written+" bytes written.");
+    }
+    public void newDescriptors(java.util.List orList) {
+        out.println("New descriptors for routers:");
+        for (Iterator i = orList.iterator(); i.hasNext(); )
+            out.println("   "+i.next());
+    }
+    public void message(String type, String msg) {
+        out.println("["+type+"] "+msg.trim());
+    }
+
+    public void unrecognized(String type, String msg) {
+        out.println("unrecognized event ["+type+"] "+msg.trim());
+    }
+
+}
+


Property changes on: puppetor/trunk/src/net/freehaven/tor/control/examples/DebuggingEventHandler.java
___________________________________________________________________
Name: svn:executable
   + *

Added: puppetor/trunk/src/net/freehaven/tor/control/examples/Main.java
===================================================================
--- puppetor/trunk/src/net/freehaven/tor/control/examples/Main.java	2007-04-25 20:09:51 UTC (rev 10031)
+++ puppetor/trunk/src/net/freehaven/tor/control/examples/Main.java	2007-04-25 20:14:34 UTC (rev 10032)
@@ -0,0 +1,150 @@
+// $Id: Main.java 9193 2006-12-25 04:37:53Z nickm $
+// Copyright 2005 Nick Mathewson, Roger Dingledine
+// See LICENSE file for copying information
+package net.freehaven.tor.control.examples;
+
+import net.freehaven.tor.control.*;
+import java.io.PrintWriter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Iterator;
+
+public class Main implements TorControlCommands {
+
+    public static void main(String args[]) {
+        if (args.length < 1) {
+            System.err.println("No command given.");
+            return;
+        }
+        try {
+            if (args[0].equals("set-config")) {
+                setConfig(args);
+            } else if (args[0].equals("get-config")) {
+                getConfig(args);
+            } else if (args[0].equals("get-info")) {
+                getInfo(args);
+            } else if (args[0].equals("listen")) {
+                listenForEvents(args);
+            } else if (args[0].equals("signal")) {
+                signal(args);
+            } else if (args[0].equals("auth")) {
+                authDemo(args);
+            } else {
+                System.err.println("Unrecognized command: "+args[0]);
+            }
+        } catch (java.io.EOFException ex) {
+            System.out.println("Control socket closed by Tor.");
+        } catch (IOException ex) {
+            System.err.println("IO exception when talking to Tor process: "+
+                               ex);
+            ex.printStackTrace(System.err);
+        } catch (TorControlError ex) {
+            System.err.println("Error from Tor process: "+
+                               ex+" ["+ex.getErrorMsg()+"]");
+        }
+    }
+
+    private static TorControlConnection getConnection(String[] args,
+                                                      boolean daemon)
+        throws IOException {
+        TorControlConnection conn = TorControlConnection.getConnection(
+                                    new java.net.Socket("127.0.0.1", 9100));
+        //if (conn instanceof TorControlConnection1) {
+        //    System.err.println("Debugging");
+        //    ((TorControlConnection1)conn).setDebugging(System.err);
+        //}
+        Thread th = conn.launchThread(daemon);
+        conn.authenticate(new byte[0]);
+        return conn;
+    }
+
+    private static TorControlConnection getConnection(String[] args)
+        throws IOException {
+        return getConnection(args, true);
+    }
+
+    public static void setConfig(String[] args) throws IOException {
+        // Usage: "set-config [-save] key value key value key value"
+        TorControlConnection conn = getConnection(args);
+        ArrayList lst = new ArrayList();
+        int i = 1;
+        boolean save = false;
+        if (args[i].equals("-save")) {
+            save = true;
+            ++i;
+        }
+        for (; i < args.length; i +=2) {
+            lst.add(args[i]+" "+args[i+1]);
+        }
+        conn.setConf(lst);
+        if (save) {
+            conn.saveConf();
+        }
+    }
+
+    public static void getConfig(String[] args) throws IOException {
+        // Usage: get-config key key key
+        TorControlConnection conn = getConnection(args);
+        List lst = conn.getConf(Arrays.asList(args).subList(1,args.length));
+        for (Iterator i = lst.iterator(); i.hasNext(); ) {
+            ConfigEntry e = (ConfigEntry) i.next();
+            System.out.println("KEY: "+e.key);
+            System.out.println("VAL: "+e.value);
+        }
+    }
+
+    public static void getInfo(String[] args) throws IOException {
+        TorControlConnection conn = getConnection(args);
+        Map m = conn.getInfo(Arrays.asList(args).subList(1,args.length));
+        for (Iterator i = m.entrySet().iterator(); i.hasNext(); ) {
+            Map.Entry e = (Map.Entry) i.next();
+            System.out.println("KEY: "+e.getKey());
+            System.out.println("VAL: "+e.getValue());
+        }
+    }
+
+    public static void listenForEvents(String[] args) throws IOException {
+        // Usage: listen [circ|stream|orconn|bw|newdesc|info|notice|warn|error]*
+        TorControlConnection conn = getConnection(args, false);
+        ArrayList lst = new ArrayList();
+        for (int i = 1; i < args.length; ++i) {
+            lst.add(args[i]);
+        }
+        conn.setEventHandler(
+            new DebuggingEventHandler(new PrintWriter(System.out, true)));
+        conn.setEvents(lst);
+    }
+
+    public static void signal(String[] args) throws IOException {
+        // Usage signal [reload|shutdown|dump|debug|halt]
+        TorControlConnection conn = getConnection(args, false);
+        // distinguish shutdown signal from other signals
+        if ("SHUTDOWN".equalsIgnoreCase(args[1])
+        		|| "HALT".equalsIgnoreCase(args[1])) {
+        	conn.shutdownTor(args[1].toUpperCase());
+        } else {
+        	conn.signal(args[1].toUpperCase());
+        }
+    }
+
+    public static void authDemo(String[] args) throws IOException {
+
+        PasswordDigest pwd = PasswordDigest.generateDigest();
+        java.net.Socket s = new java.net.Socket("127.0.0.1", 9100);
+        TorControlConnection conn = TorControlConnection.getConnection(s);
+        conn.launchThread(true);
+        conn.authenticate(new byte[0]);
+
+        conn.setConf("HashedControlPassword", pwd.getHashedPassword());
+
+        conn = TorControlConnection.getConnection(
+                                    new java.net.Socket("127.0.0.1", 9100));
+        conn.launchThread(true);
+        conn.authenticate(pwd.getSecret());
+    }
+
+}
+


Property changes on: puppetor/trunk/src/net/freehaven/tor/control/examples/Main.java
___________________________________________________________________
Name: svn:executable
   + *



More information about the tor-commits mailing list