gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[GNUnet-SVN] r137 - / freeway freeway/doc freeway/etc freeway/etc/suppor


From: grothoff
Subject: [GNUnet-SVN] r137 - / freeway freeway/doc freeway/etc freeway/etc/support freeway/etc/support/clib freeway/lib freeway/res freeway/res/gnunet-swing freeway/res/gnunet-swing/images freeway/res/jnlp freeway/res/swing freeway/res/swing/images freeway/res/swing/system freeway/res/tests freeway/src freeway/src/org freeway/src/org/gnu freeway/src/org/gnu/freeway freeway/src/org/gnu/freeway/protocol freeway/src/org/gnu/freeway/protocol/afs freeway/src/org/gnu/freeway/protocol/afs/esed2 freeway/src/org/gnu/freeway/protocol/afs/swing freeway/src/org/gnu/freeway/protocol/chat freeway/src/org/gnu/freeway/protocol/dht freeway/src/org/gnu/freeway/protocol/tbench freeway/src/org/gnu/freeway/protocol/testbed freeway/src/org/gnu/freeway/protocol/tracekit freeway/src/org/gnu/freeway/server freeway/src/org/gnu/freeway/test freeway/src/org/gnu/freeway/transport freeway/src/org/gnu/freeway/transport/nat freeway/src/org/gnu/freeway/transport/tcp freeway/src/org/gnu/freeway/transport/udp freeway/src/org/gnu/freeway/util freeway/src/org/gnu/freeway/util/crypto freeway/src/org/gnu/freeway/util/io freeway/src/org/gnu/freeway/util/net freeway/src/org/gnu/freeway/util/ui
Date: Mon, 31 Jan 2005 17:07:29 -0800 (PST)

Author: grothoff
Date: 2005-01-31 17:07:27 -0800 (Mon, 31 Jan 2005)
New Revision: 137

Added:
   freeway/
   freeway/.classpath
   freeway/.cvsignore
   freeway/.project
   freeway/AUTHORS
   freeway/COPYING
   freeway/README
   freeway/build.properties
   freeway/build.sh
   freeway/build.xml
   freeway/build/
   freeway/doc/
   freeway/doc/examples.txt
   freeway/doc/man.css
   freeway/doc/man.xml
   freeway/doc/man.xsl
   freeway/doc/packaging
   freeway/doc/todo
   freeway/doc/using eclipse
   freeway/etc/
   freeway/etc/gnunet-chat.sh
   freeway/etc/gnunet-config.sh
   freeway/etc/gnunet-directory.sh
   freeway/etc/gnunet-download.sh
   freeway/etc/gnunet-insert.sh
   freeway/etc/gnunet-peer-info.sh
   freeway/etc/gnunet-pseudonym.sh
   freeway/etc/gnunet-search.sh
   freeway/etc/gnunet-shutdown.sh
   freeway/etc/gnunet-stats.sh
   freeway/etc/gnunet-swing.sh
   freeway/etc/gnunet-tests.sh
   freeway/etc/gnunet-trace.sh
   freeway/etc/gnunet-transport-check.sh
   freeway/etc/gnunet-ui-config.sh
   freeway/etc/gnunetd.sh
   freeway/etc/support/
   freeway/etc/support/Cleaner.java
   freeway/etc/support/clib/
   freeway/etc/support/clib/freeway-clib.c
   freeway/etc/support/clib/freeway-clib.h
   freeway/etc/support/clib/links.c
   freeway/etc/support/clib/links.h
   freeway/etc/support/clib/signals.c
   freeway/etc/support/clib/signals.h
   freeway/etc/support/debug/
   freeway/etc/support/proxy/
   freeway/lib/
   freeway/lib/batik-all.jar
   freeway/lib/bcprov-jdk14-124.jar
   freeway/lib/concurrent.jar
   freeway/lib/mysql-connector-java-3.0.10-stable-bin.jar
   freeway/lib/readme
   freeway/readme.html
   freeway/res/
   freeway/res/api.css
   freeway/res/daemon.logging
   freeway/res/gnunet-swing/
   freeway/res/gnunet-swing/images/
   freeway/res/gnunet-swing/images/16x16/
   freeway/res/gnunet.client.template
   freeway/res/gnunet.daemon.template
   freeway/res/jnlp/
   freeway/res/jnlp/batik.jnlp
   freeway/res/jnlp/bouncycastle-jce.jnlp
   freeway/res/jnlp/concurrent.jnlp
   freeway/res/jnlp/freeway.jnlp
   freeway/res/jnlp/logo64.jpg
   freeway/res/jnlp/mysql.jnlp
   freeway/res/quiet.logging
   freeway/res/swing/
   freeway/res/swing/daemon-window.xml
   freeway/res/swing/download.xml
   freeway/res/swing/gnunet-config.xml
   freeway/res/swing/images/
   freeway/res/swing/images/16x16/
   freeway/res/swing/images/logo.svg
   freeway/res/swing/images/splash.svg
   freeway/res/swing/images/start.svg
   freeway/res/swing/images/stop.svg
   freeway/res/swing/images/watermark.svg
   freeway/res/swing/insert.xml
   freeway/res/swing/main.xml
   freeway/res/swing/search-panel.xml
   freeway/res/swing/search-window.xml
   freeway/res/swing/stats-window.xml
   freeway/res/swing/system/
   freeway/res/swing/system/wizard.xml
   freeway/res/tests/
   freeway/res/tests/test-charset
   freeway/res/tests/test-config
   freeway/res/tests/test-config-2
   freeway/src/
   freeway/src/org/
   freeway/src/org/gnu/
   freeway/src/org/gnu/freeway/
   freeway/src/org/gnu/freeway/AbstractApplication.java
   freeway/src/org/gnu/freeway/AbstractClient.java
   freeway/src/org/gnu/freeway/AbstractServer.java
   freeway/src/org/gnu/freeway/Application.java
   freeway/src/org/gnu/freeway/Command.java
   freeway/src/org/gnu/freeway/DaemonLauncher.java
   freeway/src/org/gnu/freeway/GNUNetChat.java
   freeway/src/org/gnu/freeway/GNUNetConfig.java
   freeway/src/org/gnu/freeway/GNUNetDaemon.java
   freeway/src/org/gnu/freeway/GNUNetDirectory.java
   freeway/src/org/gnu/freeway/GNUNetDownload.java
   freeway/src/org/gnu/freeway/GNUNetInsert.java
   freeway/src/org/gnu/freeway/GNUNetPeerInfo.java
   freeway/src/org/gnu/freeway/GNUNetPseudonym.java
   freeway/src/org/gnu/freeway/GNUNetSearch.java
   freeway/src/org/gnu/freeway/GNUNetShutdown.java
   freeway/src/org/gnu/freeway/GNUNetStats.java
   freeway/src/org/gnu/freeway/GNUNetSwing.java
   freeway/src/org/gnu/freeway/GNUNetTests.java
   freeway/src/org/gnu/freeway/GNUNetTrace.java
   freeway/src/org/gnu/freeway/GNUNetTransportCheck.java
   freeway/src/org/gnu/freeway/GNUNetUIConfig.java
   freeway/src/org/gnu/freeway/Server.java
   freeway/src/org/gnu/freeway/gnunet-check.c
   freeway/src/org/gnu/freeway/gnunet-convert.c
   freeway/src/org/gnu/freeway/gnunet-delete.c
   freeway/src/org/gnu/freeway/gnunet-tbench.c
   freeway/src/org/gnu/freeway/gnunet-testbed.c
   freeway/src/org/gnu/freeway/net/
   freeway/src/org/gnu/freeway/protocol/
   freeway/src/org/gnu/freeway/protocol/AbstractProtocol.java
   freeway/src/org/gnu/freeway/protocol/Protocol.java
   freeway/src/org/gnu/freeway/protocol/afs/
   freeway/src/org/gnu/freeway/protocol/afs/AFSProtocol.java
   freeway/src/org/gnu/freeway/protocol/afs/BloomFilter2.java
   freeway/src/org/gnu/freeway/protocol/afs/DBHandle.java
   freeway/src/org/gnu/freeway/protocol/afs/EntryCallback.java
   freeway/src/org/gnu/freeway/protocol/afs/FileIndex.java
   freeway/src/org/gnu/freeway/protocol/afs/Handler.java
   freeway/src/org/gnu/freeway/protocol/afs/IndexedFileNameCallback.java
   freeway/src/org/gnu/freeway/protocol/afs/IndirectionTableEntry.java
   freeway/src/org/gnu/freeway/protocol/afs/IterState.java
   freeway/src/org/gnu/freeway/protocol/afs/LFS.java
   freeway/src/org/gnu/freeway/protocol/afs/Manager.java
   freeway/src/org/gnu/freeway/protocol/afs/Migration.java
   freeway/src/org/gnu/freeway/protocol/afs/MySQLHandle.java
   freeway/src/org/gnu/freeway/protocol/afs/Policy2.java
   freeway/src/org/gnu/freeway/protocol/afs/QueryManager.java
   freeway/src/org/gnu/freeway/protocol/afs/QueryRecord.java
   freeway/src/org/gnu/freeway/protocol/afs/Routing.java
   freeway/src/org/gnu/freeway/protocol/afs/esed2/
   freeway/src/org/gnu/freeway/protocol/afs/esed2/AFSConstants.java
   freeway/src/org/gnu/freeway/protocol/afs/esed2/AFSUtils.java
   freeway/src/org/gnu/freeway/protocol/afs/esed2/Block.java
   freeway/src/org/gnu/freeway/protocol/afs/esed2/CSDelete3Hash.java
   freeway/src/org/gnu/freeway/protocol/afs/esed2/CSDeleteChk.java
   freeway/src/org/gnu/freeway/protocol/afs/esed2/CSGetAvgPriority.java
   freeway/src/org/gnu/freeway/protocol/afs/esed2/CSIndexBlock.java
   freeway/src/org/gnu/freeway/protocol/afs/esed2/CSIndexFile.java
   freeway/src/org/gnu/freeway/protocol/afs/esed2/CSIndexSuper.java
   freeway/src/org/gnu/freeway/protocol/afs/esed2/CSInsert3Hash.java
   freeway/src/org/gnu/freeway/protocol/afs/esed2/CSInsertChk.java
   freeway/src/org/gnu/freeway/protocol/afs/esed2/CSInsertSBlock.java
   freeway/src/org/gnu/freeway/protocol/afs/esed2/CSLinkFile.java
   freeway/src/org/gnu/freeway/protocol/afs/esed2/CSNSQuery.java
   freeway/src/org/gnu/freeway/protocol/afs/esed2/CSQuery.java
   freeway/src/org/gnu/freeway/protocol/afs/esed2/CSResult3Hash.java
   freeway/src/org/gnu/freeway/protocol/afs/esed2/CSResultChk.java
   freeway/src/org/gnu/freeway/protocol/afs/esed2/CSResultSBlock.java
   freeway/src/org/gnu/freeway/protocol/afs/esed2/CSUnindexBlock.java
   freeway/src/org/gnu/freeway/protocol/afs/esed2/CSUnindexFile.java
   freeway/src/org/gnu/freeway/protocol/afs/esed2/CSUnindexSuper.java
   freeway/src/org/gnu/freeway/protocol/afs/esed2/CSUploadFile.java
   freeway/src/org/gnu/freeway/protocol/afs/esed2/ChkHashes.java
   freeway/src/org/gnu/freeway/protocol/afs/esed2/ContentBlock.java
   freeway/src/org/gnu/freeway/protocol/afs/esed2/ContentEncoding.java
   freeway/src/org/gnu/freeway/protocol/afs/esed2/ContentIndex.java
   freeway/src/org/gnu/freeway/protocol/afs/esed2/DBlock.java
   freeway/src/org/gnu/freeway/protocol/afs/esed2/DeleteURI.java
   freeway/src/org/gnu/freeway/protocol/afs/esed2/DeleteUtil.java
   freeway/src/org/gnu/freeway/protocol/afs/esed2/DownloadURI.java
   freeway/src/org/gnu/freeway/protocol/afs/esed2/DownloadUtil.java
   freeway/src/org/gnu/freeway/protocol/afs/esed2/EncryptedSBlock.java
   freeway/src/org/gnu/freeway/protocol/afs/esed2/Extractor.java
   freeway/src/org/gnu/freeway/protocol/afs/esed2/FileIdentifier.java
   freeway/src/org/gnu/freeway/protocol/afs/esed2/GNDirectory.java
   freeway/src/org/gnu/freeway/protocol/afs/esed2/GNDirectoryDatabase.java
   freeway/src/org/gnu/freeway/protocol/afs/esed2/GeneralURI.java
   freeway/src/org/gnu/freeway/protocol/afs/esed2/IBlock.java
   freeway/src/org/gnu/freeway/protocol/afs/esed2/IBlockData.java
   freeway/src/org/gnu/freeway/protocol/afs/esed2/IOContext.java
   freeway/src/org/gnu/freeway/protocol/afs/esed2/InsertURI.java
   freeway/src/org/gnu/freeway/protocol/afs/esed2/InsertUtil.java
   freeway/src/org/gnu/freeway/protocol/afs/esed2/InsertWrapper.java
   freeway/src/org/gnu/freeway/protocol/afs/esed2/KeyWords.java
   freeway/src/org/gnu/freeway/protocol/afs/esed2/Listener.java
   freeway/src/org/gnu/freeway/protocol/afs/esed2/NSSearchResultCallback.java
   freeway/src/org/gnu/freeway/protocol/afs/esed2/Node.java
   freeway/src/org/gnu/freeway/protocol/afs/esed2/NodeContext.java
   freeway/src/org/gnu/freeway/protocol/afs/esed2/P2P3HashResult.java
   freeway/src/org/gnu/freeway/protocol/afs/esed2/P2PChkResult.java
   freeway/src/org/gnu/freeway/protocol/afs/esed2/P2PNSQuery.java
   freeway/src/org/gnu/freeway/protocol/afs/esed2/P2PQuery.java
   freeway/src/org/gnu/freeway/protocol/afs/esed2/P2PSBlockResult.java
   freeway/src/org/gnu/freeway/protocol/afs/esed2/Policy.java
   freeway/src/org/gnu/freeway/protocol/afs/esed2/Priority.java
   freeway/src/org/gnu/freeway/protocol/afs/esed2/ProgressModel.java
   freeway/src/org/gnu/freeway/protocol/afs/esed2/ProgressStats.java
   freeway/src/org/gnu/freeway/protocol/afs/esed2/Pseudonym.java
   freeway/src/org/gnu/freeway/protocol/afs/esed2/RequestContinuation.java
   freeway/src/org/gnu/freeway/protocol/afs/esed2/RequestEntry.java
   freeway/src/org/gnu/freeway/protocol/afs/esed2/RequestManager.java
   freeway/src/org/gnu/freeway/protocol/afs/esed2/RootNode.java
   freeway/src/org/gnu/freeway/protocol/afs/esed2/RootNodeCallback.java
   freeway/src/org/gnu/freeway/protocol/afs/esed2/SBlock.java
   freeway/src/org/gnu/freeway/protocol/afs/esed2/SearchResultCallback.java
   freeway/src/org/gnu/freeway/protocol/afs/esed2/SearchURI.java
   freeway/src/org/gnu/freeway/protocol/afs/esed2/SendNSQueryContext.java
   freeway/src/org/gnu/freeway/protocol/afs/esed2/SendQueriesContext.java
   freeway/src/org/gnu/freeway/protocol/afs/esed2/TestTerminateThread.java
   freeway/src/org/gnu/freeway/protocol/afs/esed2/URI.java
   freeway/src/org/gnu/freeway/protocol/afs/swing/
   freeway/src/org/gnu/freeway/protocol/afs/swing/CreatePseudonymDialog.java
   freeway/src/org/gnu/freeway/protocol/afs/swing/DaemonWindow.java
   freeway/src/org/gnu/freeway/protocol/afs/swing/DeletePseudonymDialog.java
   freeway/src/org/gnu/freeway/protocol/afs/swing/DownloadAdapter.java
   freeway/src/org/gnu/freeway/protocol/afs/swing/DownloadModel.java
   freeway/src/org/gnu/freeway/protocol/afs/swing/DownloadWindow.java
   freeway/src/org/gnu/freeway/protocol/afs/swing/InsertDialog.java
   freeway/src/org/gnu/freeway/protocol/afs/swing/SController.java
   freeway/src/org/gnu/freeway/protocol/afs/swing/SearchAdapter.java
   freeway/src/org/gnu/freeway/protocol/afs/swing/SearchOverviewPanel.java
   freeway/src/org/gnu/freeway/protocol/afs/swing/SearchPanel.java
   freeway/src/org/gnu/freeway/protocol/afs/swing/SearchWindow.java
   freeway/src/org/gnu/freeway/protocol/afs/swing/SelectDialog.java
   freeway/src/org/gnu/freeway/protocol/afs/swing/StatsWindow.java
   freeway/src/org/gnu/freeway/protocol/afs/swing/about.c
   freeway/src/org/gnu/freeway/protocol/afs/swing/about.h
   freeway/src/org/gnu/freeway/protocol/afs/swing/delete.c
   freeway/src/org/gnu/freeway/protocol/afs/swing/delete.h
   freeway/src/org/gnu/freeway/protocol/afs/swing/directory.c
   freeway/src/org/gnu/freeway/protocol/afs/swing/directory.h
   freeway/src/org/gnu/freeway/protocol/afs/swing/directorydisplay.c
   freeway/src/org/gnu/freeway/protocol/afs/swing/directorydisplay.h
   freeway/src/org/gnu/freeway/protocol/afs/swing/download.c
   freeway/src/org/gnu/freeway/protocol/afs/swing/download.h
   freeway/src/org/gnu/freeway/protocol/afs/swing/helper.c
   freeway/src/org/gnu/freeway/protocol/afs/swing/helper.h
   freeway/src/org/gnu/freeway/protocol/afs/swing/insert.c
   freeway/src/org/gnu/freeway/protocol/afs/swing/insert.h
   freeway/src/org/gnu/freeway/protocol/afs/swing/insertprogress.c
   freeway/src/org/gnu/freeway/protocol/afs/swing/insertprogress.h
   freeway/src/org/gnu/freeway/protocol/afs/swing/main.c
   freeway/src/org/gnu/freeway/protocol/afs/swing/main.h
   freeway/src/org/gnu/freeway/protocol/afs/swing/namespace.c
   freeway/src/org/gnu/freeway/protocol/afs/swing/namespace.h
   freeway/src/org/gnu/freeway/protocol/afs/swing/pseudonyms.c
   freeway/src/org/gnu/freeway/protocol/afs/swing/pseudonyms.h
   freeway/src/org/gnu/freeway/protocol/afs/swing/saveas.c
   freeway/src/org/gnu/freeway/protocol/afs/swing/saveas.h
   freeway/src/org/gnu/freeway/protocol/afs/swing/search.c
   freeway/src/org/gnu/freeway/protocol/afs/swing/search.h
   freeway/src/org/gnu/freeway/protocol/afs/swing/statistics.c
   freeway/src/org/gnu/freeway/protocol/afs/swing/statistics.h
   freeway/src/org/gnu/freeway/protocol/chat/
   freeway/src/org/gnu/freeway/protocol/chat/CSChatMessage.java
   freeway/src/org/gnu/freeway/protocol/chat/ChatProtocol.java
   freeway/src/org/gnu/freeway/protocol/chat/P2PChatMessage.java
   freeway/src/org/gnu/freeway/protocol/dht/
   freeway/src/org/gnu/freeway/protocol/dht/DHTConstants.java
   freeway/src/org/gnu/freeway/protocol/dht/DHTDataContainer.java
   freeway/src/org/gnu/freeway/protocol/dht/DHTDataList.java
   freeway/src/org/gnu/freeway/protocol/dht/DHTDataListItem.java
   freeway/src/org/gnu/freeway/protocol/dht/DHTFetchResult.java
   freeway/src/org/gnu/freeway/protocol/dht/DHTMessageHeader.java
   freeway/src/org/gnu/freeway/protocol/dht/DHTProtocol.java
   freeway/src/org/gnu/freeway/protocol/dht/DHTReplyFailure.java
   freeway/src/org/gnu/freeway/protocol/dht/DHTReplyResults.java
   freeway/src/org/gnu/freeway/protocol/dht/DHTReplyStandard.java
   freeway/src/org/gnu/freeway/protocol/dht/DHTReplyStatus.java
   freeway/src/org/gnu/freeway/protocol/dht/DHTRequestAPIId.java
   freeway/src/org/gnu/freeway/protocol/dht/DHTRequestCreate.java
   freeway/src/org/gnu/freeway/protocol/dht/DHTRequestDrop.java
   freeway/src/org/gnu/freeway/protocol/dht/DHTRequestFetch.java
   freeway/src/org/gnu/freeway/protocol/dht/DHTRequestInsert.java
   freeway/src/org/gnu/freeway/protocol/dht/DHTRequestInserted.java
   freeway/src/org/gnu/freeway/protocol/dht/DHTRequestJoin.java
   freeway/src/org/gnu/freeway/protocol/dht/DHTRequestLeave.java
   freeway/src/org/gnu/freeway/protocol/dht/DHTRequestStatus.java
   freeway/src/org/gnu/freeway/protocol/dht/DHTRequestTables.java
   freeway/src/org/gnu/freeway/protocol/dht/DHTResultSet.java
   freeway/src/org/gnu/freeway/protocol/dht/DHTResultSetItem.java
   freeway/src/org/gnu/freeway/protocol/dht/DHTStoredDataReference.java
   freeway/src/org/gnu/freeway/protocol/dht/DHTTableConfig.java
   freeway/src/org/gnu/freeway/protocol/dht/DHTTableHandle.java
   freeway/src/org/gnu/freeway/protocol/dht/DHTTableId.java
   freeway/src/org/gnu/freeway/protocol/dht/DHTTableMetaData.java
   freeway/src/org/gnu/freeway/protocol/dht/DHTTableSet.java
   freeway/src/org/gnu/freeway/protocol/dht/DHTTableSetItem.java
   freeway/src/org/gnu/freeway/protocol/tbench/
   freeway/src/org/gnu/freeway/protocol/tbench/CSBenchReply.java
   freeway/src/org/gnu/freeway/protocol/tbench/CSBenchRequest.java
   freeway/src/org/gnu/freeway/protocol/tbench/P2PBenchReply.java
   freeway/src/org/gnu/freeway/protocol/tbench/P2PBenchRequest.java
   freeway/src/org/gnu/freeway/protocol/tbench/TBenchProtocol.java
   freeway/src/org/gnu/freeway/protocol/testbed/
   freeway/src/org/gnu/freeway/protocol/testbed/commands.c
   freeway/src/org/gnu/freeway/protocol/testbed/commands.h
   freeway/src/org/gnu/freeway/protocol/testbed/connect.php3
   freeway/src/org/gnu/freeway/protocol/testbed/display.php3
   freeway/src/org/gnu/freeway/protocol/testbed/functions.sh
   freeway/src/org/gnu/freeway/protocol/testbed/get-stats.c
   freeway/src/org/gnu/freeway/protocol/testbed/get-stats.h
   freeway/src/org/gnu/freeway/protocol/testbed/socket.c
   freeway/src/org/gnu/freeway/protocol/testbed/socket.h
   freeway/src/org/gnu/freeway/protocol/testbed/startup.php3
   freeway/src/org/gnu/freeway/protocol/testbed/testbed.c
   freeway/src/org/gnu/freeway/protocol/testbed/testbed.h
   freeway/src/org/gnu/freeway/protocol/tracekit/
   freeway/src/org/gnu/freeway/protocol/tracekit/CSTraceProbe.java
   freeway/src/org/gnu/freeway/protocol/tracekit/CSTraceReply.java
   freeway/src/org/gnu/freeway/protocol/tracekit/P2PTraceProbe.java
   freeway/src/org/gnu/freeway/protocol/tracekit/P2PTraceReply.java
   freeway/src/org/gnu/freeway/protocol/tracekit/RTE.java
   freeway/src/org/gnu/freeway/protocol/tracekit/TraceKitProtocol.java
   freeway/src/org/gnu/freeway/server/
   freeway/src/org/gnu/freeway/server/BufferEntry.java
   freeway/src/org/gnu/freeway/server/BufferEntryCallback.java
   freeway/src/org/gnu/freeway/server/BufferFillCallback.java
   freeway/src/org/gnu/freeway/server/CSGetCSMessageSupported.java
   freeway/src/org/gnu/freeway/server/CSGetClientCount.java
   freeway/src/org/gnu/freeway/server/CSGetHostInfo.java
   freeway/src/org/gnu/freeway/server/CSGetOptionReply.java
   freeway/src/org/gnu/freeway/server/CSGetOptionRequest.java
   freeway/src/org/gnu/freeway/server/CSGetP2PMessageSupported.java
   freeway/src/org/gnu/freeway/server/CSHostInfo.java
   freeway/src/org/gnu/freeway/server/CSShutdownRequest.java
   freeway/src/org/gnu/freeway/server/CSStatistics.java
   freeway/src/org/gnu/freeway/server/CSStatisticsRequest.java
   freeway/src/org/gnu/freeway/server/CSTrafficInfo.java
   freeway/src/org/gnu/freeway/server/CSTrafficRequest.java
   freeway/src/org/gnu/freeway/server/Capability.java
   freeway/src/org/gnu/freeway/server/ClientExitHandler.java
   freeway/src/org/gnu/freeway/server/ClientServer.java
   freeway/src/org/gnu/freeway/server/ConnectionService.java
   freeway/src/org/gnu/freeway/server/Core.java
   freeway/src/org/gnu/freeway/server/CoreForProtocol.java
   freeway/src/org/gnu/freeway/server/CoreForTransport.java
   freeway/src/org/gnu/freeway/server/CoreService.java
   freeway/src/org/gnu/freeway/server/DirectedTrafficCounter.java
   freeway/src/org/gnu/freeway/server/HELOCallback.java
   freeway/src/org/gnu/freeway/server/HELOLoader.java
   freeway/src/org/gnu/freeway/server/HelloExchangeService.java
   freeway/src/org/gnu/freeway/server/Host.java
   freeway/src/org/gnu/freeway/server/HostFailure.java
   freeway/src/org/gnu/freeway/server/HostIterator.java
   freeway/src/org/gnu/freeway/server/KnownHostsService.java
   freeway/src/org/gnu/freeway/server/LocalIdentity.java
   freeway/src/org/gnu/freeway/server/MessagesDispatcher.java
   freeway/src/org/gnu/freeway/server/P2PCapability.java
   freeway/src/org/gnu/freeway/server/P2PHangUp.java
   freeway/src/org/gnu/freeway/server/P2PHello.java
   freeway/src/org/gnu/freeway/server/P2PNoise.java
   freeway/src/org/gnu/freeway/server/P2PPing.java
   freeway/src/org/gnu/freeway/server/P2PPong.java
   freeway/src/org/gnu/freeway/server/P2PSequence.java
   freeway/src/org/gnu/freeway/server/P2PSessionKey.java
   freeway/src/org/gnu/freeway/server/PerNodeCallback.java
   freeway/src/org/gnu/freeway/server/PingPongService.java
   freeway/src/org/gnu/freeway/server/PolicyService.java
   freeway/src/org/gnu/freeway/server/SendEntry.java
   freeway/src/org/gnu/freeway/server/TrafficService.java
   freeway/src/org/gnu/freeway/server/TransportCallback.java
   freeway/src/org/gnu/freeway/server/TransportService.java
   freeway/src/org/gnu/freeway/test/
   freeway/src/org/gnu/freeway/test/AbstractTest.java
   freeway/src/org/gnu/freeway/test/BloomTest.java
   freeway/src/org/gnu/freeway/test/CRCTest.java
   freeway/src/org/gnu/freeway/test/CharsetTest.java
   freeway/src/org/gnu/freeway/test/ConfigTest.java
   freeway/src/org/gnu/freeway/test/CronTest.java
   freeway/src/org/gnu/freeway/test/FilterTest.java
   freeway/src/org/gnu/freeway/test/HashTest.java
   freeway/src/org/gnu/freeway/test/LayoutTest.java
   freeway/src/org/gnu/freeway/test/MySQLTest.java
   freeway/src/org/gnu/freeway/test/RSATest.java
   freeway/src/org/gnu/freeway/test/SemaphoreTest.java
   freeway/src/org/gnu/freeway/test/ShutdownTest.java
   freeway/src/org/gnu/freeway/test/SignalsTest.java
   freeway/src/org/gnu/freeway/test/StateTest.java
   freeway/src/org/gnu/freeway/test/StatusCallsTest.java
   freeway/src/org/gnu/freeway/test/StorageTest.java
   freeway/src/org/gnu/freeway/test/SymCipherTest.java
   freeway/src/org/gnu/freeway/test/TCPTest.java
   freeway/src/org/gnu/freeway/test/TableTest.java
   freeway/src/org/gnu/freeway/test/TimerTest.java
   freeway/src/org/gnu/freeway/test/high_db_test.c
   freeway/src/org/gnu/freeway/test/low_db_test.c
   freeway/src/org/gnu/freeway/test/weakkeytest.c
   freeway/src/org/gnu/freeway/transport/
   freeway/src/org/gnu/freeway/transport/AbstractSession.java
   freeway/src/org/gnu/freeway/transport/AbstractTransport.java
   freeway/src/org/gnu/freeway/transport/MessageHandler.java
   freeway/src/org/gnu/freeway/transport/MessagePack.java
   freeway/src/org/gnu/freeway/transport/Session.java
   freeway/src/org/gnu/freeway/transport/Transport.java
   freeway/src/org/gnu/freeway/transport/http.c
   freeway/src/org/gnu/freeway/transport/nat/
   freeway/src/org/gnu/freeway/transport/nat/NATAddress.java
   freeway/src/org/gnu/freeway/transport/nat/NATTransport.java
   freeway/src/org/gnu/freeway/transport/smtp.c
   freeway/src/org/gnu/freeway/transport/tcp/
   freeway/src/org/gnu/freeway/transport/tcp/TCPAddress.java
   freeway/src/org/gnu/freeway/transport/tcp/TCPMessagePack.java
   freeway/src/org/gnu/freeway/transport/tcp/TCPSession.java
   freeway/src/org/gnu/freeway/transport/tcp/TCPTransport.java
   freeway/src/org/gnu/freeway/transport/tcp/TCPWelcome.java
   freeway/src/org/gnu/freeway/transport/tcp6.c
   freeway/src/org/gnu/freeway/transport/udp/
   freeway/src/org/gnu/freeway/transport/udp/UDPAddress.java
   freeway/src/org/gnu/freeway/transport/udp/UDPMessage.java
   freeway/src/org/gnu/freeway/transport/udp/UDPSession.java
   freeway/src/org/gnu/freeway/transport/udp/UDPTransport.java
   freeway/src/org/gnu/freeway/transport/udp6.c
   freeway/src/org/gnu/freeway/util/
   freeway/src/org/gnu/freeway/util/AbstractAction.java
   freeway/src/org/gnu/freeway/util/AbstractFormatter.java
   freeway/src/org/gnu/freeway/util/AbstractIterator.java
   freeway/src/org/gnu/freeway/util/AbstractLogFormatter.java
   freeway/src/org/gnu/freeway/util/AbstractService.java
   freeway/src/org/gnu/freeway/util/Action.java
   freeway/src/org/gnu/freeway/util/BloomFilter.java
   freeway/src/org/gnu/freeway/util/CIDRNetwork.java
   freeway/src/org/gnu/freeway/util/CIDRNetworks.java
   freeway/src/org/gnu/freeway/util/ConfigurationParser.java
   freeway/src/org/gnu/freeway/util/ConsoleFormatter.java
   freeway/src/org/gnu/freeway/util/Constant.java
   freeway/src/org/gnu/freeway/util/DurationFormatter.java
   freeway/src/org/gnu/freeway/util/DynamicLibrary.java
   freeway/src/org/gnu/freeway/util/EvalAction.java
   freeway/src/org/gnu/freeway/util/FileFormatter.java
   freeway/src/org/gnu/freeway/util/IdentityService.java
   freeway/src/org/gnu/freeway/util/LoggedObject.java
   freeway/src/org/gnu/freeway/util/MasterTask.java
   freeway/src/org/gnu/freeway/util/NulAction.java
   freeway/src/org/gnu/freeway/util/OSAccess.java
   freeway/src/org/gnu/freeway/util/Option.java
   freeway/src/org/gnu/freeway/util/OptionsParser.java
   freeway/src/org/gnu/freeway/util/OutputFilter.java
   freeway/src/org/gnu/freeway/util/Prefs.java
   freeway/src/org/gnu/freeway/util/SafeAction.java
   freeway/src/org/gnu/freeway/util/ScheduledTask.java
   freeway/src/org/gnu/freeway/util/Scheduler.java
   freeway/src/org/gnu/freeway/util/Service.java
   freeway/src/org/gnu/freeway/util/ServiceException.java
   freeway/src/org/gnu/freeway/util/ServiceManager.java
   freeway/src/org/gnu/freeway/util/SignalEvent.java
   freeway/src/org/gnu/freeway/util/SignalListener.java
   freeway/src/org/gnu/freeway/util/SignalManager.java
   freeway/src/org/gnu/freeway/util/Signals.java
   freeway/src/org/gnu/freeway/util/SlaveTask.java
   freeway/src/org/gnu/freeway/util/StarFormatter.java
   freeway/src/org/gnu/freeway/util/Stat.java
   freeway/src/org/gnu/freeway/util/Statistics.java
   freeway/src/org/gnu/freeway/util/StatusCallsService.java
   freeway/src/org/gnu/freeway/util/Task.java
   freeway/src/org/gnu/freeway/util/TaskHome.java
   freeway/src/org/gnu/freeway/util/TrafficCounter.java
   freeway/src/org/gnu/freeway/util/Unit.java
   freeway/src/org/gnu/freeway/util/Utils.java
   freeway/src/org/gnu/freeway/util/Voyager.java
   freeway/src/org/gnu/freeway/util/crypto/
   freeway/src/org/gnu/freeway/util/crypto/Crypto.java
   freeway/src/org/gnu/freeway/util/crypto/EncryptedData.java
   freeway/src/org/gnu/freeway/util/crypto/HashCode160.java
   freeway/src/org/gnu/freeway/util/crypto/HostIdentity.java
   freeway/src/org/gnu/freeway/util/crypto/PrivateKey.java
   freeway/src/org/gnu/freeway/util/crypto/PublicKey.java
   freeway/src/org/gnu/freeway/util/crypto/SessionKey.java
   freeway/src/org/gnu/freeway/util/crypto/Signature.java
   freeway/src/org/gnu/freeway/util/io/
   freeway/src/org/gnu/freeway/util/io/Connector.java
   freeway/src/org/gnu/freeway/util/io/ConnectorEndPoint.java
   freeway/src/org/gnu/freeway/util/io/DirLocation.java
   freeway/src/org/gnu/freeway/util/io/FileLocation.java
   freeway/src/org/gnu/freeway/util/io/IOUtils.java
   freeway/src/org/gnu/freeway/util/io/LineDecoder.java
   freeway/src/org/gnu/freeway/util/io/LinkLocation.java
   freeway/src/org/gnu/freeway/util/io/Location.java
   freeway/src/org/gnu/freeway/util/io/MappedFile.java
   freeway/src/org/gnu/freeway/util/io/TextDecoder.java
   freeway/src/org/gnu/freeway/util/io/Traverser.java
   freeway/src/org/gnu/freeway/util/io/TraverserContext.java
   freeway/src/org/gnu/freeway/util/io/storage.c
   freeway/src/org/gnu/freeway/util/net/
   freeway/src/org/gnu/freeway/util/net/CSHandler.java
   freeway/src/org/gnu/freeway/util/net/CSMessage.java
   freeway/src/org/gnu/freeway/util/net/CSResult.java
   freeway/src/org/gnu/freeway/util/net/CSServer.java
   freeway/src/org/gnu/freeway/util/net/CSSession.java
   freeway/src/org/gnu/freeway/util/net/CSSessionHandler.java
   freeway/src/org/gnu/freeway/util/net/CSUnknown.java
   freeway/src/org/gnu/freeway/util/net/ErrorReporter.java
   freeway/src/org/gnu/freeway/util/net/NetUtils.java
   freeway/src/org/gnu/freeway/util/net/P2PHandler.java
   freeway/src/org/gnu/freeway/util/net/P2PMessage.java
   freeway/src/org/gnu/freeway/util/net/Persistent.java
   freeway/src/org/gnu/freeway/util/net/PersistentDecoder.java
   freeway/src/org/gnu/freeway/util/net/PersistentHelper.java
   freeway/src/org/gnu/freeway/util/net/PersistentReader.java
   freeway/src/org/gnu/freeway/util/net/PersistentSocket.java
   freeway/src/org/gnu/freeway/util/net/PersistentWriter.java
   freeway/src/org/gnu/freeway/util/net/TCPServer.java
   freeway/src/org/gnu/freeway/util/net/TCPSession.java
   freeway/src/org/gnu/freeway/util/ui/
   freeway/src/org/gnu/freeway/util/ui/AbstractAdapter.java
   freeway/src/org/gnu/freeway/util/ui/Adapter.java
   freeway/src/org/gnu/freeway/util/ui/CompoundComparator.java
   freeway/src/org/gnu/freeway/util/ui/Controller.java
   freeway/src/org/gnu/freeway/util/ui/DefaultComparator.java
   freeway/src/org/gnu/freeway/util/ui/GBar.java
   freeway/src/org/gnu/freeway/util/ui/GColumn.java
   freeway/src/org/gnu/freeway/util/ui/GConsole.java
   freeway/src/org/gnu/freeway/util/ui/GDialog.java
   freeway/src/org/gnu/freeway/util/ui/GFile.java
   freeway/src/org/gnu/freeway/util/ui/GForm.java
   freeway/src/org/gnu/freeway/util/ui/GFrame.java
   freeway/src/org/gnu/freeway/util/ui/GProgress.java
   freeway/src/org/gnu/freeway/util/ui/GScrollPane.java
   freeway/src/org/gnu/freeway/util/ui/GSplash.java
   freeway/src/org/gnu/freeway/util/ui/GStack.java
   freeway/src/org/gnu/freeway/util/ui/GStatus.java
   freeway/src/org/gnu/freeway/util/ui/GStyledText.java
   freeway/src/org/gnu/freeway/util/ui/GSwap.java
   freeway/src/org/gnu/freeway/util/ui/GTable.java
   freeway/src/org/gnu/freeway/util/ui/GText.java
   freeway/src/org/gnu/freeway/util/ui/GWizard.java
   freeway/src/org/gnu/freeway/util/ui/GWizardPage.java
   freeway/src/org/gnu/freeway/util/ui/ListToTableModel.java
   freeway/src/org/gnu/freeway/util/ui/SVGRenderer.java
   freeway/src/org/gnu/freeway/util/ui/SortedListModel.java
   freeway/src/org/gnu/freeway/util/ui/SortedSelectionModel.java
   freeway/src/org/gnu/freeway/util/ui/TileLayout.java
   freeway/src/org/gnu/freeway/util/ui/UIAction.java
   freeway/src/org/gnu/freeway/util/ui/UIHelper.java
   freeway/src/org/gnu/freeway/util/ui/UIResources.java
Log:
i

Added: freeway/.classpath
===================================================================
--- freeway/.classpath  2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/.classpath  2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+       <classpathentry kind="src" path="src"/>
+       <classpathentry kind="src" path="etc/support"/>
+       <classpathentry kind="con" 
path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+       <classpathentry kind="lib" 
path="/Users/svallee/Dev/Projects/freeway/lib/concurrent.jar"/>
+       <classpathentry kind="lib" 
path="/Users/svallee/Dev/Projects/freeway/lib/batik-all.jar"/>
+       <classpathentry kind="output" path=".build"/>
+</classpath>

Added: freeway/.cvsignore
===================================================================
--- freeway/.cvsignore  2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/.cvsignore  2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1 @@
+build

Added: freeway/.project
===================================================================
--- freeway/.project    2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/.project    2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>freeway</name>
+       <comment></comment>
+       <projects>
+       </projects>
+       <buildSpec>
+               <buildCommand>
+                       <name>org.eclipse.jdt.core.javabuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+       </buildSpec>
+       <natures>
+               <nature>org.eclipse.jdt.core.javanature</nature>
+       </natures>
+       <linkedResources>
+               <link>
+                       <name>*config*</name>
+                       <type>2</type>
+                       <location>/Users/svallee/.freeway</location>
+               </link>
+       </linkedResources>
+</projectDescription>

Added: freeway/AUTHORS
===================================================================
--- freeway/AUTHORS     2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/AUTHORS     2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,54 @@
+Primary developers (at the moment):
+Blake Matheny <address@hidden>
+Christian Grothoff <address@hidden>
+Glenn McGrath <address@hidden>
+Igor Wronsky <address@hidden>
+Krista Bennett <address@hidden>
+Stéphane Vallée <address@hidden>
+Nils Durner <address@hidden>
+
+Code contributions also came from:
+Eric Haumant <address@hidden>
+Gerd Knorr <address@hidden>
+Hendrik Pagenhardt <address@hidden>
+Ioana Patrascu <address@hidden>
+<address@hidden>
+James Blackwell <address@hidden>
+James Liang <address@hidden>
+Jan Vitek <address@hidden>
+Jürgen Appel <address@hidden>
+Larry Waldo <address@hidden>
+Ludovic Courtès <address@hidden>
+Paul Ruth <address@hidden>
+Renaldo Ferreira <address@hidden>
+Tiberius Stef <address@hidden>
+Tzvetan Horozov <address@hidden>
+Uli Luckas <address@hidden>
+Werner Koch <address@hidden> [original code of libgcrypt]
+Kevin Vandersloot <address@hidden> [original code of gnome-system-monitor]
+
+DHT-module was contributed by:
+Marko Räihä
+Risto Saarelma
+Antti Salonen
+Tuomas Toivonen
+Tomi Tukiainen 
+Simo Viitanen
+
+Translations:
+Chinese  : Di Ma <address@hidden>
+Danish   : Jens Palsberg <address@hidden>
+Deutsch  : Christian Grothoff <address@hidden>
+French   : Mathieu <address@hidden>, Eric Haumant <address@hidden>, 
address@hidden
+Japanese : Hiroshi Yamauchi <address@hidden>
+Polish   : Adam Welc <address@hidden>
+Romaneste: Bogdan Carbunar <address@hidden>
+
+Logos:
+GNU in Net  : Christian Muellner <address@hidden>
+GNU with Net: Christian Muellner <address@hidden>
+AFS Face    : Alex Jones <address@hidden>
+
+Maintainers:
+FreeBSD Port - Kirill Ponomarew <address@hidden>
+Debian Package - Glenn McGrath <address@hidden>

Added: freeway/COPYING
===================================================================
--- freeway/COPYING     2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/COPYING     2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,340 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+
+           How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year  name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.

Added: freeway/README
===================================================================
--- freeway/README      2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/README      2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,72 @@
+                       Welcome to Freeway
+
+Freeway is a Java port of the secure peer-to-peer GNUnet framework.
+For a longer description of the GNUnet system, please see GNUnet's
+webpages (http://www.gnu.org/projects/GNUnet/ and
+http://www.ovmj.org/GNUnet/).
+
+Goals
+-----
+
+Freeway aims to...
+  . spread the use of secure peer-to-peer tools
+  . reach a larger audience by bringing GNUnet project to the Java community
+  . educate by providing a clean Java implementation that uses 
+    well-known design patterns
+
+
+Release
+-------
+
+The Freeway team is very happy to announce that the initial version of
+Freeway is now available.  This release provides a minimal
+implementation of the GNUnet core along with a few command tools. A
+first draft of a Swing client is also available.
+
+Please note that this is an ALPHA release. The framework has not been
+tested on a large scale yet. Bugs are common and the documentation may
+not be adequate for unexperienced users.
+
+
+How to build it ?
+-----------------
+
+You need Jikes to compile Freeway.
+An ant build file has been provided. To build needed jars, type:
+> cd $PROJECT_HOME
+> ant build
+
+
+How to use it ?
+---------------
+
+The default configuration should be okay for most of the systems
+($PROJECT_HOME/res/gnunet.root and $PROJECT_HOME/res/gnunet.user). For
+more information on available options and their meanings, please see
+pages on http://www.ovmj.org/GNUnet/.
+
+To start daemon, use gnunetd.sh script in $PROJECT_HOME/etc:
+> cd $PROJECT_HOME
+> ./etc/gnunetd.sh -L INFO
+
+Don't forget to set logging level to appropriate value since a lot of
+logging may be generated.
+
+
+Make the project live
+---------------------
+
+Any contribution will be greatly appreciated. Please submit bugs to
+http://www.ovmj.org/~mantis/ and patches via E-Mail to
address@hidden
+
+
+Stay tuned
+----------
+
+http://www.ovmj.org/GNUnet/freeway/
+http://mail.gnu.org/mailman/listinfo/freeway-developers
+
+
+Want to freely travel from peer to peer?  Take the freeway!
+The Freeway team.

Added: freeway/build.properties
===================================================================
--- freeway/build.properties    2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/build.properties    2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,11 @@
+# Put here some additional configuration you need to execute targets
+#
+
+api.target=../freeway-docs/doc/api
+
+archive.target=..
+
+jnlp.target=../freeway-docs/doc/jnlp
+jnlp.target.url=http://localhost/~svallee/dev/freeway-docs/jnlp/
+jnlp.sign.alias=freeway-team
+jnlp.sign.password=blurp9998

Added: freeway/build.sh
===================================================================
--- freeway/build.sh    2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/build.sh    2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,33 @@
+#!/bin/sh
+#
+# Build C library used by Freeway.
+#
+
+rm -f  \
+       build/*.o       \
+       build/*.jnilib
+
+cc -Wall       \
+       -I etc/support/clib     \
+       -c etc/support/clib/signals.c   \
+       -o build/signals.o
+
+cc -Wall       \
+       -I etc/support/clib     \
+       -c etc/support/clib/links.c     \
+       -o build/links.o
+
+cc -Wall       \
+       -I etc/support/clib     \
+       -I $JAVA_HOME/include   \
+       -c etc/support/clib/freeway-clib.c      \
+       -o build/freeway-clib.o
+
+
+# fixme: Mac OS X specific
+
+cc -dynamiclib \
+       -framework JavaVM       \
+       build/signals.o build/links.o build/freeway-clib.o      \
+       -o build/libfreeway-clib.jnilib
+

Added: freeway/build.xml
===================================================================
--- freeway/build.xml   2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/build.xml   2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,309 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<project name="Freeway" default="build" basedir=".">
+       <property name="build.compiler" value="jikes" />
+       <property name="build.sysclasspath" value="ignore" />
+       <property file="build.properties" />
+
+       <target name="init">
+               <tstamp>
+                       <format property="stamp" pattern="MMddyyyyHHmmss" />
+               </tstamp>
+
+               <property name="project.root" value="${basedir}" />
+               <dirname property="project.parent" file="${project.root}" />
+               <basename property="project.name" file="${project.root}" />
+
+               <property name="project.build" location="${project.root}/build" 
/>
+               <property name="project.doc" location="${project.root}/doc" />
+               <property name="project.etc" location="${project.root}/etc" />
+               <property name="project.lib" location="${project.root}/lib" />
+               <property name="project.res" location="${project.root}/res" />
+               <property name="project.src" location="${project.root}/src" />
+
+               <path id="build.classpath">
+                       <fileset dir="${project.lib}">
+                               <include name="*.jar" />
+                       </fileset>
+               </path>
+       </target>
+
+       <target name="build" depends="init">
+               <mkdir dir="${project.build}/classes" />
+
+               <javac srcdir="${project.src}"
+                       destdir="${project.build}/classes"
+                       classpathref="build.classpath"
+                       includeJavaRuntime="no"
+                       includeAntRuntime="no"
+                       source="1.4"
+                       encoding="UTF-8"
+                       debug="on"
+                       optimize="off"
+                       />
+
+               <jar destfile="${project.build}/freeway.jar">
+                       <fileset dir="${project.build}/classes">
+                               <exclude name="org/gnu/freeway/protocol/**" />
+                               <exclude name="org/gnu/freeway/transport/**" />
+                       </fileset>
+
+                       <fileset dir="${project.build}/classes" 
includes="org/gnu/freeway/protocol/Protocol.class" />
+                       <fileset dir="${project.build}/classes" 
includes="org/gnu/freeway/transport/MessagePack.class" />
+                       <fileset dir="${project.build}/classes" 
includes="org/gnu/freeway/transport/Session.class" />
+                       <fileset dir="${project.build}/classes" 
includes="org/gnu/freeway/transport/Transport.class" />
+               </jar>
+
+               <jar destfile="${project.build}/freeway-afs.jar">
+                       <fileset dir="${project.build}/classes" 
includes="org/gnu/freeway/protocol/afs/**" 
excludes="org/gnu/freeway/protocol/afs/MySQLHandle*" />
+                       <fileset dir="${project.build}/classes" 
includes="org/gnu/freeway/protocol/AbstractProtocol*" />
+                       <fileset dir="${project.res}/swing" />
+                       <manifest>
+                               <attribute name="Main-Class" 
value="org.gnu.freeway.protocol.afs.AFSProtocol" />
+                       </manifest>
+               </jar>
+
+               <jar destfile="${project.build}/freeway-afs-mysql.jar">
+                       <fileset dir="${project.build}/classes" 
includes="org/gnu/freeway/protocol/afs/MySQLHandle*" />
+                       <manifest>
+                               <attribute name="Main-Class" 
value="org.gnu.freeway.protocol.afs.MySQLHandle" />
+                       </manifest>
+               </jar>
+
+               <jar destfile="${project.build}/freeway-chat.jar">
+                       <fileset dir="${project.build}/classes" 
includes="org/gnu/freeway/protocol/chat/**" />
+                       <fileset dir="${project.build}/classes" 
includes="org/gnu/freeway/protocol/AbstractProtocol*" />
+                       <manifest>
+                               <attribute name="Main-Class" 
value="org.gnu.freeway.protocol.chat.ChatProtocol" />
+                       </manifest>
+               </jar>
+
+               <jar destfile="${project.build}/freeway-dht.jar">
+                       <fileset dir="${project.build}/classes" 
includes="org/gnu/freeway/protocol/dht/**" />
+                       <fileset dir="${project.build}/classes" 
includes="org/gnu/freeway/protocol/AbstractProtocol*" />
+                       <manifest>
+                               <attribute name="Main-Class" 
value="org.gnu.freeway.protocol.dht.DHTProtocol" />
+                       </manifest>
+               </jar>
+
+               <jar destfile="${project.build}/freeway-tbench.jar">
+                       <fileset dir="${project.build}/classes" 
includes="org/gnu/freeway/protocol/tbench/**" />
+                       <fileset dir="${project.build}/classes" 
includes="org/gnu/freeway/protocol/AbstractProtocol*" />
+                       <manifest>
+                               <attribute name="Main-Class" 
value="org.gnu.freeway.protocol.tbench.TBenchProtocol" />
+                       </manifest>
+               </jar>
+
+               <jar destfile="${project.build}/freeway-tracekit.jar">
+                       <fileset dir="${project.build}/classes" 
includes="org/gnu/freeway/protocol/tracekit/**" />
+                       <fileset dir="${project.build}/classes" 
includes="org/gnu/freeway/protocol/AbstractProtocol*" />
+                       <manifest>
+                               <attribute name="Main-Class" 
value="org.gnu.freeway.protocol.tracekit.TraceKitProtocol" />
+                       </manifest>
+               </jar>
+
+               <jar destfile="${project.build}/freeway-tcp.jar">
+                       <fileset dir="${project.build}/classes" 
includes="org/gnu/freeway/transport/tcp/**" />
+                       <fileset dir="${project.build}/classes" 
includes="org/gnu/freeway/transport/AbstractSession.class" />
+                       <fileset dir="${project.build}/classes" 
includes="org/gnu/freeway/transport/AbstractTransport.class" />
+                       <manifest>
+                               <attribute name="Main-Class" 
value="org.gnu.freeway.transport.tcp.TCPTransport" />
+                       </manifest>
+               </jar>
+
+               <jar destfile="${project.build}/freeway-udp.jar">
+                       <fileset dir="${project.build}/classes" 
includes="org/gnu/freeway/transport/udp/**" />
+                       <fileset dir="${project.build}/classes" 
includes="org/gnu/freeway/transport/AbstractSession.class" />
+                       <fileset dir="${project.build}/classes" 
includes="org/gnu/freeway/transport/AbstractTransport.class" />
+                       <manifest>
+                               <attribute name="Main-Class" 
value="org.gnu.freeway.transport.udp.UDPTransport" />
+                       </manifest>
+               </jar>
+
+               <jar destfile="${project.build}/freeway-nat.jar">
+                       <fileset dir="${project.build}/classes" 
includes="org/gnu/freeway/transport/nat/**" />
+                       <fileset dir="${project.build}/classes" 
includes="org/gnu/freeway/transport/AbstractSession.class" />
+                       <fileset dir="${project.build}/classes" 
includes="org/gnu/freeway/transport/AbstractTransport.class" />
+                       <manifest>
+                               <attribute name="Main-Class" 
value="org.gnu.freeway.transport.nat.NATTransport" />
+                       </manifest>
+               </jar>
+
+               <jar destfile="${project.build}/freeway-for-apps.jar">
+                       <fileset dir="${project.build}/classes" />
+                       <zipfileset dir="${project.res}/tests" prefix="tests" />
+                       <zipfileset dir="${project.res}/swing" prefix="" />
+                       <zipfileset dir="${project.res}" includes="*.template" 
prefix="" />
+                       <manifest>
+                               <attribute name="Main-Class" 
value="org.gnu.freeway.GNUNetSwing" />
+                       </manifest>
+               </jar>
+
+               <exec dir="." executable="./build.sh" />
+       </target>
+
+       <target name="fix-build" depends="build">
+               <javac srcdir="${project.etc}/support"
+                       destdir="${project.build}/classes"
+                       includeJavaRuntime="no"
+                       includeAntRuntime="no"
+                       source="1.4"
+                       encoding="UTF-8"
+                       debug="on"
+                       optimize="off"
+                       includes="Cleaner.java">
+                       <classpath>
+                               <path refid="build.classpath" />
+                               <path location="${project.build}/classes" />
+                       </classpath>
+               </javac>
+       </target>
+
+       <target name="fix-tabs" depends="fix-build">
+               <java classname="Cleaner" fork="yes" dir="${project.build}">
+                       <classpath>
+                               <path refid="build.classpath" />
+                               <path location="${project.build}/classes" />
+                       </classpath>
+
+                       <jvmarg value="-ea" />
+                       <arg value="${project.src}" />
+               </java>
+       </target>
+
+       <target name="test" depends="init">
+               <mkdir dir="${project.build}/classes" />
+               <javac srcdir="${project.etc}/support"
+                       destdir="${project.build}/classes"
+                       classpathref="build.classpath"
+                       includeJavaRuntime="no"
+                       includeAntRuntime="no"
+                       source="1.4"
+                       encoding="UTF-8"
+                       debug="on"
+                       optimize="off"
+                       includes="ReachTest.java"
+                       />
+               <java classname="ReachTest" fork="yes" dir="${project.build}">
+                       <classpath>
+                               <path refid="build.classpath" />
+                               <path location="${project.build}/classes" />
+                       </classpath>
+
+                       <jvmarg value="-ea" />
+                       <jvmarg value="-Djava.nio.preferSelect=true" />
+               </java>
+       </target>
+
+       <target name="make-headers" depends="build">
+               <javah outputFile="${project.etc}/support/clib/freeway-clib.h">
+                       <class name="org.gnu.freeway.util.OSAccess" />
+                       <classpath>
+                               <path refid="build.classpath" />
+                               <path location="${project.build}/freeway.jar" />
+                       </classpath>
+               </javah>
+       </target>
+
+       <!-- Delete All Compilation Generated Files -->
+
+       <target name="clean" depends="init">
+               <delete dir="${project.build}/classes" quiet="true" />
+               <delete dir="${project.build}/doc" quiet="true" />
+
+               <delete quiet="true">
+                       <fileset dir="${project.build}" 
includes="*.jar,*.o,*.jnilib" />
+               </delete>
+       </target>
+
+       <!-- API Documentation Generation -->
+
+       <target name="doc" depends="init" if="api.target">
+               <mkdir dir="${project.build}/doc/api" />
+
+               <javadoc sourcepath="${project.src}"
+                       destdir="${project.build}/doc/api"
+                       author="true" version="true" private="true" use="true" 
splitindex="true"
+                       windowtitle="Freeway API"
+                       stylesheetfile="${project.res}/api.css"
+                       packagenames="org.gnu.*"
+                       classpathref="build.classpath"
+                       additionalparam="-breakiterator"
+                       source="1.4">
+
+                       <doctitle><![CDATA[<h1>Freeway API 
${stamp}</h1>]]></doctitle>
+                       <header><![CDATA[Freeway API ${stamp}]]></header>
+                       <bottom><![CDATA[<i>2004 GNU</i>]]></bottom>
+
+                       <link href="http://java.sun.com/j2se/1.4/docs/api/"; />
+                       <link href="http://xml.apache.org/batik/javadoc/"; />
+
+                       <group title="NAT Transport Layer" 
packages="org.gnu.freeway.transport.nat*" />
+                       <group title="TCP Transport Layer" 
packages="org.gnu.freeway.transport.tcp*" />
+                       <group title="UDP Transport Layer" 
packages="org.gnu.freeway.transport.udp*" />
+
+                       <group title="AFS Protocol" 
packages="org.gnu.freeway.protocol.afs*" />
+                       <group title="Chat Protocol" 
packages="org.gnu.freeway.protocol.chat*" />
+                       <group title="DHT Protocol" 
packages="org.gnu.freeway.protocol.dht*" />
+                       <group title="TBench Protocol" 
packages="org.gnu.freeway.protocol.tbench*" />
+                       <group title="TraceKit Protocol" 
packages="org.gnu.freeway.protocol.tracekit*" />
+               </javadoc>
+
+               <delete quiet="true">
+                       <fileset dir="${api.target}" excludes="**/CVS/**/*" />
+               </delete>
+
+               <copy todir="${api.target}">
+                       <fileset dir="${project.build}/doc/api" />
+               </copy>
+       </target>
+
+       <!-- Nothing But A Clean Archive (Nor CVS, Neither Build Stuff) -->
+
+       <target name="make-archive" depends="clean" if="archive.target">
+               <property name="backup.name" 
value="${project.name}-${stamp}.tar.bz2" />
+
+               <delete file="${archive.target}/${backup.name}" quiet="true" />
+
+               <tar destfile="${archive.target}/${backup.name}"
+                       basedir="${project.parent}"
+                       compression="bzip2">
+
+                       <include name="${project.name}/**/*" />
+                       <exclude name="${project.name}/.*" />
+                       <exclude name="${project.name}/.*/**/*" />
+                       <exclude name="${project.name}/**/CVS/**/*" />
+               </tar>
+       </target>
+
+       <!-- JNLP Package Creation -->
+
+       <target name="install-jnlp" depends="build" if="jnlp.target">
+               <delete quiet="true">
+                       <fileset dir="${jnlp.target}" includes="**/*" />
+               </delete>
+
+               <copy todir="${jnlp.target}">
+                       <fileset dir="${project.build}" 
includes="freeway-for-apps.jar" />
+                       <fileset dir="${project.lib}" includes="*.jar" />
+                       <fileset dir="${project.res}/jnlp" includes="logo.png" 
/>
+               </copy>
+
+               <copy todir="${jnlp.target}" overwrite="true">
+                       <fileset dir="${project.res}/jnlp" includes="*.jnlp" />
+                       <filterset>
+                               <filter token="REMOTE_URL" 
value="${jnlp.target.url}"/>
+                       </filterset>
+               </copy>
+
+               <signjar jar="${jnlp.target}/freeway-for-apps.jar" 
alias="${jnlp.sign.alias}" storepass="${jnlp.sign.password}" />
+               <signjar jar="${jnlp.target}/batik-all.jar" 
alias="${jnlp.sign.alias}" storepass="${jnlp.sign.password}" />
+               <signjar jar="${jnlp.target}/concurrent.jar" 
alias="${jnlp.sign.alias}" storepass="${jnlp.sign.password}" />
+               <signjar 
jar="${jnlp.target}/mysql-connector-java-3.0.10-stable-bin.jar" 
alias="${jnlp.sign.alias}" storepass="${jnlp.sign.password}" />
+
+               <mkdir dir="${jnlp.target}/daemon" />
+               <copy todir="${jnlp.target}/daemon">
+                       <fileset dir="${project.build}" includes="*.jar" 
excludes="freeway-for-apps.jar" />
+               </copy>
+       </target>
+</project>

Added: freeway/doc/examples.txt
===================================================================
--- freeway/doc/examples.txt    2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/doc/examples.txt    2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,6 @@
+gnunet-download -o "COPYING" -- 
gnunet://afs/77DBE6F971D66F641B2262DDCE78CA8FB6815E60.E34BFD7843D4C8469EFC3DAD260F1F71783E2A87.0466DC92.17992
+=> The GNU Public License <= (mimetype: text/plain)
+
+gnunet-download -o "not_the_gpl.txt" -- 
gnunet://afs/1A6ECAD995A986CE8AA30E28FB0C43FD64D014B5.AB4C7104D0F29379BAC660BC380FE02A2222A45D.EE7E98D4.25
+=> This is not the GNU GPL <= (mimetype: unknown)
+

Added: freeway/doc/man.css
===================================================================
--- freeway/doc/man.css 2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/doc/man.css 2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,66 @@
+/* manual pages' stylesheet */
+
+body {
+       color:                          #000000;
+       background-color:       #ffffff;
+       font:                           normal normal normal 15px "Trebuchet 
MS";
+       padding:                                13px;
+       margin:                         0px;
+       }
+
+.block0 {
+       font-size:                      20px;
+       margin:                         0px 0px 30px 0px;
+       }
+
+.block1 {
+       font-size:                      15px;
+       margin:                         0px 0px 13px 0px;
+       }
+
+.block2 {
+       font-size:                      12px;
+       margin:                         2px 0px 7px 45px;
+       }
+
+.block3 {
+       font-size:                      12px;
+       margin:                         2px 0px 0px 45px;
+       }
+
+.emp {
+       background-color:       #dddddd;
+       border-bottom:          solid 1px black;
+       margin-bottom:          13px;
+       }
+
+.value {
+       color:                          #215788;
+       font-weight:                    bold;
+       font-style:                     italic;
+       }
+
+.code {
+       font-family:                    monospace;
+       zborder:                                solid 1px black;
+       background-color:       rgb(127,159,191);
+       padding:                                3px 3px 3px 10px;
+       margin:                         3px 3px 3px 0px;
+       }
+
+a {
+       color:                          #42adff;
+       }
+
+a:visited {
+       color:                          #215788;
+       }
+
+a:hover {
+       color:                          #215788;
+       }
+
+a:active {
+       color:                          #215788;
+       }
+

Added: freeway/doc/man.xml
===================================================================
--- freeway/doc/man.xml 2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/doc/man.xml 2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,1544 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<?xml-stylesheet type="text/xsl" href="man.xsl" ?>
+
+<man-pages>
+
+<man-page id="GNUNET-CHAT" level="1" name="gnunet-chat" last="2004/04/24">
+       <description>a command line interface to chat via GNUnet</description>
+
+       <long-description>
+       Chat via GNUnet.
+       </long-description>
+
+       <synopsis>gnunet-chat [OPTIONS]</synopsis>
+
+       <options>
+               <option short="h" long="help">
+               Print help page.
+               </option>
+
+               <option short="c" long="config" arg="FILENAME">
+               Load config file <arg /> (default is 
<v>~/.gnunet/gnunet.conf</v>).
+               </option>
+
+               <option short="v" long="version">
+               Print the version number.
+               </option>
+
+               <option short="n" arg="NICKNAME">
+               Use <arg /> for the nick.
+               </option>
+
+               <option short="L" long="loglevel" arg="LOGLEVEL">
+               Change the log level to <arg />.
+               Possible values for <arg /> are <v>NOTHING</v>, <v>FATAL</v>, 
<v>ERROR</v>, <v>FAILURE</v>, <v>WARNING</v>, <v>MESSAGE</v>, <v>INFO</v>, 
<v>DEBUG</v>, <v>CRON</v> and <v>EVERYTHING</v>.
+               </option>
+       </options>
+
+       <notes>
+       <page /> is meant as a demo application for how to implement 
applications using the GNUnet infrastructure.
+       It is not meant as a serious tool. You need to enable the "chat" in 
gnunet.conf in order to use <page />.<break />
+       <page /> is neither efficient nor secure and has serious design flaws. 
+       </notes>
+
+       <files>
+               <file path="~/.gnunet/gnunet.conf" content="GNUnet 
configuration file" />
+       </files>
+
+       <reporting-bugs />
+
+       <see-also>
+               <page id="GNUNET.CONF" level="5" />
+               <page id="GNUNETD" level="1" />
+       </see-also>
+</man-page>
+
+<man-page id="GNUNET-CHECK" level="1" name="gnunet-check" last="2004/04/26">
+       <description>a tool to check gnunetd's storage for 
consistency</description>
+
+       <long-description>
+               <page /> checks gnunetd's AFS databases for consistency.
+               This includes verifying that the different lookup methods
+               used by GNUnet (index list, database buckets, bloomfilters, 
+               etc) are consistent with each other. In some cases the
+               only solution available is to remove the problematic data entry.
+               <break />
+
+               Warning: each problematic but fixable, nonindexed content 
+               block will be restored with priority 0. This means that as
+               the database gets full, the restored content is among
+               the first to flush out. 
+       </long-description>
+
+       <synopsis>
+               gnunet-check [OPTIONS] 
+       </synopsis>
+
+       <options>
+               <option short="h" long="help">
+               Print help page.
+               </option>
+               
+               <option short="c" long="config" arg="FILENAME">
+               Load config file <arg /> (default is <v>/etc/gnunet.conf</v>).
+               </option>
+               
+               <option short="v" long="version">
+               Print the version number.
+               </option>
+               
+               <option short="u" long="update">
+               Update AFS databases from one version of GNUnet to another (if 
possible).
+               </option>
+               
+               <option short="a" long="all">
+               Check everything.
+               </option>
+               
+               <option short="f" long="files">
+               Check only the file list database (re-inserts all indexed files 
and checks if the database is consistent with that).
+               </option>
+               
+               <option short="d" long="data">
+               Check the block database (walks over all blocks in the database 
and checks if they make sense).
+               </option>
+               
+               <option short="p" long="prio" arg="PRIORITY">
+               Priority for the restored, non-indexed content.<break />
+               For indexed content, the config file value 
<v>GNUNET-INSERT/CONTENT-PRIORITY</v> will be used.<break />
+               Note: priority defaults to <v>0</v> which will cause the 
restored content to flush out when the disk gets full. 
+               </option>
+
+               <option short="n" long="nofix">
+               Check but do not fix problems.
+               </option>
+
+               <option short="V" long="verbose">
+               Be verbose.
+               </option>
+
+               <option short="q" long="quiet">
+               Be quiet.
+               </option>
+
+               <option short="L" long="loglevel" arg="LOGLEVEL">
+               Change the log level to <arg />.
+               Possible values for <arg /> are <v>NOTHING</v>, <v>FATAL</v>, 
<v>ERROR</v>, <v>FAILURE</v>, <v>WARNING</v>, <v>MESSAGE</v>, <v>INFO</v>, 
<v>DEBUG</v>, <v>CRON</v> and <v>EVERYTHING</v>.
+               </option>
+       </options>
+
+       <notes>
+       <page /> can run for a long, long time, depending on
+       how much content you are sharing.  You should
+       also expect to see some heavy disk activity.
+       </notes>
+
+       <files>
+               <file path="~/.gnunet/gnunet.conf" content="Used GNUnet 
configuration file" />
+               <file path="~/.gnunet/data/afs/*" content="AFS database related 
files." />
+               <file path="~/.gnunet/data/afs/database*" content="Indexing 
database files." />
+       </files>
+
+       <reporting-bugs />
+
+       <see-also>
+               <page id="GNUNET-INSERT" level="1" />
+               <page id="GNUNET-GTK" level="1" />
+               <page id="GNUNET-SEARCH" level="1" />
+               <page id="GNUNET-DOWNLOAD" level="1" />
+               <page id="GNUNET.CONF" level="5" />
+               <page id="GNUNETD" level="1" />
+       </see-also>
+</man-page>
+
+<man-page id="GNUNET-CONVERT" level="1" name="gnunet-convert" 
last="2004/04/20">
+       <description>a tool to convert the GNUnet AFS database</description>
+
+       <long-description>
+               <page /> can convert an existing GNUnet AFS block database from
+               one format to another.  Current formats are "gdbm", "tdb", 
"bdb",
+               "mysql" and "directory".  
+               You must call <page /> whenever you change the quota or the 
database type.  
+       </long-description>
+
+       <synopsis>
+               gnunet-convert [OPTIONS] 
+       </synopsis>
+
+       <options>
+               <option short="h" long="help">
+               Print help page.
+               </option>
+
+               <option short="c" long="config" arg="FILENAME">
+               Load config file (default is <v>/etc/gnunet.conf</v>).
+               </option>
+
+               <option short="V" long="verbose">
+               Be verbose.
+               </option>
+
+               <option short="q" long="quiet">
+               Be quiet.
+               </option>
+
+               <option short="L" long="loglevel" arg="LOGLEVEL">
+               Change the log level to <arg />.
+               Possible values for <arg /> are <v>NOTHING</v>, <v>FATAL</v>, 
<v>ERROR</v>, <v>FAILURE</v>, <v>WARNING</v>, <v>MESSAGE</v>, <v>INFO</v>, 
<v>DEBUG</v>, <v>CRON</v> and <v>EVERYTHING</v>.
+               </option>
+       </options>
+
+       <notes>
+       You cannot convert databases of GNUnet versions older than 0.5.3 with 
this tool.
+       </notes>
+
+       <files>
+               <file path="~/.gnunet/gnunet.conf" content="Users GNUnet 
configuration file" />
+               <file path="~/.gnunet/data/afs/content*" content="Default 
location of the GNUnet AFS database." />
+       </files>
+
+       <reporting-bugs />
+
+       <see-also>
+               <page id="GNUNET-INSERT" level="1" />
+               <page id="GNUNET-GTK" level="1" />
+               <page id="GNUNET-SEARCH" level="1" />
+               <page id="GNUNET-DOWNLOAD" level="1" />
+               <page id="GNUNET-CHECK" level="1" />
+               <page id="GNUNET.CONF" level="5" />
+               <page id="GNUNETD" level="1" />
+       </see-also>
+</man-page>
+
+<man-page id="GNUNET-DELETE" level="1" name="gnunet-delete" last="2003/04/05">
+       <description>a command line interface for deleting indexed files from 
GNUnet</description>
+
+       <long-description>
+               <page /> is used for deleting indexed files from GNUnet. 
+       </long-description>
+
+       <synopsis>
+               gnunet-delete [OPTIONS] -f FILENAME 
+       </synopsis>
+
+       <options>
+               <option short="h" long="help">
+               print help page
+               </option>
+
+               <option short="c" long="config" arg="FILENAME">
+               use config file (defaults: ~/.gnunet/gnunet.conf)
+               </option>
+
+               <option short="v" long="version">
+               print the version number
+               </option>
+
+               <option short="V" long="verbose">
+               be berbose; prints progress information and the hash-pair, CRC 
and filesize that can be used to download the file
+               </option>
+
+               <option short="f" long="file" arg="FILENAME">
+               delete <arg /> (obligatory)
+               </option>
+
+               <option short="L" long="loglevel" arg="LOGLEVEL">
+               Change the log level to <arg />.
+               Possible values for <arg /> are <v>NOTHING</v>, <v>FATAL</v>, 
<v>ERROR</v>, <v>FAILURE</v>, <v>WARNING</v>, <v>MESSAGE</v>, <v>INFO</v>, 
<v>DEBUG</v>, <v>CRON</v> and <v>EVERYTHING</v>.
+               </option>
+       </options>
+
+       <notes>
+       The option -f is used to specify the name of the file that should be 
deleted.<break />
+       You can only delete files that you indexed (or inserted?? FIXME!) and 
that you still have
+       available locally in full.  You should use <page /> on files that you 
indexed (not
+       inserted) and that you are going to delete locally.
+       </notes>
+
+       <files>
+               <file path="~/.gnunet/gnunet.conf" content="GNUnet 
configuration file" />
+       </files>
+
+       <reporting-bugs />
+
+       <see-also>
+               <page id="GNUNET-INSERT" level="1" />
+               <page id="GNUNET-GTK" level="1" />
+               <page id="GNUNET-SEARCH" level="1" />
+               <page id="GNUNET-DOWNLOAD" level="1" />
+               <page id="GNUNET.CONF" level="5" />
+               <page id="GNUNETD" level="1" />
+       </see-also>
+</man-page>
+
+<man-page id="GNUNET-DIRECTORY" level="1" name="gnunet-directory" 
last="2004/04/30">
+       <description>display directories and show or delete lists of 
encountered file identifiers</description>
+
+       <long-description>
+       <page /> lists the contents of a GNUnet directory.
+       It also can be used to manipulate the file-identifier database which is 
used by GNUnet for building directories.        <break />
+       <page /> will always list the contents of the GNUnet directories that 
are passed as filenames.<break />
+       Manipulating the file-identifier database is done by passing additional 
options to <page />.
+       Note that by default GNUnet does not build the file-identifier database 
and the database will thus always be empty.
+       You need to edit your configuration before the database is used.  The 
reason is that storing file identifiers
+       in plaintext in the database can compromise your privacy if your 
machine should fall under the control of an adversary.
+       </long-description>
+
+       <synopsis>
+               gnunet-directory [OPTIONS] FILENAMES
+       </synopsis>
+
+       <options>
+               <option short="a" long="list-all">
+               display all entries from the file-identifier database
+               </option>
+
+               <option short="A" long="kill-all">
+               delete all entries from the file-identifier database
+               </option>
+
+               <option short="h" long="help">
+               print help page
+               </option>
+
+               <option short="i" long="list-insert">
+               display entries from the insertion category in the 
file-identifier database
+               </option>
+
+               <option short="I" long="kill-insert">
+               delete all entries from the insertion category in the 
file-identifier database
+               </option>
+
+               <option short="L" long="loglevel" arg="LOGLEVEL">
+               Change the log level to <arg />.
+               Possible values for <arg /> are <v>NOTHING</v>, <v>FATAL</v>, 
<v>ERROR</v>, <v>FAILURE</v>, <v>WARNING</v>, <v>MESSAGE</v>, <v>INFO</v>, 
<v>DEBUG</v>, <v>CRON</v> and <v>EVERYTHING</v>.
+               </option>
+
+               <option short="s" long="list-search">
+               display entries from the search-result category in the 
file-identifier database
+               </option>
+
+               <option short="S" long="kill-search">
+               delete all entries from the search-result category in the 
file-identifier database
+               </option>
+
+               <option short="x" long="list-directory">
+               display entries from the directory-contents category in the 
file-identifier databae
+               </option>
+
+               <option short="X" long="kill-directory">
+               delete all entries from the directory-contents category in the 
file-identifier databae
+               </option>
+       </options>
+
+       <notes>
+       In order to allow users maximum flexibility when building directories, 
GNUnet collects file-identifiers of files that the clients encounter in the 
file-identifier database.  The file-identifiers are classified into four 
categories (search, insert, directory, namespace) according to how they were 
obtained (as search results, by insertion of that file, as part of a directory 
that was downloaded).  When a user uses gnunet-gtk to build a directory, he can 
build the directory using a subset of the file-identifiers stored in the 
file-identifier database.
+       A GNUnet directory is a file containing a list of keys.  The keys can 
point to files, other directories or files in namespaces.  In other words, a 
GNUnet directory is similar to UNIX directories.  The difference to tar and zip 
is that GNUnet directory does not contain the actual files, just symbolic 
(links), similar to directories with symbolic links in UNIX filesystems.  The 
benefit is that the individual files can be retrieved separately (if desired) 
and if some of the files are inserted to another node in GNUnet, this just 
increases their availability but does not produce useless duplicates (for 
example, it is a better idea to publish a collection of pictures or compressed 
sound files using a GNUnet directory instead of processing them with archivers 
such as tar or zip first).
+       Directories can be created at the moment by gnunet-gtk and 
gnunet-insert.  They can point to content created by the user or content 
inserted by others.  Just like ordinary files, a directory can be published 
under a pseudonym-signed namespace.  A directory can also contain links to 
files in namespaces.
+       GNUnet-directories use the (unregistered) mimetype 
application/gnunet-directory.  They can show up among normal search results or 
be pointed-to by sblocks. The directory file can be downloaded to disk by 
gnunet-download(1) for later processing or be handled more directly by 
gnunet-gtk(1).
+       </notes>
+
+       <reporting-bugs />
+
+       <see-also>
+               <page id="GNUNET-GTK" level="1" />
+               <page id="GNUNET-INSERT" level="1" />
+               <page id="GNUNET-SEARCH" level="1" />
+               <page id="GNUNET-DOWNLOAD" level="1" />
+               <page id="GNUNETD" level="1" />
+       </see-also>
+</man-page>
+
+<man-page id="GNUNET-DOWNLOAD" level="1" name="gnunet-download" 
last="2004/04/18">
+       <description>a command line interface for downloading files from 
GNUnet</description>
+
+       <long-description>
+               Download files from GNUnet.
+       </long-description>
+
+       <synopsis>gnunet-download [OPTIONS] -- GNUNET_URI</synopsis>
+
+       <options>
+               <option short="a" long="anonymity" arg="LEVEL">
+               set additional receiver anonymity enforcing level. If unset,
+               value specified in config file is used. Default is 0.
+               </option>
+
+               <option short="h" long="help">
+               print help page
+               </option>
+
+               <option short="c" long="config" arg="FILENAME">
+               use config file (defaults: ~/.gnunet/gnunet.conf)
+               </option>
+
+               <option short="o" long="output" arg="FILENAME">
+               write the file to <arg /> (required)
+               </option>
+
+               <option short="R" long="recursive">
+               download directories recursively and in parallel
+               </option>
+
+               <option short="t" long="threads" arg="NUMBER">
+               when downloading directories recursively, this option specifies 
how many downloads are done in parallel.  The default is 30.  If you have not 
so much memory or if your system can not support about 64 threads, you may want 
to pass a smaller value here.  The default can be changed by adding an option 
PARALLELIZATION in the gnunet.conf file in the section [GNUNET-DOWNLOAD].
+               </option>
+
+               <option short="v" long="version">
+               print the version number
+               </option>
+
+               <option short="V" long="verbose">
+               print progress information
+               </option>
+
+               <option short="L" long="loglevel" arg="LOGLEVEL">
+               Change the log level to <arg />.
+               Possible values for <arg /> are <v>NOTHING</v>, <v>FATAL</v>, 
<v>ERROR</v>, <v>FAILURE</v>, <v>WARNING</v>, <v>MESSAGE</v>, <v>INFO</v>, 
<v>DEBUG</v>, <v>CRON</v> and <v>EVERYTHING</v>.
+               </option>
+       </options>
+
+       <notes>
+       The GNUNET_URI is typically obtained from gnunet-search. gnunet-gtk 
+       can also be used instead of <page />.
+       If you ever have to abort a download, you can at any time continue it 
by re-issuing <page /> with the same filename. In that case GNUnet will not 
download blocks again that are already present. GNUnets file-encoding will 
ensure file integrity, even if the existing file was not downloaded from GNUnet 
in the first place. Temporary information will be stored in FILENAME.X files 
until the download is completed. These files are used only if a download is 
resumed later. If you abort a download for good, you should remember to delete 
these files.
+
+       <break title="SETTING ANONYMITY LEVEL" />
+       The -a option can be used to specify additional anonymity
+       constraints. If set to 0, GNUnet will try to download the
+       file as fast as possible without any additional
+       slowdown by the anonymity code. Note that you
+       will still have a fair degree of anonymity depending
+       on the current network load and the power of the
+       adversary. The download is still unlikely to be
+       terribly fast since the sender may have requested
+       sender-anonymity and since in addition to that,
+       GNUnet will still do the anonymous routing.
+       
+       This option can be used to limit requests further
+       than that. In particular, you can require GNUnet to
+       receive certain amounts of traffic from other peers
+       before sending your queries. This way, you can gain
+       very high levels of anonymity - at the expense of
+       much more traffic and much higher latency. So set
+       it only if you really believe you need it.
+
+       The definition of ANONYMITY-RECEIVE is the following:
+       <![CDATA[If the value v is < 1000, it means that if GNUnet
+         routes n bytes of messages from foreign peers, it
+         may originate n/v bytes of queries in the same time-period.
+         The time-period is twice the average delay that GNUnet
+         deferrs forwarded queries.
+       
+         If the value v is >= 1000, it means that if GNUnet
+         routes n bytes of QUERIES from at least (v % 1000)
+         peers, it may originate n/v/1000 bytes of queries
+         in the same time-period.]]>
+
+       The default is 0 and this should be fine for most
+       users. Also notice that if you choose values above
+       1000, you may end up having no throughput at all,
+       especially if many of your fellow GNUnet-peers do
+       the same.
+       </notes>
+
+       <files>
+               <file path="~/.gnunet/gnunet.conf" content="GNUnet 
configuration file" />
+       </files>
+
+       <reporting-bugs />
+
+       <see-also>
+               <page id="GNUNET-INSERT" level="1" />
+               <page id="GNUNET-GTK" level="1" />
+               <page id="GNUNET-SEARCH" level="1" />
+               <page id="GNUNET-DOWNLOAD" level="1" />
+               <page id="GNUNET.CONF" level="5" />
+               <page id="GNUNETD" level="1" />
+       </see-also>
+</man-page>
+
+<man-page id="GNUNET-GTK" level="1" name="gnunet-gtk" last="2002/06/06">
+       <description>a gtk interface for accessing GNUnet</description>
+
+       <long-description>
+               gnunet-gtk is a gtk+ based GUI for searching, downloading and 
inserting files on GNUnet. It supports queries of the form foo AND bar (notice 
that AND must be capitalized and that keywords are case-sensitive), just like 
gnunet-search. It can also be used to create and publish content directories 
out of search results and inserted files. 
+       </long-description>
+
+       <synopsis>
+               gnunet-gtk [OPTIONS]
+       </synopsis>
+
+       <options>
+               <option short="h" long="help">
+               print help page
+               </option>
+
+               <option short="c" long="config" arg="FILENAME">
+               load config file (defaults: ~/.gnunet/gnunet.conf)
+               </option>
+
+               <option short="v" long="version">
+               print the version number
+               </option>
+       </options>
+
+       <notes>
+       Multiple files can be downloaded by holding down CTRL while selecting,
+       or by SHIFT, if you wish to select everything between the last 
selection and the currently pointed row. If you want to insert multiple files 
at once,
+       use gnunet-insert. Some functionality of gnunet-gtk can be
+       accessed by pressing down the right mouse button in appropriate windows.
+       </notes>
+
+       <files>
+               <file path="~/.gnunet/gnunet.conf" content="Users GNUnet 
configuration file" />
+       </files>
+
+       <reporting-bugs />
+
+       <see-also>
+               <page id="GNUNET-INSERT" level="1" />
+               <page id="GNUNET-SEARCH" level="1" />
+               <page id="GNUNET-DOWNLOAD" level="1" />
+               <page id="GNUNET.CONF" level="5" />
+               <page id="GNUNETD" level="1" />
+       </see-also>
+</man-page>
+
+<man-page id="GNUNET-INSERT" level="1" name="gnunet-insert" last="2004/05/02">
+       <description>a command line interface for inserting new content into 
GNUnet</description>
+
+       <long-description>
+               In order to share files with other GNUnet users, the files must 
first be made
+               available to GNUnet.  GNUnet does not automatically share all 
files from a
+               certain directory.  In fact, even files that are downloaded are 
not automatically shared.
+               <break />
+
+               In order to start sharing files, the files must be added either 
using
+               gnunet-insert or gnunet-gtk.  The command line tool 
gnunet-insert is more
+               useful if many files are supposed to be added.  gnunet-insert 
can
+               automatically insert batches of files, recursively insert 
directories, create
+               directories that can be browsed within GNUnet and publish file 
lists
+               in a namespace.
+               <break />
+
+               If libextractor was available when GNUnet was compiled, 
gnunet-insert can
+               automatically extract keywords from the files that are shared.  
Users that
+               want to download files from GNUnet use keywords to search for 
the appropriate
+               content.  You can disable keyword extraction with the -x 
option.  You can
+               manually add keywords using the -k and -K options.
+               <break />
+
+               In addition to searching for files by keyword, GNUnet allows 
organizing
+               files into directories.  With directories, the user only needs 
to find the
+               directory in order to be able to download any of the files 
listed in the
+               directory.   Directories can contain pointers to other 
directories.
+               <break />
+
+               With gnunet-insert, it is easy to create new directories 
simultaneously
+               when adding the files. Simply add the option -b to create a 
directory.
+               With -b alone, a directory containing all of the files listed 
at the command
+               line is created.  Together with the -R (recursive) option, an 
entire directory
+               tree can be added to GNUnet.  The structure of the tree is 
preserved.
+               <break />
+
+               Since keywords can be spammed (any user can add any content 
under any
+               keyword), GNUnet supports namespaces.  A namespace is a subset 
of the
+               searchspace into which only the holder of a certain pseudonym 
can add content.
+               Any GNUnet user can create any number of pseudonyms using
+               gnunet-pseudonym-create. Pseudonyms are stored in the users 
GNUnet 
+               directory and can  be additionally protected with a password.  
While 
+               pseudonyms are locally identified with an arbitrary string that 
+               the user selects when the pseudonym is created, the namespace 
is  
+               globally known only under the hash of the public key of the 
pseudonym. 
+               Since only the owner of the pseudonym can add content to the 
namespace, 
+               it is impossible for other users to pollute the namespace.  
+               gnunet-insert automatically inserts the top-directory (or the 
only
+               file if only one file is specified) into the namespace if a 
pseudonym is
+               specified.  If no specific namespace-identifier is specified 
(option -t),
+               gnunet-insert selects a random identifier.
+               <break />
+
+               It is possible to update content in GNUnet if that content was 
placed and
+               obtained from a particular namespace.  Updates are only 
possible for content
+               in namespaces since this is the only way to assure that a 
malicious party can
+               not supply counterfeited updates.  GNUnet supports two types of 
updateable content,
+               sporadically updated content and periodically updated content. 
If content is
+               periodically updated (every day, every week, etc.), the period 
must be passed
+               to gnunet-insert with the -i option. The -S option is used to 
indicate
+               sporadically updated content. You can use the -N option to 
specify the future
+               identifier of the update (only for the first update of 
periodically updated
+               content). Without -N, gnunet-insert will select (and output) a 
random
+               identifier that must be used for the update.
+               <break />
+
+               To update content in a namespace, gnunet-insert options -e and 
-o can be used. 
+               On the first insert, -o can be used to output the namespace 
block data into a file. 
+               On subsequent updates, -e can be used to read the previous 
block, which is used
+               to automatically calculate the information required for an 
update. Currently
+               the most convenient way to make regular namespace updates 
efficiently
+               remains a bit awkward. The trick is to insert a filesystem 
directory 
+               that instead of containing a possibly very large directory 
hierarchy, 
+               contains only the top level ".gnd" GNUnet directory files you 
have previously 
+               created or downloaded. That is, what is inserted is a flat 
directory,
+               with time cost same as inserting the .gnd files, but the result
+               will still be equal to a full-blown directory tree having 
+               actual files and further subdirectories.
+               <break />
+
+               You can use libextractor or command-line options to specify a 
mimetype and a
+               description for the files.  The description and keywords are 
used to help
+               users in searching for files on the network.  The keywords are 
case-sensitive.
+               GNUnet supports two styles of publishing files on the network.  
Inserting
+               a file means that a copy of the file is made in the local (!) 
database of
+               the node.  Indexing a file means that an index is added to the 
local (!)
+               database with pointers to the file itself.  Since 0.6.2 GNUnet 
will make
+               a copy of the file in the directory specified in gnunet.conf.  
The copy
+               will be in plaintext and have the RIPE160MD hash of the entire 
file as
+               the filename.  This makes it possible to create links to that 
file, if
+               gnunetd is running locally.  Note that for indexed files a 
different quota
+               is applied than for the normal GNUnet AFS database.  Indexing 
is generally 
+               significantly more efficient and the default choice.  In either 
case,
+               the file is slowly (depending on how often it is requested and 
on how much
+               bandwidth is available) dispersed into the network.  If you 
insert or index
+               a file and then leave the network, it will almost always NOT be 
available
+               anymore.
+       </long-description>
+
+       <synopsis>
+               gnunet-insert [OPTIONS] FILENAME*
+       </synopsis>
+
+       <options>
+               <option short="h" long="help">
+               Print a brief help page with all the options.
+               </option>
+
+               <option short="c" long="config" arg="FILENAME">
+               Use alternate config file (if this option is not specified, the 
default is ~/.gnunet/gnunet.conf).
+               </option>
+
+               <option short="v" long="version">
+               Print the version number.
+               </option>
+
+               <option short="L" long="loglevel" arg="LOGLEVEL">
+               Change the log level to <arg />.
+               Possible values for <arg /> are <v>NOTHING</v>, <v>FATAL</v>, 
<v>ERROR</v>, <v>FAILURE</v>, <v>WARNING</v>, <v>MESSAGE</v>, <v>INFO</v>, 
<v>DEBUG</v>, <v>CRON</v> and <v>EVERYTHING</v>.
+               </option>
+
+               <option short="V" long="verbose">
+               Be verbose.  Using this option causes gnunet-insert to print 
progress information and at the end the file identification that can be used to 
 download the file from GNUnet.
+               </option>
+
+               <option short="R" long="recursive">
+               Process directories recursively.  Without this option, 
directories are ignored.
+               With this option, gnunet-insert will process files in 
directories recursively.  
+               </option>
+
+               <option short="b" long="builddir">
+               Build a directory.  With this option, gnunet-insert will create 
a directory
+               containing all the files specified at the command-line and 
insert that directory into GNUnet.
+               This allows grouping multiple files together.  Together with 
the -R option, gnunet-insert can be used
+               to mirror an entire directory tree in GNUnet.  The directory 
structure is preserved (not all files are placed at the top-level directory).  
+               </option>
+
+               <option short="E" long="extract">
+               Print the list of keywords that will be extracted.  Do not 
perform any indexing or insertion.
+               </option>
+
+               <option short="l" long="link">
+               When indexing a file, gnunet-insert will create a copy of the 
file in the "share" directory of gnunetd.  If that directory happens to be on 
the local machine (i.e. gnunetd runs on localhost) then gnunet-insert can 
instead just use a link.  This will not work over the network, if the 
file-permissions do not allow gnunetd to read the file or if the file maybe 
changed afterwards.  Hence the default is to be inefficient and to make a copy. 
 With this option you can cause gnunet-insert to TRY to make a link.  If the 
link does not work for some reason that GNUnet can detect, gnunet-insert will 
fall back to creating a copy.
+               </option>
+
+               <option short="k" long="key" arg="KEYWORD">
+               additional key to index the content with (to add multiple keys, 
specify multiple times). Each additional key is case-sensitive. Can be 
specified multiple times.  The keyword is only applied to the top-level file or 
directory.
+               </option>
+
+               <option short="K" long="global-key" arg="KEYWORD">
+               additional key to index the content with.  Keywords specified 
with -K are applied to files and directories encountered on the command-line or 
in the recursive scan.  This is the only difference to the -k option.  This 
option can be specified multiple times.
+               </option>
+
+               <option short="s" long="pseudonym" arg="NAME">
+               For each file at the command line (or, if -b is specified, for 
the top-level directory) create an SBlock that places the file into the 
namespace specified by the pseudonym <arg />.  
+               </option>
+
+               <option short="P" long="pass" arg="PASSWORD">
+               Specifies the password for the pseudonym (if needed).  If the 
password is invalid gnunet-insert aborts with an error.  This option is only 
valid together with the -s option.
+               </option>
+
+               <option short="t" long="this" arg="ID">
+               Specifies the identifier <arg /> of the SBlock.  This option is 
only valid together with the -s option and together with either the option -b 
or only a single filename on the command-line.
+               The identifier can be given in HEX notation, otherwise the HEX 
code is derived by hashing the given identifier <arg /> string
+               which may be a natural language keyword.
+               </option>
+
+               <option short="T" long="time" arg="TIME">
+               Specifies the SBlock creation time. Requires format 
+               "DAY-MONTHNUMBER-YEAR HOUR:MINUTE" 
+               for <arg />. This option can be used to publish past and future 
periodical 
+               SBlocks. The option works best when used together with -e. 
Default time is the current time.
+               </option>
+
+               <option short="N" long="next" arg="ID">
+               Specifies the next identifier <arg /> of a future version of 
the SBlock.  This option is only valid together with the -s option and together 
with either the option -b or only a single filename on the command-line.  This 
option can be used to specify what the identifier of an updated version will 
look like.  Without the -i option, a one-shot update SBlock is used 
(a-periodic).  With the -i option, the difference between  the current 
identifier (this) and the next identifier is used to compute all future 
identifiers.  Note that specifying -i and -N without -t hardly ever makes 
sense. 
+               The identifier <arg /> can be given in HEX notation, otherwise 
the HEX code is derived by hashing the given identifier string.
+               </option>
+
+               <option short="e" long="sprev" arg="FILENAME">
+               Specifies the previous SBlock file that contains the necessary 
information to update a periodical SBlock. On the first time of inserting a 
periodical, use -o to create the file, without -e. Filenames in -o and -e can 
be the same.
+               </option>
+
+               <option short="o" long="sout" arg="FILENAME">
+               Write the created SBlock to a file. This is especially useful 
with periodical updates done by a script. The SBlock file contains the 
necessary information to update the periodical SBlock. Filenames in -o and -e 
can be the same.
+               </option>
+
+               <option short="i" long="interval" arg="SECONDS">
+               Specifies the update frequency of the content in seconds. This 
option is only valid together with the -s option. If no current and next 
identifier are specified, the system picks some random start values for the 
sequence. 
+               Most recent update can be found by gnunet-gtk automatically. 
gnunet-search will print all edition ids 
+               between the insertion time and the current time. A new search 
can be then performed with one of the printed keys. 
+               Also, using gnunet-insert for updating content is cumbersome, 
in the future gnunet-gtk will provide a more interactive 
+               way to manage content updates.
+               </option>
+
+               <option short="S" long="sporadic">
+               This option specifies that the file will be updated 
sporadically but not periodically.  It is only valid in conjunction with the -s 
option.  It is implied if  -N is specified but not -i.  It can not be used 
together with the -i option.  Use -S if you intend to publish an update at an 
unknown point in the future and if you want gnunet-insert to pick a random  
identifier for that future content.  
+               If you use -s but not -S, -N or -i, the content will not be 
updateable.
+               </option>
+
+               <option short="m" long="mime" arg="MIMETYPE">
+               Set the mime-type of all (!) files to be <arg />.  This option 
has no effect on directories (option -b). If not supplied and the option -x is 
not specified,  gnunet-insert will attempt to determine the mime-type using 
libextractor and otherwise use "unknown".
+               </option>
+
+               <option short="p" long="prio" arg="PRIORITY">
+               Executive summary: You probably don't need it.<break />
+               Set the priority of the inserted content (default: 65535).  If 
the local database is full, GNUnet will discard the content with the lowest 
ranking.  Note that ranks change over time depending on popularity.  The 
default should be high enough to preserve the locally inserted content in favor 
of content that migrates from other peers.
+               </option>
+
+               <option short="n" long="noindex">
+               Executive summary: You probably don't need it.<break />
+               Do not index, full insertion.  Note that directories, RBlocks, 
SBlocks and IBlocks are always inserted (even without this option).  With this 
option, every block of the actual files is stored in encrypted form in the 
block database of the local peer.  While this adds security if the local node 
is compromised (the adversary snags your machine), it is significantly less 
efficient compared to on-demand encryption and is definitely not recommended 
for large files.
+               </option>
+
+               <option short="x" long="extraction">
+               Executive summary: You probably don't need it.<break />
+               Disable automatic keyword extraction. This option is only 
available if you compiled GNUnet with libextractor.  With this option, you can 
disable the use of libextractor to obtain meta-data (mime-type, description, 
keywords) from the files being processed.  In this case, you probably want to 
manually supply a list of keywords and a description on the command line.  Note 
that if you process multiple files or do recursive processing, the description, 
mime-type and  keywords will be used for all files (and directories).
+               -x implies -X.
+               </option>
+
+               <option short="X" long="nodirectindex">
+               Executive summary: You probably don't need it.<break />
+               With this option, gnunet-insert will not create individual 
RBlocks for all files except for keywords specified with the -K option.  With 
-X, gnunet-insert will  create an RBlock for each keyword infered from the 
files.  It does not disable the use of libextractor for finding the description 
and mime-types for these files.  
+               </option>
+
+               <option short="f" long="name" arg="NAME">
+               Executive summary: You probably don't need it.<break />
+               If a single file is inserted with RBlocks or an SBlock, use 
<arg /> as the published name for that file.  If multiple files are specified 
and no directory is created, <arg /> is again used for all of these files. In 
both cases, if this option is not given, the default is to preserve the actual 
filenames without the path.
+               If a directory is created, <arg /> is used for the directory. 
The names for the individual files are still the original filenames without 
path. If a directory is created recursively from a single filename on the 
command line and if <arg /> is not specified, the last component of the 
specified filename is used for the directory.  If a directory is created from 
multiple filenames specified on the command line, "not set" is used for the 
name of the directory.
+               </option>
+       </options>
+
+       <notes>
+       Basic examples
+
+       Index a file COPYING:
+
+       # gnunet-insert COPYING
+
+       Insert a file COPYING:
+
+       # gnunet-insert -n COPYING
+
+       Index a file COPYING with the keywords gpl and test:
+
+       # gnunet-insert -k gpl -k test COPYING
+
+       Index a file COPYING with description "GNU License"
+       and keywords gpl and test:
+
+       # gnunet-insert -D "GNU License" -k gpl -k test COPYING
+
+       Using directories
+
+       Index the files COPYING and AUTHORS with keyword test and 
+       build a directory containing the two files.  
+       Make the directory itself available under keyword gnu:
+
+       # gnunet-insert -K test -k gnu -b COPYING AUTHORS
+
+       Neatly publish an image gallery in kittendir/ and its
+       subdirs with keyword kittens for the directory but no 
+       keywords for the individual files or subdirs (-brX). 
+       Force description for all files:
+
+       # gnunet-insert -bRX -D "Kitten collection" -k kittens kittendir
+
+       Secure publishing with namespaces
+
+       Insert file COPYING with pseudonym RIAA (-s) and password MPAA (-P) 
+       with identifier gpl (-t) and no updates:
+
+       # gnunet-insert -s RIAA -P MPAA -t gpl COPYING
+
+       Recursively (-R) index /home/ogg and build a matching directory 
structure (-b).
+       Insert the top-level directory into the namespace under the pseudonym 
+       RIAA (-s) with password MPAA (-P) under identifier MUSIC (-t) and 
+       promise to provide an update with identifier VIDEOS (-N) at an 
+       arbitrary point in the future (-S is implied by lack of -i 
+       and presence of -N):
+
+       # gnunet-insert -Rb -s RIAA -P MPAA -t MUSIC -N VIDEOS /home/ogg
+
+       Recursively (-R) insert (-n) /var/lib/mysql and build a matching 
directory
+       structure (-b) but disable the use of libextractor to extract keywords 
+       (-X) while allowing the use of libextractor to provide descriptions 
(lack
+       of -x).  Print the file identifiers (-V) that can be used to retrieve
+       the files.  This will store a copy of the MySQL database in GNUnet but 
+       without adding any keywords to search for it.  Thus only people that
+       have been told the secret file identifiers printed with the -V option
+       can retrieve the (secret?) files:
+
+       # gnunet-insert -RnbXV /var/lib/mysql
+
+       Create a periodical SBlock with 24h update interval 
+       and store the created block to a file sblock.dat (unencrypted):
+
+       # gnunet-insert -s RIAA -P MPAA -o sblock.dat -i 86400 -D "My noisy 
file" -t noise noise.mp3
+
+       Update the periodical SBlock using settings from a previous time:
+
+       # gnunet-insert -s RIAA -P MPAA -e sblock.dat -o sblock.dat -D "My 
updated noisy file" noise_updated.mp3
+       </notes>
+
+       <files>
+               <file path="~/.gnunet/gnunet.conf" content="GNUnet 
configuration file" />
+       </files>
+
+       <reporting-bugs />
+
+       <see-also>
+               <page id="GNUNET-GTK" level="1" />
+               <page id="GNUNET-PSEUDONYM" level="1" />
+               <page id="GNUNET-SEARCH" level="1" />
+               <page id="GNUNET-DOWNLOAD" level="1" />
+               <page id="GNUNET.CONF" level="5" />
+               <page id="GNUNETD" level="1" />
+       </see-also>
+</man-page>
+
+<man-page id="GNUNET-PEER-INFO" level="1" name="gnunet-peer-info" 
last="2004/04/20">
+       <description>Display information about other known nodes.</description>
+
+       <long-description>
+               gnunet-peer-info display the last known IP and GNUnet host 
address of known nodes.
+       </long-description>
+
+       <synopsis>
+               gnunet-peer-info [ options ]
+       </synopsis>
+
+       <options>
+               <option short="h" long="help">
+               Print help page
+               </option>
+
+               <option short="c" long="config" arg="FILENAME">
+               Load config file (default: /etc/gnunet.conf)
+               </option>
+
+               <option short="v" long="version">
+               Print the version number
+               </option>
+
+               <option short="L" long="loglelvel" arg="LOGLEVEL">
+               Set the loglevel
+               </option>
+       </options>
+
+       <reporting-bugs />
+
+       <see-also>
+               <page id="GNUNET.CONF" level="5" />
+               <page id="GNUNETD" level="1" />
+       </see-also>
+</man-page>
+
+<man-page id="GNUNET-PSEUDONYM" level="1" name="gnunet-pseudonym" 
last="2004/04/31">
+       <description>create, delete or list pseudonyms</description>
+
+       <long-description>
+               By default, gnunet-pseudonyms lists all pseudonyms created 
locally. 
+       </long-description>
+
+       <synopsis>
+               gnunet-pseudonym [options]
+       </synopsis>
+
+       <options>
+               <option short="p" long="password" arg="PASS">
+               Specify password to decrypt pseudonyms encountered or to 
encrypt pseudonyms created.  The namespace identifiers are displayed for the 
pseudonyms that could be decrypted.
+               </option>
+
+               <option short="C" long="create" arg="NAME">
+               Creates a new pseudonym with the given <arg />.
+               </option>
+
+               <option short="D" long="delete" arg="NAME">
+               Deletes the pseudonym with the given <arg />.
+               </option>
+
+               <option short="q" long="quiet">
+               Do not print the list of pseudonyms (only perform create or 
delete operation).
+               </option>
+
+               <option short="h" long="help">
+               print help page
+               </option>
+       </options>
+
+       <files>
+               <file path="~/.gnunet/data/pseudonyms/" content="Directory 
where the pseudonyms are stored" />
+       </files>
+
+       <reporting-bugs />
+
+       <see-also>
+               <page id="GNUNET-INSERT" level="1" />
+               <page id="GNUNET-SEARCH" level="1" />
+               <page id="GNUNETD" level="1" />
+       </see-also>
+</man-page>
+
+<man-page id="GNUNET-SEARCH" level="1" name="gnunet-search" last="2004/03/20">
+       <description>a command line interface to search for content on 
GNUnet</description>
+
+       <long-description>
+               Search for content on GNUnet. The keywords are case-sensitive.
+       </long-description>
+
+       <synopsis>
+               gnunet-search [OPTIONS] KEYWORD [AND KEYWORD]*
+       </synopsis>
+
+       <options>
+               <option short="a" long="anonymity" arg="LEVEL">
+               set additional receiver anonymity enforcing. If unset, 
+               value specified in config file is used. Default is 0.
+               </option>
+
+               <option short="h" long="help">
+               print help page
+               </option>
+
+               <option short="c" long="config" arg="FILENAME">
+               use config file (defaults: ~/.gnunet/gnunet.conf)
+               </option>
+
+               <option short="v" long="version">
+               print the version number
+               </option>
+
+               <option short="t" arg="TIMEOUT">
+               wait <arg /> seconds before aborting the search.
+               </option>
+
+               <option short="m" arg="MAXRESULTS">
+               exit after finding <arg /> results.
+               </option>
+
+               <option short="n" arg="NAMESPACE">
+               Searches the given <arg />.  The keyword argument specified is 
used as the identifier in the namespace.
+               </option>
+
+               <option short="o" long="output" arg="PREFIX">
+               This option only works together with -s (at the moment).
+               Writes encountered (unencrypted) SBlocks to files with name
+               <arg />.XXX, where XXX is a number. This may be useful e.g.
+               if you forgot to save the SBlock when inserting a periodical.
+               </option>
+
+               <option short="L" long="loglevel" arg="LOGLEVEL">
+               Change the log level to <arg />.
+               Possible values for <arg /> are <v>NOTHING</v>, <v>FATAL</v>, 
<v>ERROR</v>, <v>FAILURE</v>, <v>WARNING</v>, <v>MESSAGE</v>, <v>INFO</v>, 
<v>DEBUG</v>, <v>CRON</v> and <v>EVERYTHING</v>.
+               </option>
+       </options>
+
+       <notes>
+       The -t option specifies that the query should timeout after 
approximately TIMEOUT seconds.
+       A value of zero is interpreted as never.
+       The default value can be changed via the SEARCHTIMEOUT variable in the 
GNUnet configuration file.
+       If multiple words are passed as keywords that are not separated by an 
AND, gnunet-search will concatenate them
+       to one bigger keyword. Thus
+
+       <code><page /> Das Kapital<break /></code>
+
+       and<break />
+
+       <code><page /> "Das Kapital"</code>
+
+       are identical. You can use AND to separate keywords.
+       In that case, <page /> will only display results that match all the 
keywords.
+       <page /> can not do multiple independent queries ("OR"), you must use 
multiple processes for that.
+       Mind that each GNUnet-server (gnunetd) has a build-in limit on the 
number of clients
+       (like <page /> or <xpage id="GNUNET-DOWNLOAD" level="1" />) that can 
connect simultaniously
+       (you can change it in src/include/config.h).<break />
+
+       Search results are printed by <page /> like this:<break />
+
+       <code><xpage id="GNUNET-DOWNLOAD" level="1" /> -o "COPYING" 
gnunet://afs/77DBE6F971D66F641B2262DDCE78CA8FB6815E60.E34BFD7843D4C8469EFC3DAD260F1F71783E2A87.466DC92.17992<break
 />
+       <![CDATA[=> The GNU Public License <= (mimetype: unknown)]]></code>
+
+       The first value line is the command to run to download the file.
+       The suggested filename in the example is COPYING.
+       The GNUnet URI consists of the key and query hash of the file, the 
CRC32 (GNUnet style) and finally the size of the file.
+       The second line contains the description of the file, here "The GNU 
Public License" and the mime-type
+       (see the options for gnunet-insert on how to change these).
+
+       <break title="SETTING ANONYMITY LEVEL" />
+       The -a option can be used to specify additional anonymity
+       constraints. If set to 0, GNUnet will try to download the
+       file as fast as possible without any additional
+       slowdown by the anonymity code. Note that you
+       will still have a fair degree of anonymity depending
+       on the current network load and the power of the
+       adversary. The download is still unlikely to be
+       terribly fast since the sender may have requested
+       sender-anonymity and since in addition to that,
+       GNUnet will still do the anonymous routing.
+       
+       This option can be used to limit requests further
+       than that. In particular, you can require GNUnet to
+       receive certain amounts of traffic from other peers
+       before sending your queries. This way, you can gain
+       very high levels of anonymity - at the expense of
+       much more traffic and much higher latency. So set
+       it only if you really believe you need it. 
+       
+       The definition of ANONYMITY-RECEIVE is the following:
+         <![CDATA[If the value v is < 1000, it means that if GNUnet
+         routes n bytes of messages from foreign peers, it
+         may originate n/v bytes of queries in the same time-period.
+         The time-period is twice the average delay that GNUnet
+         deferrs forwarded queries.
+       
+         If the value v is >= 1000, it means that if GNUnet
+         routes n bytes of QUERIES from at least (v % 1000)
+         peers, it may originate n/v/1000 bytes of queries
+         in the same time-period.]]>
+       
+       The default is 0 and this should be fine for most
+       users. Also notice that if you choose values above
+       1000, you may end up having no throughput at all,
+       especially if many of your fellow GNUnet-peers do
+       the same.
+       </notes>
+
+       <files>
+               <file path="~/.gnunet/gnunet.conf" content="GNUnet 
configuration file; specifies the default value for the timeout" />
+       </files>
+
+       <reporting-bugs />
+
+       <see-also>
+               <page id="GNUNET-GTK" level="1" />
+               <page id="GNUNET-INSERT" level="1" />
+               <page id="GNUNET-DOWNLOAD" level="1" />
+               <page id="GNUNET.CONF" level="5" />
+               <page id="GNUNETD" level="1" />
+       </see-also>
+</man-page>
+
+<man-page id="GNUNET-STATS" level="1" name="gnunet-stats" last="2003/04/30">
+       <description>Display statistics about your GNUnet server.</description>
+
+       <long-description>
+               <page /> is used to display detailed information about various 
aspect of gnunetd's operation.
+       </long-description>
+
+       <synopsis>
+               gnunet-stats [ options ]
+       </synopsis>
+
+       <options>
+               <option short="p" long="protocols">
+               print supported protocol messages
+               </option>
+
+               <option short="h" long="help">
+               print this page
+               </option>
+
+               <option short="c" long="config" arg="FILENAME">
+               load config file
+               </option>
+
+               <option short="v" long="version">
+               print version number
+               </option>
+
+               <option short="H" long="host" arg="HOSTNAME">
+               on which host is gnunetd running (default: localhost)
+               </option>
+
+               <option short="L" long="loglevel" arg="LOGLEVEL">
+               set the loglevel
+               </option>
+
+               <option short="d" long="debug">
+               run in debug mode, write logs to stderr
+               </option>
+       </options>
+
+       <reporting-bugs />
+
+       <see-also>
+               <page id="GNUNET.CONF" level="5" />
+               <page id="GNUNETD" level="1" />
+       </see-also>
+</man-page>
+
+<man-page id="GNUNET-TBENCH" level="1" name="gnunet-tbench" last="2003/05/08">
+       <description>transport profiling tool</description>
+
+       <long-description>
+               <page /> can be used to test the performance of the GNUnet
+               core (link-to-link encryption and the available transport 
services).
+               <page /> is useless to most ordinary users since its primary
+               function is to test the performance and correctness 
+               of GNUnet transport service implementations. 
+               <break />
+
+               <page /> sends a sequence of messages to another peer that
+               has the tbench module loaded. The service then measures the 
+               throughput, latency and loss of the messages round-trip. 
+               <page /> can only be used to test a direct peer-to-peer
+               connection. You must load the tbench module (via
+               the configuration gnunet.conf, section GNUNETD under 
APPLICATIONS)
+               in each of the two peers before <page /> can be used.
+               <break />
+
+               The two peers must know of each other and be connected (use
+               gnunet-stats to test for connections). Typically, <page />
+               reports the time it took to sent all specified messages and the
+               percentage of messages lost. 
+       </long-description>
+
+       <synopsis>
+               gnunet-tbench [OPTIONS] 
+       </synopsis>
+
+       <options>
+               <option short="r" long="rec" arg="RECEIVER">
+               use this option to specify the identity of the
+               <arg /> peer that is used for the benchmark. This option is 
required.
+               </option>
+
+               <option short="h" long="help">
+               print help page
+               </option>
+
+               <option short="c" long="config" arg="FILENAME">
+               load config file (defaults: ~/.gnunet/gnunet.conf)
+               </option>
+
+               <option short="v" long="version">
+               print the version number
+               </option>
+
+               <option short="n" long="msg" arg="MESSAGES">
+               how many messages should be used in each iteration (used to
+               compute average, min, max, etc.)
+               </option>
+
+               <option short="s" long="size" arg="SIZE">
+               test using the specified message size
+               </option>
+
+               <option short="i" long="iterations" arg="ITER">
+               perform <arg /> iterations of the benchmark
+               </option>
+
+               <option short="t" long="timeout" arg="TIMEOUT">
+               wait <arg /> milli-seconds for replies to arrive before aborting
+               </option>
+
+               <option short="S" long="space" arg="SPACE">
+               use <arg /> milli-seconds of delays between trains of messages 
+               </option>
+
+               <option short="X" long="xspace" arg="COUNT">
+               use trains of <arg /> messages 
+               </option>
+
+               <option short="g" long="gnuplot">
+               create output in two colums suitable for gnuplot. 
+               When using this option, concatenate the output of multiple
+               runs with various options into a file 'tbench' and run
+               the following gnuplot script to visualize the time/loss 
ratio:<break />
+               set xlabel "time"<break />
+               set ylabel "percent transmitted"<break />
+               plot "tbench" title 'Transport benchmarking' with points
+               </option>
+
+               <option short="L" long="loglevel" arg="LOGLEVEL">
+               Change the log level to <arg />.
+               Possible values for <arg /> are <v>NOTHING</v>, <v>FATAL</v>, 
<v>ERROR</v>, <v>FAILURE</v>, <v>WARNING</v>, <v>MESSAGE</v>, <v>INFO</v>, 
<v>DEBUG</v>, <v>CRON</v> and <v>EVERYTHING</v>.
+               </option>
+       </options>
+
+       <notes>
+       <page /> can run for a long time, depending on how high you have set 
the numbers.
+       Run first with small numbers to get an initial estimate on the runtime.
+       </notes>
+
+       <files>
+               <file path="~/.gnunet/gnunet.conf" content="Users GNUnet 
configuration file" />
+       </files>
+
+       <reporting-bugs />
+
+       <see-also>
+               <page id="GNUNET.CONF" level="5" />
+               <page id="GNUNETD" level="1" />
+       </see-also>
+</man-page>
+
+<man-page id="GNUNET-TESTBED" level="1" name="gnunet-testbed" 
last="2003/12/09">
+       <description>interactive environment for GNUnet 
simulations</description>
+
+       <long-description>
+               <page /> can be used to control GNUnet peers to perform testing 
and benchmarking of the system.
+               Note that <page /> is at this point still a very new tool and 
not functional.
+               <page /> is fully scriptable and uses bash as a scripting 
language.  All features of bash
+               are supported, including the execution of arbitrary shell 
commands.  <page /> extends bash
+               with additional, testbed specific commands.
+       </long-description>
+
+       <synopsis>
+               gnunet-testbed [OPTIONS]
+       </synopsis>
+
+       <options>
+               <option short="h" long="help">
+               print help page
+               </option>
+
+               <option short="c" long="config" arg="FILENAME">
+               load config file (defaults: ~/.gnunet/gnunet.conf)
+               </option>
+
+               <option short="v" long="version">
+               print the version number
+               </option>
+
+               <option short="L" long="loglevel" arg="LOGLEVEL">
+               Change the log level to <arg />.
+               Possible values for <arg /> are <v>NOTHING</v>, <v>FATAL</v>, 
<v>ERROR</v>, <v>FAILURE</v>, <v>WARNING</v>, <v>MESSAGE</v>, <v>INFO</v>, 
<v>DEBUG</v>, <v>CRON</v> and <v>EVERYTHING</v>.
+               </option>
+       </options>
+
+       <notes>
+       <page /> is not yet fully implemented.  Peers that participate in a 
testbed must load the
+       testbed module.
+       </notes>
+
+       <files>
+               <file path="~/.gnunet/gnunet.conf" content="GNUnet 
configuration file" />
+       </files>
+
+       <reporting-bugs />
+
+       <see-also>
+               <page id="GNUNET.CONF" level="5" />
+               <page id="GNUNETD" level="1" />
+       </see-also>
+</man-page>
+
+<man-page id="GNUNET-TRACEKIT" level="1" name="gnunet-tracekit" 
last="2003/02/20">
+       <description>overlay network tracing</description>
+
+       <long-description>
+               <page /> can be used to visualize the GNUnet overlay
+               network.  This tool broadcasts a message into the network
+               querying every peer about a list of other connected peers.
+               The result is then printed to the screen.
+               <break />
+
+               <page /> is primarily useful for developers to determine
+               the structure of the network.  End-users will find little use
+               for the tool itself (except to satisfy their curiosity) but
+               should at the moment still enable this protocol to help the
+               developers that trace the network.
+       </long-description>
+
+       <synopsis>
+               gnunet-tracekit [OPTIONS] 
+       </synopsis>
+
+       <options>
+               <option short="h" long="help">
+               print help page
+               </option>
+
+               <option short="c" long="config" arg="FILENAME">
+               load config file (defaults: ~/.gnunet/gnunet.conf)
+               </option>
+
+               <option short="W" long="wait" arg="WAIT">
+               set how many seconds to wait for replies
+               </option>
+
+               <option short="D" long="depth" arg="DEPTH">
+               until which depth should the network be traced (how many hops)
+               </option>
+
+               <option short="F" long="format" arg="FORMAT">
+               specifies the output format, 0 is human readable, 1 is dot
+               </option>
+
+               <option short="P" long="priority" arg="PRIORITY">
+               what should the priority of the probe be
+               </option>
+
+               <option short="v" long="version">
+               print the version number
+               </option>
+
+               <option short="L" long="loglevel" arg="LOGLEVEL">
+               Change the log level to <arg />.
+               Possible values for <arg /> are <v>NOTHING</v>, <v>FATAL</v>, 
<v>ERROR</v>, <v>FAILURE</v>, <v>WARNING</v>, <v>MESSAGE</v>, <v>INFO</v>, 
<v>DEBUG</v>, <v>CRON</v> and <v>EVERYTHING</v>.
+               </option>
+       </options>
+
+       <notes>
+       <page /> may not be supported by all GNUnet peers.  Peers that
+       do not support tracekit will still be discovered and will be marked 
specially
+       in the output.
+       </notes>
+
+       <files>
+               <file path="~/.gnunet/gnunet.conf" content="Users GNUnet 
configuration file" />
+       </files>
+
+       <reporting-bugs />
+
+       <see-also>
+               <page id="GNUNET.CONF" level="5" />
+               <page id="GNUNETD" level="1" />
+       </see-also>
+</man-page>
+
+<man-page id="GNUNET-TRANSPORT-CHECK" level="1" name="gnunet-transport-check" 
last="2003/12/19">
+       <description>a tool to test a GNUnet transport service</description>
+
+       <long-description>
+               <page /> can be used to test or profile
+               a GNUnet transport service.  The tool can be used to test
+               both the correctness of the software as well as the correctness
+               of the configuration.  <page /> features two modes,
+               called loopback mode and ping mode.  In loopback mode the test 
is limited to testing if the
+               transport can be used to communicate with itself (loopback).
+               This mode does not include communication with other peers which
+               may be blocked by firewalls and other general Internet 
connectivity
+               problems.  The loopback mode is particularly useful to test
+               the SMTP transport service since this service is fairly hard to
+               configure correctly and most problems can be reveiled by just
+               testing the loopback.  In ping mode the tool will attempt to 
download
+               peer advertisements from the URL specified in the configuration 
file
+               and then try to contact each of the peers.  Note that it is 
perfectly
+               normal that some peers do not respond, but if no peer responds 
something
+               is likely to be wrong.  The configuration is always taken
+               from the configuration file.  Do not run gnunetd while running
+               <page /> since the transport services can not 
+               be used by two processes at the same time.
+               <break />
+
+               <page /> will always produce an error-message for
+               the NAT transport in loopback mode.  If NAT is configured in 
accept-mode (as in,
+               accept connections from peers using network address 
translation),
+               the check will fail with the message "could not create HELO",
+               which is correct since the peer itself is clearly not going to
+               advertise itself as a NAT.  If the peer is configured in 
NAT-mode,
+               that is, the peer is behind a NAT box, the message will be
+               'could not connect'.  For NAT, both messages are NOT errors
+               but exactly what is supposed to happen.
+               <break />
+
+               Similarly, a NAT-ed peer should typically configure the TCP 
transport
+               to use port 0 (not listen on any port).  In this case, 
+               <page /> will print 'could not create HELO' for the
+               TCP transport.  This is also ok.  In fact, a correctly 
configured
+               peer using NAT should give just two errors (could not connect 
for
+               tcp and could not create HELO for NAT) when tested using
+               <page />.  The reason is, that <page />
+               only tests loopback connectivity, and for a NAT-ed peer, that 
just
+               does not apply.
+               <break />
+
+               Note that in ping mode the HTTP download times out after 5 
minutes,
+               so if the list of peers is very large and not all peers can be
+               queried within the 5 minutes the tool may abort before trying 
all
+               peers.
+       </long-description>
+
+       <synopsis>
+               gnunet-transport-check [OPTIONS] 
+       </synopsis>
+
+       <options>
+               <option short="c" long="config" arg="FILENAME">
+               use config file (default: /etc/gnunet.conf)
+               </option>
+
+               <option short="h" long="help">
+               print help page
+               </option>
+
+               <option short="L" long="loglevel" arg="LOGLEVEL">
+               Change the log level to <arg />.
+               Possible values for <arg /> are <v>NOTHING</v>, <v>FATAL</v>, 
<v>ERROR</v>, <v>FAILURE</v>, <v>WARNING</v>, <v>MESSAGE</v>, <v>INFO</v>, 
<v>DEBUG</v>, <v>CRON</v> and <v>EVERYTHING</v>.
+               </option>
+
+               <option short="p" long="ping">
+               use ping mode (loopback mode is default)
+               </option>
+
+               <option short="r" long="repeat" arg="COUNT">
+               send <arg /> messages in a sequence over the same connection
+               </option>
+
+               <option short="s" long="size" arg="SIZE">
+               test using the specified message size, default is 11
+               </option>
+
+               <option short="t" long="transport" arg="TRANSPORT">
+               run using the specified transport, if not given the transports
+               configured in the configuration file are used.
+               </option>
+
+               <option short="v" long="version">
+               print the version number
+               </option>
+
+               <option short="V" long="verbose">
+               be verbose
+               </option>
+       </options>
+
+       <notes>
+       <page /> can run for a long time, depending on
+       how high you have set the COUNT level. Run first with small numbers
+       for COUNT to get an initial estimate on the runtime.
+       </notes>
+
+       <files>
+               <file path="/etc/gnunet.conf" content="default gnunetd 
configuration file" />
+       </files>
+
+       <reporting-bugs />
+
+       <see-also>
+               <page id="GNUNET.CONF" level="5" />
+               <page id="GNUNETD" level="1" />
+       </see-also>
+</man-page>
+
+<man-page id="GNUNET.CONF" level="5" name="gnunet.conf" last="2003/12/10">
+       <description>GNUnet configuration file</description>
+
+       <long-description>
+               GNUnet uses two configuration files.  /etc/gnunet.conf is the 
default location for the configuration file used by gnunetd, the GNUnet daemon. 
 It contains the network and resource configuration for the peer.  A template 
can be found in contrib/gnunet.root.  The RPM installs the template to 
/etc/gnunet.conf.
+               <break />
+
+               Another configuration file is used to allow users to customize 
GNUnet according to their needs.  It is used by all of the GNUnet tools and the 
default location is ~/.gnunet/gnunet.conf.  The user configuration allows the 
specification of personal options, such as the nickname in the chat.  A 
template can be found in contrib/gnunet.user.  The RPM installs a template for 
this file in /etc/skel/.gnunet/gnunet.conf. 
+               <break />
+
+               Both configuration files use the same basic syntax.  The file 
is split into sections.  Every section begins with [SECTIONNAME] and contains a 
number of options of the form OPTION=VALUE.  Empty lines and lines beginning 
with a # are treated as comments.  Some options are optional, in particular 
certain sections are not used at all unless the corresponding service module is 
loaded (e.g. you do not have to configure the SMTP transport unless you decide 
to use it).
+       </long-description>
+
+       <notes>
+               ~/.gnunet/gnunet.conf and /etc/gnunet.conf
+       </notes>
+
+       <files>
+               <file path="/etc/gnunet.conf" content="gnunetd configuration 
file" />
+               <file path="~/.gnunet/gnunet.conf" content="GNUnet user 
configuration file" />
+       </files>
+
+       <reporting-bugs />
+
+       <see-also>
+               <page id="GNUNET-GTK" level="1" />
+               <page id="GNUNET-INSERT" level="1" />
+               <page id="GNUNET-SEARCH" level="1" />
+               <page id="GNUNET-DOWNLOAD" level="1" />
+               <page id="GNUNET.CONF" level="5" />
+               <page id="GNUNETD" level="1" />
+       </see-also>
+</man-page>
+
+<man-page id="GNUNETD" level="1" name="gnunetd" last="2004/04/20">
+       <description>The GNUnet deamon</description>
+
+       <long-description>
+               The GNUnet deamon, required for any GNUnet operations 
(searches, downloads, etc). gnunetd does not require root rights.
+       </long-description>
+
+       <synopsis>
+               gnunetd [OPTIONS]
+       </synopsis>
+
+       <options>
+               <option short="h" long="help">
+               print help page
+               </option>
+
+               <option short="c" long="config" arg="FILENAME">
+               load config file (default: /etc/gnunet.conf)
+               </option>
+
+               <option short="v" long="version">
+               print the version number
+               </option>
+
+               <option short="d" long="debug">
+               do not detach from the console (for debugging); log messages 
are written to stderr instead of the logfile in -d mode.
+               </option>
+
+               <option short="u" long="user" arg="USER">
+               run as user <arg /> (and if available as group <arg />). Note 
that to use this option, you will probably have to start gnunetd as
+               root. It is typically better to directly start gnunetd as that 
user instead.
+               </option>
+
+               <option short="L" long="loglevel" arg="LOGLEVEL">
+               Change the log level to <arg />.
+               Possible values for <arg /> are <v>NOTHING</v>, <v>FATAL</v>, 
<v>ERROR</v>, <v>FAILURE</v>, <v>WARNING</v>, <v>MESSAGE</v>, <v>INFO</v>, 
<v>DEBUG</v>, <v>CRON</v> and <v>EVERYTHING</v>.
+               </option>
+       </options>
+
+       <notes>
+       Before you can share, search or download files from GNUnet you must 
start the GNUnet server, gnunetd.
+       If you start gnunetd as root (which is not required), you can use the 
-u option such that GNUnet runs as a different user. If a group of the same 
name exists, GNUnet will also change to that group. If GNUnet can not change 
its UID to USER or GID to USER, a warning will be printed and gnunetd will 
*continue* with the rights of the user that invoked gnunetd.
+       </notes>
+
+       <files>
+               <file path="/etc/gnunet.conf" content="gnunetd configuration 
file (default location).  " />
+               <file path="/var/lib/GNUnet/.hostkey" content="Nodes GNUnet RSA 
private key.  Keep secret." />
+               <file path="/var/lib/GNUnet/data/hosts/" content="Hostkeys.  
GNUnet stores contact information and public keys of other nodes here. You may 
want to make this directory publically available." />
+               <file path="/var/lib/GNUnet/data/afs/content/" content="Block 
database for anonymous file sharing. Depending on how you compiled GNUnet, a 
gdbm or tdb database or a directory is used.  GNUnet will store isolated blocks 
of files here. " />
+               <file path="/var/lib/GNUnet/data/credit/" content="Trust 
directory.  GNUnet stores economic information about other nodes here, in 
particular how much useful data we have received from which remote node." />
+       </files>
+
+       <reporting-bugs />
+
+       <see-also>
+               <page id="GNUNET-GTK" level="1" />
+               <page id="GNUNET-INSERT" level="1" />
+               <page id="GNUNET-SEARCH" level="1" />
+               <page id="GNUNET-DOWNLOAD" level="1" />
+               <page id="GNUNET.CONF" level="5" />
+               <page id="GNUNETD" level="1" />
+       </see-also>
+</man-page>
+
+</man-pages>

Added: freeway/doc/man.xsl
===================================================================
--- freeway/doc/man.xsl 2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/doc/man.xsl 2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,184 @@
+<?xml version="1.0"?>
+
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"; version="1.0">
+
+<xsl:template match="/">
+       <html>
+
+       <head>
+       <title>Manual Pages</title>
+       <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
+       <link href="man.css" rel="stylesheet" type="text/css" />
+       </head>
+
+       <body>
+       <xsl:apply-templates select="/man-pages/man-page" />
+       </body>
+
+       </html>
+</xsl:template>
+
+<xsl:template match="man-page">
+       <a name='address@hidden/address@hidden'></a>
+
+       <div class="block0">
+               <div class="emp">
+               <b><xsl:value-of select="@name" /></b> - <i><xsl:value-of 
select="description/text()" /></i>
+               </div>
+
+               <xsl:apply-templates />
+       </div>
+</xsl:template>
+
+
+<!-- **************** DESCRIPTION and LONG DESCRIPTION **************** -->
+
+<xsl:template match="description" />
+
+<xsl:template match="long-description">
+       <div class="block1">    DESCRIPTION
+       <div class="block2">    
+       <xsl:apply-templates />
+       </div>
+       </div>
+</xsl:template>
+
+
+<!-- **************** SYNOPSIS **************** -->
+
+<xsl:template match="synopsis">
+       <div class="block1">    SYNOPSIS
+       <div class="block2">    
+       <xsl:apply-templates />
+       </div>
+       </div>
+</xsl:template>
+
+
+<!-- **************** OPTIONS **************** -->
+
+<xsl:template match="options">
+       <div class="block1">    OPTIONS
+       <xsl:apply-templates />
+       </div>
+</xsl:template>
+
+<xsl:template match="option">
+       <div class="block2">
+               <xsl:if test="@short!=''">
+                       -<xsl:value-of select="@short" />
+                       <xsl:if test="@arg!=''"><xsl:text> 
</xsl:text><b><xsl:value-of select="@arg" /></b></xsl:if>
+               </xsl:if>
+
+               <xsl:if test="@short!='' and @long!=''">,</xsl:if>
+
+               <xsl:if test="@long!=''">
+                       --<xsl:value-of select="@long" />
+                       <xsl:if test="@arg!=''">=<b><xsl:value-of select="@arg" 
/></b></xsl:if>
+               </xsl:if>
+
+               <div class="block3"><xsl:apply-templates />
+               </div>
+       </div>
+</xsl:template>
+
+<xsl:template match="option/arg">
+       <b><xsl:value-of select="../@arg" /></b>
+</xsl:template>
+
+<xsl:template match="option/v">
+       <span class='value'><xsl:apply-templates /></span>
+</xsl:template>
+
+
+<!-- **************** NOTES **************** -->
+
+<xsl:template match="notes">
+       <div class="block1">    NOTES
+       <div class="block2">    
+       <xsl:apply-templates />
+       </div>
+       </div>
+</xsl:template>
+
+
+<!-- **************** FILES **************** -->
+
+<xsl:template match="files">
+       <div class="block1">    FILES
+       <xsl:apply-templates />
+       </div>
+</xsl:template>
+
+<xsl:template match="files/file">
+       <div class="block2" style="xpadding-bottom: 10px;">
+       <b><xsl:value-of select="@path" /></b>
+               <div class="block3" style="xpadding-bottom: 5px; ">
+               <xsl:value-of select="@content" />
+               </div>
+       </div>
+</xsl:template>
+
+
+<!-- **************** REPORTING BUGS **************** -->
+
+<xsl:template match="reporting-bugs">
+       <div class="block1">    REPORTING BUGS
+       <div class="block2">
+               Report bugs by using mantis <a 
href="http://www.ovmj.org/~mantis/";>http://www.ovmj.org/~mantis/</a>
+               or by sending electronic mail to <a 
href="mailto:address@hidden";>address@hidden</a>.
+       </div>
+       </div>
+</xsl:template>
+
+
+<!-- **************** SEE ALSO **************** -->
+
+<xsl:template match="see-also">
+       <div class="block1">    SEE ALSO
+       <div class="block2">    <xsl:apply-templates /></div>
+       </div>
+</xsl:template>
+
+<xsl:template match="see-also/page">
+       <xsl:variable name="target-id" select="@id" />
+       <xsl:variable name="target-level" select="@level" />
+       <xsl:variable name="target" select="//address@hidden and 
@level=$target-level]" />
+
+       <b><a href="#{$target-id}/{$target-level}"><xsl:value-of 
select="$target/@name" /></a></b>
+
+       <xsl:if test="position()!=last()-1">,
+       </xsl:if>
+</xsl:template>
+
+
+<!-- **************** BREAK **************** -->
+
+<xsl:template match="break[not(@title!='')]">
+       <br />
+</xsl:template>
+
+<xsl:template match="address@hidden'']">
+       <br /><br /><b><xsl:value-of select="@title" /></b><br />
+</xsl:template>
+
+
+<xsl:template match="code">
+       <div class="code"><xsl:apply-templates />
+       </div>
+</xsl:template>
+
+
+<xsl:template match="page[not(@id!='')]">
+       <b><xsl:value-of 
select="ancestor-or-self::node()address@hidden'']/@name" /></b>
+</xsl:template>
+
+<xsl:template match="address@hidden'']">
+       <xsl:variable name="target-id" select="@id" />
+       <xsl:variable name="target-level" select="@level" />
+       <xsl:variable name="target" select="//address@hidden and 
@level=$target-level]" />
+
+       <b><a href="#{$target-id}/{$target-level}"><xsl:value-of 
select="$target/@name" /></a></b>
+</xsl:template>
+
+</xsl:stylesheet>

Added: freeway/doc/packaging
===================================================================
--- freeway/doc/packaging       2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/doc/packaging       2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,36 @@
+When building Freeway, many jars are created.
+
+freeway.jar
+       core classes (no protocol, no transport)
+       no resources
+
+freeway-afs.jar
+       AFS protocol classes only
+
+freeway-afs-mysql.jar
+       MySQL access classes only (loaded by AFS protocol)
+
+freeway-chat.jar
+       Chat protocol classes
+
+freeway-tracekit.jar
+       Trace protocol classes
+
+freeway-tcp.jar
+       TCP transport classes
+
+freeway-udp.jar
+       UDP transport classes
+
+freeway-nat.jar
+       NAT transport classes
+
+
+freeway-for-apps.jar
+       *All* classes
+       resources : tests/*             prefix = tests
+       resources : swing/*             no prefix
+       resources : *.template  no prefix
+
+Warning : freeway-for-apps.jar is a all-in-one jar. No need to load additional 
libraries, all classes are already loaded.
+Used by client applications that do not need to be aware of 
transport/protocols classloading issues

Added: freeway/doc/todo
===================================================================
--- freeway/doc/todo    2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/doc/todo    2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,22 @@
+
+
+daemon
+------
+
+
+tools
+-----
+
+
+ui
+--
+       . gerer le focus pour les wizards
+       . creer des logos pour les joptionpane : logo freeway + un signe
+       . setSelectedFile sur file chooser ne semble pas marcher
+
+
+       . quand rien de configuré sur les dimensions
+               - ouvrir les GFrame centrées à peu près à 1/3 écran
+               - ouvrir les GDialog 1/2 de la GFrame owner
+       -> important quand une configuration neuve...
+

Added: freeway/doc/using eclipse
===================================================================
--- freeway/doc/using eclipse   2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/doc/using eclipse   2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,27 @@
+Using Eclipse to access freeway sources
+---------------------------------------
+WARNING: tested on Eclipse 3.0M7/Mac OS X
+
+Set up
+       . get a developer cvs account (see [pending])
+       . enable SSH2 protocol (diabled by default) in 
/Preferences/Team/CVS/SSH2 Connection Method
+       . create the repository in CVS perspective
+               connection type : extssh
+               user : your cvs account user name
+               host : ovjm.org
+               repository : /home/cvs/GNUnet
+       . a SSH2 Client error dialog might appears the first time you log in
+       . if you've used a passphrase to protect your keystore, a dialog will 
next appear to ask it
+
+Create project
+       . expand HEAD
+       . use context menu 'Check Out As...' on freeway item
+       a lot of problems should have been found
+       . open Project Properties dialog
+       . in Source tab, open Add Folder dialog and select 'src' subdirectory
+       . in Libraries tab, open Add External Jars dialog and select jars found 
in 'lib' subdirectory
+
+
+Please note that *all* Java sources are UTF-8 encoded. Be sure you've 
configured Eclipse
+to have this encoding as default one when editing sources.
+

Added: freeway/etc/gnunet-chat.sh
===================================================================
--- freeway/etc/gnunet-chat.sh  2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/etc/gnunet-chat.sh  2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,17 @@
+#!/bin/sh
+#
+
+CLASSPATH="freeway-for-apps.jar"
+
+for JAR in lib/*.jar; do
+       BASE=`basename "$JAR"`
+       CLASSPATH="$CLASSPATH:../lib/$BASE"
+done
+
+cd build;      \
+       java -ea        \
+       -classpath "$CLASSPATH" \
+       -Djava.library.path=.   \
+       -Djava.nio.preferSelect=true    \
+       org.gnu.freeway.GNUNetChat "$@"
+

Added: freeway/etc/gnunet-config.sh
===================================================================
--- freeway/etc/gnunet-config.sh        2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/etc/gnunet-config.sh        2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,17 @@
+#!/bin/sh
+#
+
+CLASSPATH="freeway-for-apps.jar"
+
+for JAR in lib/*.jar; do
+       BASE=`basename "$JAR"`
+       CLASSPATH="$CLASSPATH:../lib/$BASE"
+done
+
+cd build;      \
+       java -ea        \
+       -classpath "$CLASSPATH" \
+       -Djava.library.path=.   \
+       -Djava.nio.preferSelect=true    \
+       org.gnu.freeway.GNUNetConfig "$@"
+

Added: freeway/etc/gnunet-directory.sh
===================================================================
--- freeway/etc/gnunet-directory.sh     2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/etc/gnunet-directory.sh     2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,17 @@
+#!/bin/sh
+#
+
+CLASSPATH="freeway-for-apps.jar"
+
+for JAR in lib/*.jar; do
+       BASE=`basename "$JAR"`
+       CLASSPATH="$CLASSPATH:../lib/$BASE"
+done
+
+cd build;      \
+       java -ea        \
+       -classpath "$CLASSPATH" \
+       -Djava.library.path=.   \
+       -Djava.nio.preferSelect=true    \
+       org.gnu.freeway.GNUNetDirectory "$@"
+

Added: freeway/etc/gnunet-download.sh
===================================================================
--- freeway/etc/gnunet-download.sh      2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/etc/gnunet-download.sh      2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,17 @@
+#!/bin/sh
+#
+
+CLASSPATH="freeway-for-apps.jar"
+
+for JAR in lib/*.jar; do
+       BASE=`basename "$JAR"`
+       CLASSPATH="$CLASSPATH:../lib/$BASE"
+done
+
+cd build;      \
+       java -ea        \
+       -classpath "$CLASSPATH" \
+       -Djava.library.path=.   \
+       -Djava.nio.preferSelect=true    \
+       org.gnu.freeway.GNUNetDownload "$@"
+

Added: freeway/etc/gnunet-insert.sh
===================================================================
--- freeway/etc/gnunet-insert.sh        2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/etc/gnunet-insert.sh        2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,17 @@
+#!/bin/sh
+#
+
+CLASSPATH="freeway-for-apps.jar"
+
+for JAR in lib/*.jar; do
+       BASE=`basename "$JAR"`
+       CLASSPATH="$CLASSPATH:../lib/$BASE"
+done
+
+cd build;      \
+       java -ea        \
+       -classpath "$CLASSPATH" \
+       -Djava.library.path=.   \
+       -Djava.nio.preferSelect=true    \
+       org.gnu.freeway.GNUNetInsert "$@"
+

Added: freeway/etc/gnunet-peer-info.sh
===================================================================
--- freeway/etc/gnunet-peer-info.sh     2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/etc/gnunet-peer-info.sh     2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,17 @@
+#!/bin/sh
+#
+
+CLASSPATH="freeway-for-apps.jar"
+
+for JAR in lib/*.jar; do
+       BASE=`basename "$JAR"`
+       CLASSPATH="$CLASSPATH:../lib/$BASE"
+done
+
+cd build;      \
+       java -ea        \
+       -classpath "$CLASSPATH" \
+       -Djava.library.path=.   \
+       -Djava.nio.preferSelect=true    \
+       org.gnu.freeway.GNUNetPeerInfo "$@"
+

Added: freeway/etc/gnunet-pseudonym.sh
===================================================================
--- freeway/etc/gnunet-pseudonym.sh     2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/etc/gnunet-pseudonym.sh     2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,17 @@
+#!/bin/sh
+#
+
+CLASSPATH="freeway-for-apps.jar"
+
+for JAR in lib/*.jar; do
+       BASE=`basename "$JAR"`
+       CLASSPATH="$CLASSPATH:../lib/$BASE"
+done
+
+cd build;      \
+       java -ea        \
+       -classpath "$CLASSPATH" \
+       -Djava.library.path=.   \
+       -Djava.nio.preferSelect=true    \
+       org.gnu.freeway.GNUNetPseudonym "$@"
+

Added: freeway/etc/gnunet-search.sh
===================================================================
--- freeway/etc/gnunet-search.sh        2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/etc/gnunet-search.sh        2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,17 @@
+#!/bin/sh
+#
+
+CLASSPATH="freeway-for-apps.jar"
+
+for JAR in lib/*.jar; do
+       BASE=`basename "$JAR"`
+       CLASSPATH="$CLASSPATH:../lib/$BASE"
+done
+
+cd build;      \
+       java -ea        \
+       -classpath "$CLASSPATH" \
+       -Djava.library.path=.   \
+       -Djava.nio.preferSelect=true    \
+       org.gnu.freeway.GNUNetSearch "$@"
+

Added: freeway/etc/gnunet-shutdown.sh
===================================================================
--- freeway/etc/gnunet-shutdown.sh      2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/etc/gnunet-shutdown.sh      2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,17 @@
+#!/bin/sh
+#
+
+CLASSPATH="freeway-for-apps.jar"
+
+for JAR in lib/*.jar; do
+       BASE=`basename "$JAR"`
+       CLASSPATH="$CLASSPATH:../lib/$BASE"
+done
+
+cd build;      \
+       java -ea        \
+       -classpath "$CLASSPATH" \
+       -Djava.library.path=.   \
+       -Djava.nio.preferSelect=true    \
+       org.gnu.freeway.GNUNetShutdown "$@"
+

Added: freeway/etc/gnunet-stats.sh
===================================================================
--- freeway/etc/gnunet-stats.sh 2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/etc/gnunet-stats.sh 2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,17 @@
+#!/bin/sh
+#
+
+CLASSPATH="freeway-for-apps.jar"
+
+for JAR in lib/*.jar; do
+       BASE=`basename "$JAR"`
+       CLASSPATH="$CLASSPATH:../lib/$BASE"
+done
+
+cd build;      \
+       java -ea        \
+       -classpath "$CLASSPATH" \
+       -Djava.library.path=.   \
+       -Djava.nio.preferSelect=true    \
+       org.gnu.freeway.GNUNetStats "$@"
+

Added: freeway/etc/gnunet-swing.sh
===================================================================
--- freeway/etc/gnunet-swing.sh 2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/etc/gnunet-swing.sh 2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,18 @@
+#!/bin/sh
+#
+
+CLASSPATH="freeway-for-apps.jar"
+
+for JAR in lib/*.jar; do
+       BASE=`basename "$JAR"`
+       CLASSPATH="$CLASSPATH:../lib/$BASE"
+done
+
+cd build;      \
+       java -ea        \
+       -classpath "$CLASSPATH" \
+       -Djava.library.path=.   \
+       -Djava.nio.preferSelect=true    \
+       -Dcom.apple.mrj.application.apple.menu.about.name=GNUNetSwing   \
+       org.gnu.freeway.GNUNetSwing "$@"
+

Added: freeway/etc/gnunet-tests.sh
===================================================================
--- freeway/etc/gnunet-tests.sh 2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/etc/gnunet-tests.sh 2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,17 @@
+#!/bin/sh
+#
+
+CLASSPATH="freeway-for-apps.jar"
+
+for JAR in lib/*.jar; do
+       BASE=`basename "$JAR"`
+       CLASSPATH="$CLASSPATH:../lib/$BASE"
+done
+
+cd build;      \
+       java -ea        \
+       -classpath "$CLASSPATH" \
+       -Djava.library.path=.   \
+       -Djava.nio.preferSelect=true    \
+       org.gnu.freeway.GNUNetTests "$@"
+

Added: freeway/etc/gnunet-trace.sh
===================================================================
--- freeway/etc/gnunet-trace.sh 2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/etc/gnunet-trace.sh 2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,17 @@
+#!/bin/sh
+#
+
+CLASSPATH="freeway-for-apps.jar"
+
+for JAR in lib/*.jar; do
+       BASE=`basename "$JAR"`
+       CLASSPATH="$CLASSPATH:../lib/$BASE"
+done
+
+cd build;      \
+       java -ea        \
+       -classpath "$CLASSPATH" \
+       -Djava.library.path=.   \
+       -Djava.nio.preferSelect=true    \
+       org.gnu.freeway.GNUNetTrace "$@"
+

Added: freeway/etc/gnunet-transport-check.sh
===================================================================
--- freeway/etc/gnunet-transport-check.sh       2005-01-31 23:47:23 UTC (rev 
136)
+++ freeway/etc/gnunet-transport-check.sh       2005-02-01 01:07:27 UTC (rev 
137)
@@ -0,0 +1,17 @@
+#!/bin/sh
+#
+
+CLASSPATH="freeway-for-apps.jar"
+
+for JAR in lib/*.jar; do
+       BASE=`basename "$JAR"`
+       CLASSPATH="$CLASSPATH:../lib/$BASE"
+done
+
+cd build;      \
+       java -ea        \
+       -classpath "$CLASSPATH" \
+       -Djava.library.path=.   \
+       -Djava.nio.preferSelect=true    \
+       org.gnu.freeway.GNUNetTransportCheck "$@"
+

Added: freeway/etc/gnunet-ui-config.sh
===================================================================
--- freeway/etc/gnunet-ui-config.sh     2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/etc/gnunet-ui-config.sh     2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,17 @@
+#!/bin/sh
+#
+
+CLASSPATH="freeway-for-apps.jar"
+
+for JAR in lib/*.jar; do
+       BASE=`basename "$JAR"`
+       CLASSPATH="$CLASSPATH:../lib/$BASE"
+done
+
+cd build;      \
+       java -ea        \
+       -classpath "$CLASSPATH" \
+       -Djava.library.path=.   \
+       -Djava.nio.preferSelect=true    \
+       org.gnu.freeway.GNUNetUIConfig "$@"
+

Added: freeway/etc/gnunetd.sh
===================================================================
--- freeway/etc/gnunetd.sh      2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/etc/gnunetd.sh      2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,17 @@
+#!/bin/sh
+#
+
+CLASSPATH="freeway.jar"
+
+for JAR in lib/*.jar; do
+       BASE=`basename "$JAR"`
+       CLASSPATH="$CLASSPATH:../lib/$BASE"
+done
+
+cd build;      \
+       java -ea        \
+       -classpath "$CLASSPATH" \
+       -Djava.library.path=.   \
+       -Djava.nio.preferSelect=true    \
+       org.gnu.freeway.GNUNetDaemon "$@"
+

Added: freeway/etc/support/Cleaner.java
===================================================================
--- freeway/etc/support/Cleaner.java    2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/etc/support/Cleaner.java    2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,329 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+import org.gnu.freeway.util.io.*;
+
+import java.io.*;
+import java.nio.charset.*;
+
+
+public class Cleaner extends Object
+{
+       private static final String     EOL     =       
System.getProperty("line.separator");
+       private static final int        TAB     =       4;
+
+       private DirLocation     base;
+       private int                     totalFiles;
+       private int                     totalLines;
+       private boolean         success;
+       private boolean         printOnly;
+
+
+       public Cleaner( DirLocation dir )
+       {
+               super();
+               base=dir;
+               totalFiles=0;
+               totalLines=0;
+               success=true;
+               printOnly=false;
+       }
+
+       public String toString()
+       {
+               return "Cleaner [base="+base+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public void setPrintOnly( boolean flag )
+       {
+               printOnly=flag;
+       }
+
+       public boolean clean()
+       {
+               TraverserContext        ctx;
+
+               ctx=new TraverserContext();
+               ctx.setFilesOnly(true);
+               ctx.setFilter(IOUtils.newSuffixFilter(".java",false));
+
+               totalFiles=0;
+               totalLines=0;
+               success=true;
+               base.traverse(new Traverser() {
+                       public boolean examine( Location node, int depth )
+                       {
+                               try {
+                                       if (!clean((FileLocation) node,depth)) {
+                                               return false;
+                                               }
+                                       totalFiles++;
+                                       return true;
+                                       }
+                               catch( IOException x ) {
+                                       System.err.println("Got I/O exception 
while dealing with "+node.getPath()+"; aborting...");
+                                       x.printStackTrace(System.err);
+                                       success=false;
+                                       return false;
+                                       }
+                       }
+                       },ctx);
+
+               System.out.println();
+               System.out.println("Total files : "+totalFiles);
+               System.out.println("Total lines : "+totalLines);
+               return success;
+       }
+
+       protected boolean clean( FileLocation f, int depth ) throws IOException
+       {
+               BufferedReader  r;
+               BufferedWriter  w;
+               StringBuffer            buf;
+               String                  str;
+               FileLocation            tmp;
+               int                             line;
+               boolean                 errors;
+
+//             System.out.println(f.getName(depth)+":");
+
+               line=1;
+               errors=false;
+
+               tmp=FileLocation.temporary(f);
+
+               w=new BufferedWriter(new OutputStreamWriter(new 
FileOutputStream(tmp.getPath()),Charset.forName("UTF-8")));
+               try {
+                       r=new BufferedReader(new InputStreamReader(new 
FileInputStream(f.getPath()),Charset.forName("UTF-8")));
+                       try {
+                               buf=new StringBuffer();
+                               for (str=r.readLine(); str!=null; 
str=r.readLine()) {
+                                       if 
(clean(f,line,str.toCharArray(),buf,depth,errors)) {
+                                               w.write(buf.toString());
+                                               errors=true;
+                                               }
+                                       else {
+                                               w.write(str);
+                                               }
+                                       w.write(EOL);
+
+                                       line++;
+                                       }
+
+                               totalLines+=line;
+                               }
+                       finally {
+                               r.close();
+                               }
+                       }
+               finally {
+                       w.close();
+                       }
+
+               if (printOnly || !errors) {
+                       return tmp.delete();
+                       }
+               return renameAndDelete(new File(tmp.getPath()),new 
File(f.getPath()));
+       }
+
+       protected boolean clean( FileLocation f, int line, char[] c, 
StringBuffer buf, int depth, boolean errors )
+       {
+               int     mask;
+
+               // collect errors...
+               mask=0;
+               if (detectWhiteLine(c)) {
+                       mask|=1;
+                       }
+               else {
+                       if (detectBadIndentation(c)) {
+                               mask|=2;
+                               }
+                       if (detectBadTrailer(c)) {
+                               mask|=4;
+                               }
+                       }
+
+               if (mask==0) {
+                       return false;
+                       }
+
+               if (!errors) {
+                       System.out.println(f.getPath(depth)+":");
+                       }
+
+               // print them...
+               System.out.print("  Line #"+line+":");
+               if ((mask & 1)!=0) {
+                       System.out.print(" white line");
+                       }
+               if ((mask & 2)!=0) {
+                       System.out.print(" bad indentation");
+                       }
+               if ((mask & 4)!=0) {
+                       System.out.print(" trailing whitespace(s)");
+                       }
+               System.out.println();
+
+               // and correct them !
+               if ((mask & 1)!=0) {
+                       correctWhiteLine(c,buf);
+                       c=buf.toString().toCharArray();
+                       }
+               if ((mask & 2)!=0) {
+                       correctBadIndentation(c,buf);
+                       c=buf.toString().toCharArray();
+                       }
+               if ((mask & 4)!=0) {
+                       correctBadTrailer(c,buf);
+                       c=buf.toString().toCharArray();
+                       }
+               return true;
+       }
+
+       protected boolean detectWhiteLine( char[] c )
+       {
+               int     i;
+
+               for (i=0; i<c.length && Character.isWhitespace(c[i]); i++) {}
+               return (c.length>0 && i==c.length);
+       }
+
+       protected void correctWhiteLine( char[] c, StringBuffer buf )
+       {
+               buf.setLength(0);
+       }
+
+       protected boolean detectBadIndentation( char[] c )
+       {
+               int     i,j;
+
+               for (i=0; i<c.length && c[i]=='\t'; i++) {}
+               if (i<c.length) {
+                       for (j=i; j<c.length && c[j]==' '; j++) {}
+                       if (j>=i+TAB) {
+                               return true;
+                               }
+                       if (j<c.length && Character.isWhitespace(c[j])) {
+                               return true;
+                               }
+                       }
+               return false;
+       }
+
+       protected void correctBadIndentation( char[] c, StringBuffer buf )
+       {
+               int     pos,i;
+
+               buf.setLength(0);
+
+               pos=0;
+               for (i=0; i<c.length && Character.isWhitespace(c[i]); i++) {
+                       switch (c[i]) {
+                               case ' ':
+                                       pos++;
+                                       break;
+                               case '\t':
+                                       pos=((pos/TAB)+1)*TAB;
+                                       break;
+                               }
+                       }
+               while (pos>=TAB) {
+                       buf.append('\t');
+                       pos-=TAB;
+                       }
+               while (pos>0) {
+                       buf.append(' ');
+                       pos--;
+                       }
+               buf.append(c,i,c.length-i);
+       }
+
+       protected boolean detectBadTrailer( char[] c )
+       {
+               int     i;
+
+               for (i=c.length; i>0 && Character.isWhitespace(c[i-1]); i--) {}
+               return (i<c.length);
+       }
+
+       protected void correctBadTrailer( char[] c, StringBuffer buf )
+       {
+               int     i;
+
+               buf.setLength(0);
+
+               for (i=c.length; i>0 && Character.isWhitespace(c[i-1]); i--) {}
+               buf.append(c,0,i);
+       }
+
+       protected boolean renameAndDelete( File from, File to )
+       {
+               File    temp;
+               boolean                 ok;
+
+               ok=false;
+
+               temp=new File(to.getParent(),"blah");
+               if (to.renameTo(temp)) {
+                       if (from.renameTo(to)) {
+                               if (temp.delete()) {
+                                       ok=true;
+                                       }
+                               else {
+                                       System.err.println("Cannot delete 
temporary "+temp+" !");
+                                       }
+                               }
+                       else {
+                               System.err.println("Cannot rename "+from+" to 
"+to+" !");
+                               if (!temp.renameTo(to)) {
+                                       System.err.println("Cannot revert 
"+temp+" to "+to+" !");
+                                       }
+                               if (!from.delete()) {
+                                       System.err.println("Cannot delete 
temporary "+from+" !");
+                                       }
+                               }
+                       }
+               else {
+                       System.err.println("Cannot rename "+to+" to "+temp+" 
!");
+                       if (!from.delete()) {
+                               System.err.println("Cannot delete temporary 
"+from+" !");
+                               }
+                       }
+               return ok;
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public static void main( String[] args )
+       {
+               Cleaner         cleaner;
+               DirLocation     dir;
+
+               if (args.length==0) {
+                       System.err.println("No base directory specified.");
+                       System.exit(-1);
+                       }
+               System.out.println("Base directory is "+args[0]+".");
+
+               dir=new DirLocation(args[0]);
+               if (!dir.exists()) {
+                       System.err.println(args[0]+" does not exist or is not a 
directory.");
+                       System.exit(-1);
+                       }
+
+               cleaner=new Cleaner(dir);
+               cleaner.setPrintOnly(args.length>1 && args[1].equals("print"));
+               if (!cleaner.clean()) {
+                       System.err.println("Failed to clean "+args[0]+".");
+                       System.exit(-1);
+                       }
+
+               System.exit(0);
+       }
+}

Added: freeway/etc/support/clib/freeway-clib.c
===================================================================
--- freeway/etc/support/clib/freeway-clib.c     2005-01-31 23:47:23 UTC (rev 
136)
+++ freeway/etc/support/clib/freeway-clib.c     2005-02-01 01:07:27 UTC (rev 
137)
@@ -0,0 +1,128 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+#include <jni.h>
+
+#include "freeway-clib.h"
+#include "signals.h"
+#include "links.h"
+
+char* getString( JNIEnv* env, jstring str )
+{
+       const char*     ptr;
+       char*           p;
+       int                     len;
+
+       p=NULL;
+
+       ptr=(*env)->GetStringUTFChars(env,str,NULL);
+       if (ptr!=NULL) {
+               len=(*env)->GetStringUTFLength(env,str);
+
+               p=(char*) malloc((len+1)*sizeof(char));
+               memcpy(p,ptr,len);
+               p[len]=0;
+
+               (*env)->ReleaseStringUTFChars(env,str,ptr);
+               }
+       return p;
+}
+
+JNIEXPORT void JNICALL Java_org_gnu_freeway_util_OSAccess__1signalsInit( 
JNIEnv* env, jclass class )
+{
+       signals_init();
+}
+
+JNIEXPORT void JNICALL Java_org_gnu_freeway_util_OSAccess__1signalsDone( 
JNIEnv* env, jclass class )
+{
+       signals_done();
+}
+
+JNIEXPORT jboolean JNICALL Java_org_gnu_freeway_util_OSAccess__1signalsCatch( 
JNIEnv* env, jclass class, jint num )
+{
+       return (signals_catch((int) num) ? JNI_TRUE : JNI_FALSE);
+}
+
+JNIEXPORT jboolean JNICALL Java_org_gnu_freeway_util_OSAccess__1signalsLeave( 
JNIEnv* env, jclass class, jint num )
+{
+       return (signals_leave((int) num) ? JNI_TRUE : JNI_FALSE);
+}
+
+JNIEXPORT jint JNICALL Java_org_gnu_freeway_util_OSAccess__1signalsWait( 
JNIEnv* env, jclass class )
+{
+       return (jint) signals_wait();
+}
+
+JNIEXPORT jint JNICALL Java_org_gnu_freeway_util_OSAccess__1signalsGetPID( 
JNIEnv* env, jclass class )
+{
+       return (jint) getpid();
+}
+
+JNIEXPORT void JNICALL Java_org_gnu_freeway_util_OSAccess__1signalsSignal( 
JNIEnv* env, jclass class, jint pid, jint num )
+{
+       kill((pid_t) pid,(int) num);
+}
+
+JNIEXPORT jboolean JNICALL Java_org_gnu_freeway_util_OSAccess__1fileIsLink( 
JNIEnv* env, jclass class, jstring path )
+{
+       char*   fn;
+       int             ret;
+
+       fn=getString(env,path);
+       if (fn==NULL) {
+               return JNI_FALSE;
+               }
+       ret=links_is(fn);
+       free(fn);
+       return (ret ? JNI_TRUE : JNI_FALSE);
+}
+
+JNIEXPORT jstring JNICALL 
Java_org_gnu_freeway_util_OSAccess__1fileGetLinkTarget( JNIEnv* env, jclass 
class, jstring path )
+{
+       jstring str;
+       char*   fn;
+       char*   buf;
+
+       fn=getString(env,path);
+       if (fn==NULL) {
+               return NULL;
+               }
+
+       buf=links_get_target(fn);
+       free(fn);
+       if (buf==NULL) {
+               return NULL;
+               }
+
+       str=(*env)->NewStringUTF(env,buf);
+       free(buf);
+       return str;
+}
+
+JNIEXPORT jboolean JNICALL 
Java_org_gnu_freeway_util_OSAccess__1fileCreateLink( JNIEnv* env, jclass class, 
jstring path, jstring target )
+{
+       char*   fn;
+       char*   targ;
+       int             ret;
+
+       fn=getString(env,path);
+       if (fn==NULL) {
+               return JNI_FALSE;
+               }
+
+       targ=getString(env,target);
+       if (targ==NULL) {
+               free(fn);
+               return JNI_FALSE;
+               }
+
+       ret=links_create(fn,targ);
+
+       free(fn);
+       free(targ);
+       return (ret ? JNI_TRUE : JNI_FALSE);
+}

Added: freeway/etc/support/clib/freeway-clib.h
===================================================================
--- freeway/etc/support/clib/freeway-clib.h     2005-01-31 23:47:23 UTC (rev 
136)
+++ freeway/etc/support/clib/freeway-clib.h     2005-02-01 01:07:27 UTC (rev 
137)
@@ -0,0 +1,96 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class org_gnu_freeway_util_OSAccess */
+
+#ifndef _Included_org_gnu_freeway_util_OSAccess
+#define _Included_org_gnu_freeway_util_OSAccess
+#ifdef __cplusplus
+extern "C" {
+#endif
+/* Inaccessible static: logger */
+/* Inaccessible static: loaded */
+/* Inaccessible static: 
class_00024org_00024gnu_00024freeway_00024util_00024OSAccess */
+/*
+ * Class:     org_gnu_freeway_util_OSAccess
+ * Method:    _signalsInit
+ * Signature: ()V
+ */
+JNIEXPORT void JNICALL Java_org_gnu_freeway_util_OSAccess__1signalsInit
+  (JNIEnv *, jclass);
+
+/*
+ * Class:     org_gnu_freeway_util_OSAccess
+ * Method:    _signalsDone
+ * Signature: ()V
+ */
+JNIEXPORT void JNICALL Java_org_gnu_freeway_util_OSAccess__1signalsDone
+  (JNIEnv *, jclass);
+
+/*
+ * Class:     org_gnu_freeway_util_OSAccess
+ * Method:    _signalsCatch
+ * Signature: (I)Z
+ */
+JNIEXPORT jboolean JNICALL Java_org_gnu_freeway_util_OSAccess__1signalsCatch
+  (JNIEnv *, jclass, jint);
+
+/*
+ * Class:     org_gnu_freeway_util_OSAccess
+ * Method:    _signalsLeave
+ * Signature: (I)Z
+ */
+JNIEXPORT jboolean JNICALL Java_org_gnu_freeway_util_OSAccess__1signalsLeave
+  (JNIEnv *, jclass, jint);
+
+/*
+ * Class:     org_gnu_freeway_util_OSAccess
+ * Method:    _signalsWait
+ * Signature: ()I
+ */
+JNIEXPORT jint JNICALL Java_org_gnu_freeway_util_OSAccess__1signalsWait
+  (JNIEnv *, jclass);
+
+/*
+ * Class:     org_gnu_freeway_util_OSAccess
+ * Method:    _signalsGetPID
+ * Signature: ()I
+ */
+JNIEXPORT jint JNICALL Java_org_gnu_freeway_util_OSAccess__1signalsGetPID
+  (JNIEnv *, jclass);
+
+/*
+ * Class:     org_gnu_freeway_util_OSAccess
+ * Method:    _signalsSignal
+ * Signature: (II)V
+ */
+JNIEXPORT void JNICALL Java_org_gnu_freeway_util_OSAccess__1signalsSignal
+  (JNIEnv *, jclass, jint, jint);
+
+/*
+ * Class:     org_gnu_freeway_util_OSAccess
+ * Method:    _fileIsLink
+ * Signature: (Ljava/lang/String;)Z
+ */
+JNIEXPORT jboolean JNICALL Java_org_gnu_freeway_util_OSAccess__1fileIsLink
+  (JNIEnv *, jclass, jstring);
+
+/*
+ * Class:     org_gnu_freeway_util_OSAccess
+ * Method:    _fileGetLinkTarget
+ * Signature: (Ljava/lang/String;)Ljava/lang/String;
+ */
+JNIEXPORT jstring JNICALL 
Java_org_gnu_freeway_util_OSAccess__1fileGetLinkTarget
+  (JNIEnv *, jclass, jstring);
+
+/*
+ * Class:     org_gnu_freeway_util_OSAccess
+ * Method:    _fileCreateLink
+ * Signature: (Ljava/lang/String;Ljava/lang/String;)Z
+ */
+JNIEXPORT jboolean JNICALL Java_org_gnu_freeway_util_OSAccess__1fileCreateLink
+  (JNIEnv *, jclass, jstring, jstring);
+
+#ifdef __cplusplus
+}
+#endif
+#endif

Added: freeway/etc/support/clib/links.c
===================================================================
--- freeway/etc/support/clib/links.c    2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/etc/support/clib/links.c    2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,55 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "links.h"
+
+#define        DEBUG   0
+#define LOG    if (DEBUG) printf
+#define ERR if (DEBUG) printf
+
+int links_is( const char* fn )
+{
+       struct stat     buf;  
+
+       if (lstat(fn,&buf)!=0) {
+               ERR("Can't stat %s (%s) !\n",fn,strerror(errno));
+               return 0;
+               }
+       return (S_ISLNK(buf.st_mode) ? 1 : 0);
+}
+
+int links_create( const char* fn, const char* targ )
+{
+       if (symlink(targ,fn)!=0) {
+               ERR("Can't create symbolic link %s -> %s (%s) 
!\n",fn,targ,strerror(errno));
+               return 0;
+               }
+       return 1;
+}
+
+char* links_get_target( const char* fn )
+{
+       char*   buf;
+       int             len;
+
+       buf=(char*) malloc(1024);
+       if (buf==NULL) {
+               return NULL;
+               }
+
+       len=readlink(fn,buf,1023);
+       if (len==-1) {
+               ERR("Can't get %s's target (%s) !\n",fn,strerror(errno));
+               return NULL;
+               }
+
+       buf[len]=0;
+       return buf;
+}

Added: freeway/etc/support/clib/links.h
===================================================================
--- freeway/etc/support/clib/links.h    2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/etc/support/clib/links.h    2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,8 @@
+#ifndef _LINKS_H
+#define _LINKS_H
+
+int links_is( const char* fn );
+int links_create( const char* fn, const char* targ );
+char* links_get_target( const char* fn );
+
+#endif

Added: freeway/etc/support/clib/signals.c
===================================================================
--- freeway/etc/support/clib/signals.c  2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/etc/support/clib/signals.c  2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,149 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "signals.h"
+
+static void signals_handler( int num );
+
+#define FIFO_SIZE      16
+
+#define        DEBUG   0
+#define LOG    if (DEBUG) printf
+#define ERR printf
+
+static pthread_mutex_t mutex;
+static pthread_cond_t  cond;
+
+static int*                            fifo;
+static int                             fifo_size;
+static int                             fifo_first;
+static int                             fifo_count;
+
+
+int signals_init()
+{
+       pthread_mutexattr_t     attr;
+
+       LOG("Init signals\n");
+
+       pthread_mutexattr_init(&attr);
+       pthread_mutexattr_settype(&attr,(int) PTHREAD_MUTEX_NORMAL);
+       if (pthread_mutex_init(&mutex,&attr)!=0) {
+               ERR("Could not create mutex !\n");
+               return 0;
+               }
+       if (pthread_cond_init(&cond,NULL)!=0) {
+               ERR("Could not create conditional object !\n");
+               return 0;
+               }
+
+       fifo=(int*) malloc(FIFO_SIZE*sizeof(int));
+       fifo_size=FIFO_SIZE;
+       fifo_first=0;
+       fifo_count=0;
+       return 1;
+}
+
+int signals_done()
+{
+       LOG("Done with signals\n");
+
+       free(fifo);
+       pthread_cond_destroy(&cond);
+       pthread_mutex_destroy(&mutex);
+       return 1;
+}
+
+int signals_catch( int num )
+{
+       struct sigaction        newsig;
+       struct sigaction        oldsig;
+
+       memset(&oldsig,0,sizeof(struct sigaction));
+       memset(&newsig,0,sizeof(struct sigaction));
+       newsig.sa_handler=&signals_handler;
+       newsig.sa_flags=(SA_NODEFER | SA_RESTART);
+       sigemptyset(&newsig.sa_mask);
+
+       if (sigaction(num,&newsig,&oldsig)<0) {
+               ERR("Failed to install handler for signal #%d ! 
(%s)\n",num,strerror(errno));
+               return 0;
+               }
+
+       LOG("Installed handler for signal #%d.\n",num);
+       return 1;
+}
+
+int signals_leave( int num )
+{
+       struct sigaction        newsig;
+       struct sigaction        oldsig;
+
+       memset(&oldsig,0,sizeof(struct sigaction));
+       memset(&newsig,0,sizeof(struct sigaction));
+       newsig.sa_handler=SIG_DFL;
+       newsig.sa_flags=(SA_NODEFER | SA_RESTART);
+       sigemptyset(&newsig.sa_mask);
+
+       if (sigaction(num,&newsig,&oldsig)<0) {
+               ERR("Failed to reset handler for signal #%d ! 
(%s)\n",num,strerror(errno));
+               return 0;
+               }
+
+       LOG("Did reset handler for signal #%d.\n",num);
+       return 1;
+}
+
+int signals_wait()
+{
+       int     num;
+
+       LOG("Enter signals_wait\n");
+
+       pthread_mutex_lock(&mutex);
+       do {
+               pthread_cond_wait(&cond,&mutex);
+
+               if (fifo_count>0) {
+                       num=fifo[fifo_first++];
+                       if (fifo_first==fifo_size) {
+                               fifo_first=0;
+                               }
+                       fifo_count--;
+                       }
+               else {
+                       num=0;
+                       }
+
+               LOG("Awaken for signal #%d.\n",num);
+               }
+       while (num==0);
+       pthread_mutex_unlock(&mutex);
+
+       LOG("Exit signals_wait\n");
+       return num;
+}
+
+static void signals_handler( int num )
+{
+       LOG("Caught signal #%d.\n",num);
+
+       pthread_mutex_lock(&mutex);
+
+       if (fifo_count<fifo_size) {
+               fifo[(fifo_first+fifo_count) % fifo_size]=num;
+               fifo_count++;
+               }
+       else {
+               ERR("Buffer is full, discard signal !\n");
+               }
+
+       pthread_mutex_unlock(&mutex);
+       pthread_cond_signal(&cond);
+}

Added: freeway/etc/support/clib/signals.h
===================================================================
--- freeway/etc/support/clib/signals.h  2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/etc/support/clib/signals.h  2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,10 @@
+#ifndef _SIGNALS_H
+#define _SIGNALS_H
+
+int signals_init();
+int signals_done();
+int signals_catch( int num );
+int signals_leave( int num );
+int signals_wait();
+
+#endif

Added: freeway/lib/batik-all.jar
===================================================================
(Binary files differ)


Property changes on: freeway/lib/batik-all.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: freeway/lib/bcprov-jdk14-124.jar
===================================================================
(Binary files differ)


Property changes on: freeway/lib/bcprov-jdk14-124.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: freeway/lib/concurrent.jar
===================================================================
(Binary files differ)


Property changes on: freeway/lib/concurrent.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: freeway/lib/mysql-connector-java-3.0.10-stable-bin.jar
===================================================================
(Binary files differ)


Property changes on: freeway/lib/mysql-connector-java-3.0.10-stable-bin.jar
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: freeway/lib/readme
===================================================================
--- freeway/lib/readme  2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/lib/readme  2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,15 @@
+This folder contains third-party libraries needed to run freeway. These 
libraries
+are automatically added to classpath by scripts in etc/.
+
+       batik-all.jar .................................. 
http://xml.apache.org/batik/
+       Batik 1.5 full libraries for SVG rendering.
+
+       bcprov-jdk14-122.jar ........................... 
http://www.bouncycastle.org/
+       RIPE160 implementation.
+
+       concurrent.jar ............ 
http://gee.cs.oswego.edu/dl/concurrency-interest/
+       Provides synchronization mechanisms defined in jsr 166.
+
+       mysql-connector-java-3.0.10-stable-bin.jar ............ 
http://www.mysql.com/
+       JDBC driver for mysql database.
+

Added: freeway/readme.html
===================================================================
--- freeway/readme.html 2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/readme.html 2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,19 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" 
"http://www.w3.org/TR/html4/strict.dtd";>
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+<title>readme.html</title>
+<link href="docs/project.css" rel="stylesheet" type="text/css">
+</head>
+<body>
+
+<div>
+This is the readme.html for freeway project.
+</div>
+
+<div>
+See in <a href='docs/index.html'>docs</a> folder for more information. 
Available screenshots are in <a 
href='docs/screenshots/index.html'>docs/screenshots</a>.
+</div>
+
+</body>
+</html>

Added: freeway/res/api.css
===================================================================
--- freeway/res/api.css 2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/res/api.css 2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,62 @@
+/* Javadoc style sheet */
+
+/* Define colors, fonts and other style attributes here to override the 
defaults */
+
+/* Page background color */
+
+body {
+       color:                          #000000;
+       background-color:       #ffffff;
+       font:                           normal normal normal 12px "Trebuchet 
MS";
+       }
+
+a {
+       color:                          #42adff;
+       }
+
+a:visited {
+       color:                          #215788;
+       }
+
+a:hover {
+       color:                          #000000;
+       }
+
+a:active {
+       color:                          #000000;
+       }
+
+td {
+       font-size:      12px;
+       }
+
+tr {
+       font-size:      16px;
+       }
+
+th {
+       font-size:      16px;
+       }
+
+/* Table colors */
+.TableHeadingColor     { background: #cccccc; } /* Dark mauve */
+.TableSubHeadingColor  { background: #aaaaaa; } /* Light mauve */
+.TableRowColor         { background: #FFFFFF; } /* White */
+
+/* Font used in left-hand frame lists */
+.FrameTitleFont   { font: normal normal normal 12px "Trebuchet MS"; }
+.FrameHeadingFont { font: normal normal normal 12px "Trebuchet MS"; }
+.FrameItemFont    { font: normal normal normal 12px "Trebuchet MS"; }
+
+/* Example of smaller, sans-serif font in frames */
+/* .FrameItemFont  { font: normal normal normal 12px "Trebuchet MS"; } */
+
+/* Navigation bar fonts and colors */
+.NavBarCell1    { background-color:#EEEEFF;}/* Light mauve */
+.NavBarCell1Rev { background-color:#00008B;}/* Dark Blue */
+.NavBarFont1    { font-family: Arial, Helvetica, sans-serif; color:#000000;}
+.NavBarFont1Rev { font-family: Arial, Helvetica, sans-serif; color:#FFFFFF;}
+
+.NavBarCell2    { font-family: Arial, Helvetica, sans-serif; 
background-color:#FFFFFF;}
+.NavBarCell3    { font-family: Arial, Helvetica, sans-serif; 
background-color:#FFFFFF;}
+

Added: freeway/res/daemon.logging
===================================================================
--- freeway/res/daemon.logging  2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/res/daemon.logging  2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,9 @@
+# Daemon logging configuration
+
+org.gnu.freeway.server = INFO
+org.gnu.freeway.util.PersistentDecoder = INFO
+org.gnu.freeway.util.PersistentReader = INFO
+org.gnu.freeway.util.PersistentWriter = INFO
+
+org.gnu.freeway.transport = INFO
+

Added: freeway/res/gnunet.client.template
===================================================================
--- freeway/res/gnunet.client.template  2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/res/gnunet.client.template  2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,148 @@
+# This is the USER configuration for your GNUnet node.  Copy this file to
+# "~/.gnunet/gnunet.conf".
+# For any other location, you must tell every GNUnet application 
+# that you start where this file is.  (option -c FILENAME).
+#
+#
+#################################################
+#
+# This line gives the directory where GNUnet is putting
+# user-specific files (such as pseudonyms).  Typically,
+# GNUnet should not put more than a few of MB there.
+#
+# Default is GNUNET_HOME       = ~/.gnunet
+GNUNET_HOME    = "%clients.basedir@/org/gnu/freeway/config%"
+
+
+###############################################
+# General client options...
+###############################################
+[GNUNET]
+# Loglevel. What kinds of (debug) output should be printed? You can 
+# use NOTHING, FATAL, ERROR, FAILURE, WARNING, MESSAGE, INFO, DEBUG, 
+# CRON or EVERYTHING (which print more and more messages in this order). 
+# Default is WARNING which is usually enough to let you know if there
+# is any problems.
+LOGLEVEL        = FINEST
+
+# Where to write the messages? Leave the entry unspecified (as
+# default) to make the clients print their messages to stderr.
+# Default is unspecified (stderr).
+#LOGFILE = $GNUNET_HOME/logs
+
+# Which crypto provider do we use ? (JCE compliant)
+# Default is BouncyCastle
+CRYPTO_PROVIDER        =       
org.bouncycastle.jce.provider.BouncyCastleProvider
+
+
+###############################################
+# MySQL parameters for test app
+###############################################
+
+[TEST]
+
+MYSQL_DRIVER   = com.mysql.jdbc.Driver
+MYSQL_HOST     = "%mysql.host@/org/gnu/freeway/config%"
+MYSQL_USER     = "%mysql.user@/org/gnu/freeway/config%"
+MYSQL_PASSWORD = "%mysql.password@/org/gnu/freeway/config%"
+
+
+###############################################
+# Network options for the clients...
+###############################################
+[NETWORK]
+# Port to use to talk to gnunetd, default is 2087
+PORT = 2087
+
+# On which machine runs gnunetd (for clients) This is equivalent to
+# the -H option.  Default is localhost.
+HOST = localhost
+
+
+
+
+################################################
+# Options for anonymous filesharing (AFS).
+################################################
+[AFS]
+
+# How long should gnunet-search try to get an answer to a query before
+# timing out (in seconds).  Default is "3000", which should be enough
+# for pretty much anything. Use 0 for no timeout.
+SEARCHTIMEOUT   = 3000
+
+# Specify which additional extractor libraries should be used.
+# gnunet-insert uses libextractor to extract keywords from files.
+# libextractor can be dynamically extended to handle additional file
+# formats. If you want to use more than the default set of extractors,
+# specify additional extractor libraries here.  The format is
+# [[-]LIBRARYNAME[:[-]LIBRARYNAME]*] The default is to use filenames
+# and to break larger words at spaces (and underscores, etc.).  This
+# should be just fine for most people. The - before a library name
+# indicates that this should be executed last and makes only sense for
+# the split-library.
+# Default is libextractor_filename:-libextractor_split:-libextractor_lower
+EXTRACTORS = libextractor_filename:-libextractor_split:-libextractor_lower
+
+# Where to download files to (by default)?
+# Default is /tmp/gnunet-downloads/
+DOWNLOADDIR     = /tmp/gnunet-downloads/
+
+# This option allows enabling/disabling of the code that collects
+# file-identifiers for building directories.  Note that you can
+# build directories of files that you insert without collecting
+# file-identifiers by using the option "-b" of gnunet-insert.
+# You can not add entries from namespaces to directories without
+# at least temporarily collecting file-identifiers.
+# Default is NO
+COLLECT-FILE-IDENTIFIERS = YES
+
+
+#################################################
+# Default options for the gnunet-insert tool
+#################################################
+[GNUNET-INSERT]
+
+# What is the initial priority of content that is locally inserted? 
+# Default is 65535 which is "very high".
+CONTENT-PRIORITY = 65535
+
+##########################################
+# Defaults for gnunet-chat
+##########################################
+[GNUNET-CHAT]
+
+# You can specify your nickname here and thus avoid having to pass it
+# with -n NICK at the command-line.  The default is empty.
+#
+# NICK = "my nickname"
+
+##########################################
+# Defaults for gnunet-tracekit
+##########################################
+[GNUNET-TRACEKIT]
+
+# How long to wait for replies (in seconds)? (default: 30)
+WAIT      = 30
+
+# How many hops should the trace go? (default: 5)
+HOPS     = 5
+
+# How important is the trace message? (default: 1000)
+PRIORITY = 1000
+
+# Output format, 0 is human readable, 1 is dot (default: 1)
+FORMAT   = 1
+
+
+[GNUNET-TESTBED]
+
+# Port used internally by gnunet-testbed
+# (for the IPC in the gnunet-testbed shell).
+# The default is 2089.
+PORT = 2089
+
+# At which URL does the registration software
+# run?
+# Default is http://www.ovmj.org/GNUnet/testbed/
+REGISTERURL = "http://www.ovmj.org/GNUnet/testbed/";

Added: freeway/res/gnunet.daemon.template
===================================================================
--- freeway/res/gnunet.daemon.template  2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/res/gnunet.daemon.template  2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,629 @@
+# This is the configuration for the GNUnet daemon, gnunetd.
+# Copy this file to "/etc/gnunet.conf" if you are root. 
+# For any other location, you must explicitly tell gnunetd
+# where this file is (option -c FILENAME).
+#
+# After any change in this file, you may want to manually restart
+# gnunetd since some changes are only recognized after a re-start.
+# Sending a SIGHUP to gnunetd will trigger re-reading the following
+# options:
+# NETWORK: HELOEXCHANGE
+# GNUNETD: LOGLEVEL
+# LOAD: INTERFACES
+# LOAD: BASICLIMITING
+# LOAD: MAXNETDOWNBPSTOTAL
+# LOAD: MAXNETUPBPSTOTAL
+# LOAD: MAXCPULOAD
+#
+#
+# This file is structured as follows.
+# 1) GNUNETD_HOME - base directory for all GNUnet files
+# 2) gnunetd options (which transport and application services, logging)
+# 3) network configuration 
+# 4) load management (resource limitations)
+# 5) UDP, TCP and SMTP transport configuration
+# 6) configuration for anonymous file sharing (AFS)
+#
+#################################################
+#
+# This line gives the root-directory of the GNUnet installation. Make
+# sure there is some space left in that directory. :-)  Users inserting
+# or indexing files will be able to store data in this directory
+# up to the (global) quota specified below.  Having a few gigabytes
+# of free space is recommended.
+# Default: GNUNETD_HOME     = /var/lib/GNUnet
+GNUNETD_HOME     = "%daemon.basedir@/org/gnu/freeway/config%"
+
+#########################################
+# Options for the GNUnet server, gnunetd
+#########################################
+[GNUNETD]
+
+# Which crypto provider do we use ? (JCE compliant)
+# Default is BouncyCastle
+CRYPTO_PROVIDER        =       
org.bouncycastle.jce.provider.BouncyCastleProvider
+
+
+# How many minutes is the current IP valid?  (GNUnet will sign HELO
+# messages with this expiration timeline. If you are on dialup, 60
+# (for 1 hour) is suggested. If you are having a static IP address,
+# you may want to set this to a large value (say 14400).  The default
+# is 1440 (1 day). If your IP changes periodically, you will want to
+# choose the expiration to be smaller than the frequency with which
+# your IP changes.
+# The largest legal value is 14400 (10 days).
+# Default: HELOEXPIRES     = 1440
+HELOEXPIRES     = 1440
+
+# Loglevel, how much should be logged? You can use NOTHING, FATAL,
+# ERROR, FAILURE, WARNING, MESSAGE, INFO, DEBUG, CRON or EVERYTHING
+# (which log more and more messages in this order). Default is
+# WARNING.
+LOGLEVEL        = FINEST
+
+# In which file should daemon write the logs ?
+# If you specify nothing, logs are written to stderr (and note that if gnunetd 
runs
+# in the background, stderr is closed and all logs are discarded).
+# Default: LOGFILE         = $GNUNETD_HOME/logs
+#LOGFILE         = $GNUNETD_HOME/logs
+
+# In which file should gnunetd write the process-id of the server?  If
+# you run gnunetd as root, you may want to choose
+# /var/run/gnunetd.pid. It's not the default since gnunetd may not
+# have write rights at that location.
+# Default: PIDFILE         = $GNUNETD_HOME/gnunetd.pid
+PIDFILE         = $GNUNETD_HOME/gnunetd.pid
+
+# This directory should be made available periodically --- it contains
+# information how to join GNUnet that is in no way private to the
+# local node.  This directory can be shared between nodes AND should
+# be put on a public web-server (if possible).  You should find a list
+# of known hosts under http://www.ovmj.org/GNUnet/hosts/, you can copy
+# those files into this directory.
+# 
+# If you specify a HOSTLISTURL, the directory will be automatically
+# populated by gnunetd with an initial set of nodes.
+# Default: HOSTS       = $GNUNETD_HOME/data/hosts/
+HOSTS          = $GNUNETD_HOME/data/hosts/
+
+# GNUnet can automatically update the hostlist from the web. While
+# GNUnet internally communicates which hosts are online, it is
+# typically a good idea to get a fresh hostlist whenever gnunetd
+# starts from the WEB. By setting this option, you can specify from
+# which server gnunetd should try to download the hostlist. The
+# default should be fine for now.
+#
+# The general format is a list of space-separated URLs.  Each URL must
+# have the format http://HOSTNAME/FILENAME
+#
+# If you want to setup an alternate hostlist server, you must run a
+# permanent node and "cat data/hosts/* > hostlist" every few minutes
+# to keep the list up-to-date.
+#
+# If you do not specify a HOSTLISTURL, you must copy valid hostkeys to
+# data/hosts manually.
+# Default: HOSTLISTURL = "http://www.ovmj.org/GNUnet/download/hostlist 
http://gnunet.cryss.net/hostlist http://www.woodtick.co.uk/hostlist";
+HOSTLISTURL = "http://www.ovmj.org/GNUnet/download/hostlist 
http://gnunet.cryss.net/hostlist http://www.woodtick.co.uk/hostlist";
+
+# If you have to use a proxy for outbound HTTP connections,
+# specify the proxy configuration here.  Default is no proxy.
+# HTTP-PROXY = localhost
+# HTTP-PROXY-PORT = 1080
+
+# Which applications should gnunetd support? Specify the name of the
+# dynamic shared object (DSO) that implements the service in the
+# gnunetd core here. Separate multiple modules with spaces.
+#
+# Currently, the available applications are:
+# afs: anonymous file sharing
+# chat: broadcast chat (demo-application)
+# tbench: benchmark tool for transport performance
+# tracekit: GNUnet topology visualization toolkit
+#
+# All protocols but "afs" are potential security risks
+# and have been engineered for testing GNUnet or demonstrating how
+# GNUnet works. They should be used with caution.
+#
+# Typical choices are: "afs chat tbench tracekit"
+# Default: APPLICATIONS = "afs tbench tracekit"
+APPLICATIONS = "afs tbench chat tracekit"
+
+# Which transport mechanisms are available? Use space-separated list
+# of the modules, e.g.  "udp smtp tcp". The order is irrelevant, each
+# protocol has a build-in cost-factor and this factor determines which
+# protocols are preferred.  
+#
+# The available transports at this point are udp, tcp, http, smtp,
+# tcp6, udp6 and the special 'nat' service.
+#
+# Loading the 'nat' and 'tcp' modules is required for peers behind NAT
+# boxes that can not directly be reached from the outside.  Peers that
+# are NOT behind a NAT box and that want to *allow* peers that ARE
+# behind a NAT box to connect must ALSO load the 'nat' module.  Note
+# that the actual transfer will always be via tcp initiated by the peer
+# behind the NAT box.
+#
+# Usually, the default is just fine for most people.
+# Choices are: "udp tcp udp6 tcp6 nat http smtp"
+# Default: TRANSPORTS = "udp tcp nat"
+TRANSPORTS = "udp tcp"
+
+
+
+
+############################################
+# Network configuration
+############################################
+[NETWORK]
+
+# Which is the client-server port that is used between gnunetd and the
+# clients (TCP only).  You may firewall this port for non-local
+# machines.
+# Default: PORT = 2087
+PORT = 2087
+
+# Set if GNUnet fails to determine your IP.  GNUnet first tries to
+# determine your IP by looking at the IP that matches the interface
+# that is given with the option INTERFACE.
+# Under Windows, specify the index number reported by
+#  "gnunet-win-tool -n"
+# Default: INTERFACE = eth0
+INTERFACE = ppp0
+
+# If this fails, GNUnet will try to do a DNS lookup on your HOSTNAME,
+# which may also fail, in particular if you are on dialup.
+#
+# If both options are not viable for you, you can specify an IP in
+# this configuration file.  This may be required if you have multiple
+# interfaces (currently GNUnet can only work on one of them) or if you
+# are behind a router/gateway that performs network address
+# translation (NAT). In the latter case, set this IP to the *external*
+# IP of the router (!) and make sure that the router forwards incoming
+# UDP packets on the GNUnet port (default: 2086) to the dedicated
+# GNUnet server in the local network.
+#
+# The given example value (127.0.0.1) will NOT work!  If you do not know
+# what all this means, try without!
+# Default is no IP specified.
+# IP   = 127.0.0.1
+
+# If this host is connected to two networks, a private which is not
+# reachable from the Internet and that contains GNUnet clients and to
+# a public network, typically the Internet (and is this host is thus
+# in the position of a router, typically doing NAT), then this option
+# should be set to 'NO'. It prevents the node from forwarding HELOs
+# other than its own. If you do not know what the above is about, just
+# keep it set to YES (which is also the default when the option is not
+# given).
+# Default is yes: HELOEXCHANGE = YES
+HELOEXCHANGE = YES
+
+# With this option, you can specify which networks are trusted enough
+# to connect as clients to the TCP port.  This is useful if you run
+# gnunetd on one host of your network and want to allow all other
+# hosts to use this node as their server. By default, this is set to
+# 'loopback only'. The format is the same as for the BLACKLIST.
+# Default is: TRUSTED = 127.0.0.0/8;
+TRUSTED = 127.0.0.0/8;
+
+
+
+
+
+
+######################################
+# Options for load management 
+######################################
+[LOAD]
+# In this section you specify how many resources GNUnet is allowed to
+# use. GNUnet may exceed the limits by a small margin (network & CPU
+# are hard to control directly), but should do a reasonable job to
+# keep the average around these values
+
+# For which interfaces should we do accounting?  GNUnet will evaluate
+# the total traffic (not only the GNUnet related traffic) and adjust
+# its bandwidth usage accordingly. You can currently only specify a
+# single interface. GNUnet will also use this interface to determine
+# the IP to use. Typical values are eth0, ppp0, eth1, wlan0, etc.
+# 'ifconfig' will tell you what you have.  Never use 'lo', that just
+# won't work.
+# Under Windows, specify the index number reported by
+#  "gnunet-win-tool -n".
+# Default is: INTERFACES      = eth0
+INTERFACES      = ppp0
+
+# Use basic bandwidth limitation? YES or NO.  The basic method (YES)
+# notes only GNUnet traffic and can be used to specify simple maximum
+# bandwidth usage of GNUnet.  Choose the basic method if you don't
+# want other network traffic to interfere with GNUnet's operation, but
+# still wish to constrain GNUnet's bandwidth usage, or if you can't
+# reliably measure the maximum capabilities of your connection.  YES
+# can be very useful if other applications are causing a lot of
+# traffic on your LAN.  In this case, you do not want to limit the
+# traffic that GNUnet can inflict on your WAN connection whenever your
+# high-speed LAN gets used (e.g. by NFS).
+#
+# The advanced bandwidth limitation (NO) measures total traffic over
+# the chosen interface (including traffic by other applications), and
+# allows gnunetd to participate if the total traffic is low enough.
+# Default is: BASICLIMITING = YES
+BASICLIMITING = YES
+
+# Bandwidth limits in bytes per second. These denote the maximum
+# amounts GNUnet is allowed to use.
+# Defaults are: 
+# MAXNETUPBPSTOTAL     = 50000
+# MAXNETDOWNBPSTOTAL   = 50000
+MAXNETUPBPSTOTAL       = 50000
+MAXNETDOWNBPSTOTAL     = 50000
+
+
+# Which CPU load can be tolerated (total, GNUnet will adapt if the
+# load goes up due to other processes). A value of 50 means that once
+# your 1 minute-load average goes over 50% non-idle, GNUnet will start
+# dropping packets until it goes under that threshold again.
+# Default is MAXCPULOAD                = 50
+MAXCPULOAD             = 50
+
+
+
+
+###########################################
+# Options for the UDP transport layer.
+###########################################
+[UDP]
+
+# To which port does GNUnet bind? Default is 2086 and there is usually
+# no reason to change that.
+PORT           = 2086
+
+# With this option, you can specify which networks you do NOT want to
+# connect to. Usually you will want to filter loopback (127.0.0.1,
+# misconfigured GNUnet hosts), virtual private networks, [add a class
+# C network here], 192.168.0.0, 172.16.0.0 and 10.0.0.0 (RFC
+# 1918). The format is IP/NETMASK where the IP is specified in
+# dotted-decimal and the netmask either in CIDR notation (/16) or in
+# dotted decimal (255.255.0.0). Several entries must be separated by a
+# semicolon, spaces are not allowed.  Notice that if your host is on a
+# private network like the above, you will have to configure your NAT
+# to allow incoming requests and you will want to modify this option.
+# The idea behind this option is not to discriminate against NAT users
+# but to ensure that hosts only attempt to connect to machines that
+# they have a chance to actually reach.  Of course, you could also use
+# it against known adversaries that have a small IP range at their
+# disposal :-) 
+#
+# Example (and default):
+# 127.0.0.1/8;172.16.0.0/12;192.168.0.0/16;10.0.0.0/255.0.0.0;
+BLACKLIST = 127.0.0.1/8;172.16.0.0/12;192.168.0.0/16;10.0.0.0/255.0.0.0;
+
+
+# The MTU to use. Do not use more than your OS
+# (and firewall) can support. Typically, your 
+# network-MTU - 28 is optimal, for ethernet, this
+# is 1472, the default. Do not use less than 1200.
+#
+# The default is 1472, which is also used if you specify
+# nothing.
+MTU = 1472
+
+
+###########################################
+# Options for the TCP transport layer.
+###########################################
+[TCP]
+
+# To which port does GNUnet bind? Default is 2086 and there is usually
+# no reason to change that.  Make sure that this port does not
+# conflict with the port for GNUnet clients (section NETWORK), which
+# defaults to 2087.  
+PORT = 2086
+
+# With this option, you can specify which networks you do NOT want to
+# connect to. Usually you will want to filter loopback (127.0.0.1,
+# misconfigured GNUnet hosts), virtual private networks, [add a class
+# C network here], 192.168.0.0, 172.16.0.0 and 10.0.0.0 (RFC
+# 1918). The format is IP/NETMASK where the IP is specified in
+# dotted-decimal and the netmask either in CIDR notation (/16) or in
+# dotted decimal (255.255.0.0). Several entries must be separated by a
+# semicolon, spaces are not allowed.  Notice that if your host is on a
+# private network like the above, you will have to configure your NAT
+# to allow incoming requests and you will want to modify this option.
+# The idea behind this option is not to discriminate against NAT users
+# but to ensure that hosts only attempt to connect to machines that
+# they have a chance to actually reach.  Of course, you could also use
+# it against known adversaries that have a small IP range at their
+# disposal :-)
+# Example (and default):
+# BLACKLIST = 127.0.0.1/8;192.168.0.0/16;10.0.0.0/255.0.0.0; 
+BLACKLIST = 127.0.0.1/8;192.168.0.0/16;10.0.0.0/255.0.0.0;
+
+# The MTU to use (TCP is stream oriented, so we are pretty free to
+# choose what we want, but note that larger MTUs mean more noise if
+# traffic is low). Do not use less than 1200.  Default is 1460.
+MTU = 1460
+
+###############################################
+# Options for NAT transport
+###############################################
+[NAT]
+
+# Is this machine behind a NAT that does not allow
+# connections from the outside to the GNUnet port?
+# (if you can configure the NAT box to allow
+# direct connections from other peers, set this
+# to NO).  Set this only to YES if other peers
+# cannot contact you directly via TCP or UDP.
+# If you set this to YES, you should also set the
+# TCP port to '0' and disable UDP to indicate that you
+# can not accept inbound connections.
+#
+# Default: NO
+LIMITED = NO
+
+
+##########################################
+# IPv6 transports, don't bother unless you
+# want to use IPv6.
+##########################################
+
+[UDP6]
+
+# Default port is 2088 and MTU is 1452.
+PORT = 2088
+# BLACKLIST = 
+MTU = 1452
+
+[TCP6]
+
+# Default port is 2088 and MTU is 1440.
+PORT = 2088
+# BLACKLIST = 
+MTU = 1440
+
+[HTTP]
+
+# Default port is 1080 and MTU is 1400.
+PORT = 1080
+# BLACKLIST =
+MTU = 1400
+
+
+###############################################
+# Options for SMTP transport
+###############################################
+[SMTP]
+
+# E-mail address to use to receive messages.  Do not specify anything
+# if you do not want to allow SMTP as a receiver protocol; you can
+# still *send* email to establish connections in that case.  Example:
+# EMAIL = address@hidden
+# EMAIL =
+
+# MTU for the E-mail. How large should the E-mails be that we send
+# out? Default is 65536 (bytes).
+MTU = 65536
+
+# Port of the SMTP server for outbound mail.  If not specified, the
+# TCP/SMTP entry from /etc/services is consulted.  Default is 25.
+PORT = 25
+
+# Hostname of the SMTP server. Default is "localhost".
+SERVER = localhost
+
+# Hostname of the sender host to use in the HELO message of the SMTP
+# protocol (not to be confused with the HELO in the GNUnet p2p
+# protocol). Pick a hostname that works for your SMTP server. This
+# hostname has nothing to do with the hostname of the SMTP server or
+# your E-mail sender address (though those names should work in most
+# cases). In fact, it often does not even have to exist as a real
+# machine. Example: "myhost.example.com"
+SENDERHOSTNAME = myhost.example.com
+
+# Filter-line to use in the E-mail header. This filter will be
+# included in the GNUnet-generated E-mails and should be used to
+# filter out GNUnet traffic from the rest of your E-mail. Make sure
+# that the filter you choose is highly unlikely to occur in any other
+# message.
+#
+# Examples:
+# FILTER = "X-mailer: myGNUnetmail"
+# FILTER = "Subject: foobar5252"
+FILTER = "X-mailer: 590N"
+
+# Name of the pipe via which procmail sends the filtered E-mails to
+# the node.  Default is /tmp/gnunet.smtp
+PIPE = /tmp/gnunet.smtp
+
+
+
+
+
+################################################
+# Options for anonymous filesharing (AFS).
+################################################
+[AFS]
+
+# How much disk space (MB) is GNUnet allowed to use for anonymous file
+# sharing?  This does not take indexed files into account, only the
+# space directly used by GNUnet is accounted for.  GNUnet will gather
+# content from the network if the current space-consumption is below
+# the number given here (and if content migration is allowed below).
+#
+# IMPORTANT:
+# Note that if you change the quota, you need to run gnunet-convert,
+# otherwise your databases will be inconsistent and gnunetd will
+# refuse to work.  Default is 1024 (1 GB)
+DISKQUOTA      = 1024
+
+# Which database type should be used for content? Valid types are
+# "gdbm", "mysql", "tdb" and "directory". Specified type must have
+# been available at compile time. "directory" is available on all
+# systems but typically uses more space and can also be slower.  mysql
+# will require some additional setup of the database.
+#
+# Note that if you change the databaset type, you need to run
+# gnunet-convert, otherwise your databases will be
+# inconsistent (and gnunetd will refuse to work).  Default is gdbm.
+DATABASETYPE    = "mysql"
+
+MYSQL_DRIVER   = com.mysql.jdbc.Driver
+MYSQL_HOST     = "%mysql.host@/org/gnu/freeway/config%"
+MYSQL_USER     = "%mysql.user@/org/gnu/freeway/config%"
+MYSQL_PASSWORD = "%mysql.password@/org/gnu/freeway/config%"
+
+# What degree of receiver anonymity is required?  If set to 0, GNUnet
+# will try to download the file as fast as possible without any
+# additional slowdown by the anonymity code. Note that you will still
+# have a fair degree of anonymity depending on the current network
+# load and the power of the adversary. The download is still unlikely
+# to be terribly fast since the sender may have requested
+# sender-anonymity and since in addition to that, GNUnet will still do
+# the anonymous routing.
+#
+# This option can be used to limit requests further than that. In
+# particular, you can require GNUnet to receive certain amounts of
+# traffic from other peers before sending your queries. This way, you
+# can gain very high levels of anonymity - at the expense of much more
+# traffic and much higher latency. So set it only if you really
+# believe you need it.
+#
+# The definition of ANONYMITY-RECEIVE is the following: 
+#  If the value v # is < 1000, it means that if GNUnet routes n bytes
+#  of messages from # foreign peers, it may originate n/v bytes of
+#  queries in the same # time-period.  The time-period is twice the
+#  average delay that GNUnet # deferrs forwarded queries.
+# 
+#  If the value v is >= 1000, it means that if GNUnet routes n bytes
+#  of QUERIES from at least (v % 1000) peers, it may originate
+#  n/v/1000 bytes of queries in the same time-period.
+#
+# The default is 0 and this should be fine for most users. Also notice
+# that if you choose values above 1000, you may end up having no
+# throughput at all, especially if many of your fellow GNUnet-peers do
+# the same.
+ANONYMITY-RECEIVE = 0
+
+# You can also request a certain degree of anonymity for the files and
+# blocks that you are sharing. In this case, only a certain faction of
+# the traffic that you are routing will be allowed to be replies that
+# originate from your machine. Again, 0 means unlimited.
+#
+# The semantics of ANONYMITY-SEND are equivalent to the semantics of
+# ANONYMITY-RECEIVE.
+#
+# The default is 0 and this should be fine for most users.
+ANONYMITY-SEND = 0
+
+
+# Should we participate in content migration?  If you say yes here,
+# GNUnet will migrate content to your server, and you will not be able
+# to control what data is stored on your machine.  This option has
+# advantages and disadvantages.
+#
+# If you activate it, you can claim for *all* the non-indexed (-n to
+# gnunet-insert) content that you did not know what it was even if an
+# adversary takes control of your machine.
+#
+# If you do not activate it, it is obvious that you have knowledge of
+# all the content that is hosted on your machine and thus can be
+# considered liable for it.  
+#
+# So if you think that the legal system in your country has gone
+# postal, you may want to set it to "NO" and make sure that the
+# content you put on your machine does not get you into too much
+# trouble if an adversary takes control of your machine.  If you think
+# that you're safe if you host content that you don't know anything
+# about (like an ISP) or that you don't have to fear prosecution
+# no-matter-what, turn it to YES, which will also improve GNUnet's
+# performance and thereby your results.
+#
+# Note that as long as the adversary is not really powerful (e.g. can
+# not take control of your machine), GNUnet's build-in anonymity
+# mechanisms should protect you from being singled out easily.
+# 
+# Currently, activating active migration can cause some problems when
+# the database is getting full (gdbm reorganization can take very,
+# very long and make GNUnet look like it hangs for that time). Thus if
+# you turn it on, you may want to disable it after you hit the
+# quota. A better content management system should solve this problem
+# in the near future... [at the time of GNUnet 0.6.1c, the MySQL 
+# database module already works well even if the db is full.]
+# Default is YES.
+ACTIVEMIGRATION = YES
+
+# Where to store the AFS related data (content, etc)?
+AFSDIR          = $GNUNETD_HOME/data/afs/
+
+# Where to store indexed files (NEW!)
+# Note that you MUST not copy files directly to this
+# directory.  gnunet-insert (or gnunet-gtk) will copy
+# the files that you index to this directory.  With the
+# -l option you instead create a link (if gnunetd and
+# gnunet-insert run on the same machine) instead.
+#
+# The QUOTA option does NOT apply for this directory.
+# To limit how much can be placed in this directory
+# set the option INDEX-QUOTA.  Files that are merely
+# linked do not count towards the quota.
+#
+# If you uncomment this option gnunetd will refuse
+# content indexing requests (insertion will still be
+# possible).
+#
+# Note that files indexed with GNUnet before Version
+# 0.6.2 were not moved/linked to this directory.  But that
+# should not cause any immediate problems (the files
+# will continue to be downloadable).  What will be
+# impossible is unindexing these files with
+# gnunet-delete and GNUnet >= 0.6.2. 
+# Default is $GNUNETD_HOME/data/shared/
+INDEX-DIRECTORY = $GNUNETD_HOME/data/shared/
+
+# Indexing quota.  Default is 8192.
+INDEX-QUOTA = 8192
+
+
+#######################################
+# Experimental GDBM options
+#######################################
+
+[GDBM]
+
+# Use experimental settings for managing
+# free blocks in gdbm.  Default is YES!
+EXPERIMENTAL = YES
+
+# This option allows avoiding gdbm database reorganization
+# on startup.  It should definitely only be used together
+# with the experimental gdbm free blocks option.  Nevertheless,
+# the option has not been tested extensively yet, so to be
+# safe it should be set to 'YES' (do reorganize).  Default
+# is 'YES'.
+REORGANIZE = YES
+
+
+#######################################
+# TESTBED (experimental!)
+#######################################
+
+[TESTBED]
+
+# Where should we register the testbed service?
+# Default is "http://www.ovmj.org/GNUnet/testbed/";
+REGISTERURL = "http://www.ovmj.org/GNUnet/testbed/";
+
+# Is the testbed operator allowed to load and
+# unload modules? (somewhat of a security risk!)
+# Default is NO.
+ALLOW_MODULE_LOADING = NO
+
+# Where should file-uploads go?
+# Default is $GNUNETD_HOME/testbed
+UPLOAD-DIR = $GNUNETD_HOME/testbed
+
+# Login-name for SSH-tunnel (for secure testbed
+# connections).  Without login name the testbed-server
+# will try to make a direct TCP connection to the
+# application port (default: 2087).
+# LOGIN = 

Added: freeway/res/jnlp/batik.jnlp
===================================================================
--- freeway/res/jnlp/batik.jnlp 2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/res/jnlp/batik.jnlp 2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<jnlp spec="1.0+" codebase="@REMOTE_URL@" href="batik.jnlp">
+       <information>
+               <title>Batik</title>
+               <vendor>Batik</vendor>
+       </information>
+
+       <resources>
+               <jar href="batik-all.jar" />
+       </resources>
+
+       <security>
+               <all-permissions />
+       </security>
+
+       <component-desc />
+</jnlp> 

Added: freeway/res/jnlp/bouncycastle-jce.jnlp
===================================================================
--- freeway/res/jnlp/bouncycastle-jce.jnlp      2005-01-31 23:47:23 UTC (rev 
136)
+++ freeway/res/jnlp/bouncycastle-jce.jnlp      2005-02-01 01:07:27 UTC (rev 
137)
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<jnlp spec="1.0+" codebase="@REMOTE_URL@" href="bouncycastle-jce.jnlp">
+       <information>
+               <title>Bouncy Castle JCE</title>
+               <vendor>The Legion of the Bouncy Castle</vendor>
+       </information>
+
+       <resources>
+               <jar href="bcprov-jdk14-122.jar" />
+       </resources>
+
+       <security>
+               <all-permissions />
+       </security>
+
+       <component-desc />
+</jnlp> 

Added: freeway/res/jnlp/concurrent.jnlp
===================================================================
--- freeway/res/jnlp/concurrent.jnlp    2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/res/jnlp/concurrent.jnlp    2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<jnlp spec="1.0+" codebase="@REMOTE_URL@" href="concurrent.jnlp">
+       <information>
+               <title>Concurrent Library</title>
+               <vendor>Concurrent Library</vendor>
+       </information>
+
+       <resources>
+               <jar href="concurrent.jar" />
+       </resources>
+
+       <security>
+               <all-permissions />
+       </security>
+
+       <component-desc />
+</jnlp> 

Added: freeway/res/jnlp/freeway.jnlp
===================================================================
--- freeway/res/jnlp/freeway.jnlp       2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/res/jnlp/freeway.jnlp       2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!-- JNLP File for Freeway/GNUnet Standalone Application -->
+
+<jnlp spec="1.0+" codebase="@REMOTE_URL@" href="freeway.jnlp">
+       <information>
+               <title>Freeway/GNUnet Standalone Application</title>
+               <vendor>GNU 2004</vendor>
+               <homepage href="http://www.ovmj.org/GNUnet/freeway/"; />
+               <description>Freeway/GNUnet Standalone Application</description>
+               <icon href="logo64.jpg" />
+
+               <offline-allowed />
+       </information>
+
+       <security>
+               <all-permissions />
+       </security>
+
+       <resources>
+               <j2se version="1.4+" />
+               <jar href="freeway-for-apps.jar" />
+               <extension href="batik.jnlp" />
+               <extension href="bouncycastle-jce.jnlp" />
+               <extension href="concurrent.jnlp" />
+               <extension href="mysql.jnlp" />
+       </resources>
+
+       <application-desc main-class="org.gnu.freeway.GNUNetSwing">
+               <argument>-L</argument>
+               <argument>INFO</argument>
+               <argument>--nosplash</argument>
+               <argument>address@hidden@</argument>
+       </application-desc>
+</jnlp> 

Added: freeway/res/jnlp/logo64.jpg
===================================================================
(Binary files differ)


Property changes on: freeway/res/jnlp/logo64.jpg
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: freeway/res/jnlp/mysql.jnlp
===================================================================
--- freeway/res/jnlp/mysql.jnlp 2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/res/jnlp/mysql.jnlp 2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<jnlp spec="1.0+" codebase="@REMOTE_URL@" href="mysql.jnlp">
+       <information>
+               <title>MySQL JDBC Driver</title>
+               <vendor>MySQL</vendor>
+       </information>
+
+       <resources>
+               <jar href="mysql-connector-java-3.0.10-stable-bin.jar" />
+       </resources>
+
+       <security>
+               <all-permissions />
+       </security>
+
+       <component-desc />
+</jnlp> 

Added: freeway/res/quiet.logging
===================================================================
--- freeway/res/quiet.logging   2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/res/quiet.logging   2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,8 @@
+# Disable full logging for socket specific classes
+
+org.gnu.freeway.DaemonSocket = WARNING
+org.gnu.freeway.util.PersistentChannel = WARNING
+org.gnu.freeway.util.PersistentDecoder = WARNING
+org.gnu.freeway.util.PersistentReader = WARNING
+org.gnu.freeway.util.PersistentWriter = WARNING
+

Added: freeway/res/swing/daemon-window.xml
===================================================================
--- freeway/res/swing/daemon-window.xml 2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/res/swing/daemon-window.xml 2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
+
+<ressources>
+       <images locale="enUS">
+               <image id="start.icon" src="images/24x24/start.png" />
+               <image id="stop.icon" src="images/24x24/stop.png" />
+       </images>
+
+       <actions locale="enUS">
+               <action id="start.action" method="onLaunchDaemon">
+                       <label>Start</label>
+                       <description>Start daemon</description>
+                       <accelerator>S</accelerator>
+                       <shortcut>meta S</shortcut>
+                       <icon refid="start.icon" />
+               </action>
+
+               <action id="stop.action" method="onKillDaemon">
+                       <label>Stop</label>
+                       <description>Stop daemon</description>
+                       <icon refid="stop.icon" />
+               </action>
+
+               <action id="close.action" method="onClose">
+                       <label>Close</label>
+                       <shortcut>meta W</shortcut>
+               </action>
+       </actions>
+
+       <menus locale="enUS">
+               <menu id="main.menu">
+                       <menu>
+                               <label>Daemon</label>
+                               <accelerator>D</accelerator>
+
+                               <action refid="start.action" />
+                               <action refid="stop.action" />
+                               <separator />
+                               <action refid="close.action" />
+                       </menu>
+               </menu>
+       </menus>
+</ressources>

Added: freeway/res/swing/download.xml
===================================================================
--- freeway/res/swing/download.xml      2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/res/swing/download.xml      2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
+
+<ressources>
+       <actions locale="enUS">
+               <action id="abort.id" method="onAbort">
+                       <label>Abort selection</label>
+               </action>
+
+               <action id="close.id" method="onClose">
+                       <label>Close</label>
+               </action>
+       </actions>
+</ressources>

Added: freeway/res/swing/gnunet-config.xml
===================================================================
--- freeway/res/swing/gnunet-config.xml 2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/res/swing/gnunet-config.xml 2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
+
+<ressources>
+       <images locale="enUS">
+               <image id="application.icon" 
src="images/128x128/logo.png?bg=ffffff" />
+               <image id="application.medium.icon" 
src="images/64x64/logo.png?bg=ffffff" />
+               <image id="dot.icon" src="images/8x8/logo.png?bg=ffffff" />
+       </images>
+
+       <actions locale="enUS">
+               <action id="quit.action" method="onQuit">
+                       <label>Quit</label>
+               </action>
+       </actions>
+</ressources>

Added: freeway/res/swing/images/logo.svg
===================================================================
--- freeway/res/swing/images/logo.svg   2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/res/swing/images/logo.svg   2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN"
+  "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd";>
+
+<svg width="372" height="372" viewBox="0 0 372 372"
+       xmlns="http://www.w3.org/2000/svg";
+       xmlns:xlink="http://www.w3.org/1999/xlink";>
+
+       <defs>
+               <path id="gnu" 
d="M112.993,304.372c-3.634,0.247-8.365,1.875-11.539,7.564
+                       
c-2.591,4.646-8.661,5.387-11.495,4.451c-4.721-1.56-10.755-2.642-16.6,1.523c-5.845,4.164-12.157,1.015-11.05-6.576
+                       
c1.107-7.59,3.849-14.351,11.127-18.538c15.895-9.144,4.848-15.508,13.572-24.685c8.741-9.195,6.81-12.009,6.52-24.497
+                       
c-29.543-4.438-46.287-12.953-64.984-36.313c-18.697-23.361-25.945-42.59-27.883-71.878c-1.938-29.288-0.258-48.269,12.396-72.58
+                       
s25.977-36.014,49.065-48.153C85.21,2.553,107.832-0.354,128.742,0c20.91,0.353,34.843,1.885,46.658,8.626
+                       
c11.814,6.741,22.622,15.224,29.512,15.285c6.891,0.061,11.582-1.744,14.931-4.31c0.894,22.703-19.539,24.675-37.819,25.155
+                       
c-18.281,0.479-29.514-7.083-46.351-4.27S91.269,50.961,77.84,74.336s-13.887,31.638-12.771,51.24
+                       
c1.116,19.603,17.988,34.228,27.512,34.387c9.523,0.159,11.649-2.599,19.985-8.036c8.337-5.438,50.896-58.122,65.418-69.956
+                       
c14.523-11.834,28.854-15.488,44.328-15.678c15.474-0.19,19.428,0.335,31.908,5.45c12.48,5.114,20.149,14.533,30.211,20.291
+                       
c5.143-3.027,8.338-7.547,13.057-10.996c16.877-12.335,25.646-15.659,45.705-13.707c11.529,1.122,26.066,7.708,34.74,15.25
+                       
c15.445,13.429,28.205,32.125,38.516,46.328c8.029,11.061,17.477,24.295,30.965,28.432c14.727,4.515,25.809-14.559,27.436-25.139
+                       
c0.922-5.995,2.125-11.249,2.135-17.847c0.01-6.599-0.664-14.607-2.566-21.484s-5.16-13.72-8.688-19.394
+                       
c-5.742-9.235-15.137-18.899-24.912-23.871c-5.486-2.79-18.393-7.863-26.529-8.041c-9.969-0.216-17.564,3.133-26.781,3.761
+                       
c-11.156,0.761-31.084,3.269-39.83-5.35c-5.566-5.485-6.266-9.144-7.217-16.296c2.459,2.026,5.898,3.358,8.959,3.617
+                       
c6.986,0.59,13.527-5.114,19.166-8.419c4.645-2.723,11.223-7.589,17.324-10.048c15.676-6.32,34.932-6.138,51.48-4.445
+                       
c13.264,1.356,30.461,7.257,42.094,13.84c11.688,6.612,22.951,17.761,31.201,28.271c10.379,13.224,16.053,24.57,19.812,41.063
+                       
c4.805,21.062,6.137,52.702,0.328,73.469c-4.578,16.365-19.779,41.504-32.719,53.827c-12.762,12.155-27.498,18.022-44.256,21.231
+                       
c0.656,3.382,0.764,6.536,1.631,9.309c1.232,3.936,3.588,8.324,8.428,6.936c1.93-0.553,2.963-1.032,5.508,1.194
+                       
c2.543,2.226,1.219,10.825-5.938,14.684c-3.629,1.957-7.459,2.382-13.271,2.498s-14.883,0.141-21.193-0.964
+                       
c-6.309-1.104-10.078-6.111-17.066-6.895c-10.799-1.208-19.068-9.01-27.727-14.59c-3.613-2.329-4.832-2.353-6.914-3.53
+                       
c1.125,3.444,2.625,7.021,3.373,10.332c0.75,3.311,0.43,6.378,0.828,9.028c0.396,2.648,1.131,4.707,1.559,6.855
+                       
c1.73,1.547,2.283,3.9,5.189,4.64c2.908,0.739,7.461-0.412,10.436-0.812s4.969-2.021,7.303-1.577
+                       
c2.336,0.443,4.549,2.115,6.506,3.682s3.639,1.796,5.223,5.706s3.037,11.917,3.254,17.269s-0.383,10.183-1.969,14.43
+                       
s-2.98,6.13-7.275,10.336c-4.295,4.207-11.289,10.38-18.268,14.68c1.285,1.903,1.855,3.645,3.859,5.71
+                       
c2.004,2.066,6.182,4.381,8.041,6.56c1.857,2.179,2.799,3.742,2.826,6.186c0.029,2.443-0.09,5.619-2.074,8.283
+                       
c-4.941,6.633-10.785,7.821-17.891,10.941c1.195,7.462,2.17,12.793,0.443,20.244c-0.705,3.036-2.271,9.92-6.016,11.686
+                       
c-3.746,1.766-10.969,0.911-15.541,1.637c-4.572,0.727-7.131,1.417-10.695,2.125c-2.166,3.131-5.391,6.586-6.496,9.392
+                       
c-1.107,2.807-0.773,3.813,0.156,6.679s4.092,7.039,4.988,10.395s1.07,6.969,0.234,10.334c-0.838,3.365-2.271,5.807-5.139,8.795
+                       
c-2.867,2.989-8.508,5.336-11.318,7.758c-2.812,2.423-5.611,2.714-4.844,6.723c1.055,5.506,9.1,14.436,14.92,16.562
+                       
c3.488,1.275,8.17,0.873,12.406-0.915c-0.287,4.703-4.357,7.873-7.016,10.996c7.201-2.059,14.184-6.001,19.014-11.226
+                       
c-1.498,4.538-6.652,11.823-14.361,18.22c-7.709,6.397-7.662,5.088-11.494,7.633c6.992,1.405,13.494,1.025,18.91,0.052
+                       
c-2.969,1.637-7.836,3.939-14.283,4.616c-6.447,0.676-10.422,0.198-16.232-1.213c-5.811-1.412-11.115-6.694-18.156-10.335
+                       
c0.686,2.612,1.174,5.753,4.135,8.439c2.961,2.686,7.441,4.052,12.203,6.375c-6.062-0.688-12.432-0.896-18.389-2.209
+                       
c-4.41-0.973-7.373-2.005-10.6-3.603c-3.225-1.598-5.715-3.929-8.572-5.893c1.369,2.615,1.775,5.691,4.105,7.847
+                       
s6.117,2.959,9.18,4.44c-3.195-0.07-6.268,0.623-9.58-0.21c-3.314-0.833-6.422-2.142-9.867-4.68s-6.998-5.663-9.938-8.858
+                       
c-2.938-3.195-6.566-7.337-7.664-10.283c-1.1-2.947,0.66-5.008,0.99-7.509c-2.029,1.333-4.525,2.067-6.084,4.002
+                       
s-2.941,3.702-2.251,7.075c0.691,3.373,3.804,7.496,6.005,11.248c-6.568-5.371-10.236-7.439-11.799-13.918
+                       
c-1.463-6.066,2.322-11.572,5.199-16.159c1.429-2.278,3.567-4.398,4.903-6.597c-2.967-2.903-6.358-5.398-8.901-8.709
+                       
c-2.542-3.31-3.61-7.79-6.205-10.957c-2.596-3.168-6.088-5.173-9.129-7.756c2.673,5.742,7.066,12.785,8.02,17.229
+                       
c0.953,4.444-1.069,7.164-2.646,9.563c-1.578,2.399-4.124,2.934-6.539,4.408c-2.415,1.475-6.171,0.312-7.61,2.501
+                       
c-1.438,2.188-1.824,5.48-0.449,7.648l5.64,8.902c-2.078-1.049-5.658-4.1-7.272-6.264s-4.09-4.163-4.429-7.089
+                       
c-0.339-2.925-0.504-5.464,1.391-8.07c1.896-2.605,7.086-3.477,9.409-5.48c2.323-2.003,3.626-3.307,3.493-5.896
+                       
c-0.132-2.589-2.661-4.441-4.835-6.073c-6.637-4.978-15.151-9.565-20.787-15.565c-3.184-3.39-4.712-6.127-5.352-8.466
+                       
c-0.639-2.338,0.037-4.162,1.619-7.038c1.582-2.877,7.007-6.718,7.667-9.843c0.659-3.124-2.081-3.795-4.554-4.897
+                       
s-7.17,0.565-9.464-1.352c-2.293-1.917,0.674-5.945-1.911-8.154c-2.584-2.209-8.499-0.976-11.552-3.352
+                       
c-3.053-2.377-4.315-5.327-5.367-8.775s-1.171-7.225-0.458-9.722s2.855-2.934,4.147-4.228s2.413-2.367,0.691-4.297
+                       
c-1.722-1.929-7.365,0.108-10.609-2.369c-3.244-2.477-1.258-8.41-3.552-10.911c-2.293-2.501-6.409-1.522-8.581-2.321
+                       
c-2.171-0.799-3.023,2.02-4.355-2.438c-1.332-4.459-1.368-17.75-3.16-22.71s-5.311-0.335-6.945-5.265
+                       
c-1.634-4.929-0.085-12.812-0.821-20.257l-3.335-20.257c-11.634,7.815-19.314,10.269-31.813,12.477
+                       
c-1.043,30.582-3.916,48.591,6.464,82.52c10.38,33.928,31.387,53.924,59.726,82.649c-37.395-28.016-55.248-50.792-66.422-79.97
+                       C109.723,356.876,111.869,332.472,112.993,304.372z" />
+
+               <linearGradient id="grad1">
+                       <stop offset="0" 
style="stop-color:#ff6f00;stop-opacity:0.9059;" id="stop650"/>
+                       <stop offset="1.000000" 
style="stop-color:#ffff00;stop-opacity:0.6235;" id="stop649"/>
+               </linearGradient>
+
+               <linearGradient id="grad2" xlink:href="#grad1" x1="0.519995" 
y1="6.250001e-2" x2="0.520000" y2="0.945313"/>
+
+               <path id="arc" d="M0,0 a150 150 0 0 0 300 0" />
+       </defs>
+
+       <g>
+<!--           <rect x="0" y="0" width="372" height="372" fill="yellow" 
stroke="#000000" stroke-width="1" />-->
+
+               <g transform="translate(-28,-20)">
+               <g transform="translate(43,35) scale(1.7,1.7)">
+                       <circle cx="100" cy="100" r="100" fill="url(#grad2)" 
stroke="#510000" stroke-width="10" />
+                       <circle cx="100" cy="100" r="105" fill="none" 
stroke="#000000" stroke-width="1.5"  />
+                       <use xlink:href="#gnu" style="stroke:none; fill:black;" 
transform="translate(31,30) scale(0.25,0.25) " />
+               </g>
+
+               <g transform="translate(63,207)">
+<!--                   <use xlink:href="#arc" style="stroke:#ff0000; fill: 
none; stroke-width:1;" />-->
+       
+                       <text style="stroke-width:1.5; stroke:#000000; 
font-family:'Verdana'; font-size:60; font-weight:900; fill:#ff6f00; 
text-anchor:middle"
+                               letter-spacing="13">
+                               <textPath xlink:href="#arc" 
startOffset="50%">Freeway</textPath>
+                       </text>
+               </g>
+               </g>
+       </g>
+</svg>

Added: freeway/res/swing/images/splash.svg
===================================================================
--- freeway/res/swing/images/splash.svg 2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/res/swing/images/splash.svg 2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,98 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN"
+  "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd";>
+
+<svg width="517" height="510" viewBox="0 0 517 510"
+       xmlns="http://www.w3.org/2000/svg";
+       xmlns:xlink="http://www.w3.org/1999/xlink";>
+
+       <defs>
+               <path id="gnu" 
d="M112.993,304.372c-3.634,0.247-8.365,1.875-11.539,7.564
+                       
c-2.591,4.646-8.661,5.387-11.495,4.451c-4.721-1.56-10.755-2.642-16.6,1.523c-5.845,4.164-12.157,1.015-11.05-6.576
+                       
c1.107-7.59,3.849-14.351,11.127-18.538c15.895-9.144,4.848-15.508,13.572-24.685c8.741-9.195,6.81-12.009,6.52-24.497
+                       
c-29.543-4.438-46.287-12.953-64.984-36.313c-18.697-23.361-25.945-42.59-27.883-71.878c-1.938-29.288-0.258-48.269,12.396-72.58
+                       
s25.977-36.014,49.065-48.153C85.21,2.553,107.832-0.354,128.742,0c20.91,0.353,34.843,1.885,46.658,8.626
+                       
c11.814,6.741,22.622,15.224,29.512,15.285c6.891,0.061,11.582-1.744,14.931-4.31c0.894,22.703-19.539,24.675-37.819,25.155
+                       
c-18.281,0.479-29.514-7.083-46.351-4.27S91.269,50.961,77.84,74.336s-13.887,31.638-12.771,51.24
+                       
c1.116,19.603,17.988,34.228,27.512,34.387c9.523,0.159,11.649-2.599,19.985-8.036c8.337-5.438,50.896-58.122,65.418-69.956
+                       
c14.523-11.834,28.854-15.488,44.328-15.678c15.474-0.19,19.428,0.335,31.908,5.45c12.48,5.114,20.149,14.533,30.211,20.291
+                       
c5.143-3.027,8.338-7.547,13.057-10.996c16.877-12.335,25.646-15.659,45.705-13.707c11.529,1.122,26.066,7.708,34.74,15.25
+                       
c15.445,13.429,28.205,32.125,38.516,46.328c8.029,11.061,17.477,24.295,30.965,28.432c14.727,4.515,25.809-14.559,27.436-25.139
+                       
c0.922-5.995,2.125-11.249,2.135-17.847c0.01-6.599-0.664-14.607-2.566-21.484s-5.16-13.72-8.688-19.394
+                       
c-5.742-9.235-15.137-18.899-24.912-23.871c-5.486-2.79-18.393-7.863-26.529-8.041c-9.969-0.216-17.564,3.133-26.781,3.761
+                       
c-11.156,0.761-31.084,3.269-39.83-5.35c-5.566-5.485-6.266-9.144-7.217-16.296c2.459,2.026,5.898,3.358,8.959,3.617
+                       
c6.986,0.59,13.527-5.114,19.166-8.419c4.645-2.723,11.223-7.589,17.324-10.048c15.676-6.32,34.932-6.138,51.48-4.445
+                       
c13.264,1.356,30.461,7.257,42.094,13.84c11.688,6.612,22.951,17.761,31.201,28.271c10.379,13.224,16.053,24.57,19.812,41.063
+                       
c4.805,21.062,6.137,52.702,0.328,73.469c-4.578,16.365-19.779,41.504-32.719,53.827c-12.762,12.155-27.498,18.022-44.256,21.231
+                       
c0.656,3.382,0.764,6.536,1.631,9.309c1.232,3.936,3.588,8.324,8.428,6.936c1.93-0.553,2.963-1.032,5.508,1.194
+                       
c2.543,2.226,1.219,10.825-5.938,14.684c-3.629,1.957-7.459,2.382-13.271,2.498s-14.883,0.141-21.193-0.964
+                       
c-6.309-1.104-10.078-6.111-17.066-6.895c-10.799-1.208-19.068-9.01-27.727-14.59c-3.613-2.329-4.832-2.353-6.914-3.53
+                       
c1.125,3.444,2.625,7.021,3.373,10.332c0.75,3.311,0.43,6.378,0.828,9.028c0.396,2.648,1.131,4.707,1.559,6.855
+                       
c1.73,1.547,2.283,3.9,5.189,4.64c2.908,0.739,7.461-0.412,10.436-0.812s4.969-2.021,7.303-1.577
+                       
c2.336,0.443,4.549,2.115,6.506,3.682s3.639,1.796,5.223,5.706s3.037,11.917,3.254,17.269s-0.383,10.183-1.969,14.43
+                       
s-2.98,6.13-7.275,10.336c-4.295,4.207-11.289,10.38-18.268,14.68c1.285,1.903,1.855,3.645,3.859,5.71
+                       
c2.004,2.066,6.182,4.381,8.041,6.56c1.857,2.179,2.799,3.742,2.826,6.186c0.029,2.443-0.09,5.619-2.074,8.283
+                       
c-4.941,6.633-10.785,7.821-17.891,10.941c1.195,7.462,2.17,12.793,0.443,20.244c-0.705,3.036-2.271,9.92-6.016,11.686
+                       
c-3.746,1.766-10.969,0.911-15.541,1.637c-4.572,0.727-7.131,1.417-10.695,2.125c-2.166,3.131-5.391,6.586-6.496,9.392
+                       
c-1.107,2.807-0.773,3.813,0.156,6.679s4.092,7.039,4.988,10.395s1.07,6.969,0.234,10.334c-0.838,3.365-2.271,5.807-5.139,8.795
+                       
c-2.867,2.989-8.508,5.336-11.318,7.758c-2.812,2.423-5.611,2.714-4.844,6.723c1.055,5.506,9.1,14.436,14.92,16.562
+                       
c3.488,1.275,8.17,0.873,12.406-0.915c-0.287,4.703-4.357,7.873-7.016,10.996c7.201-2.059,14.184-6.001,19.014-11.226
+                       
c-1.498,4.538-6.652,11.823-14.361,18.22c-7.709,6.397-7.662,5.088-11.494,7.633c6.992,1.405,13.494,1.025,18.91,0.052
+                       
c-2.969,1.637-7.836,3.939-14.283,4.616c-6.447,0.676-10.422,0.198-16.232-1.213c-5.811-1.412-11.115-6.694-18.156-10.335
+                       
c0.686,2.612,1.174,5.753,4.135,8.439c2.961,2.686,7.441,4.052,12.203,6.375c-6.062-0.688-12.432-0.896-18.389-2.209
+                       
c-4.41-0.973-7.373-2.005-10.6-3.603c-3.225-1.598-5.715-3.929-8.572-5.893c1.369,2.615,1.775,5.691,4.105,7.847
+                       
s6.117,2.959,9.18,4.44c-3.195-0.07-6.268,0.623-9.58-0.21c-3.314-0.833-6.422-2.142-9.867-4.68s-6.998-5.663-9.938-8.858
+                       
c-2.938-3.195-6.566-7.337-7.664-10.283c-1.1-2.947,0.66-5.008,0.99-7.509c-2.029,1.333-4.525,2.067-6.084,4.002
+                       
s-2.941,3.702-2.251,7.075c0.691,3.373,3.804,7.496,6.005,11.248c-6.568-5.371-10.236-7.439-11.799-13.918
+                       
c-1.463-6.066,2.322-11.572,5.199-16.159c1.429-2.278,3.567-4.398,4.903-6.597c-2.967-2.903-6.358-5.398-8.901-8.709
+                       
c-2.542-3.31-3.61-7.79-6.205-10.957c-2.596-3.168-6.088-5.173-9.129-7.756c2.673,5.742,7.066,12.785,8.02,17.229
+                       
c0.953,4.444-1.069,7.164-2.646,9.563c-1.578,2.399-4.124,2.934-6.539,4.408c-2.415,1.475-6.171,0.312-7.61,2.501
+                       
c-1.438,2.188-1.824,5.48-0.449,7.648l5.64,8.902c-2.078-1.049-5.658-4.1-7.272-6.264s-4.09-4.163-4.429-7.089
+                       
c-0.339-2.925-0.504-5.464,1.391-8.07c1.896-2.605,7.086-3.477,9.409-5.48c2.323-2.003,3.626-3.307,3.493-5.896
+                       
c-0.132-2.589-2.661-4.441-4.835-6.073c-6.637-4.978-15.151-9.565-20.787-15.565c-3.184-3.39-4.712-6.127-5.352-8.466
+                       
c-0.639-2.338,0.037-4.162,1.619-7.038c1.582-2.877,7.007-6.718,7.667-9.843c0.659-3.124-2.081-3.795-4.554-4.897
+                       
s-7.17,0.565-9.464-1.352c-2.293-1.917,0.674-5.945-1.911-8.154c-2.584-2.209-8.499-0.976-11.552-3.352
+                       
c-3.053-2.377-4.315-5.327-5.367-8.775s-1.171-7.225-0.458-9.722s2.855-2.934,4.147-4.228s2.413-2.367,0.691-4.297
+                       
c-1.722-1.929-7.365,0.108-10.609-2.369c-3.244-2.477-1.258-8.41-3.552-10.911c-2.293-2.501-6.409-1.522-8.581-2.321
+                       
c-2.171-0.799-3.023,2.02-4.355-2.438c-1.332-4.459-1.368-17.75-3.16-22.71s-5.311-0.335-6.945-5.265
+                       
c-1.634-4.929-0.085-12.812-0.821-20.257l-3.335-20.257c-11.634,7.815-19.314,10.269-31.813,12.477
+                       
c-1.043,30.582-3.916,48.591,6.464,82.52c10.38,33.928,31.387,53.924,59.726,82.649c-37.395-28.016-55.248-50.792-66.422-79.97
+                       C109.723,356.876,111.869,332.472,112.993,304.372z" />
+
+               <linearGradient id="grad1">
+                       <stop offset="0" 
style="stop-color:#ff6f00;stop-opacity:0.9059;" id="stop650"/>
+                       <stop offset="1.000000" 
style="stop-color:#ffff00;stop-opacity:0.6235;" id="stop649"/>
+               </linearGradient>
+
+               <linearGradient id="grad2" xlink:href="#grad1" x1="0.519995" 
y1="6.250001e-2" x2="0.520000" y2="0.945313"/>
+
+               <path id="arc" d="M0,0 a150 150 0 0 0 300 0" />
+       </defs>
+
+       <rect x="0" y="0" width="517" height="510" fill="#dddddd" 
stroke="#000000" stroke-width="5"/>
+
+       <g>
+               <g transform="translate(45,-10)">
+                       <g transform="translate(43,35) scale(1.7,1.7)">
+                               <circle cx="100" cy="100" r="100" 
fill="url(#grad2)" stroke="#510000" stroke-width="10" />
+                               <circle cx="100" cy="100" r="105" fill="none" 
stroke="#000000" stroke-width="1.5"  />
+                               <use xlink:href="#gnu" style="stroke:none; 
fill:black;" transform="translate(31,30) scale(0.25,0.25) " />
+                       </g>
+       
+                       <g transform="translate(63,207)">
+                               <text style="stroke-width:1.5; stroke:#000000; 
font-family:'Verdana'; font-size:60; font-weight:900; fill:#ff6f00; 
text-anchor:middle"
+                                       letter-spacing="13">
+                                       <textPath xlink:href="#arc" 
startOffset="50%">Freeway</textPath>
+                               </text>
+                       </g>
+               </g>
+       </g>
+
+       <text x="65" y="423" style="font-family: Arial; font-size: 36; 
font-style: italic; font-weight: bold;">A Java port of GNUnet</text>
+       <text x="70" y="463" style="font-family: Arial; font-size: 26; 
font-style: italic;">Freeway v0.6 - GNUnet v0.6.1b</text>
+
+       <rect x="0" y="500" width="517" height="10" fill="#42adff" 
stroke="#000000" stroke-width="5"/>
+       <rect x="0" y="490" width="517" height="10" fill="#215788" 
stroke="#000000" stroke-width="5"/>
+</svg>
+

Added: freeway/res/swing/images/start.svg
===================================================================
--- freeway/res/swing/images/start.svg  2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/res/swing/images/start.svg  2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" 
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd";>
+
+<svg width="512" height="512" viewBox="-65 -65 130 130"
+       xmlns="http://www.w3.org/2000/svg";
+       xmlns:xlink="http://www.w3.org/1999/xlink";>
+
+       <defs>
+               <polygon id="triangle" points="-28,-50 52,0 -28,50" 
stroke-width="10" stroke-linejoin="round" />
+       </defs>
+
+       <g>
+               <use xlink:href="#triangle"
+                       fill="#000000"
+                       stroke="#000000"
+                       />
+
+               <use xlink:href="#triangle"
+                       fill="#42adff"
+                       stroke="#215788"
+                       transform="scale(0.7 0.7)" />
+       </g>
+</svg>

Added: freeway/res/swing/images/stop.svg
===================================================================
--- freeway/res/swing/images/stop.svg   2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/res/swing/images/stop.svg   2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" 
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd";>
+
+<svg width="512" height="512" viewBox="-65 -65 130 130"
+       xmlns="http://www.w3.org/2000/svg";
+       xmlns:xlink="http://www.w3.org/1999/xlink";>
+
+       <defs>
+               <polygon id="rect" points="-40,-40 -40,40 40,40 40,-40" 
stroke-width="10" stroke-linejoin="round" />
+       </defs>
+
+       <g>
+               <use xlink:href="#rect"
+                       fill="#000000"
+                       stroke="#000000"
+                       />
+
+               <use xlink:href="#rect"
+                       fill="#42adff"
+                       stroke="#215788"
+                       transform="scale(0.8 0.8)" />
+       </g>
+</svg>

Added: freeway/res/swing/images/watermark.svg
===================================================================
--- freeway/res/swing/images/watermark.svg      2005-01-31 23:47:23 UTC (rev 
136)
+++ freeway/res/swing/images/watermark.svg      2005-02-01 01:07:27 UTC (rev 
137)
@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN"
+  "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd";>
+
+<svg width="372" height="372" viewBox="0 0 372 372"
+       xmlns="http://www.w3.org/2000/svg";
+       xmlns:xlink="http://www.w3.org/1999/xlink";>
+
+       <defs>
+               <path id="gnu" 
d="M112.993,304.372c-3.634,0.247-8.365,1.875-11.539,7.564
+                       
c-2.591,4.646-8.661,5.387-11.495,4.451c-4.721-1.56-10.755-2.642-16.6,1.523c-5.845,4.164-12.157,1.015-11.05-6.576
+                       
c1.107-7.59,3.849-14.351,11.127-18.538c15.895-9.144,4.848-15.508,13.572-24.685c8.741-9.195,6.81-12.009,6.52-24.497
+                       
c-29.543-4.438-46.287-12.953-64.984-36.313c-18.697-23.361-25.945-42.59-27.883-71.878c-1.938-29.288-0.258-48.269,12.396-72.58
+                       
s25.977-36.014,49.065-48.153C85.21,2.553,107.832-0.354,128.742,0c20.91,0.353,34.843,1.885,46.658,8.626
+                       
c11.814,6.741,22.622,15.224,29.512,15.285c6.891,0.061,11.582-1.744,14.931-4.31c0.894,22.703-19.539,24.675-37.819,25.155
+                       
c-18.281,0.479-29.514-7.083-46.351-4.27S91.269,50.961,77.84,74.336s-13.887,31.638-12.771,51.24
+                       
c1.116,19.603,17.988,34.228,27.512,34.387c9.523,0.159,11.649-2.599,19.985-8.036c8.337-5.438,50.896-58.122,65.418-69.956
+                       
c14.523-11.834,28.854-15.488,44.328-15.678c15.474-0.19,19.428,0.335,31.908,5.45c12.48,5.114,20.149,14.533,30.211,20.291
+                       
c5.143-3.027,8.338-7.547,13.057-10.996c16.877-12.335,25.646-15.659,45.705-13.707c11.529,1.122,26.066,7.708,34.74,15.25
+                       
c15.445,13.429,28.205,32.125,38.516,46.328c8.029,11.061,17.477,24.295,30.965,28.432c14.727,4.515,25.809-14.559,27.436-25.139
+                       
c0.922-5.995,2.125-11.249,2.135-17.847c0.01-6.599-0.664-14.607-2.566-21.484s-5.16-13.72-8.688-19.394
+                       
c-5.742-9.235-15.137-18.899-24.912-23.871c-5.486-2.79-18.393-7.863-26.529-8.041c-9.969-0.216-17.564,3.133-26.781,3.761
+                       
c-11.156,0.761-31.084,3.269-39.83-5.35c-5.566-5.485-6.266-9.144-7.217-16.296c2.459,2.026,5.898,3.358,8.959,3.617
+                       
c6.986,0.59,13.527-5.114,19.166-8.419c4.645-2.723,11.223-7.589,17.324-10.048c15.676-6.32,34.932-6.138,51.48-4.445
+                       
c13.264,1.356,30.461,7.257,42.094,13.84c11.688,6.612,22.951,17.761,31.201,28.271c10.379,13.224,16.053,24.57,19.812,41.063
+                       
c4.805,21.062,6.137,52.702,0.328,73.469c-4.578,16.365-19.779,41.504-32.719,53.827c-12.762,12.155-27.498,18.022-44.256,21.231
+                       
c0.656,3.382,0.764,6.536,1.631,9.309c1.232,3.936,3.588,8.324,8.428,6.936c1.93-0.553,2.963-1.032,5.508,1.194
+                       
c2.543,2.226,1.219,10.825-5.938,14.684c-3.629,1.957-7.459,2.382-13.271,2.498s-14.883,0.141-21.193-0.964
+                       
c-6.309-1.104-10.078-6.111-17.066-6.895c-10.799-1.208-19.068-9.01-27.727-14.59c-3.613-2.329-4.832-2.353-6.914-3.53
+                       
c1.125,3.444,2.625,7.021,3.373,10.332c0.75,3.311,0.43,6.378,0.828,9.028c0.396,2.648,1.131,4.707,1.559,6.855
+                       
c1.73,1.547,2.283,3.9,5.189,4.64c2.908,0.739,7.461-0.412,10.436-0.812s4.969-2.021,7.303-1.577
+                       
c2.336,0.443,4.549,2.115,6.506,3.682s3.639,1.796,5.223,5.706s3.037,11.917,3.254,17.269s-0.383,10.183-1.969,14.43
+                       
s-2.98,6.13-7.275,10.336c-4.295,4.207-11.289,10.38-18.268,14.68c1.285,1.903,1.855,3.645,3.859,5.71
+                       
c2.004,2.066,6.182,4.381,8.041,6.56c1.857,2.179,2.799,3.742,2.826,6.186c0.029,2.443-0.09,5.619-2.074,8.283
+                       
c-4.941,6.633-10.785,7.821-17.891,10.941c1.195,7.462,2.17,12.793,0.443,20.244c-0.705,3.036-2.271,9.92-6.016,11.686
+                       
c-3.746,1.766-10.969,0.911-15.541,1.637c-4.572,0.727-7.131,1.417-10.695,2.125c-2.166,3.131-5.391,6.586-6.496,9.392
+                       
c-1.107,2.807-0.773,3.813,0.156,6.679s4.092,7.039,4.988,10.395s1.07,6.969,0.234,10.334c-0.838,3.365-2.271,5.807-5.139,8.795
+                       
c-2.867,2.989-8.508,5.336-11.318,7.758c-2.812,2.423-5.611,2.714-4.844,6.723c1.055,5.506,9.1,14.436,14.92,16.562
+                       
c3.488,1.275,8.17,0.873,12.406-0.915c-0.287,4.703-4.357,7.873-7.016,10.996c7.201-2.059,14.184-6.001,19.014-11.226
+                       
c-1.498,4.538-6.652,11.823-14.361,18.22c-7.709,6.397-7.662,5.088-11.494,7.633c6.992,1.405,13.494,1.025,18.91,0.052
+                       
c-2.969,1.637-7.836,3.939-14.283,4.616c-6.447,0.676-10.422,0.198-16.232-1.213c-5.811-1.412-11.115-6.694-18.156-10.335
+                       
c0.686,2.612,1.174,5.753,4.135,8.439c2.961,2.686,7.441,4.052,12.203,6.375c-6.062-0.688-12.432-0.896-18.389-2.209
+                       
c-4.41-0.973-7.373-2.005-10.6-3.603c-3.225-1.598-5.715-3.929-8.572-5.893c1.369,2.615,1.775,5.691,4.105,7.847
+                       
s6.117,2.959,9.18,4.44c-3.195-0.07-6.268,0.623-9.58-0.21c-3.314-0.833-6.422-2.142-9.867-4.68s-6.998-5.663-9.938-8.858
+                       
c-2.938-3.195-6.566-7.337-7.664-10.283c-1.1-2.947,0.66-5.008,0.99-7.509c-2.029,1.333-4.525,2.067-6.084,4.002
+                       
s-2.941,3.702-2.251,7.075c0.691,3.373,3.804,7.496,6.005,11.248c-6.568-5.371-10.236-7.439-11.799-13.918
+                       
c-1.463-6.066,2.322-11.572,5.199-16.159c1.429-2.278,3.567-4.398,4.903-6.597c-2.967-2.903-6.358-5.398-8.901-8.709
+                       
c-2.542-3.31-3.61-7.79-6.205-10.957c-2.596-3.168-6.088-5.173-9.129-7.756c2.673,5.742,7.066,12.785,8.02,17.229
+                       
c0.953,4.444-1.069,7.164-2.646,9.563c-1.578,2.399-4.124,2.934-6.539,4.408c-2.415,1.475-6.171,0.312-7.61,2.501
+                       
c-1.438,2.188-1.824,5.48-0.449,7.648l5.64,8.902c-2.078-1.049-5.658-4.1-7.272-6.264s-4.09-4.163-4.429-7.089
+                       
c-0.339-2.925-0.504-5.464,1.391-8.07c1.896-2.605,7.086-3.477,9.409-5.48c2.323-2.003,3.626-3.307,3.493-5.896
+                       
c-0.132-2.589-2.661-4.441-4.835-6.073c-6.637-4.978-15.151-9.565-20.787-15.565c-3.184-3.39-4.712-6.127-5.352-8.466
+                       
c-0.639-2.338,0.037-4.162,1.619-7.038c1.582-2.877,7.007-6.718,7.667-9.843c0.659-3.124-2.081-3.795-4.554-4.897
+                       
s-7.17,0.565-9.464-1.352c-2.293-1.917,0.674-5.945-1.911-8.154c-2.584-2.209-8.499-0.976-11.552-3.352
+                       
c-3.053-2.377-4.315-5.327-5.367-8.775s-1.171-7.225-0.458-9.722s2.855-2.934,4.147-4.228s2.413-2.367,0.691-4.297
+                       
c-1.722-1.929-7.365,0.108-10.609-2.369c-3.244-2.477-1.258-8.41-3.552-10.911c-2.293-2.501-6.409-1.522-8.581-2.321
+                       
c-2.171-0.799-3.023,2.02-4.355-2.438c-1.332-4.459-1.368-17.75-3.16-22.71s-5.311-0.335-6.945-5.265
+                       
c-1.634-4.929-0.085-12.812-0.821-20.257l-3.335-20.257c-11.634,7.815-19.314,10.269-31.813,12.477
+                       
c-1.043,30.582-3.916,48.591,6.464,82.52c10.38,33.928,31.387,53.924,59.726,82.649c-37.395-28.016-55.248-50.792-66.422-79.97
+                       C109.723,356.876,111.869,332.472,112.993,304.372z" />
+
+               <linearGradient id="grad1">
+                       <stop offset="0" 
style="stop-color:#ff6f00;stop-opacity:0.9059;" id="stop650"/>
+                       <stop offset="1.000000" 
style="stop-color:#ffff00;stop-opacity:0.6235;" id="stop649"/>
+               </linearGradient>
+
+               <linearGradient id="grad2" xlink:href="#grad1" x1="0.519995" 
y1="6.250001e-2" x2="0.520000" y2="0.945313"/>
+
+               <path id="arc" d="M0,0 a150 150 0 0 0 300 0" />
+       </defs>
+
+       <g opacity="0.33">
+<!--           <rect x="0" y="0" width="372" height="372" fill="yellow" 
stroke="#000000" stroke-width="1" />-->
+
+               <g transform="translate(-28,-20)">
+               <g transform="translate(43,35) scale(1.7,1.7)">
+                       <circle cx="100" cy="100" r="100" fill="url(#grad2)" 
stroke="#510000" stroke-width="10" />
+                       <circle cx="100" cy="100" r="105" fill="none" 
stroke="#000000" stroke-width="1.5"  />
+                       <use xlink:href="#gnu" style="stroke:none; fill:black;" 
transform="translate(31,30) scale(0.25,0.25) " />
+               </g>
+
+               <g transform="translate(63,207)">
+<!--                   <use xlink:href="#arc" style="stroke:#ff0000; fill: 
none; stroke-width:1;" />-->
+       
+                       <text style="stroke-width:1.5; stroke:#000000; 
font-family:'Verdana'; font-size:60; font-weight:900; fill:#ff6f00; 
text-anchor:middle"
+                               letter-spacing="13">
+                               <textPath xlink:href="#arc" 
startOffset="50%">Freeway</textPath>
+                       </text>
+               </g>
+               </g>
+       </g>
+</svg>

Added: freeway/res/swing/insert.xml
===================================================================
--- freeway/res/swing/insert.xml        2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/res/swing/insert.xml        2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
+
+<ressources>
+       <images locale="enUS">
+               <image id="application.icon" 
src="images/128x128/logo.png?bg=ffffff" />
+               <image id="application.medium.icon" 
src="images/64x64/logo.png?bg=ffffff" />
+
+               <image id="dot.icon" src="images/13x13/start.png?bg=ffffff" />
+       </images>
+
+       <actions locale="enUS">
+               <action id="quit.action" method="onQuit">
+                       <label>Quit</label>
+               </action>
+       </actions>
+</ressources>

Added: freeway/res/swing/main.xml
===================================================================
--- freeway/res/swing/main.xml  2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/res/swing/main.xml  2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
+
+<ressources>
+       <images locale="enUS">
+               <image id="splash" src="images/258x255/splash.png" />
+               <image id="app.minilogo" src="images/64x64/logo.png?bg=ffffff" 
/>
+       </images>
+</ressources>

Added: freeway/res/swing/search-panel.xml
===================================================================
--- freeway/res/swing/search-panel.xml  2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/res/swing/search-panel.xml  2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
+
+<ressources>
+       <actions locale="enUS">
+               <action id="popup.selectall" method="onSelectAll">
+                       <label>Select all</label>
+               </action>
+
+               <action id="popup.unselectall" method="onClearSelection">
+                       <label>Unselect all</label>
+               </action>
+
+               <action id="popup.searchSelectByName" method="onSelectByName">
+                       <label>Select by filename</label>
+               </action>
+
+               <action id="popup.searchSelectByDesc" 
method="onSelectByDescription">
+                       <label>Select by description</label>
+               </action>
+
+               <action id="popup.searchSelectByMime" method="onSelectByMime">
+                       <label>Select by mimetype</label>
+               </action>
+
+               <action id="popup.searchDownloadSelected" method="onDownload">
+                       <label>Download selected</label>
+               </action>
+
+               <action id="popup.searchClose" method="onAbortSearch">
+                       <label>Abort search</label>
+               </action>
+
+               <action id="download.id" method="onDownload">
+                       <label>Download</label>
+               </action>
+       </actions>
+
+       <menus locale="enUS">
+               <menu id="popup.menu">
+                       <label>Selection</label>
+
+                       <action refid="popup.selectall" />
+                       <action refid="popup.unselectall" />
+                       <separator />
+                       <action refid="popup.searchSelectByName" />
+                       <action refid="popup.searchSelectByDesc" />
+                       <action refid="popup.searchSelectByMime" />
+                       <separator />
+                       <action refid="popup.searchDownloadSelected" />
+                       <action refid="popup.searchClose" />
+               </menu>
+       </menus>
+</ressources>

Added: freeway/res/swing/search-window.xml
===================================================================
--- freeway/res/swing/search-window.xml 2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/res/swing/search-window.xml 2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,271 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
+
+<ressources>
+       <images locale="enUS">
+               <image id="splash" src="images/284x166/logo.png?bg=ffffff" />
+       </images>
+
+       <actions locale="enUS">
+               <action id="file.insert" method="openSelectFile">
+                       <label>Insert</label>
+                       <accelerator>I</accelerator>
+                       <shortcut>meta I</shortcut>
+               </action>
+
+               <action id="file.download.uri" method="fetchURI">
+                       <label>Download URI</label>
+                       <accelerator>D</accelerator>
+                       <shortcut>meta D</shortcut>
+               </action>
+
+               <action id="file.import.directory" method="importDirectory">
+                       <label>Import directory</label>
+                       <accelerator>r</accelerator>
+                       <shortcut>meta R</shortcut>
+               </action>
+
+               <action id="file.unindex.file" method="openDeleteFile">
+                       <label>Unindex file</label>
+                       <accelerator>U</accelerator>
+                       <shortcut>meta U</shortcut>
+               </action>
+
+               <action id="file.show.downloads" method="show_dlwindow">
+                       <label>Show downloads</label>
+                       <shortcut>meta W</shortcut>
+               </action>
+
+               <action id="file.show.messages" method="show_infowindow">
+                       <label>Show messages</label>
+                       <shortcut>meta M</shortcut>
+               </action>
+
+               <action id="file.show.stats" method="showStats">
+                       <label>Show statistics</label>
+                       <shortcut>meta 2</shortcut>
+               </action>
+
+               <action id="file.quit" method="destroy_stub">
+                       <label>Quit</label>
+                       <accelerator>Q</accelerator>
+                       <shortcut>meta Q</shortcut>
+               </action>
+
+               <action id="advanced.assemble.directory.from.sr" 
method="openAssembleDirectoryDialogDIR_CONTEXT_SEARCH">
+                       <label>from search results</label>
+                       <accelerator>s</accelerator>
+               </action>
+
+               <action id="advanced.assemble.directory.from.if" 
method="openAssembleDirectoryDialogDIR_CONTEXT_INSERT">
+                       <label>from inserted files</label>
+                       <accelerator>i</accelerator>
+               </action>
+
+               <action id="advanced.assemble.directory.from.lns" 
method="openAssembleDirectoryDialogDIR_CONTEXT_INSERT_SB">
+                       <label>from local namespaces</label>
+                       <accelerator>n</accelerator>
+               </action>
+
+               <action id="advanced.assemble.directory.from.fid" 
method="openAssembleDirectoryDialogDIR_CONTEXT_DIRECTORY">
+                       <label>from file identifiers from downloaded 
directories</label>
+                       <accelerator>d</accelerator>
+               </action>
+
+               <action id="advanced.assemble.directory.from.all" 
method="openAssembleDirectoryDialogDIR_CONTEXT_ALL">
+                       <label>from all known file identifiers</label>
+                       <accelerator>a</accelerator>
+               </action>
+
+               <action id="advanced.manage.pseudonyms.create" 
method="onCreatePseudonym">
+                       <label>Create new pseudonym</label>
+                       <accelerator>C</accelerator>
+                       <shortcut>meta P</shortcut>
+               </action>
+
+               <action id="advanced.manage.pseudonyms.delete" 
method="onDeletePseudonym">
+                       <label>Delete pseudonym</label>
+                       <accelerator>D</accelerator>
+               </action>
+
+               <action id="advanced.insert_into_namespace.select_sr" 
method="openAssembleNamespaceDialogDIR_CONTEXT_SEARCH">
+                       <label>Select from search results</label>
+                       <accelerator>s</accelerator>
+               </action>
+
+               <action id="advanced.insert_into_namespace.select_if" 
method="openAssembleNamespaceDialogDIR_CONTEXT_INSERT">
+                       <label>Select from inserted files</label>
+                       <accelerator>i</accelerator>
+               </action>
+
+               <action id="advanced.insert_into_namespace.select_dd" 
method="openAssembleNamespaceDialogDIR_CONTEXT_INSERT">
+                       <label>Select from results from downloaded 
directories</label>
+                       <accelerator>d</accelerator>
+               </action>
+
+               <action id="advanced.insert_into_namespace.select_lns" 
method="openAssembleNamespaceDialogDIR_CONTEXT_INSERT_SB">
+                       <label>Select from results from local namespaces</label>
+                       <accelerator>n</accelerator>
+               </action>
+
+               <action id="advanced.insert_into_namespace.select_all" 
method="openAssembleNamespaceDialogDIR_CONTEXT_ALL">
+                       <label>Select from all known file identifiers</label>
+                       <accelerator>a</accelerator>
+               </action>
+
+               <action id="advanced.search.namespace" method="searchNamespace">
+                       <label>Search Namespace</label>
+                       <accelerator>S</accelerator>
+                       <shortcut>meta S</shortcut>
+               </action>
+
+               <action id="advanced.reset_fid.list_sr" 
method="emptyDirectoryDatabaseIndDIR_CONTEXT_SEARCH">
+                       <label>List of search results</label>
+                       <accelerator>s</accelerator>
+               </action>
+
+               <action id="advanced.reset_fid.list_if" 
method="emptyDirectoryDatabaseIndDIR_CONTEXT_INSERT">
+                       <label>List of inserted files</label>
+                       <accelerator>i</accelerator>
+               </action>
+
+               <action id="advanced.reset_fid.list_lns" 
method="emptyDirectoryDatabaseIndDIR_CONTEXT_INSERT_SB">
+                       <label>List of entries in local namespaces</label>
+                       <accelerator>n</accelerator>
+               </action>
+
+               <action id="advanced.reset_fid.list_dd" 
method="emptyDirectoryDatabaseIndDIR_CONTEXT_DIRECTORY">
+                       <label>List of files from downloaded directories</label>
+                       <accelerator>d</accelerator>
+               </action>
+
+               <action id="advanced.reset_fid.all" 
method="emptyDirectoryDatabaseIndDIR_CONTEXT_ALL">
+                       <label>All known file identifiers</label>
+                       <accelerator>A</accelerator>
+               </action>
+
+               <action id="advanced.launch.gnunetd" method="onLaunchDaemon">
+                       <label>Launch daemon</label>
+               </action>
+
+               <action id="advanced.kill.gnunetd" method="onKillDaemon">
+                       <label>Stop daemon</label>
+               </action>
+
+               <action id="advanced.daemon.window" method="onDaemonWindow">
+                       <label>Show daemon window</label>
+                       <shortcut>meta 1</shortcut>
+               </action>
+
+               <action id="help.about" method="about">
+                       <label>About</label>
+                       <accelerator>A</accelerator>
+                       <icon>images/16x16/help.gif</icon>
+                       <description>Display an about dialog</description>
+               </action>
+
+               <action id="download.show.id" method="onShowDownload">
+                       <label>Download window</label>
+                       <shortcut>meta 3</shortcut>
+               </action>
+
+       </actions>
+
+       <menus locale="enUS">
+               <menu id="window-menu">
+                       <menu>
+                               <label>File</label>
+                               <accelerator>F</accelerator>
+       
+                               <action refid="file.insert" />
+                               <action refid="file.download.uri" />
+                               <action refid="file.import.directory" />
+                               <separator />
+                               <action refid="file.unindex.file" />
+                               <separator />
+                               <action refid="file.show.downloads" />
+                               <action refid="file.show.messages" />
+                               <separator />
+                               <action refid="file.quit" />
+                       </menu>
+       
+                       <menu>
+                               <label>Advanced</label>
+                               <accelerator>A</accelerator>
+       
+                               <menu>
+                                       <label>Assemble Directory</label>
+                                       <accelerator>A</accelerator>
+       
+                                       <action 
refid="advanced.assemble.directory.from.sr" />
+                                       <action 
refid="advanced.assemble.directory.from.if" />
+                                       <action 
refid="advanced.assemble.directory.from.lns" />
+                                       <action 
refid="advanced.assemble.directory.from.fid" />
+                                       <separator />
+                                       <action 
refid="advanced.assemble.directory.from.all" />
+                               </menu>
+                               <separator />
+       
+                               <menu>
+                                       <label>Manage Pseudonyms</label>
+                                       <accelerator>P</accelerator>
+       
+                                       <action 
refid="advanced.manage.pseudonyms.create" />
+                                       <action 
refid="advanced.manage.pseudonyms.delete" />
+                               </menu>
+       
+                               <menu>
+                                       <label>Insert into Namespace</label>
+                                       <accelerator>I</accelerator>
+       
+                                       <action 
refid="advanced.insert_into_namespace.select_sr" />
+                                       <action 
refid="advanced.insert_into_namespace.select_if" />
+                                       <action 
refid="advanced.insert_into_namespace.select_dd" />
+                                       <action 
refid="advanced.insert_into_namespace.select_lns" />
+                                       <separator />
+                                       <action 
refid="advanced.insert_into_namespace.select_all" />
+                               </menu>
+       
+                               <action refid="advanced.search.namespace" />
+                               <separator />
+       
+                               <menu>
+                                       <label>Reset File Identifiers</label>
+                                       <accelerator>R</accelerator>
+       
+                                       <action 
refid="advanced.reset_fid.list_sr" />
+                                       <action 
refid="advanced.reset_fid.list_if" />
+                                       <action 
refid="advanced.reset_fid.list_lns" />
+                                       <action 
refid="advanced.reset_fid.list_dd" />
+                                       <separator />
+                                       <action refid="advanced.reset_fid.all" 
/>
+                               </menu>
+       
+                       </menu>
+
+                       <menu>
+                               <label>Daemon</label>
+
+                               <action refid="advanced.launch.gnunetd" />
+                               <action refid="advanced.kill.gnunetd" />
+<!--                           <separator />
+                               <action refid="advanced.daemon.window" />
+                               <action refid="file.show.stats" />-->
+                       </menu>
+       
+                       <menu>
+                               <label>Windows</label>
+
+                               <action refid="advanced.daemon.window" />
+                               <action refid="file.show.stats" />
+                               <action refid="download.show.id" />
+                       </menu>
+       
+                       <menu>
+                               <label>Help</label>
+                               <accelerator>H</accelerator>
+       
+                               <action refid="help.about" />
+                       </menu>
+               </menu>
+       </menus>
+</ressources>

Added: freeway/res/swing/stats-window.xml
===================================================================
--- freeway/res/swing/stats-window.xml  2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/res/swing/stats-window.xml  2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
+
+<ressources>
+       <actions locale="enUS">
+               <action id="main.refresh" method="onRefresh">
+                       <label>Refresh</label>
+                       <accelerator>R</accelerator>
+                       <shortcut>meta R</shortcut>
+                       <description>Poll daemon for last 
statistics</description>
+               </action>
+
+               <action id="main.close" method="onClose">
+                       <label>Close</label>
+                       <shortcut>meta W</shortcut>
+                       <description>Close this window</description>
+               </action>
+
+               <action id="help.about" method="about">
+                       <label>About</label>
+                       <accelerator>A</accelerator>
+                       <icon>images/16x16/help.gif</icon>
+                       <description>Display an about dialog</description>
+               </action>
+       </actions>
+
+       <menus locale="enUS">
+               <menu id="window-menu">
+                       <menu>
+                               <label>File</label>
+                               <accelerator>F</accelerator>
+       
+                               <action refid="main.refresh" />
+                               <separator />
+                               <action refid="main.close" />
+                       </menu>
+               </menu>
+
+               <menu id="popup-menu">
+                       <label>Popup</label>
+                       <accelerator>P</accelerator>
+
+                       <action refid="main.refresh" />
+                       <separator />
+                       <action refid="main.close" />
+               </menu>
+       </menus>
+</ressources>

Added: freeway/res/swing/system/wizard.xml
===================================================================
--- freeway/res/swing/system/wizard.xml 2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/res/swing/system/wizard.xml 2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
+
+<ressources>
+       <actions locale="enUS">
+               <action id="next.action" method="onNext">
+                       <label>Next</label>
+               </action>
+
+               <action id="previous.action" method="onPrevious">
+                       <label>Previous</label>
+               </action>
+
+               <action id="exec.action" method="onExec">
+                       <label>Go</label>
+               </action>
+       </actions>
+</ressources>

Added: freeway/res/tests/test-charset
===================================================================
--- freeway/res/tests/test-charset      2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/res/tests/test-charset      2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,4 @@
+line 1
+àéèû
+ßõöçëœ
+end.
\ No newline at end of file

Added: freeway/res/tests/test-config
===================================================================
--- freeway/res/tests/test-config       2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/res/tests/test-config       2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,19 @@
+SUBST=hello
+GNUNET_HOME=/tmp
+
+[test]
+a=a
+b=b
+five=5
+
+[more]
+c=c
+five=42
+
+               
+[last]
+test = $SUBST/world
+boom = "1 2 3 testing"
+trailing = YES 
+
address@hidden@ tests/test-config-2

Added: freeway/res/tests/test-config-2
===================================================================
--- freeway/res/tests/test-config-2     2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/res/tests/test-config-2     2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,49 @@
+# This is the configuration for your GNUnet node.
+# Put this file in ~/.gnunet/gnunet.conf and you
+# should be fine.
+# GNUnet installation. Make sure there is some
+# space left in that directory. :-)
+#
+# If you are not installing as root, ~/.gnunet
+# may be a good choice.
+
+#GNUNET_HOME   = ~/.gnunet
+GNUNET_HOME    = 'e:/.gnunet'  #dddd
+# For root, you may want to choose instead:
+# GNUNET_HOME     = /var/lib/GNUnet
+
+TEST   =       test  
+
+[TEST ESC \i\\ FIN] 
+
+[A]                    #dd
+AAA=aaa
+AAAplus        =       'aaa\'bbb'   
+AAAmore=more
+
+############################################
+# Network configuration
+############################################
+   [NETWORK  jjj;]     J=j
+
+# [error 1 !!! # <-- error
+
+
+# On which machine runs gnunetd (for clients)
+# This is equivalent to the -H option.
+HOST = localhost       \
+       ttee    \
+       oeoi\\uiie
+
+HOST2=host
+
+    REPLACE = $HOST2 + $AAA         #\   
+\
+  [error 2 !!!
+
+# Which is the client-server port that is used
+   # between gnunetd and the clients (TCP only).
+# You may firewall this port for non-local
+# machines.
+PORT = 2087
+

Added: freeway/src/org/gnu/freeway/AbstractApplication.java
===================================================================
--- freeway/src/org/gnu/freeway/AbstractApplication.java        2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/AbstractApplication.java        2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,299 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway;
+
+import org.gnu.freeway.server.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.io.*;
+import org.gnu.freeway.util.net.*;
+
+import java.security.*;
+import java.util.*;
+import java.util.logging.*;
+
+/**
+ *
+ */
+
+public abstract class AbstractApplication extends Command implements 
Application
+{
+       static {
+               // setup mac os specific ui properties
+               if 
(System.getProperty("os.name").toLowerCase().indexOf("mac")>=0) {
+                       System.setProperty("apple.laf.useScreenMenuBar","true");
+                       }
+               }
+
+       private boolean                 daemon;
+
+       private Prefs                   preferences;
+       private Statistics              statistics;
+       private ServiceManager  services;
+
+       private List                            before; //todo: toujours utile 
???
+
+
+       protected AbstractApplication( String nm, int v, boolean dm )
+       {
+               super(nm,v);
+               daemon=dm;//todo: encore utile comme distinction ???
+               services=ServiceManager.getInstance(this);
+               preferences=new Prefs();
+               before=new ArrayList();
+               statistics=new Statistics();
+
+               addCritical(new AbstractAction("FLUSH-PREFS") {
+                       public void perform()
+                       {
+                               preferences.flushGlobals();
+                               LoggedObject.flushLogs();
+                       }
+                       });
+       }
+
+       public String toString()
+       {
+               return "Abstract application [name="+getName()+", 
version="+getVersion()+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public Prefs getPreferences()
+       {
+               return preferences;
+       }
+
+       public Statistics getStatistics()
+       {
+               return statistics;
+       }
+
+       public Service service( Class c )
+       {
+               return services.service(c);
+       }
+
+
+
+
+       public DirLocation getHome()
+       {
+               DirLocation     dir;
+               String          str;
+
+               str=(daemon ? "GNUNETD_HOME" : "GNUNET_HOME");
+
+               dir=preferences.getDirLocation("",str);
+               if (dir==null) {
+                       log(Level.SEVERE,"Configuration file must specify a 
directory for GNUnet to store per-peer data under "+str+" ! Uses \"~\"...");
+                       dir=new DirLocation("~");
+                       }
+               return (dir.create() ? dir : null);
+       }
+
+       /**
+        * Re-read the loggig configuration.
+        * Call on SIGHUP if the configuration file has changed.
+        */
+
+       protected void resetLogs()
+       {
+               FileLocation            f;
+               Level                   level;
+               String                  str;
+
+               str=preferences.getString((daemon ? "GNUNETD" : 
"GNUNET"),"LOGLEVEL","WARNING");
+               level=Level.parse(str);
+
+               f=preferences.getFileLocation((daemon ? "GNUNETD" : 
"GNUNET"),"LOGFILE");
+               if (f==null || !LoggedObject.sendLogsToFile(level,f)) {
+                       LoggedObject.sendLogsToConsole(level);
+                       }
+
+               f=preferences.getFileLocation((daemon ? "GNUNETD" : 
"GNUNET"),"LOGLEVELS-FILE");
+               if (f!=null) {
+                       LoggedObject.setUpLogLevels(f);
+                       }
+       }
+
+       public int run1()
+       {
+               Action  resetAction;
+               String  str;
+               int             ret;
+
+               /* init 1: get options and basic services up */
+               preferences.readConfiguration(daemon);
+
+               resetAction=new AbstractAction() {
+                       public void perform()
+                       {
+                               AbstractApplication.this.resetLogs();
+                       }
+                       };
+               resetLogs();
+               preferences.registerConfigurationUpdateCallback(resetAction);
+
+               str=preferences.getString((daemon ? "GNUNETD" : 
"GNUNET"),"CRYPTO_PROVIDER",null);
+               if (str!=null) {
+                       try {
+                               Security.insertProviderAt((Provider) 
Class.forName(str).newInstance(),0);
+                               log(Level.INFO,"Installed cryptographic classes 
("+str+"), hope they provide enough privacy...");
+                               }
+                       catch( Throwable x ) {
+                               err("Could not install cryptographic classes 
("+str+"), exit !",x);
+                               return -4;
+                               }
+                       }
+
+               /* init 3a: start core services */
+               preferences.init(this);
+               services.start();
+               beurk0();
+               ret=run();
+               /* init 5: shutdown in inverse order */
+               services.stop();
+
+               preferences.unregisterConfigurationUpdateCallback(resetAction);
+
+               /* init 6: goodbye */
+               // Shutdown the util services in proper order.
+               services.shutdown();
+
+               beurk2();
+
+               statistics.done();
+               log(Level.INFO,"Shutdown complete.");
+               return ret;
+       }
+
+       public void beurk0()
+       {
+       }
+
+       public void beurk2()
+       {
+       }
+
+       //TODO: a mettre ailleurs !!!
+       //todo: merger HELP_CONFIG avec :
+       //      str=(daemon ? preferences.getSystemDaemonConfiguration() : 
preferences.getSystemClientsConfiguration());
+       //      opts.addOption("config|c|FILENAME","load config 
file"+(str!=null ? " (default: "+str+")" : ""));
+
+       public static final Option      HELP_CONFIG                     =       
new Option("config|c|FILENAME","use configuration FILENAME") {
+               public boolean exec( Command app )
+               {
+                       ((Application) 
app).getPreferences().setString("FILES","gnunet.conf",getValue());
+                       return true;
+               }
+               };
+
+       public static final Option      HELP_HOSTNAME           =       new 
Option("host|H|HOSTNAME","specify host on which daemon is running (default: 
localhost)") {
+               public boolean exec( Command app )
+               {
+                       ((Application) 
app).getPreferences().setString("NETWORK","HOST",getValue());
+                       return true;
+               }
+               };
+
+       public static final Option      HELP_LOGLEVEL           =       new 
Option("loglevel|L|LEVEL","set verbosity to LEVEL") {
+               public boolean exec( Command app )
+               {
+                       ((Application) 
app).getPreferences().setString((((AbstractApplication) app).daemon ? "GNUNETD" 
: "GNUNET"),"LOGLEVEL",getValue());
+                       return true;
+               }
+               };
+
+       public static final Option      HELP_LOGLEVEL_FILE      =       new 
Option("level-file||FILE","read various log levels from file") {
+               public boolean exec( Command app )
+               {
+                       ((Application) 
app).getPreferences().setString((((AbstractApplication) app).daemon ? "GNUNETD" 
: "GNUNET"),"LOGLEVELS-FILE",getValue());
+                       return true;
+               }
+               };
+
+       public static final Option      HELP_VERSION                    =       
new Option("version|v","print the version number") {
+               public boolean exec( Command app )
+               {
+                       ((AbstractApplication) app).printVersion();
+                       return false;
+               }
+               };
+
+       public void printVersion()
+       {
+               System.out.println(getName()+" 
v"+Utils.formatVersion(getVersion()));
+       }
+
+       public abstract int run();
+
+       public void addCritical( Action a )
+       {
+               if (before.size()==0) {
+                       Runtime.getRuntime().addShutdownHook(new Thread(new 
Runnable() {
+                               public void run()
+                               {
+                                       Action  ac;
+                                       int             i;
+
+                                       // don't use logging services, since 
being in shutdown
+                                       System.out.println("Exec. 
#"+before.size()+" critical actions before shutdown.");
+
+                                       for (i=0; i<before.size(); i++) {
+                                               ac=(Action) before.get(i);
+                                               try {
+                                                       ac.perform();
+                                                       }
+                                               catch( Throwable x ) {
+                                                       
System.err.println("Unable to execute action '"+ac.getName()+"' !");
+                                                       
x.printStackTrace(System.err);
+                                                       }
+                                               }
+                               }
+                               },"CRITICALS"));
+                       }
+               before.add(0,a);
+       }
+
+       /**
+        * Obtain option from a peer.
+        * @param sock
+        * @param section
+        * @param option
+        * @return null on error
+        */
+
+       //TODO: a bouger !!!
+       public static String getConfigurationOptionValue( CSSession sock, 
String section, String option )
+       {
+               CSGetOptionRequest      req;
+               CSGetOptionReply                reply;
+
+               req=new CSGetOptionRequest(section,option);
+               if (!sock.send(req)) {
+                       return null;
+                       }
+
+               reply=(CSGetOptionReply) sock.receive(CSGetOptionReply.class);
+               if (reply==null) {
+                       return null;
+                       }
+               return reply.getValue();
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public static void launch( Class c, String[] args )
+       {
+               // check minimal system config., launch configuration wizard if 
needed
+               if (!c.equals(GNUNetUIConfig.class) && 
!c.equals(GNUNetConfig.class)) {
+                       Prefs.ensureInitialized();
+                       }
+
+               Command.launch(c,args);
+       }
+}

Added: freeway/src/org/gnu/freeway/AbstractClient.java
===================================================================
--- freeway/src/org/gnu/freeway/AbstractClient.java     2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/AbstractClient.java     2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,99 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway;
+
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.net.*;
+
+import java.net.*;
+import java.util.logging.*;
+
+/**
+ *
+ */
+
+public abstract class AbstractClient extends AbstractApplication
+{
+       protected AbstractClient( String name, int version )
+       {
+               super(name,version,false);
+       }
+
+       public String toString()
+       {
+               return "Abstract client";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public void printVersion()
+       {
+               System.out.println("GNUnet 
v"+Utils.formatVersion(GNUNetDaemon.VERSION)+", "+getName()+" 
v"+Utils.formatVersion(getVersion()));
+       }
+
+       /**
+        * Get a GNUnet TCP socket that is connected to gnunetd.
+        *
+        * @return
+        */
+
+       public CSSession connect()
+       {
+               CSSession       session;
+               Prefs           prefs;
+               InetAddress     ip;
+               String          host;
+               int                     port;
+
+               prefs=getPreferences();
+               host=prefs.getString("NETWORK","HOST","localhost");
+               port=prefs.getInt("NETWORK","PORT",0);
+
+               try {
+                       ip=InetAddress.getByName(host);
+                       }
+               catch( UnknownHostException x ) {
+                       err("Could not find IP for "+host+".",x);
+                       return null;
+                       }
+
+               session=new TCPSession();
+               if (!session.connect(ip,port,true)) {
+                       log(Level.SEVERE,"Could not connect to gnunetd !");
+
+                       session.disconnect();
+                       session=null;
+                       }
+               return session;
+       }
+
+/*
+Exemple de communication:
+
+               decoder=new PersistentDecoder();
+               decoder.add(ProxyCommand.TOKEN_ID,ProxyToken.class);
+               decoder.add(ProxyCommand.ERROR_ID,ProxyError.class);
+               decoder.add(ProxyCommand.UNKNOWN_ID,ProxyUnknown.class);
+               decoder.add(ProxyCommand.CORRUPTED_ID,ProxyCorrupted.class);
+               decoder.setDefault(ProxyCommand.UNKNOWN_ID);
+               decoder.setCorrupted(ProxyCommand.CORRUPTED_ID);
+
+               session=new TCP Session(decoder,true);
+               session.connect("localhost",100);
+
+               log(Level.INFO,"Send Hello.");
+               session.send(new ProxyHello());
+
+               tok=(ProxyToken) session.receive(ProxyToken.class);
+               log(Level.INFO,"Received token : "+tok);
+
+               session.send(new ProxyConnect("www.yahoo.com",80));
+
+               session.send(new ProxySetBlocking(true));
+               session.disconnect();
+
+*/
+}

Added: freeway/src/org/gnu/freeway/AbstractServer.java
===================================================================
--- freeway/src/org/gnu/freeway/AbstractServer.java     2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/AbstractServer.java     2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,132 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway;
+
+import org.gnu.freeway.server.*;
+import org.gnu.freeway.transport.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.net.*;
+
+/**
+ *
+ */
+
+public abstract class AbstractServer extends AbstractApplication implements 
Server, P2PHandler
+{
+       private MessagesDispatcher              dispatcher;
+
+       /** Noise received-stats. */
+       private Stat                            receivedNoiseBytes;
+
+       private ClientServer    server;
+
+       /** */
+       private LocalIdentity                           keys;
+
+
+       protected AbstractServer( String name, int version )
+       {
+               super(name,version,true);
+               dispatcher=new MessagesDispatcher();
+               receivedNoiseBytes=getStatistics().getHandle("# bytes of noise 
received");
+               server=new ClientServer();
+
+               keys=new LocalIdentity();
+       }
+
+       public String toString()
+       {
+               return "Abstract server";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       /**
+        * The identity of the local node.
+        * @return
+        */
+
+       public LocalIdentity getKeys()
+       {
+               return keys;
+       }
+
+
+       public boolean isCSHandlerRegistered( int type )
+       {
+               return server.isCSHandlerRegistered(type);
+       }
+
+       public boolean registerCSHandler( int type, Class c, CSHandler handler )
+       {
+               return server.registerCSHandler(type,handler);
+       }
+
+       public boolean unregisterCSHandler( int type, CSHandler handler )
+       {
+               return server.unregisterCSHandler(type,handler);
+       }
+
+       public boolean registerCSExitHandler( ClientExitHandler callback )
+       {
+               return server.registerClientExitHandler(callback);
+       }
+
+       public boolean unregisterCSExitHandler( ClientExitHandler callback )
+       {
+               return server.unregisterClientExitHandler(callback);
+       }
+
+
+
+       public MessagesDispatcher getDispatcher()
+       {
+               return dispatcher;
+       }
+
+       public boolean handle( Session session, P2PMessage msg, boolean 
encrypted )
+       {
+               if (msg instanceof P2PNoise) {
+                       return handleNoise(session,(P2PNoise) msg);
+                       }
+               return false;
+       }
+
+       protected boolean handleNoise( Session session, P2PNoise msg )
+       {
+               receivedNoiseBytes.add(msg.getByteSize());
+               return true;
+       }
+
+       public void beurk0()
+       {
+               keys.load(getHome());
+       }
+
+       public void beurk1()
+       {
+               server.start(this);
+       }
+
+       public void beurk2()
+       {
+               server.stop();
+       }
+
+       public int run( String[] args )
+       {
+               int     ret;
+
+               dispatcher.init(this);
+               
dispatcher.registerP2PHandler(P2PMessage.IS_NOISE,P2PNoise.class,this);
+
+               ret=super.run(args);
+
+               dispatcher.unregisterP2PHandler(P2PMessage.IS_NOISE,this);
+               dispatcher.done();
+               return ret;
+       }
+}

Added: freeway/src/org/gnu/freeway/Application.java
===================================================================
--- freeway/src/org/gnu/freeway/Application.java        2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/Application.java        2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,34 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway;
+
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.io.*;
+
+/**
+ *
+ */
+
+public interface Application
+{
+       public String getName();
+       public int getVersion();
+       public Prefs getPreferences();
+       public Service service( Class c );
+       public void addCritical( Action a );
+
+
+
+       /**
+        * Return a directory where application can store local files.
+        * @return
+        */
+
+       public DirLocation getHome();
+
+       public Scheduler getScheduler();
+
+       public Statistics getStatistics();
+}

Added: freeway/src/org/gnu/freeway/Command.java
===================================================================
--- freeway/src/org/gnu/freeway/Command.java    2005-01-31 23:47:23 UTC (rev 
136)
+++ freeway/src/org/gnu/freeway/Command.java    2005-02-01 01:07:27 UTC (rev 
137)
@@ -0,0 +1,221 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway;
+
+import org.gnu.freeway.util.*;
+
+import EDU.oswego.cs.dl.util.concurrent.*;
+
+import java.util.logging.*;
+
+/**
+ *
+ *
+ * code to allow clean shutdown of application with signals
+ *
+ * Helper code for writing proper termination code when an application
+ * receives a SIGTERM/SIGHUP etc.
+ */
+
+public abstract class Command extends LoggedObject implements Signals
+{
+       public static final int ERR_CANNOT_INSTANTIATE  =       -1001;
+       public static final int ERR_UNHANDLED_EXCEPTION =       -1002;
+       public static final int ERR_PARSING_STOPPED             =       -1003;
+
+       private String                  name;
+       private int                             version;
+       private String[]                        arguments;
+       private OptionsParser           parser;
+       private Scheduler               scheduler;//todo: a garder ici ???
+
+       /** Semaphore used to signal shutdown. */
+       private Semaphore               doShutdown;
+
+       /** This flag is set if application is shutting down. */
+       private boolean                 shutdown;
+
+
+       protected Command( String str, int v )
+       {
+               super(true);
+               name=str;
+               version=v;
+               arguments=new String[] {};
+               parser=null;
+               scheduler=new Scheduler();
+
+               doShutdown=new Semaphore(0);
+               shutdown=false;
+       }
+
+       public String toString()
+       {
+               return "Command [name="+name+", version="+version+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public String getName()
+       {
+               return name;
+       }
+
+       public int getVersion()
+       {
+               return version;
+       }
+
+       public String[] getArguments()
+       {
+               return (parser!=null ? parser.getArguments() : arguments);
+       }
+
+       public OptionsParser getParser()
+       {
+               return parser;
+       }
+
+       public abstract OptionsParser createParser();
+
+       public Scheduler getScheduler()
+       {
+               return scheduler;
+       }
+
+       public int run( String[] args )
+       {
+               SignalManager   mgr;
+               SignalListener  listener;
+               int                             ret;
+
+               arguments=args;
+
+               parser=createParser();
+               if (parser!=null && !parser.parse(this,args)) {
+                       return ERR_PARSING_STOPPED;     // parse error, --help, 
etc
+                       }
+
+               // initialize the signal handlers
+               listener=new SignalListener() {
+                       public void signalReceived( SignalEvent evt )
+                       {
+                               log(Level.INFO,"Received signal 
"+evt.getSignalName()+".");
+                               shutdown(evt.getSignal());
+                       }
+                       };
+
+               mgr=SignalManager.getInstance();
+               mgr.addSignalListener(SIGINT,listener);
+               mgr.addSignalListener(SIGTERM,listener);
+               mgr.addSignalListener(SIGQUIT,listener);
+
+               scheduler.start();
+               ret=run1();
+               scheduler.stop();
+
+               mgr=SignalManager.getInstance();
+               mgr.removeSignalListener(SIGQUIT,listener);
+               mgr.removeSignalListener(SIGTERM,listener);
+               mgr.removeSignalListener(SIGINT,listener);
+
+               return ret;
+       }
+
+       public abstract int run1();
+
+       /**
+        * Test if the shutdown has been initiated.
+        * @return YES if we are shutting down, NO otherwise
+        */
+
+       public boolean isShutdowning()
+       {
+               return shutdown;
+       }
+
+       /**
+        * Try a propper shutdown of application.
+        *
+        * @param signum
+        */
+
+       public void shutdown( int signum )
+       {
+               shutdown=true;
+               doShutdown.release();
+       }
+
+       /**
+        * Wait until the shutdown has been initiated.
+        */
+
+       public void waitForShutdown()
+       {
+               try {
+                       doShutdown.acquire();
+                       }
+               catch( InterruptedException x ) {
+                       err("Could not acquire semaphore !",x);
+                       }
+       }
+
+       /**
+        * Wait until the shutdown has been initiated, or timeout expired.
+        *
+        * @param units Timeout.
+        */
+
+       public void waitForShutdown( long units )
+       {
+               getScheduler().addJob(new 
ScheduledTask("SHUTDOWN-WITH-SIGUSR1",new AbstractAction() {
+                       public void perform()
+                       {
+                               shutdown(SIGUSR1);
+                       }
+                       }),units);
+
+               waitForShutdown();
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public static void launch( Class c, String[] args )
+       {
+               Command cmd;
+               Logger  logger;
+               int             ret;
+
+               LoggedObject.sendLogsToConsole(Level.FINEST);
+
+               logger=Logger.getLogger(c.getName());
+
+               ret=0;
+               try {
+                       cmd=(Command) c.newInstance();
+                       try {
+                               ret=cmd.run(args);
+                               if (ret!=0) {
+                                       logger.log(Level.WARNING,"Command 
returned error #"+ret+".");
+                                       }
+                               }
+                       catch( Throwable x ) {
+                               logger.log(Level.SEVERE,"Unhandled exception, 
exit !",x);
+                               ret=ERR_UNHANDLED_EXCEPTION;
+                               }
+                       }
+               catch( InstantiationException x ) {
+                       logger.log(Level.SEVERE,"Can't instantiate class 
"+c.getName()+" !",x);
+                       ret=ERR_CANNOT_INSTANTIATE;
+                       }
+               catch( IllegalAccessException x ) {
+                       logger.log(Level.SEVERE,"Can't instantiate class 
"+c.getName()+" !",x);
+                       ret=ERR_CANNOT_INSTANTIATE;
+                       }
+               System.exit(ret);
+       }
+}

Added: freeway/src/org/gnu/freeway/DaemonLauncher.java
===================================================================
--- freeway/src/org/gnu/freeway/DaemonLauncher.java     2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/DaemonLauncher.java     2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,333 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway;
+
+import org.gnu.freeway.server.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.io.*;
+import org.gnu.freeway.util.net.*;
+import org.gnu.freeway.util.ui.*;
+
+import java.io.*;
+import java.security.*;
+import java.net.*;
+import java.util.*;
+import java.util.logging.*;
+
+/**
+ *
+ */
+
+public class DaemonLauncher extends LoggedObject
+{
+       //todo: utiliser un premier fichier sur REMOTE : date de derniere modif 
+ liste à charger
+       private static final String[]   REMOTE_JARS_TO_COPY     =       {
+               "daemon/freeway.jar",
+               "daemon/freeway-tcp.jar",
+               "daemon/freeway-udp.jar",
+               "daemon/freeway-nat.jar",
+               "daemon/freeway-afs.jar",
+               "daemon/freeway-afs-mysql.jar",
+               "daemon/freeway-chat.jar",
+               "daemon/freeway-tracekit.jar",
+               "batik-all.jar",
+               "bcprov-jdk14-122.jar",
+               "concurrent.jar",
+               "mysql-connector-java-3.0.10-stable-bin.jar"
+               };
+
+       private static final String[]   REMOTE_JARS_FOR_CLASSPATH       =       
{
+               "freeway.jar",
+               "batik-all.jar",
+               "bcprov-jdk14-122.jar",
+               "concurrent.jar",
+               "mysql-connector-java-3.0.10-stable-bin.jar"
+               };
+
+       private static final String[]   LOCAL_JARS_FOR_CLASSPATH        =       
{
+               "freeway.jar",
+               "../lib/batik-all.jar",
+               "../lib/bcprov-jdk14-122.jar",
+               "../lib/concurrent.jar",
+               "../lib/mysql-connector-java-3.0.10-stable-bin.jar"
+               };
+
+       /** */
+       private AbstractClient  client;
+
+       /** */
+       private Prefs                   prefs;
+
+
+       public DaemonLauncher( Application app )
+       {
+               super(true);
+               client=(AbstractClient) app;
+               prefs=app.getPreferences();
+       }
+
+       public String toString()
+       {
+               return "Daemon launcher";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       /**
+        * Checks if gnunetd is running
+        *
+        * NOTE: Uses CS_PROTO_CLIENT_COUNT query to determine if daemon is 
running
+        * @return
+        */
+
+       public boolean checkDaemonRunning()
+       {
+               CSSession       sock;
+               CSResult        rv;
+
+               sock=client.connect();
+               if (sock==null) {
+                       log(Level.WARNING,"Socket creation failed, shouldn't 
happen.");
+                       return false;
+                       }
+
+               try {
+                       if (!sock.send(new CSGetClientCount())) {
+                               log(Level.FINEST,"Daemon is NOT running.");
+                               return false;
+                               }
+
+                       rv=(CSResult) sock.receive(CSResult.class);
+                       if (rv==null) {
+                               log(Level.FINEST,"Failed to read reply from 
daemon.");
+                               return false;
+                               }
+                       }
+               finally {
+                       sock.disconnect();
+                       }
+               return true;
+       }
+
+       public boolean launchWithExec( GProgress progress, String argLine, 
ConnectorEndPoint end )
+       {
+               String[]                a;
+               DirLocation             baseDir;
+               List                    args;
+               StringBuffer    buf;
+               String                  str;
+               URL                             url;
+               int                             i;
+
+               str=prefs.getString("GNUNET-SWING","REMOTE",null);
+               if (str!=null && str.length()>0) {
+                       try {
+                               url=new URL(str);
+                               }
+                       catch( MalformedURLException x ) {
+                               err("Invalid URL !",x);
+                               return false;
+                               }
+
+                       if (!progress.getFrame().askMessage("Daemon will be 
launched as a separate Java process.\n"+
+                               "Needed libraries will be downloaded from 
'"+str+"'\n"+
+                               " after a first check to determine if they are 
up to date (not yet implemented).\n"+
+                               "Do you still want to continue ?")) {
+                               return false;
+                               }
+
+                       
str=prefs.getGlobalString(Prefs.CONFIG_NODE,Prefs.CLIENTS_BASEDIR_NAME)+File.separator+"remote";
+
+                       baseDir=new DirLocation(str);
+                       if (!baseDir.create()) {
+                               log(Level.SEVERE,"Could not access base 
directory ("+str+") !");
+                               return false;
+                               }
+
+                       if (!isBaseDirectoryUp(baseDir,url)) {
+                               if (!setBaseDirectoryUp(baseDir,url,progress)) {
+                                       return false;
+                                       }
+                               }
+
+                       buf=new StringBuffer();
+                       for (i=0; i<REMOTE_JARS_FOR_CLASSPATH.length; i++) {
+                               buf.append(baseDir.getPath());
+                               buf.append(File.separator);
+                               buf.append(REMOTE_JARS_FOR_CLASSPATH[i]);
+                               if (i<REMOTE_JARS_FOR_CLASSPATH.length-1) {
+                                       buf.append(File.pathSeparator);
+                                       }
+                               }
+                       }
+               else {
+                       log(Level.WARNING,"No remote URL given to download 
daemon classes, going to try with current dir...");
+
+                       baseDir=new DirLocation(".");
+
+                       buf=new StringBuffer();
+                       for (i=0; i<LOCAL_JARS_FOR_CLASSPATH.length; i++) {
+                               buf.append(new 
FileLocation(LOCAL_JARS_FOR_CLASSPATH[i]).getPath());
+                               if (i<LOCAL_JARS_FOR_CLASSPATH.length-1) {
+                                       buf.append(File.pathSeparator);
+                                       }
+                               }
+                       }
+
+               args=new ArrayList();
+               args.add("java");
+               args.add("-ea");        //fixme: a voir
+               args.add("-classpath");
+               args.add(buf.toString());
+               args.add("-Djava.library.path="+baseDir.getPath());
+               args.add("-Djava.nio.preferSelect=true");       //fixme: a voir
+               args.add("org.gnu.freeway.GNUNetDaemon");
+               if (argLine!=null && argLine.length()>0) {
+                       a=OptionsParser.toArgs(argLine);
+                       for (i=0; i<a.length; i++) {
+                               args.add(a[i]);
+                               }
+                       }
+               log(Level.INFO,"About to launch daemon with arguments : "+args);
+
+               final DirLocation                       _baseDir = baseDir;
+               final List                              _args = args;
+               final ConnectorEndPoint _end = end;
+
+               AccessController.doPrivileged(new PrivilegedAction() {
+                       public Object run()
+                       {
+                               Task    task;
+
+                               task=new Task("FORK",new AbstractAction() {
+                                       public void perform()
+                                       {
+                                               
launchProcess(_baseDir,(String[]) _args.toArray(new String[_args.size()]),_end);
+                                       }
+                                       });
+                               task.launch();
+                               return null;
+                       }
+                       });
+               return true;
+       }
+
+       protected String strip( String str )
+       {
+               return str.substring(str.lastIndexOf("/")+1);
+       }
+
+       protected boolean launchProcess( DirLocation baseDir, String[] args, 
ConnectorEndPoint end )
+       {
+               Process process;
+               int             ret;
+
+               try {
+                       process=Runtime.getRuntime().exec(args,null,new 
File(baseDir.getPath()));
+                       log(Level.INFO,"Launched process : "+process);
+
+                       Connector.connect(process,end);
+
+                       ret=process.waitFor();
+                       log(Level.INFO,"Process returned with code "+ret+".");
+                       return ret==0;
+                       }
+               catch( Throwable x ) {
+                       err("Failed to launch process !",x);
+                       }
+               return false;
+       }
+
+       protected boolean isBaseDirectoryUp( DirLocation dir, URL remote )
+       {
+               return false;
+       }
+
+       protected boolean setBaseDirectoryUp( DirLocation dir, URL remote, 
GProgress progress )
+       {
+               URL             url;
+               FileLocation    file;
+               String  path;
+               MappedFile      mm;
+               int             i;
+
+               if (!dir.deleteUnder()) {
+                       log(Level.WARNING,"Unable to delete existing files 
under \""+dir.getPath()+"\" !");
+                       return false;
+                       }
+
+               for (i=0; i<REMOTE_JARS_TO_COPY.length; i++) {
+                       path=remote.getPath();
+                       if (!path.endsWith("/")) {
+                               path+="/";
+                               }
+                       path+=REMOTE_JARS_TO_COPY[i];
+
+                       if (progress!=null) {
+                               
progress.progress(i+1,REMOTE_JARS_TO_COPY.length,"Downloading \""+path+"\"");
+                               }
+
+                       file=dir.getFile(strip(REMOTE_JARS_TO_COPY[i]));
+                       if (!file.create()) {
+                               log(Level.WARNING,"Failed to create local file 
\""+REMOTE_JARS_TO_COPY[i]+"\" !");
+                               return false;
+                               }
+
+                       try {
+                               mm=file.openNew();
+                               try {
+                                       url=new 
URL(remote.getProtocol(),remote.getHost(),remote.getPort(),path);
+                                       if (!mm.writeURL(url)) {
+                                               log(Level.WARNING,"Failed to 
copy \""+path+"\" !");
+                                               return false;
+                                               }
+                                       Scheduler.sleep(Scheduler.MILLIS_500);
+                                       }
+                               finally {
+                                       mm.close();
+                                       }
+                               }
+                       catch( MalformedURLException x ) {
+                               err("Bad URL format !",x);
+                               return false;
+                               }
+                       }
+               return true;
+       }
+
+       /**
+        * Kill gnunetd
+        * @return
+        */
+
+       public boolean killDaemon()
+       {
+               CSSession       sock;
+               CSResult        rv;
+
+               if (!checkDaemonRunning()) {
+                       return false;
+                       }
+
+               sock=client.connect();
+               try {
+                       if (!sock.send(new CSShutdownRequest())) {
+                               log(Level.WARNING,"Error sending shutdown 
request to daemon.");
+                               return false;
+                               }
+
+                       rv=(CSResult) sock.receive(CSResult.class);
+                       if (rv==null) {
+                               log(Level.FINEST,"Error reading shutdown reply 
from daemon.");
+                               return false;
+                               }
+                       return rv.isOkay();
+                       }
+               finally {
+                       sock.disconnect();
+                       }
+       }
+}

Added: freeway/src/org/gnu/freeway/GNUNetChat.java
===================================================================
--- freeway/src/org/gnu/freeway/GNUNetChat.java 2005-01-31 23:47:23 UTC (rev 
136)
+++ freeway/src/org/gnu/freeway/GNUNetChat.java 2005-02-01 01:07:27 UTC (rev 
137)
@@ -0,0 +1,142 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway;
+
+import org.gnu.freeway.protocol.chat.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.net.*;
+
+import java.io.*;
+import java.util.*;
+import java.util.logging.*;
+
+/**
+ * Chat command line tool
+ */
+
+public class GNUNetChat extends AbstractClient
+{
+       private Prefs   prefs;
+
+
+       protected GNUNetChat()
+       {
+               super("gnunet-chat",Utils.makeVersion(0,0,3));
+               prefs=getPreferences();
+       }
+
+       public String toString()
+       {
+               return "Chat";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public OptionsParser createParser()
+       {
+               OptionsParser   options;
+
+               options=new OptionsParser("gnunet-chat [OPTIONS]","Start GNUnet 
chat client.");
+               options.setAllowArguments(false);
+               options.addOption(HELP_CONFIG);
+               options.addOption(HELP_HOSTNAME);
+               options.addOption(HELP_LOGLEVEL);
+               options.addOption(HELP_LOGLEVEL_FILE);
+               options.addOption(new Option("nickname|n|NICK","specify 
nickname") {
+                       public boolean exec( Command c )
+                       {
+                               
prefs.setString("GNUNET-CHAT","NICK",getValue());
+                               return true;
+                       }
+                       });
+               options.addOption(HELP_VERSION);
+               return options;
+       }
+
+       public int run()
+       {
+               CSSession               sock;
+               Task                            thread;
+               int                             ret;
+               CSChatMessage   msg;
+               BufferedReader  reader;
+               String                  nick,str;
+
+               sock=connect();
+               if (sock==null) {
+                       log(Level.SEVERE,"Could not connect to gnunetd.");
+                       return -29;
+                       }
+
+               final CSSession _sock = sock;
+
+               log(Level.INFO,"Start receive thread.");
+               thread=new Task("CLIENT-RECEIVE",new AbstractAction() {
+                       public void perform()
+                       {
+                               CSChatMessage   buffer;
+
+                               buffer=(CSChatMessage) 
_sock.receive(CSChatMessage.class);
+                               while (buffer!=null) {
+                                       System.out.println("["+new 
Date()+"]["+buffer.getNickName()+"]: "+buffer.getMessage());
+
+                                       buffer=(CSChatMessage) 
_sock.receive(CSChatMessage.class);
+                                       }
+                       }
+                       });
+               thread.launch();
+
+               nick=prefs.getString("GNUNET-CHAT","NICK",null);
+               if (nick==null) {
+                       log(Level.SEVERE,"You must specify a nickname (use 
option -n).");
+                       ret=-2;
+                       }
+               else {
+                       msg=new CSChatMessage();
+                       msg.setMessage("Hi!\n");
+                       msg.setNickName(nick);
+
+                       // send first "Hi!" message to gnunetd to indicate 
"join"
+                       if (!sock.send(msg)) {
+                               log(Level.SEVERE,"Could not send join message 
to gnunetd.");
+                               ret=-3;
+                               }
+                       else {
+                               ret=0;
+
+                               // read messages from command line and send
+                               try {
+                                       reader=new BufferedReader(new 
InputStreamReader(System.in));
+                                       for (str=reader.readLine(); str!=null; 
str=reader.readLine()) {
+                                               msg.setMessage(str+"\n");
+                                               if (!sock.send(msg)) {
+                                                       log(Level.SEVERE,"Could 
not send message to gnunetd.");
+                                                       ret=-4;
+                                                       break;
+                                                       }
+                                               }
+                                       }
+                               catch( IOException x ) {
+                                       err("",x);
+                                       }
+                               }
+                       }
+
+               log(Level.INFO,"Waiting for receive thread to finish.");
+
+               sock.disconnect();
+               thread.join();
+               return ret;
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public static void main( String[] args )
+       {
+               launch(GNUNetChat.class,args);
+       }
+}

Added: freeway/src/org/gnu/freeway/GNUNetConfig.java
===================================================================
--- freeway/src/org/gnu/freeway/GNUNetConfig.java       2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/GNUNetConfig.java       2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,289 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway;
+
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.io.*;
+
+import java.io.*;
+import java.sql.*;
+
+/**
+ *
+ */
+
+public class GNUNetConfig extends AbstractClient
+{
+       private Prefs   prefs;
+       private boolean doErase;
+       private boolean doForce;
+       private boolean doPrint;
+
+
+       protected GNUNetConfig()
+       {
+               super("gnunet-config",Utils.makeVersion(1,0,0));
+               prefs=getPreferences();
+               doErase=false;
+               doForce=false;
+               doPrint=false;
+       }
+
+       public String toString()
+       {
+               return "Configuration tool";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public OptionsParser createParser()
+       {
+               OptionsParser   options;
+
+               options=new OptionsParser("gnunet-config [OPTIONS]","Start 
GNUnet configuration program.");
+               options.setAllowArguments(false);
+               options.addOption(HELP_CONFIG);
+               options.addOption(new Option("erase|e","erase all stored 
configuration (after confirmation !)") {
+                       public boolean exec( Command c )
+                       {
+                               doErase=true;
+                               return true;
+                       }
+                       });
+               options.addOption(new Option("force|f","force to reenter all 
configuration information") {
+                       public boolean exec( Command c )
+                       {
+                               doForce=true;
+                               return true;
+                       }
+                       });
+               options.addOption(new Option("print|p","print stored 
configuration information") {
+                       public boolean exec( Command c )
+                       {
+                               doPrint=true;
+                               return true;
+                       }
+                       });
+               options.addOption(HELP_HOSTNAME);
+               options.addOption(HELP_LOGLEVEL);
+               options.addOption(HELP_LOGLEVEL_FILE);
+               options.addOption(HELP_VERSION);
+               return options;
+       }
+
+       public int run()
+       {
+               String          str;
+               int                     ret;
+               boolean         check;
+
+               // sanity checks
+               if (doErase && (doForce || doPrint)) {
+                       error("You cannot specify another action with 
--erase.");
+                       return -2;
+                       }
+
+               if (doErase) {
+                       ret=askChoice("Do you *really* want to erase all 
configuration information ?","yes|no",1,false);
+                       if (ret==0) {
+                               prefs.eraseGlobals();
+                               }
+                       return 0;
+                       }
+
+               check=false;
+               if (doForce || !prefs.isSystemInitialized()) {
+                       if (doForce || 
!prefs.hasGlobalString(Prefs.CONFIG_NODE,Prefs.DAEMON_BASEDIR_NAME)) {
+                               str=askDirectory("Home directory for freeway 
daemon ?",Prefs.DAEMON_BASEDIR_NAME,Prefs.DAEMON_BASEDIR_DEFAULT);
+                               
prefs.setGlobalString(Prefs.CONFIG_NODE,Prefs.DAEMON_BASEDIR_NAME,str);
+                               }
+
+                       if (doForce || 
!prefs.hasGlobalString(Prefs.CONFIG_NODE,Prefs.CLIENTS_BASEDIR_NAME)) {
+                               str=askDirectory("Home directory for freeway 
clients ?",Prefs.CLIENTS_BASEDIR_NAME,Prefs.CLIENTS_BASEDIR_DEFAULT);
+                               
prefs.setGlobalString(Prefs.CONFIG_NODE,Prefs.CLIENTS_BASEDIR_NAME,str);
+                               }
+
+                       if (doForce || 
!prefs.hasGlobalString(Prefs.CONFIG_NODE,Prefs.MYSQL_HOST_NAME)) {
+                               str=askWithDefaultNotNull("MySQL host 
?",prefs.getGlobalString(Prefs.CONFIG_NODE,Prefs.MYSQL_HOST_NAME,Prefs.MYSQL_HOST_DEFAULT));
+                               
prefs.setGlobalString(Prefs.CONFIG_NODE,Prefs.MYSQL_HOST_NAME,str);
+                               }
+
+                       if (doForce || 
!prefs.hasGlobalString(Prefs.CONFIG_NODE,Prefs.MYSQL_USER_NAME)) {
+                               str=askWithDefaultNotNull("MySQL user 
?",prefs.getGlobalString(Prefs.CONFIG_NODE,Prefs.MYSQL_USER_NAME,Prefs.MYSQL_USER_DEFAULT));
+                               
prefs.setGlobalString(Prefs.CONFIG_NODE,Prefs.MYSQL_USER_NAME,str);
+                               check=true;
+                               }
+
+                       if (doForce || 
!prefs.hasGlobalString(Prefs.CONFIG_NODE,Prefs.MYSQL_PASSWORD_NAME)) {
+                               str=askWithDefaultNullable("MySQL password 
?",prefs.getGlobalString(Prefs.CONFIG_NODE,Prefs.MYSQL_PASSWORD_NAME,Prefs.MYSQL_PASSWORD_DEFAULT));
+                               
prefs.setGlobalString(Prefs.CONFIG_NODE,Prefs.MYSQL_PASSWORD_NAME,str);
+                               check=true;
+                               }
+                       }
+
+               if (check && !checkForMySQL()) {
+                       error("Failed to connect to MySQL database. Check your 
MySQL configuration and re-run program later.");
+                       }
+
+               str=prefs.getSystemDaemonConfiguration();
+               if (str!=null && !new FileLocation(str).exists()) {
+                       message("Create daemon configuration file. You can 
customize it later by editing \""+str+"\".");
+                       
prefs.copyTemplateWithGlobals("gnunet.daemon.template",new FileLocation(str));
+                       }
+
+               str=prefs.getSystemClientsConfiguration();
+               if (str!=null && !new FileLocation(str).exists()) {
+                       message("Create tools configuration file. You can 
customize it later by editing \""+str+"\".");
+                       
prefs.copyTemplateWithGlobals("gnunet.client.template",new FileLocation(str));
+                       }
+
+               if (prefs.isSystemInitialized()) {
+                       message("Configuration entries are okay. Please have a 
look to \""+prefs.getSystemDaemonConfiguration()+"\" and 
\""+prefs.getSystemClientsConfiguration()+"\" for further customizations.");
+                       }
+
+               if (doPrint) {
+                       prefs.printGlobals();
+                       }
+               return 0;
+       }
+
+       protected String askDirectory( String prompt, String entry, String def )
+       {
+               DirLocation     dir;
+               String          str;
+
+               
str=askWithDefaultNotNull(prompt,prefs.getGlobalString(Prefs.CONFIG_NODE,entry,def));
+               dir=new DirLocation(str);
+               while (!dir.create()) {
+                       error("Failed to create directory 
\""+dir.getPath()+"\"\n. Please, enter a new value.");
+
+                       
str=askWithDefaultNotNull(prompt,prefs.getGlobalString(Prefs.CONFIG_NODE,entry,def));
+                       dir=new DirLocation(str);
+                       }
+               return str;
+       }
+
+       protected boolean checkForMySQL()
+       {
+               Connection      conn;
+
+               try {
+                       Class.forName("com.mysql.jdbc.Driver");
+                       conn=DriverManager.getConnection(
+                               
"jdbc:mysql://"+prefs.getGlobalString(Prefs.CONFIG_NODE,Prefs.MYSQL_HOST_NAME,"")+"/",
+                               
prefs.getGlobalString(Prefs.CONFIG_NODE,Prefs.MYSQL_USER_NAME,""),
+                               
prefs.getGlobalString(Prefs.CONFIG_NODE,Prefs.MYSQL_PASSWORD_NAME,"")
+                               );
+                       conn.close();
+                       }
+               catch( SQLException x ) {
+                       err("Unable to connect to MySQL !",x);
+                       return false;
+                       }
+               catch( ClassNotFoundException x ) {
+                       err("Unable to load MySQL driver !",x);
+                       return false;
+                       }
+               return true;
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       protected void message( String str )
+       {
+               System.out.println(str);
+       }
+
+       protected void error( String str )
+       {
+               System.err.println(str);
+       }
+
+       protected String askWithDefaultNullable( String str, String def )
+       {
+               return askWithDefault(str,def,true);
+       }
+
+       protected String askWithDefaultNotNull( String str, String def )
+       {
+               return askWithDefault(str,def,false);
+       }
+
+       protected String askWithDefault( String prompt, String def, boolean 
allowNull )
+       {
+               String  str;
+
+               do {
+                       System.out.print(prompt+" ["+def+"] ? ");
+
+                       try {
+                               str=new BufferedReader(new 
InputStreamReader(System.in)).readLine();
+                               }
+                       catch( IOException x ) {
+                               err("I/O exception",x);
+                               str=null;
+                               }
+                       str=((str!=null && str.length()>0) ? str : def);
+                       }
+               while (!allowNull && (str==null || str.length()==0));
+               return str;
+       }
+
+       public int askChoice( String prompt, String choices, int def, boolean 
allowNull )
+       {
+               String[]        p;
+               String          str;
+               int                     i;
+
+               p=choices.split("\\|");
+
+               do {
+                       System.out.print(prompt+" ("+info(p)+") : ");
+                       System.out.flush();
+
+                       try {
+                               str=new BufferedReader(new 
InputStreamReader(System.in)).readLine();
+                               }
+                       catch( IOException x ) {
+                               err("I/O exception",x);
+                               str=null;
+                               }
+
+                       i=-1;
+                       if (str!=null && str.length()>0) {
+                               str=str.toLowerCase();
+                               for (i=0; i<p.length && 
!p[i].toLowerCase().startsWith(str); i++) {}
+                               }
+                       }
+               while ((i<0 && !allowNull) || i==p.length);
+               return i;
+       }
+
+       protected String info( String[] p )
+       {
+               StringBuffer    buf;
+               int                             i;
+
+               buf=new StringBuffer();
+               for (i=0; i<p.length; i++) {
+                       buf.append(p[i]);
+                       buf.append(", ");
+                       }
+               if (i>0) {
+                       buf.setLength(buf.length()-2);
+                       }
+               return buf.toString();
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public static void main( String[] args )
+       {
+               launch(GNUNetConfig.class,args);
+       }
+}

Added: freeway/src/org/gnu/freeway/GNUNetDaemon.java
===================================================================
--- freeway/src/org/gnu/freeway/GNUNetDaemon.java       2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/GNUNetDaemon.java       2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,257 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway;
+
+import org.gnu.freeway.server.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.io.*;
+import org.gnu.freeway.util.net.*;
+
+import java.util.logging.*;
+
+/**
+ * Daemon that must run on every GNUnet peer.
+ *
+ * + Helper methods for the startup of gnunetd:
+ * - install signal handling
+ * - system checks on startup
+ * - PID file handling
+ * - detaching from terminal
+ * - command line parsing
+ */
+
+public class GNUNetDaemon extends AbstractServer implements CSHandler
+{
+       public static final int VERSION =       Utils.makeVersion(0,6,2,0xb);
+
+       /** */
+       private ScheduledTask           dotTask;
+
+       /** */
+       private boolean                 download;
+
+
+
+       public GNUNetDaemon()
+       {
+               super("gnunetd",VERSION);
+               dotTask=new ScheduledTask("PRINT-DOT",new AbstractAction() {
+                       public void perform()
+                       {
+                               log(Level.FINEST, ".");
+                       }
+                       },Scheduler.SECS_1);
+               download=false;
+       }
+
+       public String toString()
+       {
+               return "GNUnet daemon";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public OptionsParser createParser()
+       {
+               OptionsParser   options;
+
+               options=new OptionsParser("gnunetd [OPTIONS]","Start gnunetd 
daemon.");
+               options.setAllowArguments(false);
+               options.addOption(HELP_CONFIG);
+               options.addOption(new Option("debug|d","run in debug mode; 
gnunetd will not daemonize and error messages will be written to stderr instead 
of a logfile") {
+                       public boolean exec( Command c )
+                       {
+                               
getPreferences().setString("GNUNETD","LOGFILE",null);
+                               return true;
+                       }
+                       });
+               options.addOption(new Option("livedot|l","print a dot every 
second to show that gnunetd is alive") {
+                       public boolean exec( Command c )
+                       {
+                               getScheduler().addJob(dotTask,Scheduler.SECS_1);
+                               return true;
+                       }
+                       });
+               options.addOption(HELP_LOGLEVEL);
+               options.addOption(HELP_LOGLEVEL_FILE);
+               options.addOption(new Option("padding|p|PADDING","enable 
padding") {
+                       public boolean exec( Command c )
+                       {
+                               
getPreferences().setString("GNUNETD-EXPERIMENTAL","PADDING",getValue());
+                               return true;
+                       }
+                       });
+               options.addOption(new Option("user|u|USER","run as user USER") {
+                       public boolean exec( Command c )
+                       {
+                               //todo: to implement
+                               return true;
+                       }
+                       });
+               options.addOption(new Option("download|","download host list") {
+                       public boolean exec( Command c )
+                       {
+                               download=true;
+                               return true;
+                       }
+                       });
+               options.addOption(HELP_VERSION);
+               return options;
+       }
+
+       public int run()
+       {
+               Prefs   prefs;
+               int             version,stop;
+               boolean firstStart;
+
+               prefs=getPreferences();
+
+               // check version; possibly not the best place here since 
gnunet-check is AFS specific (at least at the moment).
+               version=prefs.getContentAsInt("VERSION",-1);
+               if (version!=-1) {
+                       // basic idea: whenever we make an incompatible change, 
bump the version
+                       // requirement here and add the necessary update code 
to gnunet-check...
+                       if (version<0x0620) {
+                               System.err.println("Old version "+version+" 
detected. Please run gnunet-check -u first !");
+                               return -99;
+                               }
+                       firstStart=false;
+                       }
+               else {
+                       version=0x0620; // first start
+                       prefs.putContent("VERSION",version);
+                       firstStart=true;
+                       }
+
+               /* init 2: become deamon, initialize core subsystems */
+               log(Level.FINE,"Daemon starting...");
+
+               /* initialize signal handler (CTRL-C / SIGTERM) */
+               writePIDFile();
+
+               /* init 3b: load application services */
+               beurk1();
+
+               service(CoreService.class);
+               service(HelloExchangeService.class);
+               service(PingPongService.class);
+               service(ConnectionService.class);
+
+               ((CoreService) service(CoreService.class)).loadProtocols();
+               if (firstStart || download) {
+                       ((HELOLoader) 
service(HELOLoader.class)).downloadRandomHostList();      // right away !
+                       }
+
+               /* init 4: wait for shutdown */
+               /* wait for SIGTERM, SIGTERM will set
+                doShutdown to true and send this thread
+                a SIGUSR1 which will wake us up from the
+                sleep */
+               
registerCSHandler(CSMessage.IS_SHUTDOWN,CSShutdownRequest.class,this);
+               log(Level.FINE,"Daemon up and running.");
+
+               ((CoreService) service(CoreService.class)).getManager().dump();
+
+               // mechanism to stop gnunetd after a certain time without a 
signal
+               stop=getPreferences().getInt("GNUNETD","AUTOMATIC-STOP",0);
+               if (stop>0) {
+                       waitForShutdown(Scheduler.seconds(stop));
+                       }
+               else {
+                       waitForShutdown();
+                       }
+
+               log(Level.FINE,"Daemon shutting down...");
+
+               deletePIDFile();
+
+               log(Level.FINE,"Daemon has said : \"Bye\".");
+               return 0;
+       }
+
+       public boolean handle( CSSession client, CSMessage message )
+       {
+               boolean ret;
+
+               log(Level.INFO,client.getLabel()+" Shutdown request accepted.");
+
+               unregisterCSHandler(CSMessage.IS_SHUTDOWN,this);
+
+               ret=client.send(CSResult.OKAY);
+
+               // wait for a small amount of time to enable clients to receive 
responses...
+               Scheduler.sleep(Scheduler.SECS_1);      //todo: faire plus 
deterministe
+
+               shutdown(0);
+               return ret;
+       }
+
+       /**
+        * Write our process ID to the pid file.
+        */
+
+       public void writePIDFile()
+       {
+               FileLocation            pif;
+               MappedFile              f;
+
+               pif=getPreferences().getFileLocation("GNUNETD","PIDFILE");
+               if (pif==null) {
+                       log(Level.SEVERE,"No PID file is defined in 
configuration (GNUNETD/PIDFILE) !");
+                       return;
+                       }
+
+               f=pif.openNew();
+               if (f!=null) {
+                       
f.writeString(String.valueOf(SignalManager.getInstance().getPID()));
+                       f.close();
+                       }
+               else {
+                       log(Level.WARNING,"Could not write PID to file 
\""+pif.getLabel()+"\".");
+                       }
+       }
+
+       /**
+        * Delete the pid file.
+        */
+
+       public void deletePIDFile()
+       {
+               FileLocation            pif;
+
+               pif=getPreferences().getFileLocation("GNUNETD","PIDFILE");
+               if (pif==null) {
+                       log(Level.SEVERE,"No PID file is defined in 
configuration (GNUNETD/PIDFILE) !");
+                       return;
+                       }
+               pif.delete();
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       /**
+        * The main method of gnunetd. And here is how it works:
+        * <ol>
+        * <li>initialize util (parse command line, options)
+        * <li>detach from tty, initialize all coresystems
+        * <li>a) start core-services
+        *     b) initialize application services and download hostlist
+        * <li>wait for semaphore to signal shutdown
+        * <li>shutdown all services (in roughly inverse order)
+        * <li>exit
+        * </ol>
+        * @param args
+        */
+
+       public static void main( String[] args )
+       {
+               launch(GNUNetDaemon.class,args);
+       }
+
+       // You have reached the end of GNUnet. You can shutdown your computer 
and get a life now.
+}

Added: freeway/src/org/gnu/freeway/GNUNetDirectory.java
===================================================================
--- freeway/src/org/gnu/freeway/GNUNetDirectory.java    2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/GNUNetDirectory.java    2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,186 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway;
+
+import org.gnu.freeway.protocol.afs.*;
+import org.gnu.freeway.protocol.afs.esed2.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.io.*;
+
+/**
+ * Tool to list the entries stored in the database holding files for building 
directories,
+ * to delete all of these entries and to display the contents of directories.
+ */
+
+public class GNUNetDirectory extends AbstractClient implements AFSConstants
+{
+       private int             listMask;
+       private int             killMask;
+       private RootNodeCallback        printNode = new RootNodeCallback() {
+                               public void rootNode( RootNode root, Object 
closure )
+                               {
+                                       System.out.println(root.toLabel());
+                               }
+                               };
+
+
+       protected GNUNetDirectory()
+       {
+               super("gnunet-directory",AFSProtocol.VERSION);
+               listMask=0;
+               killMask=0;
+       }
+
+       public String toString()
+       {
+               return "List, delete and display database entries";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public OptionsParser createParser()
+       {
+               OptionsParser   options;
+
+               options=new OptionsParser("gnunet-directory [OPTIONS] 
[FILENAMES]","Perform directory related operations.");
+               options.setAllowArguments(true);
+               options.addOption(new Option("list-all|a","list all entries 
from the directory database") {
+                       public boolean exec( Command c )
+                       {
+                               listMask|=DIR_CONTEXT_ALL;
+                               return true;
+                       }
+                       });
+               options.addOption(new Option("kill-all|A","remove all entries 
from the directory database") {
+                       public boolean exec( Command c )
+                       {
+                               killMask=DIR_CONTEXT_ALL;
+                               return true;
+                       }
+                       });
+               options.addOption(HELP_CONFIG);
+               options.addOption(HELP_HOSTNAME);
+               options.addOption(new Option("list-insert|i","list all insert 
entries from the directory database") {
+                       public boolean exec( Command c )
+                       {
+                               listMask|=DIR_CONTEXT_INSERT;
+                               return true;
+                       }
+                       });
+               options.addOption(new Option("kill-insert|I","delete all insert 
entries from the directory database") {
+                       public boolean exec( Command c )
+                       {
+                               killMask=DIR_CONTEXT_INSERT;
+                               return true;
+                       }
+                       });
+               options.addOption(HELP_LOGLEVEL);
+               options.addOption(HELP_LOGLEVEL_FILE);
+               options.addOption(new Option("list-namespace|n","list all 
namespace entries from the directory database") {
+                       public boolean exec( Command c )
+                       {
+                               listMask|=DIR_CONTEXT_INSERT_SB;
+                               return true;
+                       }
+                       });
+               options.addOption(new Option("kill-namespace|N","delete all 
namespace entries from the directory database") {
+                       public boolean exec( Command c )
+                       {
+                               killMask=DIR_CONTEXT_INSERT_SB;
+                               return true;
+                       }
+                       });
+               options.addOption(new Option("list-search|s","list all search 
result entries from the directory database") {
+                       public boolean exec( Command c )
+                       {
+                               listMask|=DIR_CONTEXT_SEARCH;
+                               return true;
+                       }
+                       });
+               options.addOption(new Option("kill-search|S","delete all search 
result entries from the directory database") {
+                       public boolean exec( Command c )
+                       {
+                               killMask=DIR_CONTEXT_SEARCH;
+                               return true;
+                       }
+                       });
+               options.addOption(HELP_VERSION);
+               options.addOption(new Option("list-directory|x","list all 
directory entries from the directory database") {
+                       public boolean exec( Command c )
+                       {
+                               listMask|=DIR_CONTEXT_DIRECTORY;
+                               return true;
+                       }
+                       });
+               options.addOption(new Option("kill-directory|X","remove all 
directory entries from the directory database") {
+                       public boolean exec( Command c )
+                       {
+                               killMask=DIR_CONTEXT_DIRECTORY;
+                               return true;
+                       }
+                       });
+               return options;
+       }
+
+       protected void printDirectory( FileLocation filename )
+       {
+               GNDirectory     dir;
+               MappedFile      mm;
+               int                     i;
+
+               if (!filename.exists()) {
+                       System.out.println("==> Directory 
'"+filename.getLabel()+"': DOES NOT EXIST");
+                       return;
+                       }
+
+               System.out.println("==> Directory '"+filename.getLabel()+"':");
+
+               mm=filename.open();
+               dir=(GNDirectory) mm.asPersistent(GNDirectory.class);
+               mm.close();
+
+               if (dir == null) {
+                       System.out.println("No such file or invalid format for 
GNUnet directory.");
+                       return;
+                       }
+
+               System.out.println(dir.getDescription());
+               for (i=0;i<dir.number_of_files;i++) {
+                       System.out.print(i+": ");
+                       printNode.rootNode(dir.contents[i], null);
+                       }
+               System.out.println();
+       }
+
+       public int run()
+       {
+               Prefs   prefs;
+               int                                     i;
+               String[]        filenames;
+
+               prefs=getPreferences();
+
+               if (listMask!=0) {
+                       System.out.println("Listed "+new 
GNDirectoryDatabase(prefs).iterateDirectoryDatabase(listMask,printNode,null)+" 
matching entries.");
+                       }
+               if (killMask!=0) {
+                       new 
GNDirectoryDatabase(prefs).emptyDirectoryDatabase(killMask);
+                       }
+               filenames=getArguments();
+               for (i=0; i<filenames.length; i++) {
+                       printDirectory(new FileLocation(filenames[i]));
+                       }
+               return 0;
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public static void main( String[] args )
+       {
+               launch(GNUNetDirectory.class,args);
+       }
+}

Added: freeway/src/org/gnu/freeway/GNUNetDownload.java
===================================================================
--- freeway/src/org/gnu/freeway/GNUNetDownload.java     2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/GNUNetDownload.java     2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,435 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway;
+
+import org.gnu.freeway.protocol.afs.*;
+import org.gnu.freeway.protocol.afs.esed2.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.io.*;
+import org.gnu.freeway.util.net.*;
+
+import EDU.oswego.cs.dl.util.concurrent.*;
+
+import java.util.logging.*;
+
+/**
+ * Main function to download files from GNUnet.
+ */
+
+public class GNUNetDownload extends AbstractClient implements AFSConstants
+{
+       /* just not false/true */
+       private static final int        PENDING =       42;
+       private static final int        RUNNING =       43;
+       private static final int        JOINED  =       44;
+
+       private DownloadInfo[]          pending = null;
+       private int                                     pendingCount = 0;
+       private Object                          lock;
+       private Semaphore                       semSignal;
+       private Prefs   prefs;
+       private DownloadUtil                    dutil;
+       private Priority                                priority;
+       private Policy                          policy;
+
+
+       public GNUNetDownload()
+       {
+               super("gnunet-download",AFSProtocol.VERSION);
+               prefs=getPreferences();
+               dutil=new DownloadUtil();
+       }
+
+       public String toString()
+       {
+               return "Download";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public OptionsParser createParser()
+       {
+               OptionsParser   options;
+
+               options=new OptionsParser("gnunet-download [OPTIONS] 
GNUNET-URI","Download file from GNUnet.");
+               options.setAllowArguments(true);
+               options.addOption(new Option("anonymity|a|LEVEL","set the 
desired LEVEL of receiver-anonymity") {
+                       public boolean exec( Command c )
+                       {
+                               int     receivePolicy;
+
+                               try {
+                                       
receivePolicy=Integer.parseInt(getValue());
+                                       
prefs.setInt("AFS","ANONYMITY-RECEIVE",receivePolicy);
+                                       }
+                               catch( NumberFormatException x ) {
+                                       log(Level.SEVERE,"You must pass a 
number to the -a option.");
+                                       return false;
+                                       }
+                               return true;
+                       }
+                       });
+               options.addOption(HELP_CONFIG);
+               options.addOption(HELP_HOSTNAME);
+               options.addOption(HELP_LOGLEVEL);
+               options.addOption(HELP_LOGLEVEL_FILE);
+               options.addOption(new Option("output|o|FILENAME","write the 
file to FILENAME (required)") {
+                       public boolean exec( Command c )
+                       {
+                               
prefs.setString("GNUNET-DOWNLOAD","FILENAME",getValue());
+                               return true;
+                       }
+                       });
+               options.addOption(new Option("recursive|R","download a GNUnet 
directory recursively") {
+                       public boolean exec( Command c )
+                       {
+                               
prefs.setString("GNUNET-DOWNLOAD","RECURSIVE","YES");
+                               return true;
+                       }
+                       });
+               options.addOption(new Option("threads|t|NUMBER","specifies the 
NUMBER of files that maybe downloaded in parallel for a recursive download") {
+                       public boolean exec( Command c )
+                       {
+                               int     threads;
+
+                               threads=getIntValue(-1);
+                               if (threads<0) {
+                                       log(Level.WARNING,"You must pass a 
number to the -t option.");
+                                       return false;
+                                       }
+                               if (threads == 0) {
+                                       threads = 1; /* actual minimum value */
+                                       }
+                               
prefs.setInt("GNUNET-DOWNLOAD","PARALLELIZATION",threads);
+                               return true;
+                       }
+                       });
+               options.addOption(HELP_VERSION);
+               options.addOption(new Option("verbose|V","be verbose") {
+                       public boolean exec( Command c )
+                       {
+                               
prefs.setString("GNUNET-DOWNLOAD","VERBOSE","YES");
+                               return true;
+                       }
+                       });
+               return options;
+       }
+
+       /**
+        * This method is called whenever data is received.
+        * The current incarnation just ensures that the main
+        * method exits once the download is complete.
+        * @param stats
+        * @param data
+        */
+
+       protected void progressModel2( ProgressStats stats, DownloadInfo data )
+       {
+               if (prefs.testString("GNUNET-DOWNLOAD","VERBOSE","YES")) {
+                       if (stats.progress != data.lastProgress) {
+                               System.out.print("Download at 
"+stats.progress+" out of "+stats.filesize+" bytes "+
+                                       "("+(stats.progress/(double)  
Scheduler.toSeconds(Scheduler.now()-(data.startTime-1))/1024)+" kbps)"+
+                                       "\r");
+                               }
+                       }
+
+               data.lastProgress = stats.progress;
+               if (stats.progress == stats.filesize) {
+                       data.sem.release();
+                       }
+       }
+
+       protected boolean downloadFileHelper( DownloadInfo di ) throws 
InterruptedException
+       {
+               RequestManager  rm;
+               boolean                 ok;
+
+               assert(di.sem==null);
+
+               di.startTime=Scheduler.now();
+               di.lastProgress = 0;
+               di.sem = new Semaphore(0);
+
+               rm=dutil.downloadFile(
+                               this,
+                               policy,
+                               priority,
+                               di.fid,
+                               di.filename,
+                               new ProgressModel() {
+                                       public void progress( ProgressStats 
stats, Object data )
+                                       {
+                                               
progressModel2(stats,(DownloadInfo) data);
+                                       }
+                                       },
+                               di);
+               if (rm == null) {
+                       System.out.println("Download "+di.filename+" failed 
(error messages should have been provided).");
+                       return false;
+                       }
+               di.sem.acquire();
+               di.sem = null;
+               rm.destroy();
+               System.out.println();
+               System.out.println("Download "+di.filename+" 
"+((di.fid.getFileLength()==di.lastProgress) ? "complete" : "incomplete")+". "+
+                       "Speed was "+(di.lastProgress/(double) 
Scheduler.toSeconds(Scheduler.now()-di.startTime)/1024)+" kilobyte per 
second.");
+
+               ok=(di.fid.getFileLength() == di.lastProgress);
+               if (ok && 
prefs.testString("GNUNET-DOWNLOAD","RECURSIVE","YES")) {
+                       /* download files in directory! */
+                       MappedFile      exp;
+                       FileLocation    loc;
+                       GNDirectory     dir;
+                       int                     i;
+
+                       loc=new FileLocation(di.filename);
+                       loc.create();
+
+                       exp = loc.open();
+                       dir=(GNDirectory) exp.asPersistent(GNDirectory.class);
+                       exp.close();
+
+                       if (dir == null) {
+                               return ok; /* not a directory */
+                               }
+
+                       for (i=0; i<dir.number_of_files; i++) {
+                               RootNode                rn;
+                               String          fn,rfn;
+
+                               rn=dir.contents[i];
+
+                               rfn=rn.getFileName();
+
+                               fn=di.filename;
+
+                               /* remove '.gnd' or add ".dir" */
+                               if (fn.endsWith(GNUNET_DIRECTORY_EXT)) {
+                                       
fn=fn.substring(0,fn.length()-GNUNET_DIRECTORY_EXT.length());
+                                       }
+                               else {
+                                       fn+=".dir";
+                                       }
+
+                               new DirLocation(fn).create(); /* create 
directory */
+
+                               fn+="/";
+                               fn+=rfn;
+
+                               scheduleDownload(rn.getFileIdentifier(),fn);
+                               }
+                       }
+               return ok;
+       }
+
+       protected void scheduleDownload( FileIdentifier fid, String filename )
+       {
+               DownloadInfo[]  tmp;
+
+               synchronized(lock) {
+                       tmp=new DownloadInfo[pendingCount+1];
+                       System.arraycopy(pending,0,tmp,0,pending.length);
+                       pending=tmp;
+                       pendingCount++;
+
+                       pending[pendingCount-1] = new DownloadInfo();
+                       pending[pendingCount-1].fid=(FileIdentifier) 
PersistentHelper.copy(fid);
+                       pending[pendingCount-1].filename = filename;
+                       pending[pendingCount-1].result = PENDING;
+                       pending[pendingCount-1].sem = null;
+                       }
+       }
+
+       protected Object process( DownloadInfo di ) throws InterruptedException
+       {
+               di.result = (downloadFileHelper(di) ? 1 : -1);
+               semSignal.release();
+               return null;
+       }
+
+       protected boolean run( int threadLimit ) throws InterruptedException
+       {
+               int             left,running,i;
+               boolean ret;
+
+               if (threadLimit == 0) /* should never happen */
+                       threadLimit = 1; /* need at least 1! */
+
+               ret = true;
+               semSignal = new Semaphore(threadLimit);
+
+               left = pendingCount;
+               running = 0;
+               while (left>0 || running>0) {
+                       if (left > 0) {
+                               semSignal.acquire();
+                               for (i=0; i<pendingCount; i++) {
+                                       final int       _i = i;
+
+                                       if (pending[i].result == PENDING) {
+                                               pending[i].result = RUNNING;
+
+                                               pending[i].thread=new 
Task("DOWNLOAD-"+i,new AbstractAction() {
+                                                       public void perform() 
throws Throwable
+                                                       {
+                                                               
process(pending[_i]);
+                                                       }
+                                                       });
+                                               pending[i].thread.launch();
+                                               break;
+                                               }
+                                       }
+                               }
+
+                       left = 0;
+                       running = 0;
+                       for (i=0;i<pendingCount;i++) {
+                               if (pending[i].result == PENDING) {
+                                       left++;
+                                       continue;
+                                       }
+                               if (pending[i].result == RUNNING) {
+                                       running++;
+                                       continue;
+                                       }
+                               if (pending[i].result == JOINED)
+                                       continue;
+                               if (pending[i].result == -1)
+                                       ret = false;
+                               pending[i].thread.join();
+                               pending[i].result = JOINED;
+                               }
+
+                       /* wait a bit */
+                       if (left==0 && running>0) {
+                               Scheduler.sleep(Scheduler.MILLIS_150);
+                               }
+                       }
+
+               /* wait for all threads to terminate */
+               for (i=0; i<threadLimit; i++) {
+                       semSignal.acquire();
+                       }
+
+               /* join on all */
+               for (i=0; i<pendingCount; i++) {
+                       assert(pending[i].result!=PENDING);
+
+                       if (pending[i].result == JOINED) {
+                               continue;
+                               }
+                       if (pending[i].result == -1) {
+                               ret = false;
+                               }
+                       pending[i].thread.join();
+                       pending[i].result = JOINED;
+                       }
+
+               pending=null;
+               pendingCount=0;
+               return ret;
+       }
+
+       public int run()
+       {
+               CSSession       sock;
+               int                             ret;
+
+               sock=connect();
+               if (sock==null) {
+                       log(Level.SEVERE,"Could not connect to gnunetd.");
+                       return -29;
+                       }
+               ret=run2(sock);
+               return ret;
+       }
+
+       /**
+        * @param sock
+        * @return return value from download file: 0: ok, -1, 1: error
+        */
+
+       public int run2( CSSession sock )
+       {
+               FileIdentifier  fid;
+               String                  filename,fstring;
+               int                             threadLimit;
+               boolean                 ok;
+
+               if (getArguments().length!=1) {
+                       log(Level.WARNING,"Not enough/too many arguments. You 
must specify exactly one GNUnet AFS URI.");
+                       getParser().printHelp();
+                       return -11;
+                       }
+
+               prefs.setString("GNUNET-DOWNLOAD","URI",getArguments()[0]);
+
+               filename = prefs.getString("GNUNET-DOWNLOAD","FILENAME",null);
+               if (filename == null) {
+                       log(Level.SEVERE,"You must specify a filename (option 
-o).");
+                       getParser().printHelp();
+                       return -11;
+                       }
+
+               priority=new Priority(this);
+
+               threadLimit = 
prefs.getInt("GNUNET-DOWNLOAD","PARALLELIZATION",0);
+               if (threadLimit == 0)
+                       threadLimit = 30; /* default */
+
+               filename = prefs.getString("GNUNET-DOWNLOAD","FILENAME",null);
+
+               fstring = prefs.getString("GNUNET-DOWNLOAD","URI",null);
+
+               fid=FileIdentifier.parseURI(fstring);
+               if(fid == null) {
+                       log(Level.SEVERE,"Can't proceed without a valid URI.");
+                       return -1;
+                       }
+
+               lock=new Object();
+               priority.startAFSPriorityTracker();
+
+               policy=new Policy(getPreferences(),connect());
+
+               scheduleDownload(fid,   filename);
+               try {
+                       ok=run(threadLimit);
+                       }
+               catch( InterruptedException x ) {
+                       err("",x);
+                       ok=false;
+                       }
+
+               policy.doneAnonymityPolicy();
+               priority.stopAFSPriorityTracker();
+               return (ok ? 0 : 1);
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       /**
+        * Main function to download files from GNUnet.
+        * @param args command line arguments
+        */
+
+       public static void main( String[] args )
+       {
+               launch(GNUNetDownload.class,args);
+       }
+}
+
+class DownloadInfo extends Object
+{
+       public Semaphore                        sem;
+       public String                   filename;
+       public FileIdentifier           fid;
+       public long                             startTime;
+       public int                              lastProgress;
+       public int                              result;
+       public Task                             thread;
+}

Added: freeway/src/org/gnu/freeway/GNUNetInsert.java
===================================================================
--- freeway/src/org/gnu/freeway/GNUNetInsert.java       2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/GNUNetInsert.java       2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,791 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway;
+
+import org.gnu.freeway.protocol.afs.*;
+import org.gnu.freeway.protocol.afs.esed2.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.io.*;
+import org.gnu.freeway.util.net.*;
+
+import java.io.*;
+import java.text.*;
+import java.util.*;
+import java.util.logging.*;
+
+/**
+ * Tool to insert or index files into GNUnet's AFS.
+ */
+
+public class GNUNetInsert extends AbstractClient implements AFSConstants
+{
+       private Prefs           prefs;
+       private List                    topKeywords;
+       private List                    gloKeywords;
+       private InsertUtil      insertUtil;
+       private boolean         printAndReturn;
+
+
+       public GNUNetInsert()
+       {
+               super("gnunet-insert",AFSProtocol.VERSION);
+               prefs=getPreferences();
+               prefs.setString("GNUNET-INSERT","INDEX-CONTENT","YES");
+               topKeywords=new ArrayList();
+               gloKeywords=new ArrayList();
+               insertUtil=new InsertUtil(prefs);
+               printAndReturn=false;
+       }
+
+       public String toString()
+       {
+               return "Insert";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public OptionsParser createParser()
+       {
+               OptionsParser   options;
+
+               options=new OptionsParser("gnunet-insert [OPTIONS] 
FILENAME*","Make files available to GNUnet for sharing.");
+               options.setAllowArguments(true);
+               options.addOption(new Option("builddir|b","build a directory 
listing all processed files") {
+                       public boolean exec( Command c )
+                       {
+                               
prefs.setString("GNUNET-INSERT","BUILDDIR","YES");
+                               return true;
+                       }
+                       });
+               options.addOption(HELP_CONFIG);
+               options.addOption(new Option("desc|D|DESCRIPTION","set 
description for all files") {
+                       public boolean exec( Command c )
+                       {
+                               
prefs.setString("GNUNET-INSERT","DESCRIPTION",getValue());
+                               return true;
+                       }
+                       });
+               options.addOption(new Option("sprev|e|FILENAME","filename of 
the SBlock of a previous version of the content (for namespace insertions 
only)") {
+                       public boolean exec( Command c )
+                       {
+                               
prefs.setString("GNUNET-INSERT","PREVIOUS_SBLOCK",getValue());
+                               return true;
+                       }
+                       });
+               options.addOption(new Option("extract|E","print list of 
extracted keywords that would be used, but do not perform insertion or 
indexing") {
+                       public boolean exec( Command c )
+                       {
+                               printAndReturn = true;
+                               return true;
+                       }
+                       });
+               options.addOption(new Option("name|f|NAME","publish NAME as the 
name of the file or directory") {
+                       public boolean exec( Command c )
+                       {
+                               String  root;
+                               int             index;
+
+                               
prefs.setString("GNUNET-INSERT","FILENAME",getValue());
+
+                               root = 
prefs.getString("GNUNET-INSERT","FILENAMEROOT",null);
+                               if (root == null) {
+                                       // if filename is '/home/user/foo', use 
'foo' as the filenameRoot
+                                       root = getValue();
+                                       
index=root.lastIndexOf(File.separatorChar);
+                                       root=(index>=0 ? 
root.substring(index+1) : root);
+                                       }
+                               
prefs.setString("GNUNET-INSERT","FILENAMEROOT",root);
+                               return true;
+                       }
+                       });
+               options.addOption(HELP_HOSTNAME);
+               options.addOption(new Option("interval|i|SECONDS","set interval 
for availability of updates to SECONDS (for namespace insertions only)") {
+                       public boolean exec( Command c )
+                       {
+                               int     interval;
+
+                               try {
+                                       interval=Integer.parseInt(getValue());
+                                       
prefs.setInt("GNUNET-INSERT","INTERVAL",interval);
+                                       }
+                               catch( NumberFormatException x ) {
+                                       log(Level.SEVERE,"You must pass a 
positive number to the -i option.");
+                                       return false;
+                                       }
+                               return true;
+                       }
+                       });
+               options.addOption(new Option("key|k|KEYWORD","add an additional 
keyword for the top-level file or directory (this option can be specified 
multiple times)") {
+                       public boolean exec( Command c )
+                       {
+                               topKeywords.add(getValue());
+                               return true;
+                       }
+                       });
+               options.addOption(new Option("global-key|K|KEYWORD","add an 
additional keyword for all files and directories (this option can be specified 
multiple times)") {
+                       public boolean exec( Command c )
+                       {
+                               gloKeywords.add(getValue());
+                               return true;
+                       }
+                       });
+               options.addOption(new Option("link|l","if gnunetd is running on 
the local machine, create a link instead of making a copy in the GNUnet share 
directory") {
+                       public boolean exec( Command c )
+                       {
+                               prefs.setString("GNUNET-INSERT","LINK","YES");
+                               return true;
+                       }
+                       });
+               options.addOption(HELP_LOGLEVEL);
+               options.addOption(HELP_LOGLEVEL_FILE);
+               options.addOption(new Option("mime|m|MIMETYPE","set the 
mimetype for the file to be MIMETYPE") {
+                       public boolean exec( Command c )
+                       {
+                               
prefs.setString("GNUNET-INSERT","MIMETYPE",getValue());
+                               return true;
+                       }
+                       });
+               options.addOption(new Option("noindex|n","do not index, perform 
full insertion (stores entire file in encrypted form in GNUnet database)") {
+                       public boolean exec( Command c )
+                       {
+                               
prefs.setString("GNUNET-INSERT","INDEX-CONTENT","NO");
+                               return true;
+                       }
+                       });
+               options.addOption(new Option("next|N|ID","specify ID of an 
updated version to be published in the future (for namespace insertions only)") 
{
+                       public boolean exec( Command c )
+                       {
+                               HashCode160     nextId;
+
+                               nextId=HashCode160.parse(getValue());
+                               if (nextId==null)
+                                       nextId=HashCode160.create(getValue());
+                               
prefs.setString("GNUNET-INSERT","NEXTHASH",nextId.toHex());
+                               return true;
+                       }
+                       });
+               options.addOption(new Option("sout|o|FILENAME","write the 
created SBlock in plaintext to FILENAME (for namespace insertions only)") {
+                       public boolean exec( Command c )
+                       {
+                               
prefs.setString("GNUNET-INSERT","OUTPUT_SBLOCK",getValue());
+                               return true;
+                       }
+                       });
+               options.addOption(new Option("prio|p|PRIORITY","specify the 
priority of the content") {
+                       public boolean exec( Command c )
+                       {
+                               int     contentPriority;
+
+                               try {
+                                       
contentPriority=Integer.parseInt(getValue());
+                                       
prefs.setInt("GNUNET-INSERT","CONTENT-PRIORITY",contentPriority);
+                                       }
+                               catch( NumberFormatException x ) {
+                                       log(Level.SEVERE,"You must pass a 
number to the -p option.");
+                                       return false;
+                                       }
+                               return true;
+                       }
+                       });
+               options.addOption(new Option("pass|P|PASSWORD","use PASSWORD to 
decrypt the secret key of the pseudonym (for namespace insertions only)") {
+                       public boolean exec( Command c )
+                       {
+                               
prefs.setString("GNUNET-INSERT","PASSWORD",getValue());
+                               return true;
+                       }
+                       });
+               options.addOption(new Option("recursive|R","process directories 
recursively") {
+                       public boolean exec( Command c )
+                       {
+                               
prefs.setString("GNUNET-INSERT","RECURSIVE","YES");
+                               return true;
+                       }
+                       });
+               options.addOption(new Option("pseudonym|s|NAME","publish the 
files under the pseudonym NAME (place file into namespace)") {
+                       public boolean exec( Command c )
+                       {
+                               
prefs.setString("GNUNET-INSERT","PSEUDONYM",getValue());
+                               return true;
+                       }
+                       });
+               options.addOption(new Option("sporadic|S","specifies this as an 
aperiodic but updated publication (for namespace insertions only)") {
+                       public boolean exec( Command c )
+                       {
+                               
prefs.setString("GNUNET-INSERT","SPORADIC","YES");
+                               return true;
+                       }
+                       });
+               options.addOption(new Option("this|t|ID","set the ID of this 
version of the publication (for namespace insertions only)") {
+                       public boolean exec( Command c )
+                       {
+                               HashCode160     thisId;
+
+                               thisId=HashCode160.parse(getValue());
+                               if (thisId==null)
+                                       thisId=HashCode160.create(getValue());
+                               
prefs.setString("GNUNET-INSERT","THISHASH",thisId.toHex());
+                               return true;
+                       }
+                       });
+               options.addOption(new Option("time|T|TIME","specify creation 
time for SBlock (see man-page for format)") {
+                       public boolean exec( Command c )
+                       {
+                               
prefs.setString("GNUNET-INSERT","INSERTTIME",getValue());
+                               return true;
+                       }
+                       });
+               options.addOption(new Option("url|u","print the GNUnet URL of 
the inserted file(s)") {
+                       public boolean exec( Command c )
+                       {
+                               
prefs.setString("GNUNET-INSERT","PRINTURL","YES");
+                               return true;
+                       }
+                       });
+               options.addOption(HELP_VERSION);
+               options.addOption(new Option("verbose|V","be verbose") {
+                       public boolean exec( Command c )
+                       {
+                               
prefs.setString("GNUNET-INSERT","VERBOSE","YES");
+                               return true;
+                       }
+                       });
+               options.addOption(new Option("noextraction|x","disable 
automatic metadata extraction") {
+                       public boolean exec( Command c )
+                       {
+                               
prefs.setString("GNUNET-INSERT","EXTRACT-KEYWORDS","NO");
+                               return true;
+                       }
+                       });
+               options.addOption(new Option("nodirectindex|X","disable 
generation of RBlocks for keywords extracted from each file") {
+                       public boolean exec( Command c )
+                       {
+                               
prefs.setString("GNUNET-INSERT","ADDITIONAL-RBLOCKS","NO");
+                               return true;
+                       }
+                       });
+               return options;
+       }
+
+       /**
+        * Print progess message.
+        *
+        * @param stats
+        * @param verbose
+        */
+
+       protected void printstatus( ProgressStats stats, boolean[] verbose )
+       {
+               if (verbose[0]) {
+                       System.out.print(stats.progress+" of "+stats.filesize+" 
bytes inserted\r");
+                       System.out.flush();
+                       }
+       }
+
+       protected PrivateKey getUsedPseudonym()
+       {
+               PrivateKey      key;
+               String          user,password;
+
+               key=null;
+
+               user=prefs.getString("GNUNET-INSERT","PSEUDONYM",null);
+               if (user!=null) {
+                       
password=prefs.getString("GNUNET-INSERT","PASSWORD",null);
+                       key=new Pseudonym(prefs).readPseudonym(user,password);
+                       if (key==null) {
+                               System.out.println("Could not read pseudonym 
"+user+" (does not exist or password invalid).");
+                               }
+                       }
+               return key;
+       }
+
+       public int run()
+       {
+               CSSession       sock;
+               int                             ret;
+
+               sock=connect();
+               if (sock==null) {
+                       log(Level.SEVERE,"Could not connect to gnunetd.");
+                       return -29;
+                       }
+               ret=run2(sock);
+               return ret;
+       }
+
+       public int run2( CSSession sock )
+       {
+               RootNode[]              roots;
+               String[]                        fileNames,keywords;
+               Extractor[]             extractors;
+               PrivateKey              pseudonym;
+               RootNode                        r;
+               String                  fileName,description,mimetype,shortFN;
+               int                             
fileNameCount,i,num_keywords,interval,skip;
+               boolean[]               verbose;
+               String[]                        filesToInsert;
+               FileIdentifier  fid=null;       //todo: utilisation de 'fid' a 
eclaircir
+
+               filesToInsert=getArguments();
+               if (filesToInsert.length==0) {
+                       log(Level.WARNING,"You must specify a list of files to 
insert.");
+                       getParser().printHelp();
+                       return -11;
+                       }
+
+               if (printAndReturn) {
+                       Extractor[]     l;
+
+                       l=new KeyWords().getExtractors();
+                       for (i=0; i<filesToInsert.length; i++) {
+                               System.out.println("Keywords for file 
"+filesToInsert[i]+":");
+
+                               String[]                k=new 
KeyWords().extractKeywords(new File(filesToInsert[i]),null,null,l);
+                               System.out.println(Arrays.asList(k));
+                               }
+                       return 0;
+                       }
+
+               verbose = new boolean[1];
+               verbose[0]=prefs.testString("GNUNET-INSERT","VERBOSE","YES");
+
+               // check arguments
+               pseudonym=getUsedPseudonym();
+
+               /* fixme: other sanity checks here? */
+
+               fileNameCount = filesToInsert.length;
+               fileNames = filesToInsert;
+
+               if ( ( prefs.testString("GNUNET-INSERT","BUILDDIR","YES") || 
prefs.testString("GNUNET-INSERT","RECURSIVE","YES") || (fileNameCount > 1) ) && 
(null != prefs.getString("GNUNET-INSERT","FILENAMEROOT",null) ) ) {
+                       log(Level.SEVERE,"FATAL: the options -b, -r or multiple 
file arguments can not be used together with option -f.");
+                       return -1;
+                       }
+
+               if (pseudonym == null) {
+                       if (null != 
prefs.getString("GNUNET-INSERT","NEXTHASH",null)) {
+                               log(Level.SEVERE,"FATAL: Option -N makes no 
sense without -P");
+                               return -1;
+                               }
+                       if (null != 
prefs.getString("GNUNET-INSERT","THISHASH",null)) {
+                               log(Level.SEVERE,"FATAL: Option -t makes no 
sense without -P");
+                               return -1;
+                               }
+                       if (null != 
prefs.getString("GNUNET-INSERT","PASSWORD",null)) {
+                               log(Level.SEVERE,"FATAL: Option -P makes no 
sense without -P");
+                               return -1;
+                               }
+                       if (0 != prefs.getInt("GNUNET-INSERT","INTERVAL",0)) {
+                               log(Level.SEVERE,"FATAL: Option -i makes no 
sense without -P");
+                               return -1;
+                               }
+                       if (prefs.testString("GNUNET-INSERT","SPORADIC","YES")) 
{
+                               log(Level.SEVERE,"FATAL: Option -S makes no 
sense without -P");
+                               return -1;
+                               }
+                       }
+
+               if (prefs.testString("GNUNET-INSERT","EXTRACT-KEYWORDS","NO") 
&& prefs.testString("GNUNET-INSERT","ADDITIONAL-RBLOCKS","NO") )
+                       System.out.println("WARNING: -X is implied by -x.");
+
+
+               /* fundamental init */
+               if (sock == null) {
+                       log(Level.SEVERE,"FATAL: could not connect to 
gnunetd.");
+                       return -5;
+                       }
+
+               extractors = new KeyWords().getExtractors();
+
+               mimetype = prefs.getString("GNUNET-INSERT","MIMETYPE",null);
+
+               roots = new RootNode[fileNameCount];
+               skip = 0;
+               fileName = null;
+               for (i=0; i<fileNameCount; i++) {
+                       fileName = new FileLocation(fileNames[i]).getPath();
+
+                       r=insertUtil.insertRecursively(sock,
+                                       fileName,
+                                       (String[]) gloKeywords.toArray(new 
String[gloKeywords.size()]),
+                                       gloKeywords.size(),
+                                       Arrays.asList(extractors),
+                                       new ProgressModel() {
+                                               public void progress( 
ProgressStats stats, Object data )
+                                               {
+                                                       
printstatus(stats,(boolean[]) data);
+                                               }
+                                               },
+                                       verbose,
+                                       new InsertWrapper() {
+                                               public FileIdentifier insert( 
CSSession sockx, String filename, Object closure )
+                                               {
+                                                       return 
doFile(sockx,filename,(boolean[]) closure);
+                                               }
+                                               },
+                                       verbose);
+                       if (r != null) {
+                               fid=r.getFileIdentifier();
+                               roots[i]=(RootNode) PersistentHelper.copy(r);
+                               }
+                       else {
+                               fid=null;
+                               skip++;
+                               }
+                       }
+               if ( (fileNameCount == 1) && new 
DirLocation(fileName).exists()) {
+                       mimetype = GNUNET_DIRECTORY_MIME;
+                       }
+
+               shortFN = prefs.getString("GNUNET-INSERT","FILENAME",null);
+               if ( (shortFN == null) && (fileName != null)) {
+                       int index=fileName.lastIndexOf(File.separatorChar);
+                       shortFN = fileName.substring(index+1);
+                       }
+               fileNameCount -= skip;
+
+               // if build directory and > 1 file, build directory and reduce 
to 1 file
+               if (fileNameCount>1 && 
prefs.testString("GNUNET-INSERT","BUILDDIR","YES") ) {
+                       fileName = 
prefs.getString("GNUNET-INSERT","FILENAMEROOT",null);
+                       if (fileName == null)
+                               fileName = "no name specified";
+
+                       
fid=insertUtil.insertDirectory(sock,fileNameCount,roots,fileName,
+                               new ProgressModel() {
+                                       public void progress( ProgressStats 
stats, Object data )
+                                       {
+                                               printstatus(stats,(boolean[]) 
data);
+                                       }
+                                       },verbose);
+
+                       if (fid==null) {
+                               skip += fileNameCount;
+                               fileNameCount = 0;
+                               }
+                       else {
+                               if 
(prefs.testString("GNUNET-INSERT","PRINTURL",        "YES")) {
+                                       System.out.println(fid.toURI());
+                                       }
+
+                               if (verbose[0]) {
+                                       System.out.println("Directory 
"+fileName+" successfully indexed -- "+fid.toURI());
+                                       }
+
+                               description = 
prefs.getString("GNUNET-INSERT","DESCRIPTION",null);
+                               if (description == null) {
+                                       description = "no description supplied";
+                                       }
+
+                               r=insertUtil.buildDirectoryRBlock(sock,
+                                               fid,
+                                               fileName,
+                                               description,
+                                               (String[]) 
gloKeywords.toArray(new String[gloKeywords.size()]),
+                                               gloKeywords.size());
+                               mimetype = GNUNET_DIRECTORY_MIME;
+                               skip = fileNameCount - 1;
+                               fileNameCount = 1;
+                               }
+                       }
+
+               num_keywords = 0;
+               keywords = null;
+
+               /* if fileNameCount == 1 and !isDirectory(fileName): run 
libextractor! */
+               description = 
prefs.getString("GNUNET-INSERT","DESCRIPTION",null);
+               if (fileNameCount==1 && !new DirLocation(fileName).exists()) {
+                       if 
(!prefs.testString("GNUNET-INSERT","EXTRACT-KEYWORDS","NO")) {
+                               keywords=new 
KeyWords().extractKeywordsMulti(fileName,description,mimetype,extractors);
+                               }
+                       }
+               if (mimetype == null)
+                       mimetype = "unknown";
+               if (description == null)
+                       description = "no description supplied";
+
+               /* if a directory, add mimetype as key unless forbidden */
+               if (mimetype.equals(GNUNET_DIRECTORY_MIME) && 
!prefs.testString("GNUNET-INSERT","ADDITIONAL-RBLOCKS","NO")) {
+                       topKeywords.add(GNUNET_DIRECTORY_MIME);
+                       }
+
+               /* if fileNameCount == 1, add RBlocks for all keywords */
+               if ( fileNameCount == 1) {
+                       r = new RootNode(fid);
+                       r.setDescription(description);
+                       r.setFileName(shortFN);
+                       r.setMimeType(mimetype);
+
+                       for (i=0;i<gloKeywords.size();i++) {
+                               insertRBlock(sock,r, (String) 
gloKeywords.get(i));
+                               System.out.println("Inserting "+shortFN+" 
("+description+", "+mimetype+") under keyword "+gloKeywords.get(i)+".");
+                               }
+                       for (i=0; i<topKeywords.size(); i++) {
+                               insertRBlock(sock,r, (String) 
topKeywords.get(i));
+                               System.out.println("Inserting "+shortFN+" 
("+description+", "+mimetype+") under keyword "+topKeywords.get(i)+".");
+                               }
+
+                       if (! 
prefs.testString("GNUNET-INSERT","ADDITIONAL-RBLOCKS","NO") )
+                               for (i=0;i<num_keywords;i++) {
+                                       insertRBlock(sock,r, keywords[i]);
+                                       System.out.println("Inserting 
"+shortFN+" ("+description+", "+mimetype+") under keyword "+keywords[i]+".");
+                                       }
+                       }
+
+               /* if SBlock and == 1 file, create SBlock */
+               if ( (pseudonym != null) && (fileNameCount == 1) ) {
+                       HashCode160     thisId=new HashCode160();
+                       HashCode160     nextId;
+                       String          hx;
+                       String          prevname;
+                       int                     creationTime;
+                       int                     now;
+                       String          timestr;
+                       Date            d;
+
+                       timestr = 
prefs.getString("GNUNET-INSERT","INSERTTIME",null);
+                       if(timestr != null) {
+                               try {
+                                       d=new SimpleDateFormat("dd-MM-yyyy 
HH:mm").parse(timestr);
+                                       }
+                               catch( ParseException x ) {
+                                       err("Parsing time failed. Use 
\"DAY-MONTHNUMBER-YEAR HOUR:MINUTE\" format",x);
+                                       return -2;
+                                       }
+                               now = (int) (d.getTime()/1000);
+                               log(Level.FINEST,"DEBUG: read time 
"+DateFormat.getInstance().format(d));
+                               }
+                       else {
+                               /* use current time */
+                               now=(int) Scheduler.toSeconds(Scheduler.now());
+                               }
+
+                       prevname = 
prefs.getString("GNUNET-INSERT","PREVIOUS_SBLOCK",null);
+                       if(prevname != null) {
+                               /* options from the previous sblock override */
+                               SBlock          pb;
+                               PublicKey       pkey;
+                               MappedFile      f;
+
+                               f=new FileLocation(prevname).open();
+                               pb=(f!=null ? (SBlock) 
f.asPersistent(SBlock.class) : null);
+                               if (f!=null) {
+                                       f.close();
+                                       }
+                               if (pb==null) {
+                                       log(Level.SEVERE,"SBlock in 
"+prevname+" either doesn't exist or is malformed");
+                                       return -3;
+                                       }
+                               /* check that it matches the selected pseudonym 
*/
+                               pkey=pseudonym.toPublicKey();
+                               if (!pkey.equals(pb.getPublisherKey())) {
+                                       log(Level.SEVERE,"Given SBlock doesn't 
match the selected pseudonym");
+                                       return -4;
+                                       }
+
+                               if (!pb.verify()) {
+                                       log(Level.SEVERE,"Verification of 
SBlock in "+prevname+" failed");
+                                       return -5;
+                                       }
+                               new 
Pseudonym(prefs).addNamespace(pb.getPublisherNameSpace());
+
+                               interval = pb.getUpdateInterval();
+
+                               /* now, compute CURRENT ID and next ID */
+                               thisId=pb.computeIdAtTime(now);
+                               if ( (interval != SBLOCK_UPDATE_NONE) && 
(interval != SBLOCK_UPDATE_SPORADIC) ) {
+                                       int delta;
+
+                                       /* periodic update */
+                                       delta = now - pb.getCreationTime();
+                                       delta = delta / pb.getUpdateInterval();
+                                       if (delta <= 0)
+                                               delta = 1; /* force to be in 
the future from the updated block! */
+                                       creationTime = pb.getCreationTime() + 
delta * pb.getUpdateInterval();
+
+                                       /* periodic update, compute _next_ ID 
as increment! */
+                                       nextId=new HashCode160(thisId); /* n = 
k + inc */
+                                       nextId.add(pb.getIdentifierIncrement());
+                                       }
+                               else {
+                                       creationTime = now;
+                                       if (interval == SBLOCK_UPDATE_SPORADIC) 
{
+                                               log(Level.FINEST,"DEBUG: 
sporadic update in sblock...");
+                                               hx = 
prefs.getString("GNUNET-INSERT","NEXTHASH",null);
+                                               if (hx == null) {
+                                                       
nextId=HashCode160.random();
+                                                       }
+                                               else {
+                                                       
nextId=HashCode160.parse(hx);
+                                                       if (nextId==null) {
+                                                               
nextId=HashCode160.create(hx);
+                                                               }
+                                                       }
+                                               }
+                                       else {
+                                               log(Level.SEVERE,"FATAL: trying 
to update nonupdatable sblock");
+                                               return -6;
+                                               }
+                                       }
+                               }
+                       else {
+                               /* no previous sblock specified */
+                               creationTime = now;
+                               interval = 
prefs.getInt("GNUNET-INSERT","INTERVAL",0);
+
+                               hx = 
prefs.getString("GNUNET-INSERT","THISHASH",null);
+
+                               thisId=HashCode160.parse(hx);
+                               if (thisId==null) {
+                                       thisId=HashCode160.create(hx);
+                                       }
+
+                               hx = 
prefs.getString("GNUNET-INSERT","NEXTHASH",null);
+                               if (hx == null) {
+                                       if (interval == SBLOCK_UPDATE_NONE) {
+                                               /* no next id and no interval 
specified, to be    */
+                                               /* consistent with gnunet-gtk, 
nextId == thisId   */
+                                               nextId=(HashCode160) 
PersistentHelper.copy(thisId);
+                                               }
+                                       else {
+                                               nextId=HashCode160.random();
+                                               }
+                                       }
+                               else {
+                                       nextId=HashCode160.parse(hx);
+                                       if (nextId==null) {
+                                               nextId=HashCode160.create(hx);
+                                               }
+
+                                       if (interval == SBLOCK_UPDATE_NONE) {
+                                               /* if next was specified, 
aperiodic is default */
+                                               interval = 
SBLOCK_UPDATE_SPORADIC;
+                                               }
+                                       }
+                               if 
(prefs.testString("GNUNET-INSERT","SPORADIC","YES"))
+                                       interval = SBLOCK_UPDATE_SPORADIC;
+                               }
+
+
+                       log(Level.FINEST,"Building SBlock "+shortFN+": 
"+description+" -- "+mimetype);
+                       log(Level.FINEST,"Building SBlock with key 
"+thisId.toHex()+" and next key "+nextId.toHex());
+
+                       SBlock  sb;
+                       EncryptedSBlock esb;
+
+                       sb=new SBlock(fid);
+                       sb.setDescription(description);
+                       sb.setFileName(shortFN);
+                       sb.setMimeType(mimetype);
+                       sb.setCreationTime(creationTime);
+                       sb.setUpdateInterval(interval);
+
+                       esb=sb.encryptAndSign(pseudonym,thisId,nextId);
+                       if (esb==null) {
+                               trace("Insertion failure !");
+                               return -101;
+                               }
+
+                       log(Level.FINEST,"Building SBlock for namespace 
"+sb.getPublisherNameSpace().toHex()+" and query "+sb.getIdentifier().toHex());
+
+                       new 
GNDirectoryDatabase(prefs).makeRootNodeAvailable(sb,DIR_CONTEXT_INSERT_SB);
+
+                       if (insertUtil.insert(esb,sock)) {
+                               String  outname;
+
+                               outname = 
prefs.getString("GNUNET-INSERT","OUTPUT_SBLOCK",null);
+                               if (outname != null) {
+                                       MappedFile      mm;
+
+                                       mm=new FileLocation(outname).openNew();
+                                       mm.writePersistent(sb);
+                                       mm.close();
+                                       }
+
+                               /* FIXME: until URI is decided for sblocks, 
stick to SPACE KEY -format */
+                               System.out.println("File "+shortFN+" 
("+description+","+mimetype+") successfully inserted into namespace under  
"+sb.getPublisherNameSpace().toHex()+" "+thisId.toHex());
+                               }
+                       else
+                               System.out.println("Insertion of file into 
namespace failed.");
+                       }
+               return 0;
+       }
+
+       /**
+        * Insert a single file.
+        *
+        * @param sock
+        * @param filename the name of the file to insert
+        * @param verbose
+        * @return resulting file identifier for the node on success, null on 
error
+        */
+
+       protected FileIdentifier doFile( CSSession sock, String filename, final 
boolean[] verbose )
+       {
+               Block                   top;
+               FileIdentifier  fid;
+               long                            startTime;
+
+               startTime=Scheduler.now();
+               if (verbose[0]) {
+                       System.out.println("Working on file "+filename+"...");
+                       }
+
+               top = insertUtil.insertFile(sock,filename,new ProgressModel() {
+                       public void progress( ProgressStats stats, Object data )
+                       {
+                               printstatus(stats,(boolean[]) data);
+                       }
+                       },null);
+
+               if (top == null) {
+                       System.out.println("Error inserting file "+filename+". 
You may want to check whether or not you are out of space. Run gnunet-stats | 
grep \"AFS storage left\" to check.");
+                       return null;
+                       }
+
+               fid=new FileIdentifier(top);
+               if (prefs.testString("GNUNET-INSERT","PRINTURL","YES")) {
+                       System.out.println(fid.toURI());
+                       }
+
+               if (verbose[0]) {
+                       long    elapsed,speed;
+
+                       System.out.println("File "+filename+" successfully 
indexed -- "+fid.toURI());
+
+                       elapsed=Scheduler.toMillis(Scheduler.now()-startTime);
+                       if (elapsed==0) {
+                               elapsed=1;
+                               }
+                       speed=(1000*top.getFileSize())/elapsed;
+                       System.out.println("Speed was 
"+IOUtils.newSizeFormatter().format(new Long(speed))+" per second 
("+top.getFileSize()+" bytes in "+elapsed+" ms).");
+                       }
+               top.destroy(null);
+               return fid;
+       }
+
+       /**
+        * Insert the given RBlock into GNUnet.
+        *
+        * @param sock
+        * @param rb the root node
+        * @param keyword the keyword to use
+        */
+
+       public void insertRBlock( CSSession sock, RootNode rb, String keyword )
+       {
+               if 
(!insertUtil.insertRootWithKeyword(sock,rb,keyword,prefs.getInt("GNUNET-INSERT","CONTENT-PRIORITY",0)))
+                       System.out.println("ERROR inserting root node. Is 
gnunetd running and space available?");
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public static void main( String[] args )
+       {
+               launch(GNUNetInsert.class,args);
+       }
+}

Added: freeway/src/org/gnu/freeway/GNUNetPeerInfo.java
===================================================================
--- freeway/src/org/gnu/freeway/GNUNetPeerInfo.java     2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/GNUNetPeerInfo.java     2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,134 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway;
+
+import org.gnu.freeway.server.*;
+import org.gnu.freeway.transport.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.io.*;
+
+import java.util.logging.*;
+
+/**
+ * Print information about other known peers.
+ */
+
+public class GNUNetPeerInfo extends AbstractServer
+{
+       private static final String     TRUSTDIR                =       
"data/credit/";
+       private static final String     EOL                     =       
System.getProperty("line.separator");
+
+       private DirLocation     trustDirectory;
+
+
+       public GNUNetPeerInfo()
+       {
+               super("gnunet-peer-info",Utils.makeVersion(0,9));
+       }
+
+       public String toString()
+       {
+               return "Peer info";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public OptionsParser createParser()
+       {
+               OptionsParser   options;
+
+               options=new OptionsParser("gnunet-peer-info [OPTIONS]","Print 
information about GNUnet peers.");
+               options.setAllowArguments(false);
+               options.addOption(HELP_CONFIG);
+               options.addOption(HELP_LOGLEVEL);
+               options.addOption(HELP_LOGLEVEL_FILE);
+               options.addOption(HELP_VERSION);
+               return options;
+       }
+
+       public int run()
+       {
+               trustDirectory=getHome().getDirectory(TRUSTDIR);
+               trustDirectory.create();
+
+               ((KnownHostsService) 
service(KnownHostsService.class)).forEachHost(new HostIterator() {
+                       public void iterate( HostIdentity identity, int 
protocol, Object data )
+                       {
+                               printHostInfo(identity,protocol,data);
+                       }
+                       },0,null);
+               return 0;
+       }
+
+       /**
+        * Print information about the peer.
+        * Currently prints the HostIdentity, trust and the IP.
+        * Could of course do more (e.g. resolve via DNS).
+        * @param id
+        * @param proto
+        * @param data
+        */
+
+       protected void printHostInfo( HostIdentity id, int proto, Object data )
+       {
+               P2PHello                helo;
+               MappedFile      f;
+               FileLocation    loc;
+               StringBuffer    bb;
+               String          str;
+               long                    t;
+               int                     trust;
+               Transport       tr;
+
+               helo=((KnownHostsService) 
service(KnownHostsService.class)).identity2HELO(id,proto,false);
+               if (helo==null) {
+                       log(Level.WARNING,"Could not obtain HELO 
("+id.getName()+").");
+                       return;
+                       }
+
+               if (!helo.verify()) {
+                       log(Level.WARNING,"Could not verify HELO 
("+id.getName()+").");
+                       return;
+                       }
+
+               tr=((TransportService) 
service(TransportService.class)).getTransport(helo);
+               str=(tr!=null ? tr.addressToString(helo) : null);
+               if (str==null) {
+                       log(Level.WARNING,"Could not convert HELO to string 
("+id.getName()+").");
+                       return;
+                       }
+
+               trust=0;
+               loc=trustDirectory.getFile(id.getName());
+               if (loc.exists()) {
+                       f=loc.open();
+                       if (f!=null) {
+                               if (loc.getSize()==4) {
+                                       trust=f.readInt(0);
+                                       }
+                               f.close();
+                               }
+                       }
+
+               t=((TransportService) 
service(TransportService.class)).getLastNotConnect(helo);
+
+               bb=new StringBuffer();
+               bb.append("          host : "+id.getName()+EOL);
+               bb.append("         trust : "+trust+EOL);
+               bb.append("       address : "+str+EOL);
+               bb.append(" connectable ? : "+(t>0 ? "not since 
"+Scheduler.toSeconds(Scheduler.now()-t)+"s" : "don't know")+EOL);
+               System.out.println(bb);
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public static void main( String[] args )
+       {
+               launch(GNUNetPeerInfo.class,args);
+       }
+}

Added: freeway/src/org/gnu/freeway/GNUNetPseudonym.java
===================================================================
--- freeway/src/org/gnu/freeway/GNUNetPseudonym.java    2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/GNUNetPseudonym.java    2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,157 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway;
+
+import org.gnu.freeway.protocol.afs.esed2.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.util.logging.*;
+
+/**
+ * create a new pseudoynm
+ * delete a pseudoynm
+ * list all available pseudoynms
+ * create, list or delete pseudoynms
+ */
+
+public class GNUNetPseudonym extends AbstractClient
+{
+       private Prefs   prefs;
+
+
+       public GNUNetPseudonym()
+       {
+               super("gnunet-pseudonym",Utils.makeVersion(1,1,0));
+               prefs=getPreferences();
+       }
+
+       public String toString()
+       {
+               return "Pseudonym management";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public OptionsParser createParser()
+       {
+               OptionsParser   options;
+
+               options=new OptionsParser("gnunet-pseudonym [OPTIONS]","List 
existing, create or delete pseudonyms.");
+               options.setAllowArguments(false);
+               options.addOption(HELP_CONFIG);
+               options.addOption(new Option("create|C|NAME","create a new 
pseudonym (with the given password if specified)") {
+                       public boolean exec( Command c )
+                       {
+                               
prefs.setString("PSEUDONYM","CREATE",getValue());
+                               return true;
+                       }
+                       });
+               options.addOption(new Option("delete|D|NAME","delete the given 
pseudonym") {
+                       public boolean exec( Command c )
+                       {
+                               
prefs.setString("PSEUDONYM","DELETE",getValue());
+                               return true;
+                       }
+                       });
+               options.addOption(HELP_HOSTNAME);
+               options.addOption(HELP_LOGLEVEL);
+               options.addOption(HELP_LOGLEVEL_FILE);
+               options.addOption(new Option("password|p|PASS","use the given 
password for the new pseudonym or to decrypt pseudonyms from the pseudonym 
database") {
+                       public boolean exec( Command c )
+                       {
+                               
prefs.setString("PSEUDONYM","PASSWORD",getValue());
+                               return true;
+                       }
+                       });
+               options.addOption(new Option("quiet|q","do not list the 
pseudonyms from the pseudonym database") {
+                       public boolean exec( Command c )
+                       {
+                               prefs.setString("PSEUDONYM","QUIET","YES");
+                               return true;
+                       }
+                       });
+               options.addOption(HELP_VERSION);
+               return options;
+       }
+
+       public int run()
+       {
+               String[]                list;
+               PrivateKey      hk;
+               String          pname,pass;
+               int                     success;
+               int                     i;
+               String          id;
+               PrivateKey      p;
+               PublicKey       pk;
+               HashCode160     hc;
+               Pseudonym       pseudo;
+
+               pseudo=new Pseudonym(prefs);
+
+               success = 0; /* no errors */
+
+               pname =prefs.getString("PSEUDONYM","DELETE",null);
+               if (pname != null) {
+                       if (pseudo.deletePseudonym(pname)) {
+                               System.out.println("Pseudonym "+pname+" 
deleted.");
+                               }
+                       else {
+                               success += 2;
+                               System.out.println("Error deleting pseudonym 
"+pname+" (does not exist ?).");
+                               }
+                       }
+
+               pass = prefs.getString("PSEUDONYM","PASSWORD",null);
+               pname = prefs.getString("PSEUDONYM","CREATE",null);
+               if (pname != null) {
+                       if (pass==null || pass.length()==0) {
+                               log(Level.WARNING,"No password supplied.");
+                               }
+                       hk=pseudo.createPseudonym(pname,pass);
+                       if (hk==null) {
+                               System.out.println("Could not create pseudonym 
"+pname+" (exists ?).");
+                               success++;
+                               }
+                       else {
+                               System.out.println("Pseudonym "+pname+" 
created.");
+                               }
+                       }
+
+               if (prefs.testString("PSEUDONYM","QUIET","YES"))
+                       return success; /* do not print! */
+
+               list=pseudo.listPseudonyms();
+               if (list==null) {
+                       System.out.println("Could not access pseudonym 
directory.");
+                       return 127;
+                       }
+               for (i=0; i<list.length; i++) {
+                       p=pseudo.readPseudonym(list[i],pass);
+                       if (p!=null) {
+                               pk=p.toPublicKey();
+                               
hc=HashCode160.create(PersistentHelper.toBytes(pk));
+
+                               id=hc.toHex();
+                               }
+                       else {
+                               id = "not decrypted";
+                               }
+                       System.out.println(list[i]+" "+id);
+                       }
+               return success;
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public static void main( String[] args )
+       {
+               launch(GNUNetPseudonym.class,args);
+       }
+}

Added: freeway/src/org/gnu/freeway/GNUNetSearch.java
===================================================================
--- freeway/src/org/gnu/freeway/GNUNetSearch.java       2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/GNUNetSearch.java       2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,409 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway;
+
+import org.gnu.freeway.protocol.afs.esed2.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.io.*;
+import org.gnu.freeway.util.net.*;
+
+import java.io.*;
+import java.util.logging.*;
+
+/**
+ * Main function to search for files on GNUnet.
+ *
+ * Todo: namespace search should be more like the normal
+ * search (add cron for repeated queries, allow multiple results).
+ * But this requires some changes in esed2-lib first!
+ *
+ * OTOH, it would also probably be good if we had a convenience
+ * method for printing RBlocks in esed2-lib like what we have
+ * for SBlocks.
+ */
+
+public class GNUNetSearch extends AbstractClient implements AFSConstants
+{
+       private Prefs   prefs;
+       private Policy  policy;
+
+
+       public GNUNetSearch()
+       {
+               super("gnunet-search",Utils.makeVersion(1,1,0));
+               prefs=getPreferences();
+               policy=null;
+       }
+
+       public String toString()
+       {
+               return "xxx";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public OptionsParser createParser()
+       {
+               OptionsParser   options;
+
+               options=new OptionsParser("gnunet-search [OPTIONS] KEYWORD [AND 
KEYWORD]*","Search GNUnet for files.");
+               options.setAllowArguments(true);
+               options.addOption(new Option("anonymity|a|LEVEL","set the 
desired LEVEL of receiver-anonymity") {
+                       public boolean exec( Command c )
+                       {
+                               int     receivePolicy;
+
+                               receivePolicy=getIntValue(-1);
+                               if (receivePolicy<0) {
+                                       log(Level.WARNING,"FAILURE: You must 
pass a number to the -a option.");
+                                       return false;
+                                       }
+                               
prefs.setInt("AFS","ANONYMITY-RECEIVE",receivePolicy);
+                               return true;
+                       }
+                       });
+               options.addOption(HELP_CONFIG);
+               options.addOption(HELP_HOSTNAME);
+               options.addOption(HELP_LOGLEVEL);
+               options.addOption(HELP_LOGLEVEL_FILE);
+               options.addOption(new Option("max|m|LIMIT","exit after 
receiving LIMIT results") {
+                       public boolean exec( Command c )
+                       {
+                               int     max;
+
+                               max=getIntValue(-1);
+                               if (max<=0) {
+                                       log(Level.WARNING,"You must pass a 
number to the -m option.");
+                                       return false;
+                                       }
+                               prefs.setInt("AFS","MAXRESULTS",max);
+                               return true;
+                       }
+                       });
+               options.addOption(new Option("namespace|n|HEX","only search the 
namespace identified by HEX") {
+                       public boolean exec( Command c )
+                       {
+                               
prefs.setString("GNUNET-SEARCH","NAMESPACE",getValue());
+                               return true;
+                       }
+                       });
+               options.addOption(new Option("output|o|PREFIX","write 
encountered (decrypted) search results to the file PREFIX") {
+                       public boolean exec( Command c )
+                       {
+                               
prefs.setString("GNUNET-SEARCH","OUTPUT_PREFIX",getValue());
+                               return true;
+                       }
+                       });
+               options.addOption(new Option("timeout|t|TIMEOUT","wait TIMEOUT 
seconds for search results before aborting") {
+                       public boolean exec( Command c )
+                       {
+                               int     timeout;
+
+                               timeout=getIntValue(-1);
+                               if (timeout<0) {
+                                       log(Level.WARNING,"You must pass a 
number to the -t option.");
+                                       return false;
+                                       }
+                               prefs.setInt("AFS","SEARCHTIMEOUT",timeout);
+                               return true;
+                       }
+                       });
+               options.addOption(HELP_VERSION);
+               return options;
+       }
+
+       /**
+        * Handle the search result.
+        * @param rootNode
+        * @param sc
+        */
+
+       protected void handleNormalResult( RootNode rootNode, SearchClosure sc )
+       {
+               String  fstring,fname,prefix;
+
+               /* write rblock to file? */
+               prefix = prefs.getString("GNUNET-SEARCH","OUTPUT_PREFIX",null);
+               if (prefix != null) {
+                       String  outfile;
+
+                       
outfile=prefix+"."+Utils.alignRight(sc.resultCount++,3,'0');
+
+                       MappedFile      mm;
+
+                       mm=new FileLocation(outfile).openNew();
+                       mm.writePersistent(rootNode);
+                       mm.close();
+                       }
+
+               sc.max--;
+               fstring = rootNode.getFileIdentifier().toURI();
+
+               if (rootNode.getMimeType().equals(GNUNET_DIRECTORY_MIME)) {
+                       fname = 
GNDirectory.expandDirectoryName(rootNode.getFileName());
+                       }
+               else {
+                       fname = rootNode.getFileName();
+                       }
+
+               System.out.println("gnunet-download -o \""+fname+"\" "+fstring);
+               System.out.println("=> "+rootNode.getDescription()+" <= 
(mimetype: "+rootNode.getMimeType()+")");
+
+               if (sc.max==0) {
+                       shutdown(Signals.SIGUSR1);
+                       }
+       }
+
+       /**
+        * Handle namespace result.
+        * @param sb
+        * @param sqc
+        */
+
+       protected void handleNamespaceResult( SBlock sb, NSSearchClosure sqc )
+       {
+               HashCode160[]   tmp;
+               HashCode160             curK;
+               int                             i;
+               String                  prefix;
+
+               curK=HashCode160.create(PersistentHelper.toBytes(sb));
+
+               for (i=0;i<sqc.resultCount;i++) {
+                       if (curK.equals(sqc.results[i])) {
+                               log(Level.FINEST,"DEBUG: SBlock already 
seen\n");
+                               return; /* displayed already */
+                               }
+                       }
+
+               tmp=new HashCode160[sqc.resultCount+1];
+               System.arraycopy(sqc.results,0,tmp,0,sqc.resultCount);
+               sqc.results=tmp;
+               sqc.resultCount++;
+
+               sqc.results[sqc.resultCount-1]=(HashCode160) 
PersistentHelper.copy(curK);
+               sb.print(new PrintWriter(System.out,true));
+               sqc.max--;
+               /* write sblock to file */
+               prefix = prefs.getString("GNUNET-SEARCH","OUTPUT_PREFIX",null);
+               if (prefix != null) {
+                       String  outfile;
+
+                       
outfile=prefix+"."+Utils.alignRight(sqc.resultCount-1,3,'0');
+
+                       MappedFile      mm;
+
+                       mm=new FileLocation(outfile).openNew();
+                       mm.writePersistent(sb);
+                       mm.close();
+                       }
+               if (sqc.max==0) {
+                       shutdown(Signals.SIGUSR1);
+                       }
+       }
+
+       /**
+        * Perform a normal (non-namespace) search.
+        * @param sock
+        * @param keywords
+        */
+
+       protected void normalSearchMain( CSSession sock, String[] keywords )
+       {
+               final SearchClosure max=new SearchClosure();
+
+               max.max = prefs.getInt("AFS","MAXRESULTS",0);
+               max.resultCount = 0;
+               if (max.max == 0)
+                       max.max = Integer.MAX_VALUE; /* infty */
+
+               SendNSQueryContext.searchRBlock(
+                               prefs,getScheduler(),policy,
+                               sock,
+                               keywords,
+                               keywords.length,
+                               new SearchResultCallback() {
+                                       public void searchResult( RootNode root 
)
+                                       {
+                                               handleNormalResult(root,max);
+                                       }
+
+                                       public void newNameFor( RootNode root, 
String str )
+                                       {
+                                       }
+                                       },
+                               max,
+                               new TestTerminateThread() {
+                                       public boolean test()
+                                       {
+                                               return isShutdowning();
+                                       }
+                                       },
+                               null);
+               keywords=null;
+       }
+
+       /**
+        * Perform a namespace search.
+        * @param sock
+        * @param keywords
+        * @return
+        */
+
+       protected boolean namespaceSearchMain( CSSession sock, String[] 
keywords )
+       {
+               NSSearchClosure sqc;
+               HashCode160             namespace,identifier;
+               String                  nsstring,idstring;
+               int                             i;
+               boolean                 ret;
+
+               nsstring = prefs.getString("GNUNET-SEARCH","NAMESPACE",null);
+
+               sqc=new NSSearchClosure();
+               sqc.max = prefs.getInt("AFS","MAXRESULTS",0);
+               if (sqc.max == 0)
+                       sqc.max = Integer.MAX_VALUE; /* infty */
+               namespace=HashCode160.parse(nsstring);
+
+               idstring="";
+               for (i=0; i<keywords.length; i++) {
+                       idstring+=keywords[i];
+                       }
+               keywords=null;
+
+               identifier=HashCode160.parse(idstring);
+               if (identifier==null) {
+                       log(Level.FINEST,"DEBUG: namespace ID entered is not in 
HEX format, using hash of ASCII text ("+idstring+").");
+                       identifier=HashCode160.create(idstring);
+                       }
+
+               sqc.results = null;
+               sqc.resultCount = 0;
+               ret = new 
SendNSQueryContext().searchSBlock(policy,getScheduler(),prefs,
+                               sock,
+                               namespace,
+                               identifier,
+                               new TestTerminateThread() {
+                                       public boolean test()
+                                       {
+                                               return isShutdowning();
+                                       }
+                                       },
+                               new NSSearchResultCallback() {
+                                       public void searchResult( SBlock sb, 
Object data )
+                                       {
+                                               
handleNamespaceResult(sb,(NSSearchClosure) data);
+                                       }
+                                       },
+                               sqc);
+               if (!ret) {
+                       System.out.println("Sorry, nothing found.");
+                       }
+
+               sqc.results=null;
+               return ret;
+       }
+
+       public int run()
+       {
+               CSSession       sock;
+               int                             ret;
+
+               sock=connect();
+               if (sock==null) {
+                       log(Level.SEVERE,"Could not connect to gnunetd.");
+                       return -29;
+                       }
+               ret=run2(sock);
+               return ret;
+       }
+
+       /**
+        * The main function to search for files on GNUnet.
+        *
+        * @param sock
+        * @return return value from gnunetsearch: 0: ok, -1: error
+        */
+
+       public int run2( final CSSession sock )
+       {
+               Task            searchThread;
+               Priority        priority;
+               final String[]          keywords;
+
+               keywords=getArguments();
+               if (keywords.length==0) {
+                       log(Level.WARNING,"Not enough arguments. You must 
specify a keyword or identifier.");
+                       getParser().printHelp();
+                       return -11;
+                       }
+
+               if (sock == null) {
+                       log(Level.SEVERE,"Could not connect to gnunetd.");
+                       return -5;
+                       }
+
+               policy=new Policy(prefs,connect());
+
+               priority=new Priority(this);
+               priority.startAFSPriorityTracker();
+
+               searchThread=new Task("SEARCH",new AbstractAction() {
+                       public void perform()
+                       {
+                               String  ns;
+                               ns = 
prefs.getString("GNUNET-SEARCH","NAMESPACE",null);
+                               if (ns != null) {
+                                       namespaceSearchMain(sock,keywords);
+                                       }
+                               else {
+                                       normalSearchMain(sock,keywords);
+                                       }
+                       }
+                       });
+               searchThread.launch();
+
+               
waitForShutdown(Scheduler.seconds(prefs.getInt("AFS","SEARCHTIMEOUT",0)));
+
+               sock.disconnect();
+
+               priority.stopAFSPriorityTracker();
+
+               searchThread.join();
+
+               policy.doneAnonymityPolicy();
+               return 0;
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       /**
+        * The main function to search for files on GNUnet.
+        *
+        * @param args command line arguments
+        */
+
+       public static void main( String[] args )
+       {
+               launch(GNUNetSearch.class,args);
+       }
+}
+
+class SearchClosure extends Object
+{
+       public int      resultCount;
+       public int      max;
+}
+
+class NSSearchClosure extends Object
+{
+       public HashCode160[]    results;
+       public int                      resultCount;
+       public int                      max;
+}

Added: freeway/src/org/gnu/freeway/GNUNetShutdown.java
===================================================================
--- freeway/src/org/gnu/freeway/GNUNetShutdown.java     2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/GNUNetShutdown.java     2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,74 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway;
+
+import org.gnu.freeway.server.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.net.*;
+
+/**
+ *
+ */
+
+public class GNUNetShutdown extends AbstractClient
+{
+       public GNUNetShutdown()
+       {
+               super("gnunet-shutdown",Utils.makeVersion(0,6));
+       }
+
+       public String toString()
+       {
+               return "Shutdown";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public OptionsParser createParser()
+       {
+               OptionsParser   options;
+
+               options=new OptionsParser("gnunet-shutdown [OPTIONS]","Shutdown 
gracefully GNUnet daemon.");
+               options.setAllowArguments(false);
+               options.addOption(HELP_CONFIG);
+               options.addOption(HELP_HOSTNAME);
+               options.addOption(HELP_LOGLEVEL);
+               options.addOption(HELP_LOGLEVEL_FILE);
+               options.addOption(HELP_VERSION);
+               return options;
+       }
+
+       public int run()
+       {
+               CSSession               sock;
+               CSResult        rv;
+//todo: verifier avec pid() (voir helper.c dans afs/gtkui)
+
+               sock=connect();
+               try {
+                       if (!sock.send(new CSShutdownRequest())) {
+                               return -1;
+                               }
+
+                       rv=(CSResult) sock.receive(CSResult.class);
+                       if (rv==null) {
+                               return -2;
+                               }
+                       }
+               finally {
+                       sock.disconnect();
+                       }
+               return 0;
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public static void main( String[] args )
+       {
+               launch(GNUNetShutdown.class,args);
+       }
+}

Added: freeway/src/org/gnu/freeway/GNUNetStats.java
===================================================================
--- freeway/src/org/gnu/freeway/GNUNetStats.java        2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/GNUNetStats.java        2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,246 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway;
+
+import org.gnu.freeway.server.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.net.*;
+
+import java.io.*;
+import java.util.logging.*;
+
+/**
+ * Tool to obtain statistics from gnunetd.
+ */
+
+public class GNUNetStats extends AbstractClient
+{
+       private boolean printProtocols;
+       private int             minLevel;
+
+
+       public GNUNetStats()
+       {
+               super("gnunet-stats",Utils.makeVersion(2,0,1));
+               printProtocols=false;
+               minLevel=Stat.NORMAL;
+       }
+
+       public String toString()
+       {
+               return "Statistics";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public OptionsParser createParser()
+       {
+               OptionsParser   options;
+
+               options=new OptionsParser("gnunet-stats [OPTIONS]","Print 
statistics about GNUnet operations.");
+               options.setAllowArguments(false);
+               options.addOption(HELP_CONFIG);
+               options.addOption(HELP_LOGLEVEL);
+               options.addOption(HELP_LOGLEVEL_FILE);
+               options.addOption(new Option("protocols|p","prints supported 
protocol messages") {
+                       public boolean exec( Command c )
+                       {
+                               printProtocols=true;
+                               return true;
+                       }
+                       });
+               options.addOption(new Option("min-level|m|LEVEL","set minimum 
level of verbosity to LEVEL") {
+                       public boolean exec( Command c )
+                       {
+                               minLevel=getIntValue(Stat.NORMAL);
+                               return true;
+                       }
+                       });
+               options.addOption(HELP_HOSTNAME);
+               options.addOption(HELP_VERSION);
+               return options;
+       }
+
+       public int run()
+       {
+               CSSession               sock;
+               PrintWriter             out;
+               boolean                 res;
+
+               sock=connect();
+               //todo: faire le test dans la superclasse!!!
+               if (sock==null) {
+                       log(Level.SEVERE,"Could not connect to gnunetd.");
+                       return -5;
+                       }
+
+               out=new PrintWriter(System.out);
+
+               res=requestAndPrintStatistics(out,sock);
+               if (res && printProtocols) {
+                       res=requestAndPrintProtocols(out,sock);
+                       }
+               printClients(out,sock);
+               out.flush();
+
+               sock.disconnect();
+               return (res ? 0 : 1);
+       }
+
+       /**
+        * Print statistics received from TCP socket.
+        * @param out where to print the statistics
+        * @param sock the socket to use
+        * @return OK on success, false on error
+        */
+
+       protected boolean requestAndPrintStatistics( PrintWriter out, CSSession 
sock )
+       {
+               Stat[]          stats;
+               CSStatistics            statMsg;
+               long                            t;
+               int                             totalCounters,count,i;
+
+               if (!sock.send(new CSStatisticsRequest(minLevel))) {
+                       out.println("Error sending request for statistics to 
gnunetd.");
+                       return false;
+                       }
+
+               out.println("Statistics :");
+
+               count=0;
+               totalCounters=1;
+
+               while (count<totalCounters) {
+                       statMsg=(CSStatistics) sock.receive(CSStatistics.class);
+                       if (statMsg==null) {
+                               out.println("Error receiving reply for 
statistics from gnunetd.");
+                               return false;
+                               }
+
+                       if (count==0) {
+                               t=Scheduler.toSeconds(Scheduler.now());
+                               t-=statMsg.getStartTime();
+
+                               out.println(Utils.alignRight("Uptime 
(seconds)",60)+" : "+Utils.alignLeft(t,16));
+                               totalCounters=statMsg.getTotalCounters();
+                               }
+
+                       if (statMsg.getTotalCounters()!=totalCounters) {
+                               out.println("Corrupted data ?");
+                               return false;
+                               }
+
+                       stats=statMsg.getStatistics();
+                       for (i=0; i<stats.length; i++) {
+                               
out.println(Utils.alignRight(stats[i].getName()+" /"+stats[i].getLevel(),60)+" 
: "+Utils.alignLeft(stats[i].get(),16));
+                               }
+                       count+=stats.length;
+                       }
+               out.println();
+               return true;
+       }
+
+       /**
+        * Queries the server for what protocol messages are supported and 
prints a list of them.
+        *
+        * @param out where to print the statistics
+        * @param sock the socket to use
+        * @return true on success, false on error
+        */
+
+       protected boolean requestAndPrintProtocols( PrintWriter out, CSSession 
sock )
+       {
+               CSResult        rv;
+               String                  name;
+               int                             i;
+
+               out.println("Supported Peer to Peer messages :");
+               for (i=0; i<500; i++) {
+                       if (!sock.send(new CSGetP2PMessageSupported(i))) {
+                               out.println("Error sending request for p2p 
protocol status to gnunetd.");
+                               return false;
+                               }
+
+                       rv=(CSResult) sock.receive(CSResult.class);
+                       if (rv==null) {
+                               out.println("Error reading p2p protocol status 
to gnunetd.");
+                               return false;
+                               }
+
+                       if (rv.isOkay()) {
+                               out.print("\t"+i);
+                               name = P2PMessage.nameFor(i);
+                               if (name != null) {
+                                       out.print("\t("+name+")");
+                                       }
+                               out.println();
+                               }
+                       }
+               out.println();
+
+               out.println("Supported Client Server messages :");
+               for (i=0; i<1020; i++) {
+                       if (!sock.send(new CSGetCSMessageSupported(i))) {
+                               out.println("Error sending request for 
client-server protocol status to gnunetd.");
+                               return false;
+                               }
+
+                       rv=(CSResult) sock.receive(CSResult.class);
+                       if (rv==null) {
+                               out.println("Error reading client-server 
protocol status to gnunetd.");
+                               return false;
+                               }
+
+                       if (rv.isOkay()) {
+                               out.print("\t"+i);
+                               name = CSMessage.nameFor( i );
+                               if (name != null) {
+                                       out.print("\t("+name+")");
+                                       }
+                               out.println();
+                               }
+                       }
+               out.println();
+               return true;
+       }
+
+       protected boolean printClients( PrintWriter out, CSSession sock )
+       {
+               CSResult        rv;
+
+               out.println("Connected clients :");
+
+               if (!sock.send(new CSGetClientCount())) {
+                       out.println("I/O error.");
+                       return false;
+                       }
+
+               rv=(CSResult) sock.receive(CSResult.class);
+               if (rv==null) {
+                       out.println("I/O error.");
+                       return false;
+                       }
+
+               out.println("  Count : "+rv.getResult());
+               out.println();
+               return true;
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       /**
+        * The main function to obtain statistics from gnunetd.
+        *
+        * @param args command line arguments
+        */
+
+       public static void main( String[] args )
+       {
+               launch(GNUNetStats.class,args);
+       }
+}

Added: freeway/src/org/gnu/freeway/GNUNetSwing.java
===================================================================
--- freeway/src/org/gnu/freeway/GNUNetSwing.java        2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/GNUNetSwing.java        2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,409 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway;
+
+import org.gnu.freeway.protocol.afs.esed2.*;
+import org.gnu.freeway.protocol.afs.swing.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.io.*;
+import org.gnu.freeway.util.net.*;
+import org.gnu.freeway.util.ui.*;
+
+import EDU.oswego.cs.dl.util.concurrent.*;
+
+import java.beans.*;
+import java.io.*;
+import java.net.*;
+import java.util.logging.*;
+import javax.swing.*;
+
+/**
+ * This is the main file for the swing user interface.
+ *
+ * Basic structure of the code is this:
+ * main
+ * -> search -> saveas -> download
+ * -> insert
+ * -> directory -> insert
+ * -> pseudonyms (create|delete)
+ * -> namespace insert/update
+ * -> about
+ */
+
+public class GNUNetSwing extends AbstractClient implements AFSConstants
+{
+       /** */
+       private Prefs   prefs;
+       private SController     controller;
+
+       /** */
+       private Policy                                  policy;
+
+       /** This semaphore can be used to prevent the main window from killing 
GTK at an unhealty time.
+        It is used by the un-interruptable but GUI-updating insert thread.
+        This semaphore can be up'ed by threads that will need the GUI and that 
want to prevent gnunet-gtk
+        from exiting while they are running. */
+       private Semaphore                               refuseToDie;
+
+       private SearchWindow                    mainWindow;
+       private DownloadWindow                  dlWindow;
+       private StatsWindow                             statsWindow;
+       private PropertyChangeSupport   support;
+       private UIResources                             resources;
+
+
+       public GNUNetSwing()
+       {
+               super("gnunet-swing",Utils.makeVersion(0,0,1));
+               prefs=getPreferences();
+               support=new PropertyChangeSupport(this);
+               controller=createController();
+
+               resources=new UIResources("main.xml",controller.getCache());
+               resources.setGlobalTarget(this);
+       }
+
+       public String toString()
+       {
+               return "Swing basic interface";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public OptionsParser createParser()
+       {
+               OptionsParser   options;
+
+               options=new OptionsParser("gnunet-swing [OPTIONS]","Start 
GNUnet graphical client application.");
+               options.setAllowArguments(false);
+               options.addOption(HELP_CONFIG);
+               options.addOption(HELP_HOSTNAME);
+               options.addOption(HELP_LOGLEVEL);
+               options.addOption(HELP_LOGLEVEL_FILE);
+               options.addOption(HELP_VERSION);
+               options.addOption(new Option("nosplash|n","disable splash 
screen") {
+                       public boolean exec( Command c )
+                       {
+                               prefs.setString("GNUNET-SWING","SPLASH","NO");
+                               return true;
+                       }
+                       });
+               options.addOption(new Option("remote|r|URL","enable downloading 
of daemon classes from URL") {
+                       public boolean exec( Command c )
+                       {
+                               URL     url;
+
+                               try {
+                                       url=new URL(getValue());
+                                       
prefs.setString("GNUNET-SWING","REMOTE",url.toString());
+                                       }
+                               catch( MalformedURLException x ) {
+                                       err("Invalid URL !",x);
+                                       return false;
+                                       }
+                               return true;
+                       }
+                       });
+               return options;
+       }
+
+       protected SController createController()
+       {
+               return new SController() {
+                       public void enterCritical()
+                       {
+                               try {
+                                       refuseToDie.acquire();
+                                       }
+                               catch( InterruptedException x ) {
+                                       err("",x);
+                                       }
+                       }
+
+                       public void leaveCritical()
+                       {
+                               refuseToDie.release();
+                       }
+
+                       public File getCache()
+                       {
+                               return getPreferences().getSystemCache();
+                       }
+                       public Application getApplication()
+                       {
+                               return GNUNetSwing.this;
+                       }
+
+                       public UIResources getResources()
+                       {
+                               return GNUNetSwing.this.resources;
+                       }
+
+                       public Service service( Class c )
+                       {
+                               return GNUNetSwing.this.service(c);
+                       }
+
+                       public void log( Level level, String msg )
+                       {
+                               GNUNetSwing.this.log(level,msg);
+                       }
+
+                       public void err( String msg, Throwable x )
+                       {
+                               GNUNetSwing.this.err(msg,x);
+                       }
+
+                       public Policy getPolicy()
+                       {
+                               return policy;
+                       }
+
+                       public void refreshMenus()
+                       {
+                               mainWindow.refreshMenus();
+                       }
+
+                       public void showStats()
+                       {
+                               GNUNetSwing.this.showStats();
+                       }
+
+                       public void download( Node node )
+                       {
+                               openSaveAs(node);
+                       }
+
+                       public void guiMessage( String str )
+                       {
+                               JOptionPane.showMessageDialog(mainWindow,str);
+                       }
+
+                       public void infoMessage( boolean doPopup, String str )
+                       {
+                       }
+
+                       public void displayDirectory( String fileName, Node 
root )
+                       {
+                               //todo: a implementer
+                       }
+
+                       public CSSession connect()
+                       {
+                               return GNUNetSwing.this.connect();
+                       }
+       public Prefs getPreferences()
+       {
+               return GNUNetSwing.this.getPreferences();
+       }
+       public void addPropertyChangeListener( PropertyChangeListener listener )
+       {
+               support.addPropertyChangeListener(listener);
+       }
+
+       public void addPropertyChangeListener( String name, 
PropertyChangeListener listener )
+       {
+               support.addPropertyChangeListener(name,listener);
+       }
+
+       public void removePropertyChangeListener( PropertyChangeListener 
listener )
+       {
+               support.removePropertyChangeListener(listener);
+       }
+
+       public void removePropertyChangeListener( String name, 
PropertyChangeListener listener )
+       {
+               support.removePropertyChangeListener(name,listener);
+       }
+                       };
+       }
+
+       public int run()
+       {
+               GSplash         splash;
+               DirLocation     downloadDir;
+               Scheduler       scheduler;
+
+               scheduler=getScheduler();
+
+               policy=new Policy(prefs,connect());
+
+               downloadDir=prefs.getDirLocation("AFS","DOWNLOADDIR");
+               if (downloadDir!=null) {
+                       downloadDir.create();
+                       //todo: faire un chdir() sur le repertoire
+                       }
+
+               refuseToDie = new Semaphore(1);
+
+               mainWindow=new SearchWindow(controller);
+
+               if (!prefs.testString("GNUNET-SWING","SPLASH","NO")) {
+                       try {
+                               splash=new 
GSplash(resources.getImageURL("splash"),5);
+                               splash.splash();
+                               }
+                       catch( IOException x ) {
+                               err("Unable to display splash screen !",x);
+                               }
+                       }
+
+               /* Check if gnunetd is running */
+               checkForDaemon();
+
+               /* refresh the kill/launch sensitivities once per two secs */
+               scheduler.addJob(new ScheduledTask("CHECK-DAEMON",new 
EvalAction(mainWindow,"cronCheckDaemon"),Scheduler.SECS_2),0);
+
+               mainWindow.display();
+
+               Scheduler.sleep();
+
+               log(Level.FINEST,"GUI leaving...");
+               refuseToDie=null;
+               policy.doneAnonymityPolicy();
+               policy=null;
+               scheduler.deleteJob(new ScheduledTask("CHECK-DAEMON",new 
EvalAction(mainWindow,"cronCheckDaemon"),Scheduler.SECS_2));
+               return 0;
+       }
+
+       public void checkForDaemon()
+       {
+       }
+
+       public void onQuit()
+       {
+               
support.firePropertyChange(SController.QUIT_EVENT,null,Boolean.TRUE);
+       }
+
+       /**
+        * Open the window that prompts the user for the
+        * filename.
+        * This method must open the window,
+        * copy the arguments and return. After the method
+        * returns, the arguments passed to it will be
+        * freed, so pointer should not be retained.
+        * The method executes during a signal handler,
+        * so a GTK lock is not required to to GUI
+        * operations.
+        * Open the window that prompts the user for the filename.  This
+        * method must open the window, copy the arguments and return.  After
+        * the method returns, the arguments passed to it will be freed, so
+        * pointer should not be retained.  The method executes during a
+        * signal handler, so a GTK lock is not required to to GUI operations.
+        *
+        * @param node search result of the file to download
+        */
+
+       protected void openSaveAs( Node node )
+       {
+               JFileChooser            fc;
+               File                            file;
+               SaveAs                  saveas;
+               StringBuffer            buf;
+               int                             ret,i;
+               char                            c;
+
+               saveas=new SaveAs();
+               saveas.root=(Node) PersistentHelper.copy(node);
+               saveas.filename="";
+
+               // if the search result specified a suggested filename, fill it 
in !
+               /* if it is a GNUnet directory, replace suffix '/' with ".gnd" 
*/
+               if (node.getMimeType().equals(GNUNET_DIRECTORY_MIME)) {
+                       
saveas.filename=GNDirectory.expandDirectoryName(node.getFileName());
+                       }
+               else {
+                       saveas.filename=node.getFileName();
+                       }
+
+               log(Level.FINEST,">>>>>>> "+saveas.filename);
+               if (saveas.filename.length()==0 || 
prefs.testString("GNUNET-GTK","ALWAYS-ASK-SAVEAS","YES")) {
+                       fc=new JFileChooser();
+                       fc.setDialogTitle("save as");
+                       fc.setSelectedFile(new File(saveas.filename));//todo: 
ne semble pas marcher
+
+                       ret=fc.showSaveDialog(mainWindow);
+                       if (ret!=JFileChooser.APPROVE_OPTION) {
+                               log(Level.INFO,"Save aborted.");
+                               return;
+                               }
+
+                       file=fc.getSelectedFile();
+                       saveas.filename=file.getPath();
+                       }
+               else {
+                       // sanity check the filename
+                       buf=new StringBuffer();
+                       for(i=0; i<saveas.filename.length();i++) {
+                               c=saveas.filename.charAt(i);
+                               switch(c) {
+                                       case '*':
+                                       case '/':
+                                       case '\\':
+                                       case '?':
+                                       case ':':
+                                               c='_';
+                                               break;
+                                       }
+                               buf.append(c);
+                                 }
+                       saveas.filename=new 
FileLocation(buf.toString()).getPath();
+                       }
+               startDownload(saveas.filename,saveas.root);
+       }
+
+       protected void startDownload( String str, Node node )
+       {
+               log(Level.INFO,"Save to : "+str);
+
+               if (dlWindow==null) {
+                       dlWindow=new DownloadWindow(controller);
+                       }
+
+               dlWindow.display();
+               dlWindow.addDownload(str,node);
+       }
+
+       public void onShowDownload()
+       {
+               if (dlWindow==null) {
+                       dlWindow=new DownloadWindow(controller);
+                       }
+
+               dlWindow.display();
+       }
+
+       /**
+        * A not so dirty way to pump some stats from gnunetd
+        */
+
+       public void showStats()
+       {
+               if (statsWindow==null) {
+                       statsWindow=new StatsWindow(controller);
+                       }
+               statsWindow.display();
+               statsWindow.onRefresh();
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public static void main( String[] args )
+       {
+               launch(GNUNetSwing.class,args);
+       }
+}
+
+/**
+ * State of the SaveAs window
+ */
+
+class SaveAs extends Object
+{
+       public Node     root;
+       public String   filename;
+}

Added: freeway/src/org/gnu/freeway/GNUNetTests.java
===================================================================
--- freeway/src/org/gnu/freeway/GNUNetTests.java        2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/GNUNetTests.java        2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,155 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway;
+
+import org.gnu.freeway.test.*;
+import org.gnu.freeway.util.*;
+
+import java.util.logging.*;
+
+/**
+ */
+
+public class GNUNetTests extends AbstractClient implements Signals
+{
+       public GNUNetTests()
+       {
+               super("gnunet-tests",Utils.makeVersion(1,0));
+       }
+
+       public String toString()
+       {
+               return "Test suite";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public OptionsParser createParser()
+       {
+               OptionsParser   options;
+
+               options=new OptionsParser("gnunet-tests [OPTIONS]","Mini test 
suite.");
+               options.setAllowArguments(false);
+               options.addOption(HELP_CONFIG);
+               options.addOption(HELP_HOSTNAME);
+               options.addOption(HELP_LOGLEVEL);
+               options.addOption(HELP_LOGLEVEL_FILE);
+               options.addOption(HELP_VERSION);
+               return options;
+       }
+
+       public int run()
+       {
+               boolean succeedeed;
+
+               succeedeed=true;
+
+/*             out("=== Bloom Test ===");
+               ucceedeed=(succeedeed && new BloomTest().test(this));
+               out("==================");
+*/
+/*             out("=== Charset Test ===");
+               succeedeed=(succeedeed && new CharsetTest().test(this));
+               out("====================");
+*/
+/*             out("=== CRC Test ===");
+               new CRCTest().test(this);
+               out("================");
+*/
+/*             out("=== Cron Test ===");
+               new CronTest().test(this);
+               out("=================");
+*/
+/*             out("=== Filter Test ===");
+               new FilterTest().test(this);
+               out("===================");
+*/
+/*             out("=== Hash Test ===");
+               new HashTest().test(this);
+               out("=================");
+*/
+/*             out("=== Layout Test ===");
+               new LayoutTest().test(this);
+               out("===================");
+*/
+/*             out("=== MySQL Test ===");
+               new MySQLTest().test(this);
+               out("==================");
+*/
+/*             out("=== RSA Test ===");
+               new RSATest().test(this);
+               out("================");
+*/
+/*             out("=== Semaphore Test ===");
+               new SemaphoreTest().test(this);
+               out("======================");
+*/
+/*             out("=== Shutdown Test ===");
+               new ShutdownTest().test(this);
+               out("=====================");
+*/
+/*             out("=== Signals Test ===");
+               new SignalsTest().test(this);
+               out("====================");
+*/
+/*             out("=== State Test ===");
+               new StateTest().test(this);
+               out("==================");
+*/
+/*             out("=== Status Calls Test ===");
+               new StatusCallsTest().test(this);
+               out("=========================");
+*/
+               out("=== Storage Test ===");
+               new StorageTest().test(this);
+               out("====================");
+
+/*             out("=== Sym. Cypher Test ===");
+               new SymCipherTest().test(this);
+               out("====================");
+*/
+/*             out("=== TCP Test ===");
+               new TCPTest().test(this);
+               out("================");
+*/
+/*             out("=== Table Test ===");
+               new TableTest().test(this);
+               out("==================");
+*/
+/*             out("=== Timer Test ===");
+               new TimerTest().test(this);
+               out("==================");
+*/
+               // erase configuration values, must be run at the end
+/*             out("=== Config Test ===");
+               new ConfigTest().test(this);
+               out("===================");
+*/             return (succeedeed ? 0 : -1);
+       }
+
+       public void out( String str )
+       {
+               log(Level.INFO,str);
+       }
+
+       public void err( String str )
+       {
+               log(Level.WARNING,str);
+       }
+
+       public void err( String str, Throwable x )
+       {
+               err(str,x);
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public static void main( String[] args )
+       {
+               launch(GNUNetTests.class,args);
+       }
+}

Added: freeway/src/org/gnu/freeway/GNUNetTrace.java
===================================================================
--- freeway/src/org/gnu/freeway/GNUNetTrace.java        2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/GNUNetTrace.java        2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,235 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway;
+
+import org.gnu.freeway.protocol.tracekit.*;
+import org.gnu.freeway.server.*;
+import org.gnu.freeway.transport.*;
+import org.gnu.freeway.transport.tcp.*;
+import org.gnu.freeway.transport.udp.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.util.*;
+import java.util.logging.*;
+
+/**
+ * tool that sends a trace request and prints the received network topology
+ */
+
+public class GNUNetTrace extends AbstractClient
+{
+       private Prefs   prefs;
+
+
+       public GNUNetTrace()
+       {
+               super("gnunet-tracekit",Utils.makeVersion(0,1,2));
+               prefs=getPreferences();
+       }
+
+       public String toString()
+       {
+               return "xxx";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public OptionsParser createParser()
+       {
+               OptionsParser   options;
+
+               options=new OptionsParser("gnunet-tracekit [OPTIONS]","Trace 
GNUnet network topology.");
+               options.setAllowArguments(false);
+               options.addOption(HELP_CONFIG);
+               options.addOption(new Option("depth|D|DEPTH","probe network to 
the given DEPTH") {
+                       public boolean exec( Command c )
+                       {
+                               
prefs.setInt("GNUNET-TRACEKIT","HOPS",Integer.parseInt(getValue()));
+                               return true;
+                       }
+                       });
+               options.addOption(new Option("format|F|FORMAT","0 for human 
readable output, 1 for dot") {
+                       public boolean exec( Command c )
+                       {
+                               //todo: to implement
+                               return true;
+                       }
+                       });
+               options.addOption(HELP_LOGLEVEL);
+               options.addOption(HELP_LOGLEVEL_FILE);
+               options.addOption(new Option("priority|P|PRIO","use PRIO for 
the priority of the trace request") {
+                       public boolean exec( Command c )
+                       {
+                               
prefs.setInt("GNUNET-TRACEKIT","PRIORITY",Integer.parseInt(getValue()));
+                               return true;
+                       }
+                       });
+               options.addOption(HELP_VERSION);
+               options.addOption(new Option("wait|W|DELAY","wait DELAY seconds 
for replies") {
+                       public boolean exec( Command c )
+                       {
+                               
prefs.setInt("GNUNET-TRACEKIT","WAIT",Integer.parseInt(getValue()));
+                               return true;
+                       }
+                       });
+               options.addOption(HELP_HOSTNAME);
+               return options;
+       }
+
+       protected void printHostInfos( HostIdentity host )
+       {
+               P2PHello[]              helos;
+               CSSession       sock;
+               CSHostInfo              infos;
+               TCPAddress              tcpAddr;
+               UDPAddress              udpAddr;
+               int                             i;
+
+               sock=connect();
+
+               infos=null;
+               if (sock.send(new CSGetHostInfo(host))) {
+                       infos=(CSHostInfo) sock.receive(CSHostInfo.class);
+                       }
+
+               if (infos!=null) {
+                       helos=infos.getHELOs();
+
+                       System.out.println("Host \""+host.getName()+"\" is 
advertised "+helos.length+" times");
+                       for (i=0; i<helos.length; i++) {
+                               switch (helos[i].getProtocol()) {
+                                       case Transport.TCP_PROTOCOL_NUMBER:
+                                               tcpAddr=(TCPAddress) 
helos[i].decodeSenderAddress(TCPAddress.class);
+                                               System.out.println(" Protocol 
TCP : "+tcpAddr.getIP().getHostAddress()+" at "+tcpAddr.getPort());
+                                               break;
+                                       case Transport.UDP_PROTOCOL_NUMBER:
+                                               udpAddr=(UDPAddress) 
helos[i].decodeSenderAddress(UDPAddress.class);
+                                               System.out.println(" Protocol 
UDP : "+udpAddr.getIP().getHostAddress()+" at "+udpAddr.getPort());
+                                               break;
+                                       default:
+                                               System.out.println(" Protocol 
#"+helos[i].getProtocol());
+                                               break;
+                                       }
+                               }
+                       }
+               else {
+                       System.err.println("Failed to talk with daemon.");
+                       }
+
+               sock.disconnect();
+       }
+
+       public int run()
+       {
+               CSSession       sock;
+               int                     ret;
+               Task                    thread;
+
+               sock=connect();
+               if (sock==null) {
+                       log(Level.SEVERE,"Could not connect to gnunetd.");
+                       return -29;
+                       }
+
+               final CSSession _sock = sock;
+
+               log(Level.INFO,"Start receive thread.");
+               thread=new Task("CLIENT-RECEIVE",new AbstractAction() {
+                       public void perform()
+                       {
+                               receive333(_sock);
+                       }
+                       });
+               thread.launch();
+
+               ret=process2(sock);
+
+               log(Level.INFO,"Waiting for receive thread to finish.");
+
+               sock.disconnect();
+               thread.join();
+
+               return ret;
+       }
+
+       protected void receive333( CSSession sock )
+       {
+               CSTraceReply    reply;
+               List                    peersSeen;
+               List                    peersResponding;
+               int                             count,i;
+               HostIdentity    host;
+
+               peersSeen=new ArrayList();
+               peersResponding=new ArrayList();
+
+               reply=(CSTraceReply) sock.receive(CSTraceReply.class);
+               while (reply!=null) {
+                       host=reply.responderId;
+                       if (!peersResponding.contains(host)) {
+                               peersResponding.add(host);
+
+                               
System.out.println("===============================================================================================");
+                               printHostInfos(host);
+                               System.out.println();
+                               }
+
+                       count=reply.getHostsCount();
+                       if (count==0) {
+                               System.out.println(host.getName()+" is not 
connected to any peer.");
+                               }
+                       else {
+                               for (i=0; i<count; i++) {
+                                       if 
(!peersSeen.contains(reply.getHost(i))) {
+                                               
peersSeen.add(PersistentHelper.copy(reply.getHost(i)));
+                                               }
+                                       System.out.println(host.getName()+" 
connected to "+reply.getHost(i).getName()+".");
+                                       }
+                               }
+                       System.out.println();
+
+                       reply=(CSTraceReply) sock.receive(CSTraceReply.class);
+                       }
+
+               for (i=0; i<peersSeen.size(); i++) {
+                       if (!peersResponding.contains(peersSeen.get(i))) {
+                               System.out.println("Peer "+((HostIdentity) 
peersSeen.get(i)).getName()+" did not report back.");
+                               }
+                       }
+       }
+
+       protected int process2( CSSession sock )
+       {
+               CSTraceProbe    probe;
+               int                             sleepTime;
+
+               probe=new CSTraceProbe();
+               probe.hops = prefs.getInt("GNUNET-TRACEKIT","HOPS",0);
+               probe.priority= prefs.getInt("GNUNET-TRACEKIT","PRIORITY",0);
+               if (!sock.send(probe)) {
+                       log(Level.SEVERE,"Could not send request to daemon.");
+                       return -1;
+                       }
+
+               sleepTime=prefs.getInt("GNUNET-TRACEKIT","WAIT",0);
+               if (sleepTime==0) {
+                       sleepTime=30;
+                       }
+
+               waitForShutdown(Scheduler.seconds(sleepTime));
+               return 0;
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public static void main( String[] args )
+       {
+               launch(GNUNetTrace.class,args);
+       }
+}

Added: freeway/src/org/gnu/freeway/GNUNetTransportCheck.java
===================================================================
--- freeway/src/org/gnu/freeway/GNUNetTransportCheck.java       2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/GNUNetTransportCheck.java       2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,609 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway;
+
+import org.gnu.freeway.server.*;
+import org.gnu.freeway.transport.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.io.*;
+import org.gnu.freeway.util.net.*;
+
+import EDU.oswego.cs.dl.util.concurrent.*;
+
+import java.nio.*;
+import java.util.*;
+import java.util.logging.*;
+
+/**
+ * Test for the transports.
+ *
+ * This utility can be used to test if a transport mechanism for GNUnet is 
properly configured.
+ */
+
+public class GNUNetTransportCheck extends AbstractServer
+{
+       public static final byte[]      DEFAULT_DATA    =       "Hello, World 
!".getBytes();
+
+       public static final boolean     DEBUG_TRANSPORT_CHECK           =       
true;
+
+       private Semaphore       semaphore;
+       private ScheduledTask           releaseSemaphore;
+       private ScheduledTask           releaseSemaphoreEvery5S;
+       private byte[]          testData;
+       private MessagePack     received;
+       private boolean         failed;
+       private boolean         terminate;
+       private long                    timeout = Scheduler.SECS_15;
+
+       private TransportService                transports;
+       private Prefs   prefs;
+       private Scheduler                       scheduler;
+       private HELOLoader                      loader;
+
+
+       public GNUNetTransportCheck()
+       {
+               super("gnunet-transport-check",Utils.makeVersion(0,9,8));
+
+               semaphore=new Semaphore(0);
+               releaseSemaphore=new ScheduledTask("RELEASE-SEMAPHORE",new 
AbstractAction() {
+                       public void perform()
+                       {
+                               terminate=true;
+                               semaphore.release();
+                       }
+                       });
+               releaseSemaphoreEvery5S=new 
ScheduledTask("RELEASE-SEMAPHORE",new AbstractAction() {
+                       public void perform()
+                       {
+                               terminate=true;
+                               semaphore.release();
+                       }
+                       },Scheduler.SECS_5);
+
+               testData=DEFAULT_DATA;
+               received=null;
+               failed=false;
+
+               TransportService.useCoreFacade(new CoreForTransport() {
+                       public int getVersion()
+                       {
+                               return 0;
+                       }
+
+                       public HostIdentity getIdentity()
+                       {
+                               return ((CoreService) 
service(CoreService.class)).getIdentity();
+                       }
+
+                       public org.gnu.freeway.util.Service service( Class c )
+                       {
+                               return GNUNetTransportCheck.this.service(c);
+                       }
+
+                       public void receive( MessagePack mp )
+                       {
+                               if ( (semaphore == null) || (received != null) 
) {
+                                       return; /* spurious receive or 
double-receive, happens */
+                                       }
+                               if 
(prefs.testString("GNUNET-TRANSPORT-CHECK","VERBOSE","YES")) {
+                                       System.err.println(".");
+                                       }
+
+                               received = mp;
+                               semaphore.release();
+                       }
+
+                       public Application getApplication()
+                       {
+                               return GNUNetTransportCheck.this;
+                       }
+                       });
+               transports=(TransportService) service(TransportService.class);
+               prefs=getPreferences();
+               loader=(HELOLoader) service(HELOLoader.class);
+       }
+
+       public String toString()
+       {
+               return "Transport layer check";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public OptionsParser createParser()
+       {
+               OptionsParser   options;
+
+               options=new OptionsParser("gnunet-transport-check 
[OPTIONS]","Test if GNUnet transport services are operational.");
+               options.setAllowArguments(false);
+               options.addOption(HELP_CONFIG);
+               options.addOption(HELP_LOGLEVEL);
+               options.addOption(HELP_LOGLEVEL_FILE);
+               options.addOption(new Option("ping|p","ping peers from 
HOSTLISTURL that match transports") {
+                       public boolean exec( Command c )
+                       {
+                               prefs.setString("TRANSPORT-CHECK","PING","YES");
+                               return true;
+                       }
+                       });
+               options.addOption(new Option("repeat|r|COUNT","send COUNT 
messages") {
+                       public boolean exec( Command c )
+                       {
+                               int     n;
+
+                               n=getIntValue(-1);
+                               if (n<1) {
+                                       log(Level.SEVERE,"You must pass a 
strictly positive number to the -r option.");
+                                       return false;
+                                       }
+                               prefs.setInt("TRANSPORT-CHECK","REPEAT",n);
+                               return true;
+                       }
+                       });
+               options.addOption(new Option("size|s|SIZE","send messages with 
SIZE bytes payload") {
+                       public boolean exec( Command c )
+                       {
+                               int     n;
+
+                               n=Utils.parseInt(getValue(),-1);
+                               if (n<1) {
+                                       log(Level.SEVERE,"You must pass a 
strictly positive number to the -s option.");
+                                       return false;
+                                       }
+
+                               testData=new byte[n];
+                               Arrays.fill(testData,(byte) 'A');
+                               return true;
+                       }
+                       });
+               options.addOption(new Option("transport|t|TRANSPORT","specifies 
which TRANSPORT should be tested") {
+                       public boolean exec( Command c )
+                       {
+                               
prefs.setString("GNUNETD","TRANSPORTS",getValue());
+                               return true;
+                       }
+                       });
+               options.addOption(new Option("timeout|T|MS","specifies after 
how many MS to time-out") {
+                       public boolean exec( Command c )
+                       {
+                               timeout=Utils.parseLong(getValue(),-1);
+                               if (timeout<=0) {
+                                       log(Level.SEVERE,"You must pass a 
number to the -T option.");
+                                       return false;
+                                       }
+                               return true;
+                       }
+                       });
+               options.addOption(HELP_VERSION);
+               options.addOption(new Option("verbose|V","be verbose") {
+                       public boolean exec( Command c )
+                       {
+                               
prefs.setString("GNUNET-TRANSPORT-CHECK","VERBOSE","YES");
+                               return true;
+                       }
+                       });
+               options.addOption(new Option("Xrepeat|X|REPEAT","repeat test 
REPEAT times") {
+                       public boolean exec( Command c )
+                       {
+                               int     n;
+
+                               n=Utils.parseInt(getValue(),-1);
+                               if (n<1) {
+                                       log(Level.SEVERE,"You must pass a 
positive number to the -X option.");
+                                       return false;
+                                       }
+                               prefs.setInt("TRANSPORT-CHECK","X-REPEAT",n);
+                               return true;
+                       }
+                       });
+               options.addOption(new Option("Xport|P|PORT","set TCP and UDP 
port to PORT") {
+                       public boolean exec( Command c )
+                       {
+                               int     n;
+
+                               n=Utils.parseInt(getValue(),-1);
+                               if (n<=0) {
+                                       log(Level.SEVERE,"You must pass a 
number to the -P option.");
+                                       return false;
+                                       }
+                               prefs.setInt("TCP","PORT",n);
+                               prefs.setInt("UDP","PORT",n);
+                               prefs.setInt("TCP6","PORT",n);
+                               prefs.setInt("UDP6","PORT",n);
+                               prefs.setInt("HTTP","PORT",n);
+                               return true;
+                       }
+                       });
+               return options;
+       }
+
+       public int run()
+       {
+               String[]                urls;
+               int[]           stats;
+               String          str;
+               int                     count,i;
+               boolean         ping;
+
+               str=prefs.getString("GNUNETD","TRANSPORTS",null);
+               if (str==null) {
+                       log(Level.SEVERE,"You must specify a non-empty set of 
transports to test !");
+                       return 1;
+                       }
+
+               ping=prefs.testString("TRANSPORT-CHECK","PING","YES");
+               if (!ping) {
+                       System.out.println("Testing transport(s) "+str);
+                       }
+               else {
+                       System.out.println("Available transport(s): "+str);
+                       }
+
+               if (!ping) {
+                       /* disable blacklists (loopback is often 
blacklisted)... */
+                       prefs.setString("TCP","BLACKLIST",null);
+                       prefs.setString("UDP","BLACKLIST",null);
+                       prefs.setString("TCP6","BLACKLIST",null);
+                       prefs.setString("UDP6","BLACKLIST",null);
+                       prefs.setString("HTTP","BLACKLIST",null);
+                       }
+
+               count=prefs.getInt("TRANSPORT-CHECK","X-REPEAT",0);
+               if (count<1) {
+                       count=1;
+                       }
+
+               failed=false;
+
+               if (ping) {
+                       stats=new int[3];
+                       stats[0]=0;
+                       stats[1]=0;
+                       stats[2]=0;
+
+                       urls=prefs.getArray("GNUNETD","HOSTLISTURL","[\\s;]+");
+                       if (urls.length>0) {
+                               for (i=0; i<urls.length; i++) {
+                                       if (DEBUG_TRANSPORT_CHECK) {
+                                               System.err.println("URL: 
"+urls[i]);
+                                               }
+                                       loader.downloadHostList(urls[i],new 
HELOCallback() {
+                                               public void onHELO( P2PHello 
helo, Object arg )
+                                               {
+                                                       try {
+                                                               
testPING(helo,(int[]) arg);
+                                                               }
+                                                       catch( 
InterruptedException x ) {
+                                                               err("",x);
+                                                               }
+                                               }
+                                               },stats);
+                                       }
+                               System.err.println();
+                               }
+                       else {
+                               System.out.println("WARNING: no HOSTLISTURL 
specified in configuration!");
+                               }
+
+                       System.out.println(stats[2]+" out of "+stats[1]+" peers 
contacted successfully ("+(stats[0] - stats[1])+" times transport 
unavailable).");
+                       }
+               else {
+                       while (count>0 && !failed) {
+                               transports.forEachTransport(new 
TransportCallback() {
+                                       public void callback( Transport t, 
Object data )
+                                       {
+                                               log(Level.INFO,"Test transport 
\""+t.getName()+"\".");
+                                               try {
+                                                       
failed=!testTransport(t);
+                                                       }
+                                               catch( InterruptedException x ) 
{
+                                                       err("Interrupted while 
testing "+t+" !",x);
+                                                       }
+                                       }
+                                       },null);
+                               count--;
+                               }
+                       }
+               return (failed ? 1 : 0);
+       }
+
+       /**
+        * Test the given transport API.
+        * @param t
+        * @return
+        * @throws InterruptedException
+        */
+
+       protected boolean testTransport( Transport t ) throws 
InterruptedException
+       {
+               Session tsession;
+               P2PHello        helo;
+               boolean         success;
+
+               if (t!=null) {
+                       debug("Create HELO.");
+
+                       helo=t.createHELO();
+                       if (helo!=null) {
+                               debug("Connect to HELO.");
+
+                               tsession=t.connect(helo);
+                               if (tsession!=null) {
+                                       // really test it now !
+                                       success=testTransport(tsession,helo);
+
+                                       if (!tsession.unlock()) {
+                                               log(Level.SEVERE,"Could not 
disconnect !");
+                                               success=false;
+                                               }
+                                       }
+                               else {
+                                       log(Level.SEVERE,"Could not connect !");
+                                       success=false;
+                                       }
+                               }
+                       else {
+                               log(Level.SEVERE,"Could not create HELO !");
+                               success=false;
+                               }
+                       }
+               else {
+                       log(Level.SEVERE,"Could not initialize transport !");
+                       success=false;
+                       }
+               return success;
+       }
+
+       protected boolean testTransport( Session tsession, P2PHello helo ) 
throws InterruptedException
+       {
+               ByteBuffer      buf;
+               long            start,end;
+               int                     repeat;
+
+               scheduler=getScheduler();
+
+               buf=ByteBuffer.allocateDirect(testData.length);
+               buf.put(testData);
+
+               repeat=prefs.getInt("TRANSPORT-CHECK","REPEAT",0);
+               if (repeat==0) {
+                       repeat=1;
+                       prefs.setInt("TRANSPORT-CHECK","REPEAT",1);
+                       }
+
+               start=Scheduler.now();
+
+               while (repeat>0) {
+                       received=null;
+
+                       debug("Send data.");
+                       if (!tsession.send(buf)) {
+                               log(Level.SEVERE,"Could not send !");
+                               return false;
+                               }
+
+                       scheduler.addJob(releaseSemaphore,timeout);
+                       semaphore.acquire();
+                       scheduler.suspend();
+                       scheduler.deleteJob(releaseSemaphore);
+                       scheduler.resume();
+
+                       if (received==null) {
+                               log(Level.SEVERE,"Did not receive message 
within "+Scheduler.toSeconds(timeout)+" seconds !");
+                               return false;
+                               }
+
+                       if (!checkOK(received)) {
+                               log(Level.SEVERE,"Message received was invalid 
!");
+                               return false;
+                               }
+
+                       repeat--;
+                       }
+
+               end=Scheduler.now();
+
+               repeat=prefs.getInt("TRANSPORT-CHECK","REPEAT",0);
+               log(Level.INFO,
+                               "Transport 
\""+tsession.getTransport().getName()+"\" okay, "+
+                               Scheduler.toMillis(end-start)+"ms "+
+                               "for "+repeat+" messages "+
+                               "of size "+testData.length+" bytes "+
+                               "("+IOUtils.newSizeFormatter().format(new 
Long((1000*repeat*testData.length)/Scheduler.toMillis(end-start)))+"/s).");
+               return true;
+       }
+
+       protected boolean checkOK( MessagePack rec )
+       {
+               ByteBuffer      buf;
+               int                     i;
+               HostIdentity    myIdentity;
+
+               myIdentity=((CoreService) 
service(CoreService.class)).getIdentity();
+
+               if (rec.isEncrypted()) {
+                       System.out.println(">enc");
+                       return false;
+                       }
+               if (!rec.getSession().getRemote().equals(myIdentity)) {
+                       System.out.println(">sen : "+myIdentity+" : 
"+rec.getSession().getRemote());
+                       return false;
+                       }
+               if (rec.getCRC()!=Crypto.crc32(testData)) {
+                       System.out.println(">crc");
+                       return false;
+                       }
+
+               buf=rec.getMessages();
+               if (buf.position()!=testData.length) {
+                       System.out.println(">pos");
+                       return false;
+                       }
+               for (i=0; i<testData.length && buf.get(i)==testData[i]; i++) {}
+               if (i<testData.length) {
+                       System.out.println(">bytes "+i+" "+testData.length);
+                       }
+               return (i==testData.length);
+       }
+
+       protected void testPING( P2PHello xhelo, int[] stats ) throws 
InterruptedException
+       {
+               Session         tsession;
+               P2PPing         pmsg;
+               P2PHello                helo,myHelo;
+               Transport       t;
+               String          str;
+               int                     reply;
+               boolean         again;
+
+               scheduler=getScheduler();
+
+               if (prefs.testString("GNUNET-TRANSPORT-CHECK","VERBOSE","YES")) 
{
+                       t=transports.getTransport(xhelo);
+                       str=(t!=null ? t.addressToString(xhelo) : null);
+                       System.err.print("\nContacting "+str+".");
+                       }
+               else {
+                       System.err.print(".");
+                       }
+
+               helo=(P2PHello) PersistentHelper.copy(xhelo);
+
+               stats[0]++; /* one more seen */
+               if (!transports.isTransportAvailable(helo.getProtocol())) {
+                       System.err.print(" Transport "+helo.getProtocol()+" not 
available\n");
+                       return;
+                       }
+
+               myHelo=transports.transportCreateHELO(xhelo.getProtocol());
+               if (myHelo==null) {
+                       return;
+                       }
+
+               if (prefs.testString("GNUNET-TRANSPORT-CHECK","VERBOSE","YES")) 
{
+                       System.err.print( ".");
+                       }
+
+               stats[1]++; /* one more with transport 'available' */
+
+               pmsg=new P2PPing();
+               pmsg.setReceiver(helo.getSenderIdentity());
+               pmsg.setChallenge(Crypto.nextInt());
+
+               tsession=transports.transportConnect(helo);
+               if (tsession==null) {
+                       System.err.print(" Connection failed\n");
+                       return;
+                       }
+
+               if (prefs.testString("GNUNET-TRANSPORT-CHECK","VERBOSE","YES")) 
{
+                       System.err.print( ".");
+                       }
+
+               semaphore=new Semaphore(0);
+
+               /* send ping */
+               if (!tsession.send(new Persistent[] { myHelo, pmsg })) {
+                       System.err.print(" Send failed.\n");
+                       tsession.unlock();
+                       return;
+                       }
+
+               if (prefs.testString("GNUNET-TRANSPORT-CHECK","VERBOSE","YES")) 
{
+                       System.err.print( ".");
+                       }
+
+               /* check: received pong? */
+               terminate=false;
+               scheduler.addJob(releaseSemaphoreEvery5S,timeout);
+
+               reply=0;
+               again=true;
+               while (again && !terminate) {
+                       again=false;
+                       semaphore.acquire();
+
+                       if (received!=null) {
+                               P2PMessage                      part;
+                               P2PPong                         pong;
+                               PersistentDecoder       decoder;
+                               Iterator                                iter;
+                               int                                     
pos,plen;
+
+                               again=true;
+                               if 
(!xhelo.getSenderIdentity().equals(received.getSession().getRemote())) {
+                                       if (DEBUG_TRANSPORT_CHECK) {
+                                               
System.err.print(xhelo.getSenderIdentity().getName()+"!="+received.getSession().getRemote().getName());
+                                               }
+                                       received=null;
+                                       continue; /* different peer, ignore */
+                                       }
+
+                               reply = 1;
+
+                               decoder=new PersistentDecoder();
+                               decoder.add(P2PMessage.IS_PONG,P2PPong.class);
+
+                               pos = 0;
+                               iter=received.messages(decoder);
+                               while (iter.hasNext()) {
+                                       part=(P2PMessage) iter.next();
+                                       plen=part.getByteSize();
+
+                                       if (DEBUG_TRANSPORT_CHECK) {
+                                               System.err.print( 
"PRT<"+plen+","+part.getType()+">:"+pos+"@"+received.getSize());
+                                               }
+
+                                       pos+=plen;
+
+                                       if (part instanceof P2PPong) {
+                                               pong=(P2PPong) part;
+                                               if (pong.isCompatible(pmsg)) {
+                                                       if 
(prefs.testString("GNUNET-TRANSPORT-CHECK","VERBOSE","YES")) {
+                                                               
System.err.print( "OK!");
+                                                               }
+                                                       stats[2]++;
+                                                       reply = 2;
+                                                       again=false;
+                                                       break;
+                                                       }
+
+                                               if 
(prefs.testString("GNUNET-TRANSPORT-CHECK","VERBOSE","YES")) {
+                                                       System.err.print("!"); 
/* invalid pong */
+                                                       }
+                                               }
+                                       }
+                               received=null;
+                               }
+                       }
+
+               if (prefs.testString("GNUNET-TRANSPORT-CHECK","VERBOSE","YES")) 
{
+                       if (reply == 1)
+                               System.err.print( " No PONG.");
+                       else if (reply == 0)
+                               System.err.print(" No reply (within "+timeout+" 
ms).");
+                       }
+
+               scheduler.suspend();
+               scheduler.deleteJob(releaseSemaphoreEvery5S);
+               scheduler.resume();
+
+               semaphore=null;
+               received=null;
+
+               tsession.unlock();
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public static void main( String[] args )
+       {
+               launch(GNUNetTransportCheck.class,args);
+       }
+}

Added: freeway/src/org/gnu/freeway/GNUNetUIConfig.java
===================================================================
--- freeway/src/org/gnu/freeway/GNUNetUIConfig.java     2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/GNUNetUIConfig.java     2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,624 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway;
+
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.io.*;
+import org.gnu.freeway.util.ui.*;
+
+import EDU.oswego.cs.dl.util.concurrent.*;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.sql.*;
+import java.util.logging.*;
+import javax.swing.*;
+import javax.swing.text.*;
+
+/**
+ *
+ */
+
+public class GNUNetUIConfig extends AbstractClient
+{
+       private Controller      controller;
+       private UIResources     resources;
+       private GWizard         wizard;
+
+//     private boolean         doForce;
+       private Semaphore       sem;
+       private Prefs           prefs;
+
+       private boolean         fromApp;
+       private DirLocation     daemonBaseDirectory;
+       private DirLocation     clientsBaseDirectory;
+       private String          mysqlHost;
+       private String          mysqlUser;
+       private String          mysqlPassword;
+       private boolean         optionOverwrite;
+       private boolean         optionSaveOnly;
+
+       //todo: proposer de telecharger une liste d'hosts sur ovjm.org
+
+
+       protected GNUNetUIConfig()
+       {
+               super("gnunet-ui-config",Utils.makeVersion(1,1));
+               controller=new Controller() {
+                       public Application getApplication()
+                       {
+                               return GNUNetUIConfig.this;
+                       }
+                       };
+               resources=new 
UIResources("gnunet-config.xml",getPreferences().getSystemCache());
+               resources.setGlobalTarget(this);
+               wizard=null;
+
+//             doForce=false;
+               sem=new Semaphore(0);
+               prefs=getPreferences();
+
+               fromApp=false;
+               daemonBaseDirectory=new 
DirLocation(prefs.getGlobalString(Prefs.CONFIG_NODE,Prefs.DAEMON_BASEDIR_NAME,Prefs.DAEMON_BASEDIR_DEFAULT));
+               clientsBaseDirectory=new 
DirLocation(prefs.getGlobalString(Prefs.CONFIG_NODE,Prefs.CLIENTS_BASEDIR_NAME,Prefs.CLIENTS_BASEDIR_DEFAULT));
+               
mysqlHost=prefs.getGlobalString(Prefs.CONFIG_NODE,Prefs.MYSQL_HOST_NAME,Prefs.MYSQL_HOST_DEFAULT);
+               
mysqlUser=prefs.getGlobalString(Prefs.CONFIG_NODE,Prefs.MYSQL_USER_NAME,Prefs.MYSQL_USER_DEFAULT);
+               
mysqlPassword=prefs.getGlobalString(Prefs.CONFIG_NODE,Prefs.MYSQL_PASSWORD_NAME,Prefs.MYSQL_PASSWORD_DEFAULT);
+               optionOverwrite=false;
+               optionSaveOnly=false;
+       }
+
+       public String toString()
+       {
+               return "Configuration graphical tool";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public OptionsParser createParser()
+       {
+               OptionsParser   options;
+
+               options=new OptionsParser("gnunet-ui-config 
[OPTIONS]","Graphical configuration management.");
+               options.setAllowArguments(false);
+               options.addOption(HELP_CONFIG);
+               options.addOption(HELP_HOSTNAME);
+               options.addOption(HELP_LOGLEVEL);
+               options.addOption(HELP_LOGLEVEL_FILE);
+               options.addOption(HELP_VERSION);
+               options.addOption(new Option("force|f","force to reenter all 
configuration information") {
+                       public boolean exec( Command c )
+                       {
+//                             doForce=true;
+                               return true;
+                       }
+                       });
+               return options;
+       }
+
+       public void setFromApp()
+       {
+               fromApp=true;
+       }
+
+       protected boolean checkMySQL( String host, String user, String password 
)
+       {
+               Connection      conn;
+
+               try {
+                       Class.forName("com.mysql.jdbc.Driver");
+                       
conn=DriverManager.getConnection("jdbc:mysql://"+host+"/",user,password);
+                       conn.close();
+                       }
+               catch( SQLException x ) {
+                       err("Unable to connect to MySQL !",x);
+                       return false;
+                       }
+               catch( ClassNotFoundException x ) {
+                       err("Unable to load MySQL driver !",x);
+                       return false;
+                       }
+               return true;
+       }
+
+       public int run()
+       {
+               wizard=new GWizard(controller,"gnunet-config") {
+                       public GWizardPage[] getSteps()
+                       {
+                               return new GWizardPage[] {
+                                       new WelcomeStep(),
+                                       new DaemonBaseStep(),
+                                       new ClientsBaseStep(),
+                                       new MySQLStep(),
+                                       new ConfirmationStep()
+                                       };
+                       }
+
+                       public UIResources getResources()
+                       {
+                               return resources;
+                       }
+
+                       public void doTask()
+                       {
+                               task();
+                       }
+                       };
+               wizard.setTitle("GNUNet config");
+               wizard.display();
+
+               try {
+                       sem.acquire();
+                       }
+               catch( InterruptedException x ) {
+                       }
+               return 0;
+       }
+
+       public void onQuit()
+       {
+               if (fromApp || JOptionPane.showConfirmDialog(
+                               wizard,
+                               "Do you really want to quit ?",
+                               "Exiting...",
+                               JOptionPane.YES_NO_OPTION,
+                               JOptionPane.QUESTION_MESSAGE,
+                               new 
ImageIcon(resources.getImageURL("application.medium.icon"))
+                               )==JOptionPane.YES_OPTION) {
+
+                       wizard.close();
+                       sem.release();
+                       }
+       }
+
+       protected void task()
+       {
+               GProgress       dialog;
+
+               final boolean[] _beurk = new boolean[1];
+
+               _beurk[0]=false;
+
+               dialog=new GProgress(wizard,"gnunet-config-progress") {
+                       public void perform()
+                       {
+                               progress(1,8,"Check parameters...");
+                               if (!checkParams()) {
+                                       return;
+                                       }
+                               Scheduler.sleep(Scheduler.MILLIS_100);
+
+                               progress(2,8,"Store system configuration.");
+                               if (!storeParams()) {
+                                       return;
+                                       }
+                               Scheduler.sleep(Scheduler.MILLIS_100);
+
+                               progress(3,8,"Create daemon base directory...");
+                               if (!createDaemonBase()) {
+                                       return;
+                                       }
+                               Scheduler.sleep(Scheduler.MILLIS_100);
+
+                               progress(4,8,"Create daemon template config. 
file...");
+                               if (!createDaemonTemplate()) {
+                                       return;
+                                       }
+                               Scheduler.sleep(Scheduler.MILLIS_100);
+
+                               progress(5,8,"Create clients base 
directory...");
+                               if (!createClientsBase()) {
+                                       return;
+                                       }
+                               Scheduler.sleep(Scheduler.MILLIS_100);
+
+                               progress(6,8,"Create clients template config. 
file...");
+                               if (!createClientsTemplate()) {
+                                       return;
+                                       }
+                               Scheduler.sleep(Scheduler.MILLIS_100);
+
+                               progress(7,8,"Connect to MySQL...");
+                               if (!checkMySQL()) {
+                                       return;
+                                       }
+                               Scheduler.sleep(Scheduler.MILLIS_100);
+
+                               progress(8,8,"Done !");
+                               Scheduler.sleep(Scheduler.SECS_1);
+
+                               _beurk[0]=true;
+                       }
+                       };
+               dialog.setTitle("Setup");
+               dialog.launch("Please wait while Freeway is being 
configured...\n");
+               if (_beurk[0] && fromApp) {
+                       onQuit();
+                       }
+       }
+
+       protected boolean checkParams()
+       {
+               if (daemonBaseDirectory==null) {
+                       wizard.showMessage("Setup error","You must choose a 
base directory for daemon !");
+                       return false;
+                       }
+               if (!daemonBaseDirectory.exists()) {
+                       wizard.showMessage("Setup error","Invalid daemon base 
directory ("+daemonBaseDirectory.getLabel()+") !");
+                       return false;
+                       }
+               if (clientsBaseDirectory==null) {
+                       wizard.showMessage("Setup error","You must choose a 
base directory for clients !");
+                       return false;
+                       }
+               if (!clientsBaseDirectory.exists()) {
+                       wizard.showMessage("Setup error","Invalid clients base 
directory ("+clientsBaseDirectory.getLabel()+") !");
+                       return false;
+                       }
+               if (!optionSaveOnly) {
+                       if (mysqlHost.length()==0) {
+                               wizard.showMessage("Setup error","You must 
choose an host for MySQL database !");
+                               return false;
+                               }
+                       if (mysqlUser.length()==0) {
+                               wizard.showMessage("Setup error","You must 
choose an user for MySQL database !");
+                               return false;
+                               }
+                       }
+               return true;
+       }
+
+       protected boolean storeParams()
+       {
+               
prefs.setGlobalString(Prefs.CONFIG_NODE,Prefs.DAEMON_BASEDIR_NAME,daemonBaseDirectory.getLabel());
+               
prefs.setGlobalString(Prefs.CONFIG_NODE,Prefs.CLIENTS_BASEDIR_NAME,clientsBaseDirectory.getLabel());
+               
prefs.setGlobalString(Prefs.CONFIG_NODE,Prefs.MYSQL_HOST_NAME,mysqlHost);
+               
prefs.setGlobalString(Prefs.CONFIG_NODE,Prefs.MYSQL_USER_NAME,mysqlUser);
+               
prefs.setGlobalString(Prefs.CONFIG_NODE,Prefs.MYSQL_PASSWORD_NAME,mysqlPassword);
+               return true;
+       }
+
+       protected boolean createDaemonBase()
+       {
+               if (!daemonBaseDirectory.exists()) {
+                       if (!daemonBaseDirectory.create()) {
+                               wizard.showMessage("Setup error","Unable to 
create daemon base directory ("+daemonBaseDirectory.getLabel()+") !");
+                               return false;
+                               }
+                       return true;
+                       }
+               if (!optionOverwrite) {
+                       wizard.showMessage("Setup error","Daemon base directory 
("+daemonBaseDirectory.getLabel()+") already exists.\nPlease check overwrite 
option in order to continue.");
+                       return false;
+                       }
+               return true;
+       }
+
+       protected boolean createDaemonTemplate()
+       {
+               FileLocation    f;
+
+               f=daemonBaseDirectory.getFile("gnunet.conf");
+               if (f.exists()) {
+                       if (!optionOverwrite) {
+                               wizard.showMessage("Setup error","Config. file 
("+f.getLabel()+") already exists.\nPlease check overwrite option in order to 
continue.");
+                               return false;
+                               }
+                       if (!wizard.askMessage("Setup error","Config. file 
("+f.getLabel()+") already exists.\nDo you want really want to overwrite it 
?\n(all modifications will be lost)")) {
+                               return false;
+                               }
+                       }
+               prefs.copyTemplateWithGlobals("gnunet.daemon.template",f);
+               wizard.showMessage("Setup information","Have created daemon 
configuration file.\nYou can customize it by later editing 
\""+f.getLabel()+"\".");
+               return true;
+       }
+
+       protected boolean createClientsBase()
+       {
+               if (!clientsBaseDirectory.exists()) {
+                       if (!clientsBaseDirectory.create()) {
+                               wizard.showMessage("Setup error","Unable to 
create clients base directory ("+clientsBaseDirectory.getLabel()+") !");
+                               return false;
+                               }
+                       return true;
+                       }
+               if (!optionOverwrite) {
+                       wizard.showMessage("Setup error","Clients base 
directory ("+clientsBaseDirectory.getLabel()+") already exists.\nPlease check 
overwrite option in order to continue.");
+                       return false;
+                       }
+               return true;
+       }
+
+       protected boolean createClientsTemplate()
+       {
+               FileLocation    f;
+
+               f=clientsBaseDirectory.getFile("gnunet.conf");
+               if (f.exists()) {
+                       if (!optionOverwrite) {
+                               wizard.showMessage("Setup error","Config. file 
("+f.getLabel()+") already exists.\nPlease check overwrite option in order to 
continue.");
+                               return false;
+                               }
+                       if (!wizard.askMessage("Setup error","Config. file 
("+f.getLabel()+") already exists.\nDo you want really want to overwrite it 
?\n(all modifications will be lost)")) {
+                               return false;
+                               }
+                       }
+               prefs.copyTemplateWithGlobals("gnunet.client.template",f);
+               wizard.showMessage("Setup information","Have created clients 
configuration file.\nYou can customize it by later editing 
\""+f.getLabel()+"\".");
+               return true;
+       }
+
+       protected boolean checkMySQL()
+       {
+               if (!optionSaveOnly && 
!checkMySQL(mysqlHost,mysqlUser,mysqlPassword)) {
+                       wizard.showMessage("Setup error","Unable to connect to 
MySQL database at "+mysqlHost+" (user \""+mysqlUser+"\").\nPlease enter valid 
values and try again later.\n");
+                       return false;
+                       }
+               return true;
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public class WelcomeStep extends GWizardPage
+       {
+               private GStyledText     text;
+
+
+               public WelcomeStep()
+               {
+                       super("Welcome");
+               }
+
+               public JComponent createView()
+               {
+                       text=new GStyledText();
+                       StyleConstants.setFontSize(text.style(),13);
+
+                       
StyleConstants.setFontFamily(text.style("fr"),"SansSerif");
+                       StyleConstants.setBold(text.style("fr"),true);
+                       StyleConstants.setItalic(text.style("fr"),true);
+                       StyleConstants.setForeground(text.style("fr"),new 
Color(33,87,136));
+
+                       text.println("Welcome to <fr>Freeway</fr>'s initial 
configuration program.").println();
+                       text.print("First, you will be asked to enter a few 
values to describe the system you use. ");
+                       text.println("Then, the program will try to setup an 
initial configuration with collected data.").println();
+                       text.println("No file operation will be done without 
user's confirmation.");
+                       return text;
+               }
+       }
+
+       public class DaemonBaseStep extends GWizardPage
+       {
+               private GStack          stack;
+               private GStyledText     text;
+               private GFile           file;
+
+
+               public DaemonBaseStep()
+               {
+                       super("Choose daemon base directory");
+               }
+
+               public JComponent createView()
+               {
+                       text=new GStyledText();
+                       
StyleConstants.setFontFamily(text.style("fr"),"SansSerif");
+                       StyleConstants.setBold(text.style("fr"),true);
+                       StyleConstants.setItalic(text.style("fr"),true);
+                       StyleConstants.setForeground(text.style("fr"),new 
Color(33,87,136));
+                       text.print("Choose a directory where <fr>Freeway</fr> 
daemon will store needed files under.");
+
+                       file=new GFile();
+                       file.selectDirectory("Daemon base directory");
+
+                       stack=new GStack();
+                       stack.addWidget(text);
+                       stack.addWidget(file);
+                       return stack;
+               }
+
+               public void updateView()
+               {
+                       file.setSelectedLocation(daemonBaseDirectory);
+               }
+
+               public void leaveView()
+               {
+                       daemonBaseDirectory=(DirLocation) 
file.getSelectedLocation();
+               }
+       }
+
+       public class ClientsBaseStep extends GWizardPage
+       {
+               private GStack          stack;
+               private GStyledText     text;
+               private GFile           file;
+
+
+               public ClientsBaseStep()
+               {
+                       super("Choose client base directory");
+               }
+
+               public JComponent createView()
+               {
+                       text=new GStyledText();
+                       
StyleConstants.setFontFamily(text.style("fr"),"SansSerif");
+                       StyleConstants.setBold(text.style("fr"),true);
+                       StyleConstants.setItalic(text.style("fr"),true);
+                       StyleConstants.setForeground(text.style("fr"),new 
Color(33,87,136));
+                       text.print("Choose a directory where <fr>Freeway</fr>'s 
client applications will store needed files under.");
+
+                       file=new GFile();
+                       file.selectDirectory("Client base directory");
+
+                       stack=new GStack();
+                       stack.addWidget(text);
+                       stack.addWidget(file);
+                       return stack;
+               }
+
+               public void updateView()
+               {
+                       file.setSelectedLocation(clientsBaseDirectory);
+               }
+
+               public void leaveView()
+               {
+                       clientsBaseDirectory=(DirLocation) 
file.getSelectedLocation();
+               }
+       }
+
+       public class MySQLStep extends GWizardPage
+       {
+               private JTextField              hostField;
+               private JTextField              userField;
+               private JPasswordField  passwordField;
+               private GStack                  stack;
+               private GText                   text;
+               private GForm                   form;
+               private JButton                 button;
+
+
+               public MySQLStep()
+               {
+                       super("Setup MySQL database access");
+               }
+
+               public JComponent createView()
+               {
+                       hostField=new JTextField(10);
+                       userField=new JTextField(10);
+                       passwordField=new JPasswordField(10);
+
+                       text=new GText();
+                       text.setText("Enter parameters needed to connect a 
MySQL database that will store exchanged data with others peers.");
+
+                       form=new GForm();
+                       form.addWidget("Host :",hostField);
+                       form.addWidget("User :",userField);
+                       form.addWidget("Password :",passwordField);
+
+                       button=new JButton("Check...");
+                       button.addActionListener(new ActionListener() {
+                               public void actionPerformed( ActionEvent evt )
+                               {
+                                       if 
(checkMySQL(hostField.getText(),userField.getText(),new 
String(passwordField.getPassword()))) {
+                                               wizard.showMessage("MySQL 
check","Successfully established connection to database.");
+                                               }
+                                       else {
+                                               wizard.showMessage("MySQL 
check","Unable to connect to database !\nPlease correct your parameters and try 
again later.");
+                                               }
+                               }
+                               });
+
+                       stack=new GStack();
+                       stack.addWidget(text);
+                       stack.addWidget(form);
+                       stack.addWidget(button);
+                       return stack;
+               }
+
+               public void updateView()
+               {
+                       hostField.setText(mysqlHost);
+                       userField.setText(mysqlUser);
+                       passwordField.setText(mysqlPassword);
+               }
+
+               public void leaveView()
+               {
+                       mysqlHost=hostField.getText();
+                       mysqlUser=userField.getText();
+                       mysqlPassword=new String(passwordField.getPassword());
+               }
+       }
+
+       public class ConfirmationStep extends GWizardPage
+       {
+               private GStyledText     text;
+               private JCheckBox       overwrite;
+               private JCheckBox       saveOnly;
+               private Icon            dot;
+
+
+               public ConfirmationStep()
+               {
+                       super("Confirmation");
+               }
+
+               public JComponent createView()
+               {
+                       dot=new ImageIcon(resources.getImageURL("dot.icon"));
+
+                       overwrite=new JCheckBox();
+                       saveOnly=new JCheckBox();
+
+                       text=new GStyledText();
+                       StyleConstants.setLeftIndent(text.style("ind"),10);
+                       StyleConstants.setSpaceAbove(text.style("ind"),3);
+                       StyleConstants.setSpaceBelow(text.style("ind"),1);
+                       StyleConstants.setForeground(text.style("v"),new 
Color(33,87,136));
+                       
StyleConstants.setFontFamily(text.style("v"),"Monospaced");
+                       return text;
+               }
+
+               public void updateView()
+               {
+                       text.clear();
+                       text.print("Please check information you've entered are 
correct and ");
+                       text.print("choose options you would like to apply. ");
+                       text.println("Then click on Go button to complete 
configuration.");
+                       text.print("<ind>").print(dot).print(" Daemon base 
directory : <v>"+(daemonBaseDirectory!=null ? daemonBaseDirectory.getLabel() : 
"(not set)")).println("</v></ind>");
+                       text.print("<ind>").print(dot).print(" Clients base 
directory : <v>"+(clientsBaseDirectory!=null ? clientsBaseDirectory.getLabel() 
: "(not set)")).println("</v></ind>");
+                       text.print("<ind>").print(dot).print(" MySQL host : 
<v>"+mysqlHost).println("</v></ind>");
+                       text.print("<ind>").print(dot).print(" MySQL user : 
<v>"+mysqlUser).println("</v></ind>");
+                       text.print("<ind>").print(dot).print(" MySQL password : 
<v>"+(mysqlPassword.length()>0 ? "(set)" : "(not set)")).println("</v></ind>");
+                       text.println();
+                       text.print("Overwrite config. files if they already 
exist ? ").println(overwrite);
+                       text.print("Do not try to connect to MySQL, save 
parameters only ").println(saveOnly);
+
+                       overwrite.setSelected(optionOverwrite);
+                       saveOnly.setSelected(optionSaveOnly);
+               }
+
+               public void leaveView()
+               {
+                       optionOverwrite=overwrite.isSelected();
+                       optionSaveOnly=saveOnly.isSelected();
+               }
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public static void main( String[] args )
+       {
+               launch(GNUNetUIConfig.class,args);
+       }
+
+       public static void fromApp()
+       {
+               GNUNetUIConfig  config;
+               Logger                  logger;
+
+               JOptionPane.showMessageDialog(null,"Launched application needs 
minimal system configuration\nin order to run properly. Please enter needed 
information\nin wizard that will pop 
up...","Setup",JOptionPane.INFORMATION_MESSAGE);
+
+               try {
+                       config=new GNUNetUIConfig();
+                       config.setFromApp();
+                       config.run();
+                       }
+               catch( Throwable x ) {
+                       logger=Logger.getLogger(GNUNetUIConfig.class.getName());
+                       logger.log(Level.SEVERE,"Unable to run wizard !",x);
+                       }
+       }
+}

Added: freeway/src/org/gnu/freeway/Server.java
===================================================================
--- freeway/src/org/gnu/freeway/Server.java     2005-01-31 23:47:23 UTC (rev 
136)
+++ freeway/src/org/gnu/freeway/Server.java     2005-02-01 01:07:27 UTC (rev 
137)
@@ -0,0 +1,68 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway;
+
+import org.gnu.freeway.server.*;
+import org.gnu.freeway.util.net.*;
+
+/**
+ *
+ */
+
+public interface Server extends Application
+{
+       public MessagesDispatcher getDispatcher();
+
+       public LocalIdentity getKeys();
+
+       /**
+        * Return wheter or not there is a method handler
+        * registered for a specific Client-Server message type.
+        * @param type the message type
+        * @return true if there is a handler for the type,
+        *      NO if there isn't
+        */
+
+       public boolean isCSHandlerRegistered( int type );
+
+       /**
+        * Register a method as a handler for specific message types.
+        *
+        * @param type the message type
+        * @param c
+        * @param handler the method to call if a message of that type is 
received
+        * @return true on success, false if there is already a handler for 
that type
+        */
+
+       public boolean registerCSHandler( int type, Class c, CSHandler handler 
);
+
+       /**
+        * Remove a method as a handler for specific message types.
+        *
+        * @param type the message type
+        * @param handler the method to call if a message of that type is 
received
+        * @return true on success, false if there is a different handler for 
that type
+        */
+
+       public boolean unregisterCSHandler( int type, CSHandler handler );
+
+       /**
+        * Register a handler to call if any client exits.
+        *
+        * @param callback a method to call with the socket of every client 
that disconnected.
+        * @return true on success, false on error
+        */
+
+       public boolean registerCSExitHandler( ClientExitHandler callback );
+
+       /**
+        * Unregister a handler to call if any client exits.
+        *
+        * @param callback a method to call with the socket of every client 
that disconnected.
+        * @return true on success, false on error
+        */
+
+       public boolean unregisterCSExitHandler( ClientExitHandler callback );
+}

Added: freeway/src/org/gnu/freeway/gnunet-check.c
===================================================================
--- freeway/src/org/gnu/freeway/gnunet-check.c  2005-01-31 23:47:23 UTC (rev 
136)
+++ freeway/src/org/gnu/freeway/gnunet-check.c  2005-02-01 01:07:27 UTC (rev 
137)
@@ -0,0 +1,1356 @@
+/*
+     This file is part of GNUnet.
+     (C) 2001, 2002, 2003 Christian Grothoff (and other contributing authors)
+
+     GNUnet is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with GNUnet; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file applications/afs/module/gnunet-check.c
+ * @brief Little tool to do consistency check of the AFS databases.
+ * @author Christian Grothoff
+ *
+ * FIXME: If some database bucket has entries that do not belong
+ * there, the current code will just delete them. It'd be nice if they
+ * could be moved instead.
+ * 
+ **/
+
+#include "gnunet_util.h"
+#include "gnunet_afs_esed2.h"
+
+#include "fileindex.c"
+#include "large_file_support.c"
+#include "manager.c"
+#include "bloomfilter.c"
+
+static DatabaseAPI * dbAPI;
+
+/* configuration: do we fix problems? */
+static int do_fix = YES;
+/* configuration: do we reset bloomfilters? */
+static int do_reset = NO;
+
+/* priority of fixed content */
+static unsigned int fixedPriority;
+/* priority of reindexed content */
+static unsigned int indexPriority;
+
+/* tcp server result: were all the requests satisfied? */
+static int tcp_verifies;
+
+static int be_verbose = NO;
+static int be_quiet = NO;
+
+static void PRINTQ(char * format,
+                  ...) {
+  va_list args;
+  if (be_quiet == YES)
+    return;
+  va_start(args, format);
+  vfprintf(stdout, format, args);
+  va_end(args);
+}
+
+static void PRINTV(char * format,
+                  ...) {
+  va_list args;
+  if (be_verbose == NO)
+    return;
+  va_start(args, format);
+  vfprintf(stdout, format, args);
+  va_end(args);
+}
+
+
+/**
+ * Check that the content at the given offset/file
+ * has the given double hash.
+ **/
+static int checkHashMatch(unsigned short fileNameIndex,
+                         size_t offset,
+                         HashCode160 * chkquery) {
+  CONTENT_Block result;
+  CONTENT_Block eresult;
+  char * fn;
+  HashCode160 hc;
+  HashCode160 dhc;
+  int fileHandle;
+  size_t blen;
+  /* check that the specified file at the specified
+     offset actually contains the content that we
+     are looking for */
+
+  fn = getIndexedFileName(fileNameIndex);
+  if (fn == NULL)
+    return SYSERR;
+  fileHandle = OPEN(fn, O_EXCL, S_IRUSR);
+  if (fileHandle == -1) {
+    LOG(LOG_WARNING, 
+       "WARNING: Could not open file %s (%u).\n",
+       fn,
+       fileNameIndex);    
+    FREE(fn);
+    return SYSERR;
+  }
+  lseek(fileHandle, 
+       offset, SEEK_SET);
+  memset(&result, 
+        0, 
+        sizeof(CONTENT_Block));
+  blen = READ(fileHandle, 
+             &result,
+             sizeof(CONTENT_Block));
+  CLOSE(fileHandle);
+  hash(&result, 
+       blen, 
+       &hc);
+  encryptContent(&result,
+                &hc,
+                &eresult);
+  hash(&eresult,
+       sizeof(CONTENT_Block),
+       &dhc);
+  if (!equalsHashCode160(&dhc,
+                        chkquery)) {
+    LOG(LOG_WARNING, 
+       "WARNING: content found in %s at %d does not match expected hash.\n",
+       fn,
+       offset);    
+    FREE(fn);
+    return SYSERR;
+  } else {
+    FREE(fn);
+    return OK;
+  }
+}
+
+typedef struct {
+  HashCode160 hc;
+  int bucket;
+} RemoveList ;
+
+static RemoveList * removeList = NULL;
+static int removeCount = 0;
+
+/**
+ * We can't remove the bogus content instantly since that would be a
+ * concurrent modification while using the iterator. Thus we remember
+ * the keys to remove and do it later.
+ **/
+static void deferredRemove() {
+  int i;
+  HexName hex;
+
+  for (i=0;i<removeCount;i++)
+    if (OK != removeContent(&removeList[i].hc,
+                           removeList[i].bucket)) {
+      hash2hex(&removeList[i].hc,
+              &hex);
+      PRINTQ("Deferred content removal of %s failed!\n",
+            &hex);
+    }
+  GROW(removeList,
+       removeCount,
+       0);
+}
+
+/**
+ * If we are fixing problems, remove this content and
+ * print the appropriate messages.
+ **/
+static void ifFixRemove(HashCode160 * query, 
+                       int bucket) {
+  if (do_fix == YES) {    
+    GROW(removeList,
+        removeCount,
+        removeCount+1);
+    memcpy(&removeList[removeCount-1].hc,
+          query,
+          sizeof(HashCode160));
+    removeList[removeCount-1].bucket = bucket;
+    PRINTQ("Will fix (deferred).\n");
+  } else
+    PRINTQ("\n");        
+}
+
+/**
+ * This function is called for each entry in the
+ * content/index/lookup database.
+ **/
+static void checkDatabaseContent(HashCode160 * query,
+                                ContentIndex * ce,
+                                int bucket,
+                                void * result,
+                                int len) {
+  HexName hn;
+  
+  hash2hex(query,
+          &hn);  
+
+  if (computeBucketGlobal(query) != (unsigned int)bucket) {
+    PRINTQ("Entry %s is in wrong bucket %d (expected %d). ",
+          (char*)&hn,
+          bucket,
+          computeBucketGlobal(query));
+    ifFixRemove(query,
+               bucket);
+    return;
+  }
+  switch(ntohs(ce->type)) {
+  case LOOKUP_TYPE_CHK:
+    if (len != 0) {
+      if (len != sizeof(CONTENT_Block)) {
+       PRINTQ("Bad content stored for %s (bad length %d). ",
+              (char*)&hn, 
+              len);
+       ifFixRemove(query,
+                   bucket); 
+       break;
+      }
+    } else {
+      if (SYSERR == checkHashMatch(ntohs(ce->fileNameIndex),
+                                  ntohl(ce->fileOffset),
+                                  query)) {
+       PRINTQ("Bad CHK content indexed for %s ",
+              (char*)&hn);
+       ifFixRemove(query,
+                   bucket); 
+       break;
+      }
+    }
+    if (do_reset == YES) {
+       addToBloomfilter(singleBloomFilter,
+                        query);
+    } else {
+      if (testBloomfilter(singleBloomFilter,
+                         query) == NO) {
+       PRINTQ("Bloomfilter test failed for CHK content %s ",
+              (char*)&hn);
+       if (do_fix == YES) {
+         addToBloomfilter(singleBloomFilter,
+                          query);
+         PRINTQ("Fixed.\n");
+       } else
+         PRINTQ("\n"); 
+      }
+    }
+    break;
+  case LOOKUP_TYPE_CHKS:
+    if (len != 0) {
+      if (len != sizeof(CONTENT_Block)) {
+       PRINTQ("Bad content stored for %s (bad length %d) ",
+              (char*)&hn, 
+              len);
+       ifFixRemove(query,
+                   bucket); 
+       break;
+      }
+    } else {
+      if (SYSERR == checkHashMatch(ntohs(ce->fileNameIndex),
+                                  ntohl(ce->fileOffset),
+                                  query)) {
+       PRINTQ("Bad CHKS content indexed for %s ",
+              (char*)&hn);
+       ifFixRemove(query,
+                   bucket);
+       break;
+      }
+    }
+    break;
+  case LOOKUP_TYPE_3HASH:
+    if (do_reset == YES) {
+       addToBloomfilter(singleBloomFilter,
+                        query);
+    } else {
+      if (testBloomfilter(singleBloomFilter,
+                         query) == NO) {
+       PRINTQ("Bloomfilter test failed for 3HASH content %s ",
+              (char*)&hn);
+       if (do_fix == YES) {
+         addToBloomfilter(singleBloomFilter,
+                          query);
+         PRINTQ("Fixed.\n");
+       } else
+         PRINTQ("\n"); 
+      }
+    }
+    break;
+  case LOOKUP_TYPE_SUPER:
+    if (do_reset == YES) {
+       addToBloomfilter(superBloomFilter,
+                        query);
+    } else {
+      if (testBloomfilter(superBloomFilter,
+                         query) == NO) {
+       PRINTQ("Bloomfilter test failed for SUPER hash %s ",
+              (char*)&hn);
+       if (do_fix == YES) {
+         addToBloomfilter(superBloomFilter,
+                          query);
+         PRINTQ("Fixed.\n");
+       } else
+         PRINTQ("\n"); 
+      }
+    }
+    break;
+  case LOOKUP_TYPE_SBLOCK:
+    if (do_reset == YES) {
+       addToBloomfilter(singleBloomFilter,
+                        query);
+    } else {
+      if (testBloomfilter(singleBloomFilter,
+                         query) == NO) {
+        PRINTQ("Bloomfilter test failed for SBLOCK content %s ",
+              (char*)&hn);
+       if (do_fix == YES) {
+         addToBloomfilter(singleBloomFilter,
+                          query);
+         PRINTQ("Fixed.\n");
+       } else
+         PRINTQ("\n"); 
+      }
+    }
+    break;
+  default:
+    PRINTQ("ERROR: unexpected content type %d. ",
+          ntohs(ce->type));
+    ifFixRemove(query,
+               bucket);
+    break;
+  }
+}
+
+/**
+ * Check that for each entry in the contentdatabase
+ * there is an entry in the lookup-database.
+ **/
+static void checkDatabase() {
+  void * iterState;
+  int count;
+  HashCode160 hc;
+  ContentIndex ce;
+  void * data;
+  int len;
+  int bucket;
+  
+  PRINTQ("Checking Content Database\n");
+  count = 0;
+  iterState = makeDatabaseIteratorState();
+  data = NULL;
+  while (OK == databaseIterator(iterState,
+                               &hc,
+                               &ce,
+                               &bucket,
+                               &data,
+                               &len)) {
+    checkDatabaseContent(&hc,
+                        &ce,
+                        bucket,
+                        data,
+                        len);
+    count++;
+    FREENONNULL(data);
+    data = NULL;
+  } 
+  deferredRemove();
+  PRINTQ("\n==> Done checking %d entries in content database.\n", 
+        count);
+}
+
+/**
+ * Process a request to insert content from the client.
+ * @return SYSERR if the TCP connection should be closed, otherwise OK
+ **/
+static int checkInsertCHK(GNUNET_TCP_SOCKET * sock,
+                         AFS_CS_INSERT_CHK * insertRequest) {
+  CONTENT_Block * block;
+  int len;
+  int dup;
+  ContentIndex entry;
+  HashCode160 hc;
+  HexName hn;
+
+  if (ntohs(insertRequest->header.size) != 
+      sizeof(AFS_CS_INSERT_CHK)) {
+    sendTCPResult(sock, SYSERR);
+    return SYSERR;
+  }
+
+  memset(&entry,
+        0,
+        sizeof(ContentIndex));
+
+  block = NULL;
+  hash(&insertRequest->content,
+       sizeof(CONTENT_Block),
+       &hc);
+  hash2hex(&hc,
+          &hn);
+  PRINTV("* %s (ins)\n",
+        (char*)&hn);
+  len = retrieveContent(&hc,
+                       &entry,
+                       (void**)&block,
+                       0,
+                       NO);
+  if(len == sizeof(CONTENT_Block)) 
+    if (0 != memcmp(&insertRequest->content,
+                   block,
+                   sizeof(CONTENT_Block)) )      
+      len = SYSERR;
+  FREENONNULL(block);
+  if (ntohs(entry.type) != LOOKUP_TYPE_CHK) 
+    len = SYSERR;
+  
+  if (len == SYSERR || ntohl(entry.importance)<fixedPriority) {
+    PRINTQ("Content %s %s in database. ",
+          (char*) &hn,
+           (len == SYSERR ? "malformed or missing" : "has low priority"));
+    if (do_fix == YES) {
+      entry.type = htons(LOOKUP_TYPE_CHK); /* CHK or CHKS? How can we tell? 
FIXME! */
+      entry.importance = htonl(fixedPriority); 
+      memcpy(&entry.hash,
+            &hc,
+            sizeof(HashCode160));
+      entry.fileNameIndex = htons(0);
+      entry.fileOffset    = htonl(0);
+      if (OK == insertContent(&entry, 
+                             sizeof(CONTENT_Block),
+                             &insertRequest->content,
+                             NULL, /* sender = localhost */
+                             &dup)) {
+       PRINTQ("Fixed.\n");
+      } else {
+       PRINTQ("WARNING: can not fix (database full?)\n");
+      }
+    } else 
+      PRINTQ("\n");
+  }      
+
+  sendTCPResult(sock, OK);
+  return OK;
+}
+
+/**
+ * Process a request to insert content from the client.
+ * @return SYSERR if the TCP connection should be closed, otherwise OK
+ **/
+static int checkInsert3HASH(GNUNET_TCP_SOCKET * sock,
+                           AFS_CS_INSERT_3HASH * insertRequest) {
+  LOG(LOG_WARNING,
+      "WARNING: did not expect 3HASH insert invocation!\n");
+  sendTCPResult(sock, OK);
+  return OK;
+}
+
+static int checkSuper(GNUNET_TCP_SOCKET * sock,
+                     AFS_CS_INDEX_SUPER * superIndexRequest) {
+  ContentIndex entry;
+  ContentIndex entry2;
+  void * result;
+  int len;  
+  int dup;
+
+  if (ntohs(superIndexRequest->header.size) != 
+      sizeof(AFS_CS_INDEX_SUPER)) {
+    LOG(LOG_WARNING, 
+       "WARNING: super-hash indexing request from client was malformed!\n");
+    return SYSERR;
+  }
+  if (NO == testBloomfilter(superBloomFilter,
+                           &superIndexRequest->superHash)) { 
+    if (do_reset == NO)
+      PRINTQ("Super-Hash not listed in super-hash bloom filter ");
+    if (do_fix == YES) {
+      addToBloomfilter(superBloomFilter,
+                      &superIndexRequest->superHash);
+      if (do_reset == NO)
+        PRINTQ("Fixed.\n");
+    } else
+      if (do_reset == NO)
+        PRINTQ("\n");
+  }
+  entry.type
+    = htons(LOOKUP_TYPE_SUPER);
+  entry.importance
+    = htonl(fixedPriority); 
+  entry.fileNameIndex 
+    = 0; /* database */
+  entry.fileOffset 
+    = 0; /* data/content */
+  memcpy(&entry.hash,
+        &superIndexRequest->superHash,
+        sizeof(HashCode160));
+  result = NULL;
+  memset(&entry2,
+        0,
+        sizeof(ContentIndex));
+  len = retrieveContent(&superIndexRequest->superHash,
+                       &entry2,
+                       &result,
+                       0,
+                       NO);
+  FREENONNULL(result);
+  if (SYSERR == len || ntohl(entry2.importance)<fixedPriority) {
+    HexName expect;
+    
+    hash2hex(&superIndexRequest->superHash,
+            &expect);
+    PRINTQ("Did not find super-hash entry in "
+          "lookup database for hash %s (or had low priority). ",
+          (char*)&expect);    
+    if (do_fix == YES) {
+      if (OK == insertContent(&entry, 
+                             0,
+                             NULL,
+                             NULL,
+                             &dup)) {
+       PRINTQ("Fixed.\n");
+      } else {
+       PRINTQ("Failed to fix.\n");
+      }
+    } else
+      PRINTQ("\n"); 
+  } else {
+    entry2.importance = entry.importance;
+    if (0 != memcmp(&entry, 
+                   &entry2, 
+                   sizeof(ContentIndex))) {
+      HexName have;
+      HexName expect;
+      
+      hash2hex(&entry2.hash,
+              &have);
+      hash2hex(&entry.hash,
+              &expect);
+      PRINTQ("Entry in database for super-hash does not "
+            "match expectations (have: %s, %u, %u, %u; "
+            "expected: %s, %u, %u, %u). ",
+            (char*)&have, 
+            ntohl(entry2.importance),
+            ntohs(entry2.fileNameIndex), 
+            ntohl(entry2.fileOffset),
+            (char*)&expect,
+            ntohl(entry.importance), 
+            ntohs(entry.fileNameIndex), 
+            ntohl(entry.fileOffset));    
+      if (do_fix == YES) {
+       if (OK == insertContent(&entry, 
+                               0,
+                               NULL,
+                               NULL,
+                               &dup)) {
+         PRINTQ("Fixed.\n");
+       } else {
+         PRINTQ("Failed to fix.\n");
+       }
+      } else
+       PRINTQ("\n");
+    }
+  }
+  return sendTCPResult(sock, 
+                      OK);
+}
+
+/**
+ * Process a request to index content from the client.
+ * @return SYSERR if the TCP connection should be closed, otherwise OK
+ **/
+static int checkIndex(GNUNET_TCP_SOCKET * sock,
+                     AFS_CS_INDEX_BLOCK * indexingRequest) {
+  HashCode160 triple;
+  HashCode160 * query;
+  ContentIndex res;
+  HexName hn;
+  void * data;
+  int len;
+  int dup;
+
+  hash2hex(&indexingRequest->contentIndex.hash,
+          &hn);
+  PRINTV("* %s (idx)\n",
+        (char*)&hn);
+  switch (ntohs(indexingRequest->contentIndex.type)) {
+  case LOOKUP_TYPE_3HASH:
+    hash(&indexingRequest->contentIndex.hash,
+        sizeof(HashCode160),
+        &triple);
+    query = &triple;
+    break;
+  case LOOKUP_TYPE_CHK:
+  case LOOKUP_TYPE_CHKS:
+    query = &indexingRequest->contentIndex.hash;
+    break;
+  default:  
+    LOG(LOG_ERROR,
+       "ERROR: Unexpected content index type: %d.\n",
+       ntohs(indexingRequest->contentIndex.type));
+    return SYSERR; 
+  }
+  if (ntohs(indexingRequest->header.size) != 
+      sizeof(AFS_CS_INDEX_BLOCK)) {
+#if PRINT_TCP
+    printf("TCP: WARNING: indexing request malformed!\n");
+#endif
+    sendTCPResult(sock, SYSERR);
+    return SYSERR;
+  }
+#if PRINT_TCP 
+  printf("TCP: received indexing request\n");
+#endif
+  /* check if everything is already in place, and if not and 
+     we are allowed to fix, do the write-actions: */
+  memset(&res,
+        0,
+        sizeof(ContentIndex));
+
+  data = NULL;
+  len = retrieveContent(query,
+                       &res,
+                       &data,
+                       0,
+                       NO);
+  FREENONNULL(data);
+  indexingRequest->contentIndex.importance 
+    = htonl(indexPriority);
+  if ( (len == SYSERR) || 
+       (ntohl(res.importance) < indexPriority)) {
+    PRINTQ("Content %s %s in lookup database. ",
+          (char*) &hn,
+          (len == SYSERR) ? "not indexed" : "had low priority");
+    if (do_fix == YES) {
+      if (SYSERR ==
+         insertContent(&indexingRequest->contentIndex,
+                       0,
+                       NULL,
+                       NULL,
+                       &dup)) {
+       PRINTQ("Could not fix, insertion failed.\n");
+      } else {
+       PRINTQ("Fixed.\n");
+      }
+    } else
+      PRINTQ("\n");
+  } else { /* test if correct */
+    if (0 != memcmp(&res.hash, 
+                   &indexingRequest->contentIndex.hash,
+                   sizeof(HashCode160))) {
+      PRINTQ("Bad value (hash) stored in database ");
+      if (do_fix == YES) {
+       if (SYSERR ==
+           insertContent(&indexingRequest->contentIndex,
+                         0,
+                         NULL,
+                         NULL,
+                         &dup)) {
+         PRINTQ("Could not fix, insertion failed.\n");
+       } else {
+         PRINTQ("Fixed.\n");
+       }
+      } else
+       PRINTQ("\n");
+    }
+  }
+  sendTCPResult(sock, OK);
+  return OK;
+}
+
+
+/**
+ * Process a query to list a file as on-demand encoded from the client.
+ * (code copied from afs/handler.c).
+ *
+ * @return SYSERR if the TCP connection should be closed, otherwise OK
+ **/
+static int csHandleRequestIndexFile(GNUNET_TCP_SOCKET * sock,
+                                   AFS_CS_INDEX_FILE * listFileRequest) {
+  HexName hex;
+  char * filename;
+  char * prefix;
+  int ret;
+
+  if (ntohs(listFileRequest->header.size) != 
+      sizeof(AFS_CS_INDEX_FILE)) {
+    LOG(LOG_WARNING, 
+       "WARNING: file indexing request from client was malformed!\n");
+    return SYSERR;
+  }
+  hash2hex(&listFileRequest->hash,
+          &hex);
+  filename = getConfigurationString("AFS",
+                                   "INDEX-DIRECTORY");
+  if (filename == NULL) {
+    LOG(LOG_WARNING,
+       "WARNING: rejecting content-unindex request, INDEX-DIRECTORY option not 
set!\n");
+    return -1;
+  }
+  prefix = expandFileName(filename);
+  FREE(filename);
+  filename = MALLOC(strlen(prefix) + 42);
+  strcpy(filename, prefix);
+  FREE(prefix);
+  strcat(filename, "/");
+  strcat(filename, (char*) &hex);
+
+  ret = sendTCPResult(sock, 
+                     appendFilename(filename));
+  FREE(filename);
+  return ret;
+}
+
+
+/**
+ * Handle data available on the TCP socket descriptor;
+ * check that the request is already fullfilled.
+ **/
+static void checkProcessor(int * sockptr) {
+  CS_HEADER * hdr;
+  GNUNET_TCP_SOCKET sock;
+  int i;
+  int sockDescriptor;
+
+  sockDescriptor = *sockptr;
+
+  /* register the socket */
+  initGNUnetServerSocket(sockDescriptor,
+                        &sock);
+  while (1) {
+    hdr = NULL;    
+    if (SYSERR == readFromSocket(&sock,
+                                &hdr) )
+      break; /* connection closed */
+    /* demultiplex */
+    switch (ntohs(hdr->tcpType)) {
+    case AFS_CS_PROTO_INDEX_FILE:
+      i = csHandleRequestIndexFile(&sock,
+                                  (AFS_CS_INDEX_FILE*)hdr);
+      break;
+    case AFS_CS_PROTO_UPLOAD_FILE:
+      /* for now: just ignore */
+      i = sendTCPResult(&sock, 
+                       OK);
+      break;
+    case AFS_CS_PROTO_INSERT_3HASH:
+      i = checkInsert3HASH(&sock,
+                          (AFS_CS_INSERT_3HASH*)hdr);
+      break;
+    case AFS_CS_PROTO_INSERT_CHK:
+      i = checkInsertCHK(&sock,
+                        (AFS_CS_INSERT_CHK*)hdr);
+      break;
+    case AFS_CS_PROTO_INDEX_BLOCK:
+      i = checkIndex(&sock,
+                    (AFS_CS_INDEX_BLOCK*) hdr);
+      break;
+    case AFS_CS_PROTO_INDEX_SUPER: 
+      i = checkSuper(&sock,
+                    (AFS_CS_INDEX_SUPER*) hdr);
+      break;
+    default:
+      i = SYSERR;
+      break;
+    } /* end of switch */
+    if (OK != i) {
+      break;
+    }
+    FREE(hdr);
+  }
+  destroySocket(&sock);
+}
+
+/**
+ * Check that the given file is properly indexed
+ * (and fix if appropriate). Also return SYSERR
+ * if the file is gone and should thus be removed
+ * from the list.
+ **/
+int checkIndexedFile(char * name,
+                    int index,
+                    GNUNET_TCP_SOCKET * sock) {
+  int result;
+  Block * top;
+
+  PRINTQ("* %s\n",
+        name);
+  top = insertFile(sock,
+                  name,
+                  NULL,
+                  NULL);
+  if (top != NULL) {
+    top->vtbl->done(top, NULL);
+    result = tcp_verifies;
+  } else
+    result = SYSERR;
+  if (result == SYSERR) {
+    PRINTQ("Problem checking indexing of file %s ",
+          name);
+    if (do_fix == YES) {
+      PRINTQ("Removing file from list.\n");
+      return SYSERR; /* remove file, there was a problem */
+    } else {
+      PRINTQ("\n");
+      return OK;
+    }
+  }
+  return OK;
+}
+
+/**
+ * Check that all files that are listed in
+ * the list of indexed files actually exist
+ * and that they are properly indexed in the
+ * lookup (triple->double hash) database.
+ **/
+static void checkIndexedFileList() {
+  GNUNET_TCP_SOCKET * sock;
+  int count;
+
+  sock = getClientSocket();
+  if (sock == NULL)
+    errexit("FATAL: could not create socket.\n");
+  PRINTQ("Checking indexed files\n");
+  count = forEachIndexedFile((IndexedFileNameCallback)&checkIndexedFile,
+                            sock);
+  PRINTQ("==> Done with %d indexed files.\n",
+        count);
+  releaseClientSocket(sock);
+}
+
+/**
+ * Print a list of the options we offer.
+ **/
+static void printhelp() {
+  static Help help[] = {
+    HELP_CONFIG,
+    { 'a', "all", NULL,
+      "check everything" },
+    { 'D', "data", NULL,
+      "only check the content database" },
+    { 'f', "files", NULL,
+      "only check the indexed files" },
+    HELP_HELP,
+    HELP_LOGLEVEL,
+    { 'n', "nofix", NULL,
+      "do not fix problems, only report" },
+    { 'p', "prio", "PRIORITY",
+      "specifies the priority of the restored content" },
+    { 'q', "quiet", NULL,
+      "be quiet" },
+    { 'r', "reset", NULL,
+      "reset bloom-filters (requires 'a' option, slow)" },
+    { 'u', "update", NULL,
+      "perform database-updates necessary after GNUnet version change" },
+    HELP_VERSION,
+    HELP_VERBOSE,
+    HELP_END,
+  };
+  formatHelp("gnunet-check [OPTIONS]",
+            "Check GNUnet AFS databases.\n"
+            "Never run gnunet-check while gnunetd is running!",
+            help);
+}
+
+/**
+ * Perform option parsing from the command line. 
+ **/
+static int parseCommandLine(int argc, 
+                           char * argv[]) {
+  int c;
+
+  /* set the 'magic' code that indicates that
+     this process is 'gnunetd' (and not any of
+     the user-tools).  Needed such that we use
+     the right configuration file... */
+  FREENONNULL(setConfigurationString("GNUNETD",
+                                    "_MAGIC_",
+                                    "YES"));
+  FREENONNULL(setConfigurationString("GNUNETD",
+                                    "LOGFILE",
+                                    NULL));
+  FREENONNULL(setConfigurationString("GNUNET-INSERT",
+                                    "INDEX-CONTENT",
+                                    "YES"));
+  while (1) {
+    int option_index = 0;
+    static struct GNoption long_options[] = {
+      LONG_DEFAULT_OPTIONS,
+      { "all",     0, 0, 'a' },
+      { "data",    0, 0, 'D' },
+      { "files",   0, 0, 'f' },
+      { "nofix",   0, 0, 'n' },
+      { "prio",    1, 0, 'p' },
+      { "reset",   0, 0, 'r' },
+      { "update",  0, 0, 'u' },
+      { "verbose", 0, 0, 'V' },
+      { "quiet",   0, 0, 'q' },
+      { 0,0,0,0 }
+    };
+    
+    c = GNgetopt_long(argc,
+                     argv, 
+                     "vhdc:nDp:faVqruL:", 
+                     long_options, 
+                     &option_index);    
+    if (c == -1) 
+      break;  /* No more flags to process */
+    if (YES == parseDefaultOptions(c, GNoptarg))
+      continue;    
+    switch(c) {
+    case 'L':
+      FREENONNULL(setConfigurationString("GNUNETD",
+                                        "LOGLEVEL",
+                                        GNoptarg));
+     break;
+    case 'q':
+      be_quiet = YES;
+      break;
+    case 'a':
+      FREENONNULL(setConfigurationString("GNUNET-CHECK",
+                                        "MODE",
+                                        "a"));
+      break;
+    case 'D':
+      FREENONNULL(setConfigurationString("GNUNET-CHECK",
+                                        "MODE",
+                                        "d"));
+      break;
+    case 'f':
+      FREENONNULL(setConfigurationString("GNUNET-CHECK",
+                                        "MODE",
+                                        "f"));
+      break;
+    case 'h': 
+      printhelp(); 
+      return SYSERR;
+    case 'r':
+      FREENONNULL(setConfigurationString("GNUNET-CHECK",
+                                        "RESETBLOOMFILTERS",
+                                        "YES"));
+      do_reset = YES;
+      break;
+    case 'u':
+      FREENONNULL(setConfigurationString("GNUNET-CHECK",
+                                        "UPDATE",
+                                        "YES"));
+      do_reset = YES;
+      break;
+    case 'p': {
+      unsigned int prio;
+      
+      if (1 != sscanf(GNoptarg, "%ud", &prio)) {
+       LOG(LOG_FAILURE,
+           "FAILURE: You must pass a number to the -p option.\n");
+       return SYSERR;
+      }
+      setConfigurationInt("GNUNET-CHECK",
+                         "FIXED-PRIORITY",
+                         prio);
+      break;
+    }
+    case 'n':
+     do_fix = NO;
+      break;
+    case 'v': 
+      printf("GNUnet v%s, gnunet-check v%s\n",
+            VERSION, AFS_VERSION);
+      return SYSERR;
+    case 'V':
+      be_verbose = YES;
+      break;
+    default:
+      printf("Unknown option %c. Aborting.\n"
+            "Use --help to get a list of options.\n",
+            c);
+      return SYSERR;
+    } /* end of parsing commandline */
+  }
+  if (GNoptind < argc) {
+    printf("Invalid arguments: ");
+    while (GNoptind < argc)
+      printf("%s ", argv[GNoptind++]);
+    printf("\nExiting.\n");
+    return SYSERR;
+  }
+  if (do_fix == NO) 
+    PRINTQ("You selected verification only, will not fix problems!\n");
+  return OK;
+}
+
+typedef struct {
+  int fd;
+  PTHREAD_T pt;
+} CSPair;
+
+
+static Semaphore * serverSignal;
+static int listenerFD;
+
+/**
+ * Initialize the TCP port and listen for incoming connections.
+ **/
+static void * tcpListenMain() {
+  CSPair * clients = NULL;
+  int clientsSize = 0;
+
+  int incomingFD;
+  int lenOfIncomingAddr;
+  int listenerPort;
+  struct sockaddr_in serverAddr, clientAddr;
+  const int on = 1;
+
+  listenerPort = getGNUnetPort(); 
+  /* create the socket */
+  if ( (listenerFD = SOCKET(PF_INET, SOCK_STREAM, 0)) < 0) 
+    errexit("Error opening socket. Is gnunetd running?\n");
+ 
+  /* fill in the inet address structure */
+  memset((char *) &serverAddr, 0, sizeof(serverAddr));
+  serverAddr.sin_family 
+    = AF_INET;
+  serverAddr.sin_addr.s_addr
+    = htonl(INADDR_ANY);
+  serverAddr.sin_port   
+    = htons(listenerPort);
+ 
+  if ( SETSOCKOPT(listenerFD, 
+                 SOL_SOCKET, 
+                 SO_REUSEADDR, 
+                 &on, sizeof(on)) < 0 )
+    perror("setsockopt");
+
+  if (BIND(listenerFD, 
+          (struct sockaddr *) &serverAddr,
+          sizeof(serverAddr)) < 0)
+    errexit("Error (%s) binding the TCP listener to port. Is gnunetd 
running?\n",
+           STRERROR(errno));
+  
+  /* start listening for new connections */
+  LISTEN(listenerFD, 1); 
+  SEMAPHORE_UP(serverSignal);
+  /* process incoming data */
+  while (listenerFD != -1) {
+    /* wait for a connection and process it */
+    lenOfIncomingAddr = sizeof(clientAddr);
+    incomingFD = ACCEPT(listenerFD,
+                       (struct sockaddr *)&clientAddr, 
+                       &lenOfIncomingAddr);
+    if (incomingFD < 0) {
+      if (listenerFD != -1)
+       LOG(LOG_ERROR, 
+           "ERROR accepting new connection.\n");
+      continue;
+    }
+    LOG(LOG_DEBUG, 
+       "TCP: starting server\n");
+    GROW(clients,
+        clientsSize,
+        clientsSize+1);
+    clients[clientsSize-1].fd = incomingFD;
+    if ((PTHREAD_CREATE(&clients[clientsSize-1].pt,
+                       (PThreadMain) &checkProcessor, 
+                       (void *)&incomingFD,
+                       16*1024)) != 0) {
+      LOG(LOG_ERROR, 
+         "Error creating thread to handle new incoming connection.\n");    
+      CLOSE(incomingFD);
+      GROW(clients,
+          clientsSize,
+          clientsSize-1);
+    }
+  } /* while (listenerFD != -1) */
+  while (clientsSize > 0) {
+    void * unused;
+
+    SHUTDOWN(clients[clientsSize-1].fd, 2);
+    PTHREAD_JOIN(&clients[clientsSize-1].pt, &unused);
+    GROW(clients,
+        clientsSize,
+        clientsSize-1);
+  }
+  return NULL;
+} 
+
+/**
+ * Maximum length of the name of an indexed file (with path).
+ **/ 
+#define MAX_LINE_SIZE 1024
+
+/**
+ * Update from 0.6.1b to 0.6.2.  Difference is that
+ * now all files listed in the index-list must
+ * be in INDEX-DIRECTORY and have the hash of the
+ * contents for the name.  This code adds the
+ * correct links and updates the list.
+ */
+static int update061b() {
+  char * filename;
+  char * indexDir;
+  FILE * handle;
+  char * result;
+  char * line;
+  char * fil;
+  char * afsdir;
+  int fix_count;
+  char ** lines;
+  int line_count;
+  int i;
+ 
+  afsdir = getFileName("AFS",
+                      "AFSDIR",
+                      "Configuration file must specify filename for"\
+                      " storing AFS data in section"\
+                      " %s under %s.\n");
+  fil = MALLOC(strlen(afsdir)+
+              strlen(DATABASELIST)+2);
+  strcpy(fil, afsdir);
+  mkdirp(fil); /* important: the directory may not exist yet! */
+  strcat(fil, "/");
+  strcat(fil, DATABASELIST);
+  FREE(afsdir);
+
+  handle = FOPEN(fil, "r+");
+  if (handle == NULL) {
+    /* no indexed files, nothing to do! */
+    FREE(fil);
+    return OK;
+  }
+  filename = getConfigurationString("AFS",
+                                   "INDEX-DIRECTORY");
+  if (filename == NULL) {
+    LOG(LOG_WARNING,
+       "WARNING: can not fix indexed content, INDEX-DIRECTORY option not 
set!\n");
+    FREE(fil);
+    return SYSERR;
+  }
+  indexDir = expandFileName(filename);
+  mkdirp(indexDir);
+  FREE(filename);  
+
+
+  fseek(handle, 0, SEEK_SET);
+  line = MALLOC(MAX_LINE_SIZE);
+  result = line;
+  lines = NULL;
+  line_count = 0;
+  fix_count = 0;
+  while (1) {    
+    result = fgets(line, MAX_LINE_SIZE - 1, handle);
+    if (result == NULL)
+      break;
+    GROW(lines,
+        line_count,
+        line_count+1);
+    if (strlen(result) > 1) {
+      lines[line_count-1] = STRDUP(result);
+      if (0 != strncmp(result,
+                      indexDir,
+                      strlen(indexDir))) 
+       fix_count++;
+    }
+  }
+  if (fix_count == 0) {
+    fclose(handle);
+    FREE(indexDir);
+    for (i=0;i<line_count;i++)
+      FREENONNULL(lines[i]);
+    GROW(lines,
+        line_count,
+        0);
+    FREE(fil);
+    FREE(line);
+    FREE(indexDir);
+    return OK;
+  }  
+  fseek(handle, 0, SEEK_SET);
+  truncate(fil, 0);
+  
+  for (i=0;i<line_count;i++) {
+    if ( ( lines[i] != NULL) &&
+        (0 != strncmp(lines[i],
+                      indexDir,
+                      strlen(indexDir))) ) {
+      HashCode160 hc;
+      if (OK != getFileHash(lines[i],
+                           &hc)) {
+       fprintf(handle,
+               "\n");
+      } else {
+       HexName hex;
+       char * lname;
+       hash2hex(&hc,
+                &hex);
+       lname = MALLOC(strlen(indexDir) + sizeof(HexName) + 1);
+       strcpy(lname,
+              indexDir);
+       strcat(lname,
+              "/");
+       strcat(lname,
+              (char*)&hex);
+       if (0 != SYMLINK(lines[i], lname)) {
+         errexit("FATAL: could not create link from %s to %s: %s\n",
+                 lines[i],
+                 lname,
+                 strerror(errno));
+       } else {
+         fprintf(handle,
+                 "%s\n",
+                 lname);
+       }
+       FREE(lname);
+      }
+    } else {
+      fprintf(handle,
+             "%s\n",
+             lines[i] == NULL ? "" : lines[i]);
+    }
+    FREENONNULL(lines[i]);
+  }
+  GROW(lines,
+       line_count,
+       0);
+  FREE(fil);
+  FREE(line);
+  FREE(indexDir);
+  fclose(handle);  
+  return OK;
+}
+
+int main(int argc, char * argv[]) {
+  PTHREAD_T tcpPseudoServer;
+  char * checkString;
+  char check;
+  int i;
+  void * unused;
+  
+  if (SYSERR == initUtil(argc, argv, &parseCommandLine))
+    return 0;
+
+  if (testConfigurationString("GNUNET-CHECK",
+                             "UPDATE",
+                             "YES")) {
+    int * sbit;
+    int version;
+    int val;
+
+    sbit = NULL;
+    if (sizeof(int) == stateReadContent("VERSION",
+                                       (void**)&sbit)) {
+      version = *sbit;
+      FREE(sbit);
+      switch (ntohl(version)) {
+      case 0x061b: /* need to add links for indexed files */
+       printf("Updating from version %x\n",
+              version);
+       if (SYSERR == update061b())
+         errexit("Errors while updating version!\n");
+       /* finally, update version to current */
+       val = htonl(0x0620);
+       stateWriteContent("VERSION",
+                         sizeof(int),
+                         &val);
+       break;
+      case 0x0620:
+       printf("State is current, no update required.\n");
+       break;
+      default:
+       printf("WARNING: unknown GNUnet version %x\n",
+              version);
+      }
+    } else {
+      FREENONNULL(sbit);
+      version = 0; /* first start */
+    }
+  }
+  
+  checkString = getConfigurationString("GNUNET-CHECK", 
+                                      "MODE");
+  if (checkString != NULL)
+    check = checkString[0];
+  else
+    check = 'n';
+  FREENONNULL(checkString);
+  if (check == 'n') {
+    if (testConfigurationString("GNUNET-CHECK",
+                               "UPDATE",
+                               "YES")) {
+      doneUtil();
+      return 0;
+    }
+    fprintf(stderr,
+           "You must choose what to check (specify -D, -f, or -a).\n");
+    doneUtil();
+    return -1;
+  }
+
+  fixedPriority = getConfigurationInt("GNUNET-CHECK",
+                                     "FIXED-PRIORITY");
+  if (fixedPriority <= 0) {
+    LOG(LOG_WARNING, 
+        "WARNING: GNUNET-CHECK/FIXED-PRIORITY in conf either <= 0 or 
missing\n");
+    fixedPriority = 0;
+  }
+
+  indexPriority = getConfigurationInt("GNUNET-INSERT",
+                                     "CONTENT-PRIORITY");
+  if (indexPriority <= 0) {
+    LOG(LOG_WARNING,
+       "WARNING: GNUNET-INSERT/CONTENT-PRIORITY in conf either <= 0 or 
missing\n");
+    indexPriority = 65536;
+  }
+
+  initManager();
+  initFileIndex();
+  initBloomfilters();
+
+  serverSignal = SEMAPHORE_NEW(0);
+  if (0 != PTHREAD_CREATE(&tcpPseudoServer,
+                         (PThreadMain) &tcpListenMain, 
+                         NULL,
+                         16*1024))
+    errexit("FATAL: Could not create tcpServer thread\n");
+  SEMAPHORE_DOWN(serverSignal);
+  SEMAPHORE_FREE(serverSignal);
+
+  if ( (do_reset == YES) && 
+       (check != 'a') ) {
+    errexit("Can't use --reset without -a\n");
+  }
+  if ( (do_reset == YES) && 
+       (check == 'a') &&
+       (do_fix == YES) ) {
+    resetBloomfilter(singleBloomFilter);
+    resetBloomfilter(superBloomFilter);
+  }
+  if ((check == 'a') || (check == 'f'))
+    checkIndexedFileList();
+  if ((check == 'a') || (check == 'd'))
+    checkDatabase(); 
+
+  i = listenerFD;
+  listenerFD = -1;
+  SHUTDOWN(i, 2);
+  CLOSE(i);
+  PTHREAD_JOIN(&tcpPseudoServer, &unused);
+  doneBloomfilters();
+  doneManager();
+  doneFileIndex();
+  doneUtil();
+  return 0;
+}
+
+
+/* end of gnunet-check.c */

Added: freeway/src/org/gnu/freeway/gnunet-convert.c
===================================================================
--- freeway/src/org/gnu/freeway/gnunet-convert.c        2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/gnunet-convert.c        2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,300 @@
+/*
+     This file is part of GNUnet.
+     (C) 2002, 2003 Christian Grothoff (and other contributing authors)
+
+     GNUnet is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with GNUnet; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file applications/afs/module/gnunet-convert.c
+ * @brief Little tool to convert content databases from format to another.
+ *        Use if you change the database manager type or the bucket count.
+ * @author Igor Wronsky
+ * @author Christian Grothoff
+ **/
+
+#include "gnunet_util.h"
+#include "gnunet_afs_esed2.h"
+#include "manager.h"
+#include "bloomfilter.h"
+#include "manager.c"
+#include "bloomfilter.c"
+#include "large_file_support.c"
+#include "fileindex.c"
+  
+static DatabaseAPI * srcHandle;
+static DatabaseAPI * dstHandle;
+
+unsigned int insertedBlocks = 0;
+unsigned int failedBlocks = 0;
+
+static int progressDot = 0;
+static int be_verbose = NO;
+static int be_quiet = NO;
+
+static void addToDestination(HashCode160 * key,
+                            ContentIndex *ce,
+                            void * data,
+                            unsigned int dataLen,
+                            void * unused) {
+  HashCode160 hc;
+  int bucket;
+
+  progressDot++;
+  if ( ((progressDot & 255) == 0) &&
+       (be_quiet == NO) ) {
+    printf(".");
+    fflush(stdout);    
+  }
+
+  switch (ntohs(ce->type)) {
+  case LOOKUP_TYPE_CHK:
+    addToBloomfilter(singleBloomFilter,
+                    &ce->hash);                       
+    break;
+  case LOOKUP_TYPE_3HASH:
+    hash(&ce->hash,
+        sizeof(HashCode160),
+        &hc);
+    addToBloomfilter(singleBloomFilter,
+                    &hc);
+    break;
+  case LOOKUP_TYPE_SUPER:
+    addToBloomfilter(superBloomFilter,
+                    &ce->hash);
+    break;
+  case LOOKUP_TYPE_SBLOCK:
+    addToBloomfilter(singleBloomFilter,
+                    &hc);              
+    break;
+  case LOOKUP_TYPE_CHKS:
+    /* do nothing! */
+    break;
+  default:
+    LOG(LOG_WARNING,
+       "WARNING: encountered unexpected type %d\n",
+       ntohs(ce->type));
+  }
+ 
+  bucket = computeBucket(key,
+                        dstHandle->buckets);
+  if (SYSERR == dstHandle->writeContent(dstHandle->dbHandles[bucket],
+                                       ce,
+                                       dataLen,
+                                       data))
+    failedBlocks++;
+  else
+    insertedBlocks++;  
+  FREENONNULL(data);
+}
+
+/**
+ * Print a list of the options we offer.
+ **/
+static void printhelp() {
+  static Help help[] = {
+    HELP_CONFIG,
+    HELP_HELP,
+    HELP_LOGLEVEL,
+    { 'q', "quiet", NULL,
+      "be quiet" },
+    HELP_VERSION,
+    HELP_VERBOSE,
+    HELP_END,
+  };
+  formatHelp("gnunet-convert [OPTIONS]",
+            "Convert GNUnet AFS database to different QUOTA or database 
type.\n"
+            "Never run gnunet-convert while gnunetd is running!",
+            help);
+}
+
+/**
+ * Perform option parsing from the command line. 
+ **/
+static int parseCommandLine(int argc, 
+                           char * argv[]) {
+  int c;
+
+  FREENONNULL(setConfigurationString("GNUNETD",
+                                    "_MAGIC_",
+                                    "YES"));
+  FREENONNULL(setConfigurationString("GNUNETD",
+                                    "LOGFILE",
+                                    NULL));
+  while (1) {
+    int option_index = 0;
+    static struct GNoption long_options[] = {
+      LONG_DEFAULT_OPTIONS,
+      { "verbose",  0, 0, 'V' },
+      { "quiet",    0, 0, 'q' },
+      { 0,0,0,0 }
+    };
+    
+    c = GNgetopt_long(argc,
+                     argv, 
+                     "vhdc:nVqL:", 
+                     long_options, 
+                     &option_index);    
+    if (c == -1) 
+      break;  /* No more flags to process */
+    if (YES == parseDefaultOptions(c, GNoptarg))
+      continue;    
+    switch(c) {
+    case 'q':
+      be_quiet = YES;
+      break;
+    case 'V':
+      be_verbose = YES;
+      break;
+    case 'v': 
+      printf("GNUnet v%s, gnunet-convert v%s\n",
+            VERSION, 
+            AFS_VERSION);
+      return SYSERR;
+      break;
+    case 'h': 
+      printhelp(); 
+      return SYSERR;
+      break;
+    default:
+      printf("Unknown option %c. Aborting.\n"
+            "Use --help to get a list of options.\n",
+            c);
+      return SYSERR;
+    } /* end of parsing commandline */
+  }
+  if (GNoptind < argc) {
+    printf("Invalid arguments: ");
+    while (GNoptind < argc)
+      printf("%s ", argv[GNoptind++]);
+    printf("\nExiting.\n");
+    return SYSERR;
+  }
+  return OK;
+}
+
+
+
+int main(int argc, 
+        char * argv[]) {
+  char * srcDb;
+  char * dstDb;
+  char * tmp;
+  int entries;
+  int newQuota;
+  int * oldQuota;
+  int ret;
+  unsigned int i;
+
+  if (SYSERR == initUtil(argc, argv, &parseCommandLine))
+    return 0;
+
+  newQuota = getConfigurationInt("AFS",
+                                "DISKQUOTA");
+  if (newQuota == 0)
+    errexit("FATAL: you must specify available diskspace"
+           " in section AFS under DISKQUOTA\n"); 
+  oldQuota = NULL;
+  ret = stateReadContent("AFS-DISKQUOTA",
+                        (void**)&oldQuota);
+  if (ret != sizeof(unsigned int))
+    errexit("FATAL: no conversion possible, no old database known.\n");
+  tmp = NULL;
+  ret = stateReadContent("AFS-DATABASETYPE",
+                        (void**)&tmp);
+  dstDb = getConfigurationString("AFS",
+                                "DATABASETYPE");  
+  if (dstDb == NULL)
+    errexit("FATAL: you must specify a database type in section AFS.\n");
+  if ( (ret == -1) ||
+       ( (strncmp(tmp, dstDb, ret) == 0) &&
+        (newQuota == *oldQuota) ) )
+    errexit("FATAL: you need to specify a different DB type "
+           "(or quota) in gnunet.conf to run gnunet-convert.\n");
+  srcDb = MALLOC(ret+1);
+  memcpy(srcDb, tmp, ret);
+  FREENONNULL(tmp);
+  srcDb[ret] = '\0';
+  /* initialize old DB with old config! */
+  setConfigurationInt("AFS",
+                     "DISKQUOTA",
+                     *oldQuota);
+  FREENONNULL(oldQuota);
+  FREENONNULL(setConfigurationString("AFS",
+                                    "DATABASETYPE",
+                                    srcDb));
+  srcHandle = initializeDatabaseAPI(srcDb);
+
+  /* initialize new DB with new config */
+  stateWriteContent("AFS-DATABASETYPE",
+                   strlen(dstDb),
+                   dstDb);
+  setConfigurationInt("AFS",
+                     "DISKQUOTA",
+                     newQuota);
+  stateWriteContent("AFS-DISKQUOTA",
+                   sizeof(unsigned int),
+                   &newQuota);
+  FREENONNULL(setConfigurationString("AFS",
+                                    "DATABASETYPE",
+                                    dstDb));
+
+  /* FIXME: if the following call fails, we have set the new DB
+     parameters in the state module but the conversion has not been
+     done (and now the user is in deep shit (TM)).  We need to be able
+     to prevent the state module from commiting the changes until we
+     are done with everything.  The best way I can think of is to set
+     a flag in state.c "don't commit", then just record all writes and
+     once the flag is unset, do the writes. -- CG */
+
+  dstHandle = initializeDatabaseAPI(dstDb);
+  initBloomfilters(); /* from afs.c */
+  resetBloomfilter(superBloomFilter);
+  resetBloomfilter(singleBloomFilter);
+
+  /* copy old->new */
+  entries = 0;
+  for (i=0;i<srcHandle->buckets;i++)
+    entries += srcHandle->forEachEntryInDatabase(srcHandle->dbHandles[i],
+                                                &addToDestination, 
+                                                NULL);
+
+  fprintf(stdout,
+         "\n==> Done processing %d entries in index "
+         "(%d converted, %d failed).\n",
+         entries,
+         insertedBlocks,
+         failedBlocks);
+
+  /* close new, then delete old */
+  doneBloomfilters();
+  for (i=0;i<dstHandle->buckets;i++)
+    dstHandle->doneContentDatabase(dstHandle->dbHandles[i]);
+  for (i=0;i<srcHandle->buckets;i++) 
+    srcHandle->deleteDatabase(srcHandle->dbHandles[i]);
+  FREE(srcHandle->dbHandles);
+  FREE(dstHandle->dbHandles);
+  unloadDynamicLibrary(srcHandle->dynamicLibrary);
+  unloadDynamicLibrary(dstHandle->dynamicLibrary);
+  FREE(srcHandle);
+  FREE(dstHandle);
+  doneUtil();
+
+  return 0;
+}
+
+
+/* end of gnunet-convert.c */

Added: freeway/src/org/gnu/freeway/gnunet-delete.c
===================================================================
--- freeway/src/org/gnu/freeway/gnunet-delete.c 2005-01-31 23:47:23 UTC (rev 
136)
+++ freeway/src/org/gnu/freeway/gnunet-delete.c 2005-02-01 01:07:27 UTC (rev 
137)
@@ -0,0 +1,163 @@
+/*
+     This file is part of GNUnet.
+     (C) 2003 Christian Grothoff (and other contributing authors)
+
+     GNUnet is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with GNUnet; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * Tool to delete files that were indexed with gnunet-insert.
+ *
+ * @author Christian Grothoff
+ * @file applications/afs/tools/gnunet-delete.c 
+ **/
+
+#include "gnunet_afs_esed2.h"
+#include "platform.h"
+
+/**
+ * Print progess message.
+ **/
+static void printstatus(ProgressStats * stats,
+                       void * verboselevel) {
+  if (*(int*)verboselevel == YES)
+    printf("%8u of %8u bytes deleted\r",
+          (unsigned int) stats->progress,
+          (unsigned int) stats->filesize);  
+}
+
+/**
+ * Prints the usage information for this command if the user errs.
+ * Aborts the program.
+ **/
+static void printhelp() {
+  static Help help[] = {
+    HELP_CONFIG,
+    { 'f', "file", "NAME",
+      "specify the file to delete from GNUnet (obligatory, file must exist)"} ,
+    HELP_HELP,
+    HELP_HOSTNAME,
+    HELP_LOGLEVEL,
+    HELP_VERSION,
+    HELP_VERBOSE,
+    HELP_END,
+  };
+  formatHelp("gnunet-delete [OPTIONS] -f FILENAME",
+            "Remove file from GNUnet.  The specified file is not removed\n"
+            "from the filesystem but just from the local GNUnet datastore.",
+            help);
+}
+
+static int parseOptions(int argc,
+                       char ** argv) {
+  int c;
+
+  FREENONNULL(setConfigurationString("GNUNET-INSERT",
+                                    "INDEX-CONTENT",
+                                    "YES"));
+  while (1) {
+    int option_index=0;
+    static struct GNoption long_options[] = {
+      LONG_DEFAULT_OPTIONS,
+      { "file",          1, 0, 'f' },
+      { "verbose",       0, 0, 'V' },
+      { 0,0,0,0 }
+    };    
+    c = GNgetopt_long(argc,
+                     argv, 
+                     "vhdc:L:H:Vf:", 
+                     long_options, 
+                     &option_index);    
+    if (c == -1) 
+      break;  /* No more flags to process */
+    if (YES == parseDefaultOptions(c, GNoptarg))
+      continue;
+    switch(c) {
+    case 'V':
+      FREENONNULL(setConfigurationString("GNUNET-INSERT",
+                                        "VERBOSE",
+                                        "YES"));
+      break;
+    case 'f': 
+      FREENONNULL(setConfigurationString("GNUNET-DELETE",
+                                        "FILENAME",
+                                        GNoptarg));
+      break;    
+    case 'v': 
+      printf("GNUnet v%s, gnunet-delete v%s\n",
+            VERSION, 
+            AFS_VERSION);
+      return SYSERR;
+    case 'h': 
+      printhelp(); 
+      return SYSERR;
+    default: 
+      LOG(LOG_FAILURE,
+         "FAILURE: Unknown option %c. Aborting.\n"\
+         "Use --help to get a list of options.\n",
+         c);
+      return SYSERR;
+    } /* end of parsing commandline */
+  } /* while (1) */
+  return OK;
+}
+
+
+/**
+ * The main function to delete files from GNUnet.
+ * @param argc number of arguments from the command line
+ * @param argv command line arguments
+ * @return return 0 for ok, -1 on error
+ **/   
+int main(int argc, char ** argv) {
+  int beVerbose;
+  GNUNET_TCP_SOCKET * sock;
+  int ok;
+  char * filename;
+  
+  if (SYSERR == initUtil(argc, argv, &parseOptions)) 
+    return 0;
+  beVerbose = testConfigurationString("GNUNET-INSERT",
+                                     "VERBOSE",
+                                     "YES");
+
+  filename = getFileName("GNUNET-DELETE",
+                        "FILENAME",
+                        "ERROR: you must specify a filename (option -f)\n");
+  sock = getClientSocket();
+  if (sock == NULL)
+    errexit("FATAL: could not connect to gnunetd.\n");
+  ok = deleteFile(sock,
+                 filename,
+                 &printstatus,
+                 &beVerbose);
+  if (ok != OK) {
+    LOG(LOG_DEBUG,
+       "DEBUG: error in deleteFile\n");
+    printf("Error deleting file %s.\n"
+          "Probably a few blocks were already missing from the database.\n",
+          filename);
+  }
+  releaseClientSocket(sock); 
+  doneUtil();
+  FREE(filename);
+  if (ok == OK)
+    return 0;
+  else 
+    return -1;
+}  
+
+/* end of gnunet-delete.c */

Added: freeway/src/org/gnu/freeway/gnunet-tbench.c
===================================================================
--- freeway/src/org/gnu/freeway/gnunet-tbench.c 2005-01-31 23:47:23 UTC (rev 
136)
+++ freeway/src/org/gnu/freeway/gnunet-tbench.c 2005-02-01 01:07:27 UTC (rev 
137)
@@ -0,0 +1,266 @@
+/*
+     This file is part of GNUnet.
+     (C) 2001, 2002 Christian Grothoff (and other contributing authors)
+
+     GNUnet is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with GNUnet; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file applications/tbench/gnunet-tbench.c 
+ * @brief Transport mechanism benchmarking tool
+ * @author Paul Ruth
+ **/
+
+#include "tbench.h"
+#include "platform.h"
+
+#define TBENCH_VERSION "0.0.3"
+
+#define DEFAULT_MESSAGE_SIZE   10
+#define DEFAULT_TIMEOUT                2
+#define DEFAULT_SPACING                0
+
+#define OF_HUMAN_READABLE 0
+#define OF_GNUPLOT_INPUT 1
+
+static int  messageSize = DEFAULT_MESSAGE_SIZE;
+static int  messageCnt  = 1;
+static char messageReceiver[256];
+static int  messageIterations = 1;
+static int  messageTrainSize = 1;
+static int  messageTimeOut = DEFAULT_TIMEOUT;
+static int  messageSpacing = DEFAULT_SPACING;
+static int outputFormat = OF_HUMAN_READABLE;
+
+/**
+ * Parse the options, set the timeout.
+ * @param argc the number of options
+ * @param argv the option list (including keywords)
+ * @return OK on error, SYSERR if we should exit 
+ **/
+static int parseOptions(int argc,
+                       char ** argv) {
+  int option_index;
+  int c;  
+
+  FREENONNULL(setConfigurationString("GNUNETD",
+                                    "LOGFILE",
+                                    NULL));
+  while (1) {
+    static struct GNoption long_options[] = {
+      LONG_DEFAULT_OPTIONS,
+      { "gnuplot", 0, 0, 'g' },
+      { "rec", 1, 0, 'r'},
+      { "msg", 1, 0, 'n'},
+      { "iterations", 1, 0, 'i'},
+      { "timeout", 1, 0, 't' },
+      { "space", 1, 0, 'S' },
+      { "xspace", 1, 0, 'X' },
+      { 0,0,0,0 }
+    };    
+    option_index=0;
+    c = GNgetopt_long(argc,
+                     argv, 
+                     "vhdc:L:H:n:s:r:i:t:S:X:g", 
+                     long_options, 
+                     &option_index);    
+    if (c == -1) 
+      break;  /* No more flags to process*/
+    if (YES == parseDefaultOptions(c, GNoptarg))
+      continue;
+    switch(c) {
+    case 'v': 
+      printf("GNUnet v%s, gnunet-tbench v%s\n",
+            VERSION,
+            TBENCH_VERSION);
+      return SYSERR;
+
+    case 'h': {
+      static Help help[] = {
+       HELP_CONFIG,
+       HELP_HELP,
+       { 'g', "gnuplot", NULL,
+         "output in gnuplot format" },
+       { 'i', "iterations", "ITER",
+         "number of iterations" }, 
+       HELP_LOGLEVEL,  
+       { 'n', "msg", "MESSAGES",
+         "number of messages to use per iteration"},
+       { 'r', "rec", "RECEIVER",
+         "receiver host identifier (HEX file name)" },
+       { 's', "size", "SIZE",
+         "message size" },
+       { 'S', "space", "SPACE",
+         "inter-train message spacing" },
+       { 't', "timeout", "TIMEOUT",
+         "time to wait for the arrival of a response" },
+       HELP_VERSION,
+       { 'X', "xspace", "COUNT",
+         "sleep for SPACE ms after COUNT messages"},
+       HELP_END,
+      };
+      formatHelp("gnunet-chat [OPTIONS]",
+                "Start GNUnet chat client.",
+                help);
+      return SYSERR;
+    }
+    case 's': 
+      if(!sscanf(GNoptarg,"%d",&messageSize)){
+       printf("-s argument not a number\n");
+       exit(1);
+      }
+      break;
+    case 'g': 
+      outputFormat = OF_GNUPLOT_INPUT;
+      break;
+    case 'X':
+      if(!sscanf(GNoptarg,"%d",&messageTrainSize)){
+       printf("-X argument not a number\n");
+       exit(1);
+      }
+      break;
+    case 'n': 
+      if(!sscanf(GNoptarg,"%d",&messageCnt)){
+       printf("-n argument not a number\n");
+       exit(1);
+      }
+      break;
+
+    case 'r': 
+      strncpy(messageReceiver, GNoptarg, strlen(GNoptarg));
+      break;
+
+    case 'i': 
+      if(!sscanf(GNoptarg,"%d",&messageIterations)){
+       printf("-i argument not a number\n");
+       exit(1);
+      }
+      break;
+
+    case 't':
+      if(!sscanf(GNoptarg,"%d",&messageTimeOut)){
+       printf("-t argument not a number\n");
+       exit(1);
+      }
+      break;
+
+    case 'S':
+      if(!sscanf(GNoptarg,"%d",&messageSpacing)){
+       printf("-S argument not a number\n");
+       exit(1);
+      }
+      break;
+
+    default: 
+      LOG(LOG_FAILURE,
+         "FAILURE: Unknown option %c. Aborting.\n"\
+         "Use --help to get a list of options.\n",
+         c);
+      return -1;
+    } /* end of parsing commandline */
+  } /* while (1) */
+  return OK;
+}
+
+/**
+ * Tool to benchmark the performance of the P2P transports.
+ *
+ * @param argc number of arguments from the command line
+ * @param argv command line arguments
+ * @return return value from gnunetsearch: 0: ok, -1: error
+ **/   
+int main(int argc, char ** argv) {
+  GNUNET_TCP_SOCKET * sock;
+  TBENCH_CS_MESSAGE msg;
+  TBENCH_CS_REPLY * buffer;
+  float messagesPercentLoss;
+
+  if (SYSERR == initUtil(argc, argv, &parseOptions))
+    return 0; /* parse error, --help, etc. */ 
+  sock = getClientSocket();
+  if (sock == NULL)
+    errexit("FATAL: could not connect to gnunetd.\n");
+
+  memset(&msg,
+        0,
+        sizeof(TBENCH_CS_MESSAGE));
+  msg.msgSize     =htons(messageSize);
+  msg.msgCnt      =htons(messageCnt);
+  msg.iterations  =htons(messageIterations);
+  msg.intPktSpace =htons(messageSpacing);
+  msg.trainSize   =htons(messageTrainSize);
+  msg.timeOut     =htonl(messageTimeOut);
+  if (strlen(messageReceiver) != 2*sizeof(HashCode160))
+    errexit("Invalid receiver peer ID specified (%s is not %d character hex 
name)\n",
+           messageReceiver,
+           2*sizeof(HashCode160));
+  hex2hash((HexName*)messageReceiver,
+          &msg.receiverId.hashPubKey);
+  msg.header.size = htons(sizeof(TBENCH_CS_MESSAGE));
+  msg.header.tcpType = htons(TBENCH_CS_PROTO_REQUEST);
+
+  if (SYSERR == writeToSocket(sock,
+                             &msg.header))
+    return -1;
+  
+  buffer = MALLOC(MAX_BUFFER_SIZE);
+  LOG(LOG_DEBUG,
+      "DEBUG: reading from readFromSocket\n");
+  if (OK == readFromSocket(sock, (CS_HEADER**)&buffer)) {
+    if((float)buffer->mean_loss <= 0){
+      messagesPercentLoss = 0.0;
+    } else {
+      messagesPercentLoss = (buffer->mean_loss/((float)htons(msg.msgCnt)));
+    }
+    switch (outputFormat) {
+    case OF_HUMAN_READABLE:
+      printf("Time:\n");
+      printf("\tmax      %d\n",
+            htons(buffer->max_time));
+      printf("\tmin      %d\n",
+            htons(buffer->min_time));
+      printf("\tmean     %f\n",
+            buffer->mean_time);
+      printf("\tvariance %f\n",
+            buffer->variance_time);
+      
+      printf("Loss:\n");
+      printf("\tmax      %d\n",
+            htons(buffer->max_loss));
+      printf("\tmin      %d\n",
+            htons(buffer->min_loss));
+      printf("\tmean     %f\n",
+            buffer->mean_loss);
+      printf("\tvariance %f\n",
+            buffer->variance_loss); 
+      break;
+    case OF_GNUPLOT_INPUT:
+      printf("%f %f\n",
+            buffer->mean_time,
+            1.0-messagesPercentLoss);
+      break;
+    default:
+      printf("WARNING: output format not known, this should not happen.\n");
+    }
+  } else 
+    printf("\nDid not receive the message from gnunetd. Is gnunetd 
running?\n");  
+  FREE(buffer);
+
+  releaseClientSocket(sock);
+  doneUtil();
+  return 0;
+}
+/* end of gnunet-tbench.c */ 

Added: freeway/src/org/gnu/freeway/gnunet-testbed.c
===================================================================
--- freeway/src/org/gnu/freeway/gnunet-testbed.c        2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/gnunet-testbed.c        2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,640 @@
+/*
+     This file is part of GNUnet.
+     (C) 2003, 2004 Christian Grothoff (and other contributing authors)
+
+     GNUnet is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with GNUnet; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file applications/testbed/gnunet-testbed.c 
+ * @brief A Testbed tool for performing distributed experiments
+ * @author Ronaldo Alves Ferreira
+ * @author Christian Grothoff
+ * @author Murali Krishan Ramanathan
+ *
+ * Todo:
+ * - test secure sign-on (and test testbed setup script!)
+ * - allow removing of peers (explicitly AND when peer shuts down!)
+ *   Problem: what happens to the peer-IDs in that case?
+ * - general problem: the way we use tcpio means that any rouge
+ *   testbed-gnunetd can stall gnunet-testbed indefinitely!
+ * - security: limit "exec" to certain processes
+ **/
+
+#include "gnunet_util.h"
+#include "platform.h"
+#include "testbed.h"
+#include "socket.h"
+#include "commands.h"
+#include <signal.h>
+
+#ifndef sighandler_t
+typedef void (*sighandler_t)(int);
+#endif
+
+#define TESTBED_VERSION                "0.0.4"
+#define HELPER                  "==HELPER=="
+
+/* we may want to change SHELL and PORT into values
+   obtained from the configuration... */
+
+#define SHELL  (NULL == getenv("BASH") ? "/bin/bash" : getenv("BASH"))
+
+#define PORT getConfigurationInt("GNUNET-TESTBED","PORT")
+
+/* TB_ALIASES should probably be forced to live somewhere
+   under ~/.gnunet */
+#define TB_ALIASES "/tmp/gnunet-testbed-aliasdefinitions"
+
+
+/**
+ * Name of gnunet-testbed binary.
+ */
+static char * testbedArg0;
+
+
+/* ****************** scriptability *************** */
+
+#ifndef MINGW /* FIXME MINGW */
+
+
+
+/**
+ * Parse the options, set the timeout.
+ *
+ * @param argc the number of options
+ * @param argv the option list (including keywords)
+ * @return OK on error, SYSERR if we should exit 
+ **/
+static int helperParseOptions(int argc, char *argv[]) {
+  int c, option_index;
+  
+  FREENONNULL(setConfigurationString("GNUNETD", 
+                                    "LOGFILE", 
+                                    NULL));
+  while (1) {
+    static struct GNoption long_options[] = {
+      LONG_DEFAULT_OPTIONS,
+      { 0,0,0,0 }
+    };    
+    
+    option_index = 0;
+    c = GNgetopt_long(argc,
+                     argv, 
+                     "vhdc:L:", 
+                     long_options, 
+                     &option_index);    
+    if (c == -1) 
+      break;  /* No more flags to process */
+    if (YES == parseDefaultOptions(c, GNoptarg))
+      continue;
+    switch(c) {
+    case 'v': 
+      printf("GNUnet v%s, gnunet-testbed v%s\n",
+            VERSION,
+            TESTBED_VERSION);
+      return SYSERR;     
+    case 'h': {
+      static Help help[] = {
+       HELP_CONFIG,
+       HELP_HELP,
+       HELP_LOGLEVEL,  
+       HELP_VERSION,
+       HELP_END,
+      };
+      formatHelp("gnunet-testbed ==HELPER== [OPTIONS] [COMMAND]",
+                "Start GNUnet-testbed helper.",
+                help);
+      return SYSERR;
+    }      
+    default: 
+      LOG(LOG_FAILURE,
+         "FAILURE: Unknown option %c. Aborting.\n"             \
+         "Use --help to get a list of options.\n",
+               c);
+      return -1;
+    } /* end of parsing commandline */
+  } /* while (1) */
+  setConfigurationStringList(&argv[GNoptind],
+                            argc - GNoptind);
+  return OK;
+}
+
+
+/**
+ * This is the main method of the helper-process that
+ * is invoked from the bash-process.  Helper encapsulates
+ * the command from the shell in a stream, sends it
+ * over the socket to the main gnunet-testbed process,
+ * retrieves there result and outputs he result back
+ * to bash.
+ */
+static int helper_main(int argc,
+                      char * argv[]) {
+  int    i, retVal, len, res;
+  char  *buf;
+  struct sockaddr_in soaddr;
+  
+  if (SYSERR == initUtil(argc,
+                        argv,
+                        &helperParseOptions))
+    return -1;
+
+  argc = getConfigurationStringList(&argv);  
+  
+  if (argc == 0) {
+    fprintf(stderr,
+           "ERROR: must have at least one argument!\n");
+    return -1;
+  }  
+  sock = SOCKET(PF_INET, 
+               SOCK_STREAM,
+               6); /* 6: TCP */
+  if (sock == -1) {
+    LOG(LOG_FAILURE,
+       "FAILURE: Cannot create socket (%s).\n",
+       STRERROR(errno));
+    return SYSERR;
+  }
+  soaddr.sin_family
+    = AF_INET;
+  soaddr.sin_addr.s_addr
+    = htonl(INADDR_LOOPBACK);
+  soaddr.sin_port 
+    = htons(PORT);
+  res = CONNECT(sock,
+               (struct sockaddr*)&soaddr, 
+               sizeof(soaddr));
+  if ( (res < 0) && 
+       (errno != EINPROGRESS) ) {
+    LOG(LOG_INFO,
+       "INFO: tcpio: Cannot connect to LOOPBACK:%d (%s)\n",
+       PORT,
+       STRERROR(errno));
+    CLOSE(sock);
+    sock = -1;
+    return SYSERR;
+  }
+  
+  /* write command to socket */
+  socketSend(strlen(argv[0]),
+            SOCKET_BEGIN_COMMAND, 
+            argv[0]);
+  FREE(argv[0]);
+  /* write args to socket */
+  for (i=1;i<argc;i++) {
+    socketSend(strlen(argv[i]), 
+              SOCKET_ADD_ARGUMENT, 
+              argv[i]);
+    FREE(argv[i]);
+  }
+  FREE(argv);
+  socketSend(0,
+            SOCKET_END_COMMAND, 
+            NULL);
+  
+  /* read result from socket, print to stderr,  obtain retVal */
+  i = SOCKET_PRINTF;
+  buf = NULL;
+  while (i == SOCKET_PRINTF) {
+    FREENONNULL(buf);
+    buf = NULL;
+    i = readSocket(&buf, &len);
+    if (i == SOCKET_PRINTF)
+      fprintf(stdout, 
+             "%.*s", 
+             (int) len, 
+             buf);
+  }  
+  retVal = *(int*)buf;
+  FREE(buf);  
+  close(sock);
+  return retVal;
+}
+
+/**
+ * A child (the shell) has quit.  So we exit,
+ * too.
+ */
+static void sigChildHandler(int signal,
+                           siginfo_t * info,
+                           void * extra) {
+  do_quit = YES;
+}
+
+/**
+ * This is the "bash" process.  Execs bash.
+ * @returns never
+ */
+static void bash_main() {
+  int   i;
+  FILE *aliases;
+  char * configFile;
+  char *argv[] = {
+    NULL,              /* replaced by SHELL */
+    "--init-file",
+    TB_ALIASES,
+    "-i",
+    NULL,
+  };
+  
+  configFile = getConfigurationString("FILES",
+                                     "gnunet.conf");
+  if (configFile == NULL)
+    errexit("Assertion failed at %s:%d\n",
+           __FILE__, __LINE__);
+  argv[0] = SHELL;
+  aliases = FOPEN(TB_ALIASES, "w+");
+  fprintf(aliases, 
+         "export PS1=\"[GTB]%% \"\n");
+  i=0;
+  while (commands[i].command != NULL) {
+    if (0 == strcmp("exit", commands[i].command)) {
+      fprintf(aliases,
+             "alias exit=\"%s ==HELPER== -c %s exit ; exit\"\n",
+             testbedArg0,
+             configFile);
+    } else {
+      fprintf(aliases,
+             "alias %s=\"%s ==HELPER== -c %s %s\"\n",
+             commands[i].command,
+             testbedArg0,
+             configFile,
+             commands[i].command);
+    }
+    i++;
+  }
+  FREE(configFile);
+  fclose(aliases);
+  doneUtil(); 
+  execvp(SHELL, argv);       
+  fprintf(stderr,
+         "FATAL: could not execute %s: %s\n",
+         SHELL,
+         STRERROR(errno));
+}
+
+/**
+ * Configuration...
+ **/
+static CIDRNetwork * trustedNetworks_ = NULL;
+
+/**
+ * Is this IP labeled as trusted for CS connections?
+ **/
+static int isWhitelisted(IPaddr ip) {   
+  return checkIPListed(trustedNetworks_,
+                      ip);
+}
+
+/**
+ * This is the main method of the server.  It reads
+ * commands from the socket that are created by helper process.
+ * It is the main process that also keeps the state
+ * throughout the session.
+ *
+ * @param bash_pid the process ID of the child that is bash
+ */
+static int server_main(pid_t bash_pid) {
+  int   i, status, ssock, lenOfIncomingAddr;
+  int   secs = 5;
+  const int on = 1;
+  struct sockaddr_in serverAddr, clientAddr;
+  struct sigaction oldAct;
+  struct sigaction newAct;
+  sigset_t set;
+  sigset_t oset;
+
+  
+  /* create the socket */
+ CREATE_SOCKET:
+  while ( (ssock = SOCKET(PF_INET, SOCK_STREAM, 0)) < 0) {
+    LOG(LOG_ERROR, 
+       "ERROR opening socket (%s). No client service"  \
+       "started. Trying again in 30 seconds.\n",
+       STRERROR(errno));
+    sleep(30);
+  }
+  
+  serverAddr.sin_family
+    = AF_INET;
+  serverAddr.sin_addr.s_addr 
+    = htonl(INADDR_ANY);
+  serverAddr.sin_port
+    = htons(PORT);
+  
+  if (SETSOCKOPT(ssock, 
+                SOL_SOCKET,
+                SO_REUSEADDR,
+                &on,
+                sizeof(on)) < 0)
+    perror("setsockopt");
+  
+  /* bind the socket */
+  if (BIND(ssock,
+          (struct sockaddr*)&serverAddr,
+          sizeof(serverAddr)) < 0) {
+    LOG(LOG_ERROR, 
+       "ERROR (%s) binding the TCP listener to "               \
+       "port %d. No proxy service started.\nTrying "           \
+       "again in %d seconds...\n",
+       STRERROR(errno),
+       PORT, 
+       secs);
+    sleep(secs);
+    secs += 5; /* slow progression... */
+    CLOSE(ssock);
+    goto CREATE_SOCKET;
+  }
+  
+  do_quit = NO;
+  /* signal handler is needed if the child did exit 
+     (e.g. with CTRL-D and not with the command 'exit') */
+  newAct.sa_sigaction = &sigChildHandler;
+  sigfillset(&newAct.sa_mask);
+  newAct.sa_flags = SA_NOCLDSTOP | SA_SIGINFO | SA_RESTART;
+  if (0 != sigaction(SIGCHLD,
+                    &newAct,
+                    &oldAct)) 
+    errexit("FATAL: could not install SIGCHLD handler: %s\n",
+           strerror(errno));
+  sigemptyset(&set);
+  sigaddset(&set, SIGCHLD);
+  if (0 != sigprocmask(SIG_UNBLOCK,
+                      &set,
+                      &oset))
+    errexit("FATAL: could not activate SIGCHLD handler: %s\n",
+           strerror(errno));
+
+  LISTEN(ssock, 5);
+  while ( (do_quit == NO) &&
+         (0 == waitpid(bash_pid, 
+                       &status, 
+                       WNOHANG)) ) {
+    int   argc;
+    char *command;
+    char **args;
+    char *buf;
+    unsigned int len;
+    fd_set rset;
+    fd_set wset;
+    fd_set eset;
+    IPaddr ipaddr;
+    
+    lenOfIncomingAddr = sizeof(clientAddr); 
+    /* accept is not interrupted by SIGCHLD,
+       so we must do a select first; yuck. */
+    FD_ZERO(&rset);
+    FD_ZERO(&wset);
+    FD_ZERO(&eset);
+    FD_SET(ssock, &rset);
+    sock = select(ssock+1, &rset, &wset, &eset, NULL);
+    if (sock == -1)
+      continue;
+    sock = ACCEPT(ssock,
+                 (struct sockaddr *)&clientAddr,
+                 &lenOfIncomingAddr);
+    if (sock < 0) {
+      fprintf(stderr,
+             "ACCEPT returned %d: %s\n",
+             sock,
+             strerror(errno));
+      continue;
+    }
+    /* access control! */
+    if (sizeof(struct in_addr) != sizeof(IPaddr))
+      errexit("FATAL: assertion failed at %s:%d\n",
+             __FILE__, __LINE__);
+    memcpy(&ipaddr,
+          &clientAddr.sin_addr,
+          sizeof(struct in_addr));   
+    if (NO == isWhitelisted(ipaddr)) {
+      LOG(LOG_WARNING,
+         "WARNING: Rejected unauthorized connection from %d.%d.%d.%d.\n",
+         PRIP(ntohl(*(int*)&clientAddr.sin_addr)));
+      CLOSE(sock);
+      continue;
+    }
+
+
+    /* read from socket, run reaction,
+       return result value and stdout value;
+       possibly set doExit to 1 to exit */    
+    buf = NULL;
+    if (SOCKET_BEGIN_COMMAND != readSocket(&buf, &len)) {
+      fprintf(stderr,
+             "FATAL: protocol violation on socket. "   \
+             "Expected command.\n");
+      return -1;
+    }
+    command = MALLOC(len+1);
+    memcpy(command, buf, len);
+    command[len] = '\0';    
+    argc = 0;
+    args = NULL;
+    FREE(buf);
+    buf = NULL;
+    while (SOCKET_ADD_ARGUMENT == readSocket(&buf, &len)) {
+      GROW(args, argc, argc+1);
+      args[argc-1] = MALLOC(len+1);
+      memcpy(args[argc-1], buf, len);       
+      args[argc-1][len] = '\0';
+      FREE(buf);
+      buf = NULL;
+    }
+    FREENONNULL(buf);
+    i = 0;
+    while (commands[i].command != NULL) {
+      if (0 == strcmp(commands[i].command, command)) {
+       int ret;
+       ret = commands[i].handler(argc, args);
+       socketSend(sizeof(unsigned int), 
+                  SOCKET_RETVAL,
+                  &ret);
+       break;
+      }
+      i++;
+    }
+    for (i=0;i<argc;i++)
+      FREE(args[i]);
+    GROW(args, argc, 0);
+    if (commands[i].command == NULL) {
+      /* should never happen unless the user
+        plays by hand with the aliases... */
+      i = -1;
+      PRINTF("ERROR: command %s not found!\n",
+            command);
+      socketSend(sizeof(unsigned int), 
+                SOCKET_RETVAL, 
+                &i);
+    }
+    FREE(command);    
+    close(sock);
+    sock = -1;
+  }
+  /* just to be certain, we could have
+     left the main loop due to doExit... */
+  waitpid(bash_pid, 
+         &status, 
+         WNOHANG);
+  /* restore... */
+  if (0 != sigaction(SIGCHLD,
+                    &oldAct,
+                    &newAct)) 
+    LOG(LOG_WARNING,
+       "WARNING: could not restore SIGCHLD handler: %s\n",
+       strerror(errno));
+  return status;
+}
+
+#endif
+
+/* *************** command line options *********** */
+
+
+/**
+ * Parse the options, set the timeout.
+ *
+ * @param argc the number of options
+ * @param argv the option list (including keywords)
+ * @return OK on error, SYSERR if we should exit 
+ **/
+static int parseOptions(int argc, char *argv[]) {
+  int c, option_index;
+  
+  FREENONNULL(setConfigurationString("GNUNETD", "LOGFILE", NULL));
+  while (1) {
+    static struct GNoption long_options[] = {
+      LONG_DEFAULT_OPTIONS,
+      { 0,0,0,0 }
+    };    
+    
+    option_index = 0;
+    c = GNgetopt_long(argc,
+                     argv, 
+                     "vhdc:L:", 
+                     long_options, 
+                     &option_index);    
+    if (c == -1) 
+      break;  /* No more flags to process */
+    if (YES == parseDefaultOptions(c, GNoptarg))
+      continue;
+    switch(c) {
+    case 'v': 
+      printf("GNUnet v%s, gnunet-testbed v%s\n",
+            VERSION,
+            TESTBED_VERSION);
+      return SYSERR;
+      
+    case 'h': {
+      static Help help[] = {
+       HELP_CONFIG,
+       HELP_HELP,
+       HELP_LOGLEVEL,  
+       HELP_VERSION,
+       HELP_END,
+      };
+      formatHelp("gnunet-testbed [OPTIONS]",
+                "Start GNUnet testbed controller.",
+                help);
+      return SYSERR;
+    }      
+    default: 
+      LOG(LOG_FAILURE,
+         "FAILURE: Unknown option %c. Aborting.\n"             \
+         "Use --help to get a list of options.\n",
+               c);
+      return -1;
+    } /* end of parsing commandline */
+  } /* while (1) */
+  return OK;
+}
+
+/* **************** main **************** */
+
+/**
+ * Create a testbed environment with multiple peers to collect 
+ * GNUnet statistics.  Note that the same binary is used for
+ * two distinct purposes.  One is the stateful master process
+ * that maintains the testbed.  A second type of process is
+ * created if HELPER is the first argument.  These processes
+ * are created by the user from the SHELL and they merely serve
+ * to communicate back the user-commands to the master-process.
+ *
+ * @param argc number of arguments from the command line
+ * @param argv command line arguments
+ * @return 0 on success, -1 on error
+ **/   
+int main(int argc, char *argv[]) {
+#ifndef MINGW /* FIXME MINGW */
+  pid_t pid;
+  char * ch;
+  
+  testbedArg0 = expandFileName(argv[0]);
+  /* first, evaluate if we are the special
+     helper process.  If so, eat HELPER
+     argument and run helper_main */
+  if (argc > 1) {
+    if (strcmp(argv[1], HELPER) == 0) {
+      argv[1] = argv[0];
+      return helper_main(argc -1, &argv[1]);
+    }
+  }
+  
+  if (SYSERR == initUtil(argc, argv, &parseOptions))
+    return -1; 
+
+  ch = getConfigurationString("GNUNET-TESTBED",
+                             "TRUSTED");
+  if (ch == NULL) {
+    trustedNetworks_ = parseRoutes("127.0.0.0/8;"); /* by default, trust 
localhost only */
+  } else {
+    trustedNetworks_ = parseRoutes(ch);    
+    if (trustedNetworks_ == NULL) 
+      errexit("Malformed entry in the configuration in section %s under %s: 
%s\n",
+             "GNUNET-TESTBED",
+             "TRUSTED", 
+             ch); 
+    FREE(ch);
+  }
+  
+  /* we are the main testbed process.  Fork of
+     a shell and start processing from the socket */
+  
+  pid = fork();
+  if (pid < 0)
+    errexit("FATAL: fork failed: %s\n", STRERROR(errno));
+  if (pid == 0) {
+    FREE(trustedNetworks_);
+    bash_main();
+    return 0; /* unreached */
+  } else {
+    int ret;
+    /* run actual main loop */
+    ret = server_main(pid);
+    /* just to be certain */
+    kill(pid, SIGHUP);
+    /* proper shutdown... */
+    doneUtil();
+    FREE(trustedNetworks_);
+    UNLINK(TB_ALIASES);
+    FREE(testbedArg0);
+    return ret;
+  } 
+#endif
+}
+
+/* end of gnunet-testbed.c */ 

Added: freeway/src/org/gnu/freeway/protocol/AbstractProtocol.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/AbstractProtocol.java  2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/AbstractProtocol.java  2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,141 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol;
+
+import org.gnu.freeway.*;
+import org.gnu.freeway.server.*;
+import org.gnu.freeway.transport.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.util.*;
+import java.util.logging.*;
+
+/**
+ *
+ */
+
+public abstract class AbstractProtocol extends LoggedObject implements Protocol
+{
+       protected CoreForProtocol               coreAPI;
+
+       private String                          name;
+
+       private CSHandler                       csHandler;
+       private PersistentDecoder               csDecoder;
+       private ClientExitHandler               exitHandler;
+       private P2PHandler                      p2pHandler;
+       private List                                    ids;
+
+
+       protected AbstractProtocol( String str )
+       {
+               super(true);
+               coreAPI=null;
+
+               name=str;
+
+               csHandler=new CSHandler() {
+                       public boolean handle( CSSession client, CSMessage 
message )
+                       {
+                               return onCSMessage(client,message);
+                       }
+                       };
+               csDecoder=new PersistentDecoder();
+               exitHandler=new ClientExitHandler() {
+                       public void handle( CSSession client )
+                       {
+                               onClientExit(client);
+                       }
+                       };
+               p2pHandler=new P2PHandler() {
+                       public boolean handle( Session session, P2PMessage 
message, boolean encrypted )
+                       {
+                               return 
onP2PMessage(session.getRemote(),message);
+                       }
+                       };
+               ids=new ArrayList();
+       }
+
+       public String toString()
+       {
+               return "Abstract protocol [name="+name+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public String getName()
+       {
+               return name;
+       }
+
+       public boolean init( CoreForProtocol capi )
+       {
+               boolean res;
+
+               res=true;
+               coreAPI=capi;
+               if (!((Server) 
coreAPI.getApplication()).registerCSExitHandler(exitHandler)) {
+                       res=false;
+                       }
+               return res;
+       }
+
+       public void done()
+       {
+               int     i;
+
+               for (i=0; i<ids.size(); i++) {
+                       ((Server) 
coreAPI.getApplication()).getDispatcher().unregisterP2PHandler(
+                               ((Integer) ids.get(i)).intValue(),p2pHandler);
+                       }
+
+               int[]   xxx=csDecoder.getIDs();
+               for (i=0; i<xxx.length; i++) {
+                       ((Server) 
coreAPI.getApplication()).unregisterCSHandler(xxx[i],csHandler);
+                       }
+
+               ((Server) 
coreAPI.getApplication()).unregisterCSExitHandler(exitHandler);
+               coreAPI=null;
+       }
+
+       public PersistentDecoder createCSDecoder()
+       {
+               return csDecoder;
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       protected boolean addCSHandler( int id, Class c )
+       {
+               log(Level.FINE,"Register c/s handler for type #"+id+" 
("+CSMessage.nameFor(id)+").");
+
+               csDecoder.add(id,c);
+               return ((Server) 
coreAPI.getApplication()).registerCSHandler(id,c,csHandler);
+       }
+
+       protected boolean addP2PHandler( int id, Class c )
+       {
+               ids.add(new Integer(id));
+               return ((Server) 
coreAPI.getApplication()).getDispatcher().registerP2PHandler(id,c,p2pHandler);
+       }
+
+       protected boolean onCSMessage( CSSession client, CSMessage msg )
+       {
+               return false;
+       }
+
+       protected boolean onP2PMessage( HostIdentity sender, P2PMessage msg )
+       {
+               return false;
+       }
+
+       protected void onClientExit( CSSession client )
+       {
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/Protocol.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/Protocol.java  2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/protocol/Protocol.java  2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,19 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol;
+
+import org.gnu.freeway.server.*;
+import org.gnu.freeway.util.net.*;
+
+/**
+ */
+
+public interface Protocol
+{
+       public boolean init( CoreForProtocol api );
+       public void done();
+
+       public PersistentDecoder createCSDecoder();
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/AFSProtocol.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/AFSProtocol.java   2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/AFSProtocol.java   2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,240 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs;
+
+import org.gnu.freeway.protocol.*;
+import org.gnu.freeway.protocol.afs.esed2.*;
+import org.gnu.freeway.server.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.util.logging.*;
+
+/**
+ * main functions of the anonymous file sharing service
+ *
+ * AFS CORE. This is the code that is plugged into the GNUnet core to
+ * enable Anonymous File Sharing.
+ * support for ESED2 encoding of files
+ */
+
+public class AFSProtocol extends AbstractProtocol
+{
+       /**
+        * Just the version number of the AFS implementation.
+        * History:
+        *
+        * 1.x.x: initial version with triple hash and merkle tree
+        * 2.x.x: root node with mime-type, filename and version number
+        * 2.1.x: combined CHK/3HASH encoding with 25:1 super-nodes
+        * 2.2.x: with directories
+        * 3.0.x: with namespaces
+        */
+
+       public static final int VERSION =       Utils.makeVersion(3,0,4);
+
+       /** */
+       private BloomFilter2    bloomFilter;
+
+       /** */
+       private FileIndex               fileIndex;
+
+       /** */
+       private Policy                  policy;
+
+       /** */
+       private Handler                 handler;
+
+       /** */
+       private Manager                 manager;
+
+       /** */
+       private Routing                 routing;
+
+       /** */
+       private Migration               migration;
+
+       /** */
+       private QueryManager    queryManager;
+
+
+       public AFSProtocol()
+       {
+               super("AFS");
+               bloomFilter=new BloomFilter2();
+               fileIndex=new FileIndex();
+               policy=null;
+               handler=new Handler();
+               manager=new Manager();
+               routing=new Routing();
+               migration=new Migration(manager);
+               queryManager=new QueryManager();
+       }
+
+       public String toString()
+       {
+               return "AFS";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       /**
+        * Initialize the AFS module. This method name must match
+        * the library name (libgnunet_XXX => initialize_XXX).
+        * @param capi
+        * @return false on errors
+        */
+
+       public boolean init( CoreForProtocol capi )
+       {
+               Prefs   prefs;
+
+               super.init(capi);
+
+               prefs=capi.getApplication().getPreferences();
+               if (prefs.getInt("AFS","DISKQUOTA",0)<=0) {
+                       log(Level.SEVERE,"You must specify a postive number for 
the DISKQUOTA in section AFS.");
+                       return false;
+                       }
+
+               fileIndex.init(capi);
+
+               policy=new Policy(prefs,capi);
+
+               manager.init(capi);
+               bloomFilter.init(capi);
+               queryManager.init(capi);
+               routing.init(capi);
+               handler.init(capi);
+               migration.init(capi);
+
+               handler.policy=new Policy2(capi);
+               handler.singleBloomFilter=bloomFilter.singleBloomFilter;
+               handler.superBloomFilter=bloomFilter.superBloomFilter;
+               handler.fileIndex=fileIndex;
+               handler.manager=manager;
+               handler.routing=routing;
+
+               routing.policy=policy;
+               routing.manager=manager;
+               routing.queryManager=queryManager;
+               routing.superBloomFilter=bloomFilter.superBloomFilter;
+               routing.singleBloomFilter=bloomFilter.singleBloomFilter;
+               // **** fin beurk
+
+               // p2p handlers
+               addP2PHandler(P2PMessage.IS_QUERY,P2PQuery.class);
+               addP2PHandler(P2PMessage.IS_3HASH_RESULT,P2P3HashResult.class);
+               addP2PHandler(P2PMessage.IS_CHK_RESULT,P2PChkResult.class);
+               addP2PHandler(P2PMessage.IS_NSQUERY,P2PNSQuery.class);
+               
addP2PHandler(P2PMessage.IS_SBLOCK_RESULT,P2PSBlockResult.class);
+
+               // c/s handlers
+               addCSHandler(CSMessage.IS_QUERY,CSQuery.class);
+               addCSHandler(CSMessage.IS_INSERT_CHK,CSInsertChk.class);
+               addCSHandler(CSMessage.IS_INSERT_3HASH,CSInsert3Hash.class);
+               addCSHandler(CSMessage.IS_INDEX_BLOCK,CSIndexBlock.class);
+               addCSHandler(CSMessage.IS_INDEX_FILE,CSIndexFile.class);
+               addCSHandler(CSMessage.IS_INDEX_SUPER,CSIndexSuper.class);
+               addCSHandler(CSMessage.IS_DELETE_CHK,CSDeleteChk.class);
+               addCSHandler(CSMessage.IS_DELETE_3HASH,CSDelete3Hash.class);
+               addCSHandler(CSMessage.IS_UNINDEX_BLOCK,CSUnindexBlock.class);
+               addCSHandler(CSMessage.IS_UNINDEX_FILE,CSUnindexFile.class);
+               addCSHandler(CSMessage.IS_UNINDEX_SUPER,CSUnindexSuper.class);
+               addCSHandler(CSMessage.IS_NSQUERY,CSNSQuery.class);
+               addCSHandler(CSMessage.IS_INSERT_SBLOCK,CSInsertSBlock.class);
+               addCSHandler(CSMessage.IS_UPLOAD_FILE,CSUploadFile.class);
+               addCSHandler(CSMessage.IS_LINK_FILE,CSLinkFile.class);
+               
addCSHandler(CSMessage.IS_GET_AVG_PRIORITY,CSGetAvgPriority.class);
+               return true;
+       }
+
+       public void done()
+       {
+               super.done();
+               bloomFilter.done();
+               migration.done();
+               queryManager.done();
+               routing.done();
+               manager.done();
+               fileIndex.done();
+               policy.doneAnonymityPolicy();
+       }
+
+       protected boolean onCSMessage( CSSession client, CSMessage msg )
+       {
+               if (msg instanceof CSNSQuery) {
+                       return 
handler.csHandleRequestNSQuery(client,(CSNSQuery) msg);
+                       }
+               if (msg instanceof CSInsertSBlock) {
+                       return 
handler.csHandleRequestInsertSBlock(client,(CSInsertSBlock) msg);
+                       }
+               if (msg instanceof CSQuery) {
+                       return handler.csHandleRequestQuery(client,(CSQuery) 
msg);
+                       }
+               if (msg instanceof CSInsertChk) {
+                       return 
handler.csHandleRequestInsertCHK(client,(CSInsertChk) msg);
+                       }
+               if (msg instanceof CSInsert3Hash) {
+                       return 
handler.csHandleRequestInsert3HASH(client,(CSInsert3Hash) msg);
+                       }
+               if (msg instanceof CSIndexBlock) {
+                       return 
handler.csHandleRequestIndexBlock(client,(CSIndexBlock) msg);
+                       }
+               if (msg instanceof CSIndexFile) {
+                       return 
handler.csHandleRequestIndexFile(client,(CSIndexFile) msg);
+                       }
+               if (msg instanceof CSIndexSuper) {
+                       return 
handler.csHandleRequestIndexSuper(client,(CSIndexSuper) msg);
+                       }
+               if (msg instanceof CSDeleteChk) {
+                       return 
handler.csHandleRequestDeleteCHK(client,(CSDeleteChk) msg);
+                       }
+               if (msg instanceof CSDelete3Hash) {
+                       return 
handler.csHandleRequestDelete3HASH(client,(CSDelete3Hash) msg);
+                       }
+               if (msg instanceof CSUnindexBlock) {
+                       return 
handler.csHandleRequestUnindexBlock(client,(CSUnindexBlock) msg);
+                       }
+               if (msg instanceof CSUnindexFile) {
+                       return 
handler.csHandleRequestUnindexFile(client,(CSUnindexFile) msg);
+                       }
+               if (msg instanceof CSUnindexSuper) {
+                       return 
handler.csHandleRequestUnindexSuper(client,(CSUnindexSuper) msg);
+                       }
+               if (msg instanceof CSUploadFile) {
+                       return 
handler.csHandleRequestUploadFile(client,(CSUploadFile) msg);
+                       }
+               if (msg instanceof CSLinkFile) {
+                       return 
handler.csHandleRequestLinkFile(client,(CSLinkFile) msg);
+                       }
+               if (msg instanceof CSGetAvgPriority) {
+                       return 
routing.csHandleRequestAvgPriority(client,(CSGetAvgPriority) msg);
+                       }
+               return false;
+       }
+
+       protected boolean onP2PMessage( HostIdentity sender, P2PMessage msg )
+       {
+               if (msg instanceof P2PNSQuery) {
+                       return handler.handleNSQUERY(sender,msg);
+                       }
+               if (msg instanceof P2PSBlockResult) {
+                       return handler.handleSBLOCK_CONTENT(sender,msg);
+                       }
+               if (msg instanceof P2PQuery) {
+                       return handler.handleQUERY(sender,msg);
+                       }
+               if (msg instanceof P2P3HashResult) {
+                       return handler.handle3HASH_CONTENT(sender,msg);
+                       }
+               if (msg instanceof P2PChkResult) {
+                       return handler.handleCHK_CONTENT(sender,msg);
+                       }
+               return false;
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/BloomFilter2.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/BloomFilter2.java  2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/BloomFilter2.java  2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,107 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs;
+
+import org.gnu.freeway.protocol.afs.esed2.*;
+import org.gnu.freeway.server.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.io.*;
+
+import java.nio.*;
+import java.util.logging.*;
+
+/**
+ * Bloomfilter implementation.
+ */
+
+public class BloomFilter2 extends LoggedObject
+{
+       public static final String      BLOOMFILTER1    =       
"content_bloomfilter";
+       public static final String      BLOOMFILTER2    =       
"keyword_bloomfilter";
+
+       /** Filters. */
+       public BloomFilter      superBloomFilter;
+       public BloomFilter      singleBloomFilter;
+
+
+       public BloomFilter2()
+       {
+               super(true);
+       }
+
+       public String toString()
+       {
+               return "xxx";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public void init( CoreForProtocol capi )
+       {
+               ByteBuffer      buf;
+               String          fn,bf;
+               int                     quota,superbf_size,singlbf_size;
+
+               Statistics              
stats=capi.getApplication().getStatistics();
+               Prefs   prefs=capi.getApplication().getPreferences();
+
+               fn = prefs.getString("AFS","AFSDIR",null);
+               if (fn==null) {
+                       trace("Configuration must specify directory for AFS 
data in section AFS under AFSDIR.");
+                       return;
+                       }
+               new DirLocation(fn).create();
+
+               /* read existing quota, check if it changed */
+               quota = prefs.getInt("AFS","DISKQUOTA",0);
+
+               buf=prefs.getContent("AFS-DISKQUOTA");
+               if (buf==null || buf.capacity()!=4) {
+                       buf=ByteBuffer.allocateDirect(4);
+                       buf.putInt(quota);
+                       prefs.putContent("AFS-DISKQUOTA",buf);
+                       }
+               else if (buf.getInt()!=quota) {
+                       log(Level.SEVERE,"AFS-Quota changed, run gnunet-convert 
!");
+                       return;
+                       }
+               quota = quota * 1024; /* convert to kb */
+               singlbf_size = quota;    /* 8 bit per entry/kb in DB */
+               superbf_size = quota;
+
+               bf=fn+"/"+BLOOMFILTER1;
+               superBloomFilter=BloomFilter.load(stats,bf,superbf_size,5); /* 
approx. 3% false positives at max use */
+
+               bf=fn+"/"+BLOOMFILTER2;
+               singleBloomFilter=BloomFilter.load(stats,bf,singlbf_size,5); /* 
approx. 3% false positives at max use */
+       }
+
+       public void done()
+       {
+               singleBloomFilter.free();
+               superBloomFilter.free();
+       }
+
+       public void bf_deleteEntryCallback( HashCode160 key, ContentIndex ce, 
Object data, int datalen, Object closure )
+       {
+               switch (ce.type) {
+                       case ContentIndex.LOOKUP_TYPE_CHK:
+                       case ContentIndex.LOOKUP_TYPE_3HASH:
+                       case ContentIndex.LOOKUP_TYPE_SBLOCK:
+                               singleBloomFilter.delete(key);
+                               break;
+                       case ContentIndex.LOOKUP_TYPE_SUPER:
+                               superBloomFilter.delete(key);
+                               break;
+                       case ContentIndex.LOOKUP_TYPE_CHKS:
+                               break;
+                       default:
+                               log(Level.WARNING,"Bloom filter notified of 
deletion of unexpected type of content entry: "+ce.type+".");
+                               break;
+                       }
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/DBHandle.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/DBHandle.java      2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/DBHandle.java      2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,170 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs;
+
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.protocol.afs.esed2.*;
+import org.gnu.freeway.util.crypto.*;
+
+/**
+ * Handle for a high-level database (mysql, simple)
+ * mysql wrapper
+ */
+
+public interface DBHandle
+{
+       /**
+        * Open the database.
+        *
+        * @param prefs
+        * @param backend       string used to distinguish multiple backends of 
the same type.
+        * @param name          parameter for naming the database configuration 
(e.g. quota)
+        * @return                      the database handle
+        */
+
+       public boolean open( Prefs prefs, String backend, String name );
+
+       /**
+        * Close the database.
+        */
+
+       public void close();
+
+       /**
+        * Remove the database (entirely!). Also implicitly close database.
+        * Close and delete the database.
+        */
+
+       public void drop();
+
+       /**
+        * Read the contents of a bucket to a buffer.
+        * Read the contents of a block to a buffer. In the case of 3HASH
+        * query, readContent must also return the respective 2HASH in
+        * ce.hash [so that hash(ce.hash)==3HASH].
+        *
+        * @param query the query hash (3HASH o CHK)/the hashcode representing 
the entry
+        * @param ce    what to look for (will be modified on return)/the 
meta-data of the entry (set)
+        * @param prio  the amount to change priority of the entry if found/by 
how much should the priority of the content be changed
+        * @return              the content on success, null on failure
+        */
+
+       public byte[] readContent( HashCode160 query, ContentIndex ce, int prio 
);
+
+       /**
+        * Write content to a file. Check for reduncancy and eventually
+        * append.
+        * Write content to the db.  Overwrites existing data.
+        * If ce.type is LOOKUP_TYPE_3HASH, ce.hash will contain
+        * a double hash which must be converted to 3HASH, later to be
+        * retrievable by 3HASH, but the 2HASH must be stored so it can
+        * be retrieved by readContent(). For indexed content,
+        * ce.fileOffset and ce.fileNameIndex must be stored.
+        * Note that block can be null for on-demand encoded content
+        * (in this case, len must also be 0).
+        * Write content to the db.  Overwrites existing data.
+        * If ce.type is LOOKUP_TYPE_3HASH, ce.hash will contain
+        * a double hash which must be converted to 3HASH, later to be
+        * retrievable by 3HASH, but the 2HASH must be stored so it can
+        * be retrieved by readContent(). For indexed content,
+        * ce.fileOffset and ce.fileNameIndex must be stored.
+        * Note that block can be null for on-demand encoded content
+        * (in this case, len must also be 0).
+        *
+        * @param ce information related to the block to store/the meta-data 
for the entry/the meta-data for the entry
+        * @param block the data to store
+        * @param offset offset
+        * @param length the size of the block
+        * @return false on error, true if ok.
+        */
+
+       public boolean writeContent( ContentIndex ce, byte[] block, int offset, 
int length );
+
+       /**
+        * Get the number of entries in the database.
+        * Get the number of entries in the database.
+        *
+        * @return -1 on error, otherwise the number of entries
+        */
+
+       public int countContentEntries();
+
+       /**
+        * Get the lowest priority of content in the DB.
+        * Get the lowest priority of content in the store.
+        * Get the lowest priority value of all content in the store.
+        *
+        * @return the lowest priority
+        */
+
+       public int getMinimumPriority();
+
+       /**
+        * Call a method for each key in the database and
+        * call the callback method on it.
+        * Call a method for each key in the database and
+        * call the callback method on it.
+        *
+        * @param callback the callback method
+        * @param data second argument to all callback calls
+        * @return the number of items stored in the content database
+        */
+
+       public int forEachEntry( EntryCallback callback, Object data );
+
+       /**
+        * Return a random key from the database (just the key, not the
+        * content!).
+        * Get a random content block from MySQL database.
+        * Tries to use indexes efficiently.
+        *
+        * Code supplied by H. Pagenhardt
+        *
+        * @param ce the meta-data of the random content (set)/output 
information about the key
+        * @return true on success, false on error
+        */
+
+       public boolean getRandomContent( ContentIndex ce );
+
+       /**
+        * Delete low-priority content from the database
+        * Deletes some least important content
+        *
+        * @param count the number of entries to delete/the number of 1kb 
blocks to free
+        * @param callback method to call on each deleted entry/method to call 
on each deleted item
+        * @param closure extra argument to callback
+        * @return true on success, false on error
+        */
+
+       public boolean deleteContent( int count, EntryCallback callback, Object 
closure );
+
+       /**
+        * Estimate how many blocks can be stored in the DB
+        * before the quota is reached.
+        * Estimate how many blocks can be stored in the DB
+        * before the quota is reached.
+        *
+        * NOTE: this function can not be performed relying on
+        * Data_length+Index_length from "SHOW TABLE STATUS" because
+        * those values seem not to be decreasing in real time.
+        * On mysql 4.0.16, Avg_row_len seems to be updating in real
+        * time w.r.t. insertions and deletions.
+        *
+        * @param quota the number of kb available for the DB
+        * @return number of blocks left
+        */
+
+       public int estimateAvailableBlocks( int quota );
+
+       /**
+        * Free space in the database by removing an entry.
+        *
+        * @param name the key of the entry to remove/the query of the entry to 
remove/hashcode for the block to be deleted
+        * @return false on error, true if ok.
+        * Free space in the database by removing one block
+        */
+
+       public boolean unlink( HashCode160 name );
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/EntryCallback.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/EntryCallback.java 2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/EntryCallback.java 2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,33 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs;
+
+import org.gnu.freeway.protocol.afs.esed2.*;
+import org.gnu.freeway.util.crypto.*;
+
+/**
+ *
+ */
+
+public interface EntryCallback
+{
+       /**
+        * Callback function type used by the iterator.  Contains the key,
+        * index information, the block (null if there is no block in the
+        * database), the length of the block and the closure.
+        *
+        * The callback is responsible for freeing data if data is not null.
+        *
+        * Note that the callback function may not perform additional
+        * read, write or delete operations on the database!
+        * @param key
+        * @param ce
+        * @param data
+        * @param dataLen
+        * @param closure
+        */
+
+       public void onEntry( HashCode160 key, ContentIndex ce, Object data, int 
dataLen, Object closure );
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/FileIndex.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/FileIndex.java     2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/FileIndex.java     2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,376 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs;
+
+import org.gnu.freeway.server.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.io.*;
+
+import EDU.oswego.cs.dl.util.concurrent.*;
+
+import java.io.*;
+import java.util.*;
+import java.util.logging.*;
+
+/**
+ * This module is responsible for storing the names
+ * of indexed files.
+ * access to the list of indexed files
+ *
+ * This module is responsible for storing the names
+ * of indexed files. The index for a file is always
+ * >0, since 0 is reserved for "not indexed".
+ */
+
+public class FileIndex extends LoggedObject
+{
+       public static final String      DATABASELIST    =       "database.list";
+
+       /** names of indexed files */
+       private String[]                                indexed_files;
+
+       /** Size of the indexed_files list. */
+       private int                                             
indexed_files_size;
+
+       /** number of files that are indexed */
+       private int                                             
indexed_files_count;
+
+       /** Mutex for synced access to indexed_files */
+       private Sync                                    lock;
+
+       /** stat handle for indexed_files_count */
+       private Stat                            stat_indexed_files_count;
+
+       /** stat handle for total size of indexed files */
+       private Stat                            stat_indexed_files_size;
+
+       /** */
+       private String                                  shared_file_list;
+
+       /** */
+       private Statistics      stats;
+
+       /** */
+       private Prefs   prefs;
+
+
+       public FileIndex()
+       {
+               super(true);
+       }
+
+       public String toString()
+       {
+               return "File index";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       /**
+        * Initialize the fileindex module.
+        * @param capi
+        */
+
+       public void init( CoreForProtocol capi )
+       {
+               stats=capi.getApplication().getStatistics();
+               prefs=capi.getApplication().getPreferences();
+
+               shared_file_list = getSharedFileList();
+               stat_indexed_files_count=stats.getHandle("# indexed files");
+               stat_indexed_files_size= stats.getHandle("# size of indexed 
files");
+               lock=new Mutex();
+               if (!scanDatabaseList()) {
+                       trace("Could not initialize file index module !");
+                       }
+       }
+
+       /**
+        * Shutdown the fileindex module.
+        */
+
+       public void done()
+       {
+               indexed_files=null;
+               shared_file_list=null;
+               lock=null;
+       }
+
+       /**
+        * Get the name of the file where we store the
+        * list of indexed files.
+        * @return
+        */
+
+       protected String getSharedFileList()
+       {
+               String  str;
+
+               str=prefs.getString("AFS","AFSDIR",null);
+               if (str==null) {
+                       log(Level.SEVERE,"Configuration file must specify 
filename for storing AFS data in section AFS under AFSDIR.");
+                       return null;
+                       }
+               new DirLocation(str).create();  // important, the directory may 
not exist yet !
+               return new FileLocation(str+"/"+DATABASELIST).getPath();
+       }
+
+       /**
+        * Scan the list of on-demand shared files to initialize indexed_files
+        * @return OK on success, false on error
+        */
+
+       protected boolean scanDatabaseList()
+       {
+               BufferedReader  handle;
+               String                  str;
+               long                    totalSize;
+               List                    list;
+               FileLocation            f;
+
+               try {
+                       lock.acquire();
+                       try {
+                               indexed_files = null;
+                               indexed_files_count = 0;
+                               indexed_files_size = 0;
+                               stat_indexed_files_count.reset();
+                               stat_indexed_files_size.reset();
+                               totalSize = 0;
+
+                               try {
+                                       handle=new BufferedReader(new 
InputStreamReader(new FileInputStream(shared_file_list)));
+                                       try {
+                                               list=new ArrayList();
+                                               for (str=handle.readLine(); 
str!=null; str=handle.readLine()) {
+                                                       if (str.length()>0) {
+                                                               
indexed_files_count++;
+                                                               list.add(str);
+
+                                                               f=new 
FileLocation(str);
+                                                               totalSize += 
f.getSize();
+                                                               }
+                                                       else {
+                                                               list.add(null);
+                                                               }
+                                                       }
+
+                                               if (indexed_files_count == 0) {
+                                                       return true;
+                                                       }
+
+                                               indexed_files_size = 
list.size();
+                                               indexed_files = (String[]) 
list.toArray(new String[list.size()]);
+                                               }
+                                       finally {
+                                               handle.close();
+                                               }
+                                       }
+                               catch( FileNotFoundException x ) {
+                                       // file not found : not an error
+                                       }
+                               catch( IOException x ) {
+                                       err("Could not open 
"+shared_file_list+" !",x);
+                                       return false;
+                                       }
+
+                               
stat_indexed_files_count.set(indexed_files_count);
+                               stat_indexed_files_size.set(totalSize);
+                               }
+                       finally {
+                               lock.release();
+                               }
+                       }
+               catch( InterruptedException x ) {
+                       err("",x);
+                       return false;
+                       }
+               return true;
+       }
+
+       /**
+        * Get the name of an indexed file.
+        *
+        * @param index the index of the file (must be strictly positive)
+        * @return the filename
+        */
+
+       public String getIndexedFileName( int index )
+       {
+               if (index<=0 || index>indexed_files_size) {
+                       log(Level.WARNING,"getIndexedFileName called with index 
out of bounds ("+index+")");
+                       return null;
+                       }
+
+               try {
+                       lock.acquire();
+                       try {
+                               return indexed_files[index-1];
+                               }
+                       finally {
+                               lock.release();
+                               }
+                       }
+               catch( InterruptedException x ) {
+                       err("",x);
+                       return null;
+                       }
+       }
+
+       /**
+        * Invoke a method on each of the filenames of the indexed files. If
+        * the method returns false, the file is removed from the list of
+        * indexed files!
+        *
+        * @param method the method to invoke for each indexed file
+        * @param data the last argument to method
+        * @return the number of shared files (after changes caused by this 
call)
+        */
+
+       public int forEachIndexedFile( IndexedFileNameCallback method, Object 
data )
+       {
+               PrintWriter     handle;
+               int                     i;
+               boolean         changed,erase;
+
+               try {
+                       lock.acquire();
+                       try {
+                               changed=false;
+                               for (i=0; i<indexed_files_size; i++) {
+                                       if (indexed_files[i] != null) {
+                                               lock.release();
+                                               try {
+                                                       
erase=!method.onFile(indexed_files[i],i+1,data);
+                                                       }
+                                               finally {
+                                                       lock.acquire();
+                                                       }
+
+                                               if (erase) {
+                                                       indexed_files[i]=null;
+                                                       changed=true;
+                                                       }
+                                               }
+                                       }
+
+                               if (changed) {
+                                       /* write changed list to the drive */
+
+                                       try {
+                                               handle = new PrintWriter(new 
OutputStreamWriter(new FileOutputStream(shared_file_list)),true);
+                                               try {
+                                                       for (i=0; 
i<indexed_files_size; i++) {
+                                                               if 
(indexed_files[i]!=null) {
+                                                                       
handle.println(indexed_files[i]);
+                                                                       }
+                                                               else {
+                                                                       
handle.println();
+                                                                       }
+                                                               }
+                                                       }
+                                               finally {
+                                                       handle.close();
+                                                       }
+                                               }
+                                       catch( IOException x ) {
+                                               err("List "+shared_file_list+" 
of directly shared filenames not available !",x);
+                                               return -1;
+                                               }
+                                       }
+                               }
+                       finally {
+                               lock.release();
+                               }
+                       }
+               catch( InterruptedException x ) {
+                       err("",x);
+                       return -1;
+                       }
+               return indexed_files_count;
+       }
+
+       /**
+        * Add a name to the list of filenames.
+        *
+        * @param filename the name of the file to add
+        * @return the index of that file in the list in [1,65536], -1 on 
error. <em>NEVER</em> returns 0.
+        */
+
+       public int appendFilename( String filename )
+       {
+               BufferedReader  handle;
+               PrintWriter     out;
+               String          str;
+               int                     pos;
+
+               assert(filename!=null);
+
+               try {
+                       lock.acquire();
+                       try {
+                               pos=0;
+                               filename=new FileLocation(filename).getPath();
+
+                               try {
+                                       handle=new BufferedReader(new 
InputStreamReader(new FileInputStream(shared_file_list)));
+                                       try {
+                                               for (str=handle.readLine(); 
str!=null; str=handle.readLine()) {
+                                                       pos++;
+                                                       if 
(str.equals(filename)) {
+                                                               debug("File 
already at index "+pos+".");
+                                                               return pos;     
// already there !
+                                                               }
+                                                       }
+                                               }
+                                       finally {
+                                               handle.close();
+                                               }
+                                       }
+                               catch( FileNotFoundException x ) {
+                                       // file not found : not an error
+                                       }
+                               catch( IOException x ) {
+                                       err("List "+shared_file_list+" of 
directly shared filenames not available !",x);
+                                       return -1;
+                                       }
+
+                               if (pos > 0xFFFF) {
+                                       log(Level.WARNING,"Too many files 
indexed (limit is 65535).");
+                                       return -1;
+                                       }
+
+                               // not there, append
+                               try {
+                                       out = new PrintWriter(new 
OutputStreamWriter(new FileOutputStream(shared_file_list,true)),true);
+                                       try {
+                                               out.println(filename);
+                                               }
+                                       finally {
+                                               out.close();
+                                               }
+                                       }
+                               catch( IOException x ) {
+                                       err("List "+shared_file_list+" of 
directly shared filenames not available !",x);
+                                       return -1;
+                                       }
+                               }
+                       finally {
+                               lock.release();
+                               }
+                       }
+               catch( InterruptedException x ) {
+                       err("",x);
+                       return -1;
+                       }
+
+               scanDatabaseList();
+
+               debug("Added file to index at position "+pos+".");
+
+               return pos; /* return index */
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/Handler.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/Handler.java       2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/Handler.java       2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,897 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs;
+
+import org.gnu.freeway.protocol.afs.esed2.*;
+import org.gnu.freeway.server.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.io.*;
+import org.gnu.freeway.util.net.*;
+
+import java.util.logging.*;
+
+/**
+ * Handlers for incoming AFS requests (p2p and CS).
+ * Handlers for AFS related messages (CS and p2p).
+ */
+
+public class Handler extends LoggedObject implements AFSConstants
+{
+       private CoreForProtocol coreAPI;
+
+       private Prefs   prefs;
+
+       private Stat    stat_p2p_query_count;
+       private Stat    stat_p2p_superquery_count;
+       private Stat    stat_p2p_chk_replies;
+       private Stat    stat_p2p_3hash_replies;
+       private Stat    stat_cs_query_count;
+       private Stat    stat_cs_insert_chk_count;
+       private Stat    stat_cs_insert_3hash_count;
+       private Stat    stat_cs_index_block_count;
+       private Stat    stat_cs_index_file_count;
+       private Stat    stat_cs_index_super_count;
+       private Stat    stat_cs_delete_chk_count;
+       private Stat    stat_cs_delete_3hash_count;
+       private Stat    stat_cs_unindex_block_count;
+       private Stat    stat_cs_unindex_file_count;
+       private Stat    stat_cs_unindex_super_count;
+       private Stat    stat_cs_upload_file_count;
+       private Stat    stat_cs_insert_sblock_count;
+       private Stat    stat_cs_nsquery_count;
+       private Stat    stat_p2p_nsquery_count;
+       private Stat    stat_p2p_sblock_replies;
+
+       public Policy2          policy;
+       public BloomFilter      singleBloomFilter;
+       public BloomFilter      superBloomFilter;
+       public FileIndex                fileIndex;
+       public Manager          manager;
+       public Routing          routing;
+
+
+       public Handler()
+       {
+               super(true);
+       }
+
+       public String toString()
+       {
+               return "xxx";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       /**
+        * Initialize the handler module. Registers counters
+        * with the statistics module.
+        * @param capi
+        */
+
+       public void init( CoreForProtocol capi )
+       {
+               Statistics      stats;
+
+               coreAPI=capi;
+
+               prefs=capi.getApplication().getPreferences();
+
+               stats=capi.getApplication().getStatistics();
+               stat_p2p_query_count=stats.getHandle("# p2p queries received");
+               stat_p2p_superquery_count=stats.getHandle("# p2p super queries 
received");
+               stat_p2p_chk_replies=stats.getHandle("# p2p CHK content 
received (kb)");
+               stat_p2p_3hash_replies=stats.getHandle("# p2p search results 
received (kb)");
+               stat_cs_query_count=stats.getHandle("# client queries 
received",Stat.VERBOSE);
+               stat_cs_insert_chk_count=stats.getHandle("# client CHK content 
inserted (kb)",Stat.VERBOSE);
+               stat_cs_insert_3hash_count=stats.getHandle("# client 3HASH 
search results inserted (kb)",Stat.VERBOSE);
+               stat_cs_index_block_count=stats.getHandle("# client file index 
requests received",Stat.VERBOSE);
+               stat_cs_index_file_count=stats.getHandle("# file index requests 
received",Stat.VERBOSE);
+               stat_cs_index_super_count=stats.getHandle("# super query index 
requests received",Stat.VERBOSE);
+               stat_cs_delete_chk_count=stats.getHandle("# client CHK content 
deleted (kb)",Stat.VERBOSE);
+               stat_cs_delete_3hash_count=stats.getHandle("# client 3HASH 
search results deleted (kb)",Stat.VERBOSE);
+               stat_cs_unindex_block_count=stats.getHandle("# client file 
unindex requests received",Stat.VERBOSE);
+               stat_cs_unindex_file_count=stats.getHandle("# file unindex 
requests received",Stat.VERBOSE);
+               stat_cs_unindex_super_count=stats.getHandle("# super query 
unindex requests received",Stat.VERBOSE);
+               stat_cs_insert_sblock_count=stats.getHandle("# client SBlock 
insert requests received",Stat.VERBOSE);
+               stat_cs_nsquery_count=stats.getHandle("# client namespace 
queries received",Stat.VERBOSE);
+               stat_cs_upload_file_count=stats.getHandle("# client file upload 
requests",Stat.VERBOSE);
+               stat_p2p_nsquery_count=stats.getHandle("# p2p namespace queries 
received");
+               stat_p2p_sblock_replies=stats.getHandle("# p2p SBlocks 
received");
+       }
+
+       /**
+        * Handle query for content. Depending on how we like the sender,
+        * lookup, forward or even indirect.
+        * @param sender
+        * @param msg
+        * @return
+        */
+
+       public boolean handleQUERY( HostIdentity sender, P2PMessage msg )
+       {
+               int                             qp;
+               P2PQuery        qmsg;
+               int                             queries;
+               int                             ttl;
+               int                             prio;
+               double                  preference;
+
+               queries = (msg.getByteSize() - P2PQuery.SIZE) / 
HashCode160.SIZE;       //todo: AFS_p2p_QUERY.getQueriesCount()
+               if (queries<=0 || (msg.getByteSize()!=P2PQuery.SIZE + queries * 
HashCode160.SIZE) ) {
+                       log(Level.WARNING,"Query received was malformed.");
+                       return false;
+                       }
+               if (queries>1)
+                       stat_p2p_superquery_count.inc();
+               stat_p2p_query_count.inc();
+               qmsg = (P2PQuery) msg;
+
+               debug("Received query "+qmsg.getQuery(0).toHex()+" 
("+queries+") TTL "+qmsg.getTTL()+" PR "+qmsg.getPriority()+" from 
"+sender.getName()+".");
+
+               /* decrement ttl (always) */
+               ttl = qmsg.getTTL();
+               debug("Received query for "+qmsg.getQuery(0).toHex()+" with ttl 
"+ttl+".");
+
+               if (ttl < 0) {
+                       ttl = (int) (ttl - 2*TTL_DECREMENT - 
Crypto.nextLong(TTL_DECREMENT));
+                       if (ttl > 0)
+                               return true; /* just abort */
+               } else
+                       ttl = (int) (ttl - 2*TTL_DECREMENT - 
Crypto.nextLong(TTL_DECREMENT));
+               qp = policy.evaluateQuery(sender,qmsg.getPriority());
+               if ((qp & Policy2.QUERY_DROPMASK) == 0)
+                       return true; /* straight drop. */
+
+               preference=qp & Policy2.QUERY_PRIORITY_BITMASK;
+               if (preference < Policy2.QUERY_BANDWIDTH_VALUE)
+                       preference = Policy2.QUERY_BANDWIDTH_VALUE;
+               coreAPI.preferTrafficFrom(sender,
+                               preference);
+
+               /* adjust priority */
+               prio = qmsg.getPriority();
+               if ( (qp & Policy2.QUERY_PRIORITY_BITMASK) < prio) {
+                       prio = qp & Policy2.QUERY_PRIORITY_BITMASK;
+                       qmsg.setPriorityAndTTL(prio,qmsg.getTTL());
+               }
+               prio = prio / queries; /* effective priority for ttl */
+
+               /* adjust TTL */
+               if ( (ttl > 0) &&
+                               (ttl > (prio+3)*TTL_DECREMENT) )
+                       ttl = (int) ((prio+3)*TTL_DECREMENT); /* bound! */
+               qmsg.setPriorityAndTTL(qmsg.getPriority(),ttl);
+
+               routing.execQuery(qp, qmsg, null);
+               return true;
+       }
+
+       /**
+        * Receive content, do something with it!  There are 3 basic
+        * possiblilities. Either our node did the request and we should send
+        * the result to a client via TCP, or the content was requested by
+        * another node and we forwarded the request (and thus we now have to
+        * fwd the reply) or 3rd somebody just send us some content we did NOT
+        * ask for - and we can choose to store it or just discard it.
+        * @param sender
+        * @param msg
+        * @return
+        */
+
+       public boolean handleCHK_CONTENT( HostIdentity sender, P2PMessage msg )
+       {
+               int                                     prio;
+               HashCode160                     queryHash;
+               ContentIndex            ce=new ContentIndex();
+               P2PChkResult    cmsg;
+               boolean                         ret;
+               double                          preference;
+               boolean[]               dupe=new boolean[1];
+
+               if (msg.getByteSize()!=P2PChkResult.SIZE) {
+                       log(Level.WARNING,"WARNING: CHK content message 
received was malformed");
+                       return false;
+                       }
+               stat_p2p_chk_replies.inc();
+               cmsg = (P2PChkResult) msg;
+               
queryHash=HashCode160.create(PersistentHelper.toBytes(cmsg.result));
+               prio = routing.useContent(sender,queryHash,msg);
+               if (sender == null) /* no migration, this is already content 
from the local node */
+                       return true;
+               preference = prio;
+               prio = policy.evaluateContent(queryHash,prio);
+               if (prio != -1)
+                       preference+=prio;
+               if (preference < Policy2.CONTENT_BANDWIDTH_VALUE)
+                       preference = Policy2.CONTENT_BANDWIDTH_VALUE;
+               coreAPI.preferTrafficFrom(sender,preference);
+
+               if (prio == -1)
+                       return true; /* straight drop */
+               ce.hash=(HashCode160) PersistentHelper.copy(queryHash);         
//todo: copie utile ?
+               ce.importance    = prio;
+               ce.type          = ContentIndex.LOOKUP_TYPE_CHK;
+               ce.fileNameIndex = 0;
+               ce.fileOffset    = 0;
+               ret = manager.insertContent(ce,
+                               ContentBlock.SIZE,
+                               cmsg.result,
+                               sender,
+                               dupe);
+               if (ret && !dupe[0])
+                       singleBloomFilter.add(queryHash);
+               return true;
+       }
+
+       /**
+        * Receive content, do something with it!  There are 3 basic
+        * possiblilities. Either our node did the request and we should send
+        * the result to a client via TCP, or the content was requested by
+        * another node and we forwarded the request (and thus we now have to
+        * fwd the reply) or 3rd somebody just send us some content we did NOT
+        * ask for - and we can choose to store it or just discard it.
+        * @param sender
+        * @param msg
+        * @return
+        */
+
+       public boolean handle3HASH_CONTENT( HostIdentity sender, P2PMessage msg 
)
+       {
+               int                                             prio;
+               P2P3HashResult  cmsg;
+               HashCode160                             tripleHash;
+               ContentIndex                    ce=new ContentIndex();
+               boolean                                 ret;
+               double                                  preference;
+               boolean[]               dupe=new boolean[1];
+
+               if (msg.getByteSize() != P2P3HashResult.SIZE) {
+                       log(Level.WARNING,"WARNING: content message received 
was malformed");
+                       return false;
+                       }
+               stat_p2p_3hash_replies.inc();
+               cmsg = (P2P3HashResult) msg;
+               tripleHash=cmsg.getTripleHash();
+
+               debug("DEBUG: received 3HASH search result for 
"+tripleHash.toHex()+" from peer");
+
+               prio = routing.useContent(sender,tripleHash,msg);
+               if (sender == null) { /* no migration, this is already content 
from the local node */
+                       //fixme: how is it possible to have sender == null ???
+                       debug("Content migration not needed, content is 
local.");
+                       return true;
+                       }
+               preference = prio;
+
+               debug("Content migration with preference "+prio);
+
+               prio = policy.evaluateContent(tripleHash,prio);
+               if (prio != -1)
+                       preference += prio;
+               if (preference < Policy2.CONTENT_BANDWIDTH_VALUE)
+                       preference = Policy2.CONTENT_BANDWIDTH_VALUE;
+               coreAPI.preferTrafficFrom(sender,preference);
+
+               if (prio == -1) {
+                       debug("DEBUG: content not important enough, not 
replicated");
+                       return true; /* straight drop */
+                       }
+               debug("DEBUG: content replicated with total preference "+prio);
+
+               ce.hash=cmsg.getDoubleHash();
+               ce.importance    = prio;
+               ce.type          = ContentIndex.LOOKUP_TYPE_3HASH;
+               ce.fileNameIndex = 0;
+               ce.fileOffset    = 0;
+
+               ret = 
manager.insertContent(ce,ContentBlock.SIZE,cmsg.getResult(),sender,dupe);
+               if (ret && !dupe[0])
+                       singleBloomFilter.add(tripleHash);
+               return true;
+       }
+
+       /**
+        * Process a query from the client. Forwards to the network.
+        * @param sock
+        * @param queryRequest
+        * @return false if the TCP connection should be closed, otherwise true
+        */
+
+       public boolean csHandleRequestQuery( CSSession sock, CSQuery 
queryRequest )
+       {
+               int                             qp = 
Policy2.QUERY_ANSWER|Policy2.QUERY_FORWARD|Policy2.QUERY_INDIRECT|Policy2.QUERY_PRIORITY_BITMASK;
+               P2PQuery        msg;
+               int                             queries;
+               int                             ttl;
+               boolean                 ret;
+
+               queries = (queryRequest.getByteSize() - CSQuery.SIZE) / 
HashCode160.SIZE;
+               if ( (queries <= 0) || (queryRequest.getByteSize() != 
CSQuery.SIZE + queries * HashCode160.SIZE) ) {
+                       log(Level.WARNING,"Received malformed query from 
client.");
+                       return false;
+                       }
+               stat_cs_query_count.inc();
+
+               debug("Received "+queries+" queries 
("+queryRequest.getQuery(0).toHex()+") with ttl "+queryRequest.getTTL()+" and 
priority "+queryRequest.getPriority()+".");
+
+               msg = new P2PQuery(coreAPI.getIdentity());
+               msg.setQueries(queryRequest.getQueries());
+               /* adjust TTL */
+               ttl = queryRequest.getTTL();
+               if ( (ttl > 0) && (ttl > (msg.getPriority()+8)*TTL_DECREMENT) )
+                       ttl = (int) ((msg.getPriority()+8)*TTL_DECREMENT); /* 
bound! */
+
+               msg.setPriorityAndTTL(queryRequest.getPriority(),ttl);
+
+               ret = routing.execQuery(qp, msg, sock);
+               debug("Executed "+queries+" queries with result "+ret+".");
+               return sock.send(new CSResult(ret));
+       }
+
+       /**
+        * Process a request to insert content from the client.
+        * @param sock
+        * @param insertRequest
+        * @return false if the TCP connection should be closed, otherwise true
+        */
+
+       public boolean csHandleRequestInsertCHK( CSSession sock, CSInsertChk 
insertRequest )
+       {
+               ContentIndex    entry=new ContentIndex();
+               boolean                 ret;
+               boolean[]               dupe=new boolean[1];
+
+               stat_cs_insert_chk_count.inc();
+
+               
entry.hash=HashCode160.create(PersistentHelper.toBytes(insertRequest.getContent()));
+
+               debug("DEBUG: received CHK insert request for block 
"+entry.hash.toHex());
+
+               entry.type= ContentIndex.LOOKUP_TYPE_CHK;
+               entry.importance= insertRequest.getImportance();
+               entry.fileNameIndex= 0; /* database */
+               entry.fileOffset = 0; /* data/content */
+
+               ret = 
manager.insertContent(entry,ContentBlock.SIZE,insertRequest.getContent(),null,dupe);
+               if (ret && !dupe[0])
+                       singleBloomFilter.add(entry.hash);
+               return sock.send(new CSResult(ret));
+       }
+
+       /**
+        * Process a request to insert content from the client.
+        * @param sock
+        * @param insertRequest
+        * @return false if the TCP connection should be closed, otherwise true
+        */
+
+       public boolean csHandleRequestInsert3HASH( CSSession sock, 
CSInsert3Hash insertRequest )
+       {
+               ContentIndex    entry=new ContentIndex();
+               HashCode160             tripleHash;
+               boolean                 ret;
+               boolean[]               dupe=new boolean[1];
+
+               stat_cs_insert_3hash_count.inc();
+
+               entry.hash=(HashCode160) 
PersistentHelper.copy(insertRequest.getDoubleHash());  //todo: copie utile ?
+               
tripleHash=HashCode160.create(PersistentHelper.toBytes(insertRequest.getDoubleHash()));
+
+               debug("Received 3HASH insert request for "+tripleHash.toHex()+" 
from client");
+
+               entry.type= ContentIndex.LOOKUP_TYPE_3HASH;
+               entry.importance= insertRequest.getImportance();
+               entry.fileNameIndex= 0; /* database */
+               entry.fileOffset = 0; /* data/content */
+               ret = 
manager.insertContent(entry,ContentBlock.SIZE,insertRequest.getContent(),null,dupe);
+               if (ret && !dupe[0]) {
+                       singleBloomFilter.add(tripleHash);
+                       }
+               return sock.send(new CSResult(ret));
+       }
+
+       /**
+        * Process a request to index content from the client.
+        * @param sock
+        * @param indexingRequest
+        * @return false if the TCP connection should be closed, otherwise true
+        */
+
+       public boolean csHandleRequestIndexBlock( CSSession sock, CSIndexBlock 
indexingRequest )
+       {
+               boolean[]               dupe=new boolean[1];
+
+               debug("Indexing content 
"+indexingRequest.getContentIndex().hash.toHex()+" at offset 
"+indexingRequest.getContentIndex().fileOffset);
+
+               stat_cs_index_block_count.inc();
+               return sock.send(new 
CSResult(manager.insertContent(indexingRequest.getContentIndex(),0,(byte[]) 
null,null,dupe)));
+       }
+
+       /**
+        * Process a query to list a file as on-demand encoded from the client.
+        * @param sock
+        * @param listFileRequest
+        *
+        * @return false if the TCP connection should be closed, otherwise true
+        */
+
+       public boolean csHandleRequestIndexFile( CSSession sock, CSIndexFile 
listFileRequest )
+       {
+               DirLocation     dir;
+               String          str;
+               long                    quota,usage;
+               int                     ret;
+
+               stat_cs_index_file_count.inc();
+
+               str = prefs.getString("AFS","INDEX-DIRECTORY",null);
+               if (str == null) {
+                       log(Level.WARNING,"Rejecting content-unindex request, 
INDEX-DIRECTORY option not set !");
+                       return sock.send(CSResult.ERR);
+                       }
+
+               dir=new DirLocation(str);
+               dir.create();
+
+               quota=prefs.getInt("AFS","INDEX-QUOTA",0) * 1024 * 1024;
+               if (quota != 0) {
+                       usage=dir.getSizeWithoutLinks();
+                       if (usage + listFileRequest.filesize > quota) {
+                               log(Level.WARNING,"Rejecting file index 
request, quota exeeded: "+(usage / 1024 / 1024)+" of "+(quota / 1024 / 1024)+" 
(MB)");
+                               return sock.send(CSResult.ERR);
+                               }
+                       }
+
+               str=dir.getPath()+"/"+listFileRequest.hash.toHex();
+               ret = fileIndex.appendFilename(str);
+               if (ret == 0)
+                       ret = -1;
+               return sock.send(new CSResult(ret));
+       }
+
+       /**
+        * Process a client request to upload a file (indexing).
+        * @param sock
+        * @param uploadRequest
+        * @return
+        */
+
+       public boolean csHandleRequestUploadFile( CSSession sock, CSUploadFile 
uploadRequest )
+       {
+               DirLocation     prefix;
+               MappedFile      fd;
+               FileLocation    filename;
+               String          str;
+
+               stat_cs_upload_file_count.inc();
+
+               str=prefs.getString("AFS","INDEX-DIRECTORY",null);
+               if (str==null) {
+                       log(Level.WARNING,"WARNING: rejecting content-upload 
request, INDEX-DIRECTORY option not set!");
+                       return sock.send(CSResult.ERR);
+                       }
+
+               prefix=new DirLocation(str);
+               prefix.create();
+
+               filename=prefix.getFile(uploadRequest.hash.toHex());
+               filename.create();
+
+               fd=filename.open();
+               if (fd==null) {
+                       log(Level.SEVERE,"ERROR: OPEN() failed on "+filename);
+                       return sock.send(CSResult.ERR);
+                       }
+
+               try {
+                       fd.seek(uploadRequest.pos);
+                       if (!fd.writeBytes(uploadRequest.getChunk())) {
+                               return sock.send(CSResult.ERR);
+                               }
+                       }
+               finally {
+                       fd.close();
+                       }
+               return sock.send(new CSResult(uploadRequest.getChunk().length));
+       }
+
+       /**
+        * Process a client request to extend our super-query bloom
+        * filter.
+        * @param sock
+        * @param superIndexRequest
+        * @return
+        */
+
+       public boolean csHandleRequestIndexSuper( CSSession sock, CSIndexSuper 
superIndexRequest )
+       {
+               ContentIndex entry;
+               boolean[]               dupe=new boolean[1];
+
+               stat_cs_index_super_count.inc();
+
+               superBloomFilter.add(superIndexRequest.getSuperHash());
+
+               entry=new ContentIndex();
+               entry.type= ContentIndex.LOOKUP_TYPE_SUPER;
+               entry.importance= superIndexRequest.getImportance();
+               entry.fileNameIndex     = 0; /* database */
+               entry.fileOffset = 0; /* data/content */
+               entry.hash=(HashCode160) 
PersistentHelper.copy(superIndexRequest.getSuperHash());       //todo: copie 
utile ???
+               return sock.send(new 
CSResult(manager.insertContent(entry,0,(byte[]) null,null,dupe)));
+       }
+
+       /**
+        * Process a request from the client to delete content.
+        * @param sock
+        * @param insertRequest
+        * @return false if the TCP connection should be closed, otherwise true
+        */
+
+       public boolean csHandleRequestDeleteCHK( CSSession sock, CSDeleteChk 
insertRequest )
+       {
+               HashCode160     hc;
+               boolean         ret;
+
+               stat_cs_delete_chk_count.inc();
+
+               
hc=HashCode160.create(PersistentHelper.toBytes(insertRequest.content));
+
+               debug("DEBUG: received CHK remove request for block 
"+hc.toHex());
+
+               ret = manager.removeContent(hc,-1);
+               if (ret)
+                       if (singleBloomFilter.test(hc))
+                               singleBloomFilter.delete(hc);
+               return sock.send(new CSResult(ret));
+       }
+
+       /**
+        * Process a request from the client to delete content.
+        * @param sock
+        * @param insertRequest
+        * @return false if the TCP connection should be closed, otherwise true
+        */
+
+       public boolean csHandleRequestDelete3HASH( CSSession sock, 
CSDelete3Hash insertRequest )
+       {
+               HashCode160     tripleHash;
+               boolean         ret;
+
+               stat_cs_delete_3hash_count.inc();
+
+               
tripleHash=HashCode160.create(PersistentHelper.toBytes(insertRequest.doubleHash));
+
+               debug("DEBUG: received 3HASH delete request for 
"+tripleHash.toHex()+" from client");
+
+               ret = manager.removeContent(tripleHash,-1);
+               if (ret)
+                       singleBloomFilter.delete(tripleHash);
+
+               return sock.send(new CSResult(ret));
+       }
+
+       /**
+        * Process a request from the client to unindex content.
+        * @param sock
+        * @param indexingRequest
+        * @return false if the TCP connection should be closed, otherwise true
+        */
+
+       public boolean csHandleRequestUnindexBlock( CSSession sock, 
CSUnindexBlock indexingRequest )
+       {
+               stat_cs_unindex_block_count.inc();
+               return sock.send(new 
CSResult(manager.removeContent(indexingRequest.contentIndex.hash,-1)));
+       }
+
+       /**
+        * Callback used to select the file in the fileindex
+        * that is to be removed.
+        * @param fn
+        * @param i
+        * @param search
+        * @return
+        */
+
+       protected boolean removeMatch( String fn, int i, String search )
+       {
+               return !fn.equals(search);
+       }
+
+       /**
+        * Process a query from the client to remove an on-demand encoded file.
+        * n.b. This function just zeroes the correct row in the list of
+        * on-demand encoded files, if match (deletion is done by 
forEachIndexedFile).
+        * The index of the filename that was removed is returned to the client.
+        *
+        * FIXME: It lookslike if listFileRequest.filename was NOT in 
database.list,
+        * it gets appended to it, removed from it, and client gets a false idx.
+        * This unnecessarily bloats the database.list by one empty line.
+        * @param sock
+        * @param listFileRequest
+        *
+        * @return false if the TCP connection should be closed, otherwise true
+        */
+
+       public boolean csHandleRequestUnindexFile( CSSession sock, 
CSUnindexFile listFileRequest )
+       {
+               DirLocation     dir;
+               String          str;
+               int                     idx;
+
+               stat_cs_unindex_file_count.inc();
+
+               str=prefs.getString("AFS","INDEX-DIRECTORY",null);
+               if (str==null) {
+                       log(Level.WARNING,"WARNING: rejecting content-unindex 
request, INDEX-DIRECTORY option not set!");
+                       return sock.send(CSResult.ERR);
+                       }
+
+               dir=new DirLocation(str);
+               dir.create();
+
+               str=dir.getPath()+"/"+listFileRequest.hash.toHex();
+               idx=fileIndex.appendFilename(str);
+               if (idx==-1) {
+                       return sock.send(CSResult.ERR);
+                       }
+
+               assert(idx!=0) : "Index can't be null !";
+
+               fileIndex.forEachIndexedFile(new IndexedFileNameCallback() {
+                       public boolean onFile( String fn, int idxx, Object data 
)
+                       {
+                               return removeMatch(fn,idxx,(String) data);
+                       }
+                       },str);
+
+               if (!new FileLocation(str).delete()) {
+                       log(Level.WARNING,"Could not remove indexed file");
+                       idx = -1; /* remove failed!? */
+                       }
+               return sock.send(new CSResult(idx));
+       }
+
+       /**
+        * @param sock
+        * @param linkFileRequest
+        * @return false if the TCP connection should be closed, otherwise true
+        */
+
+       public boolean csHandleRequestLinkFile( CSSession sock, CSLinkFile 
linkFileRequest )
+       {
+               String          filename,tname;
+               DirLocation     prefix;
+               HashCode160     hc;
+               FileLocation    loc;
+               LinkLocation    f;
+
+               /* stat_cs_link_file_count.inc(); */    //todo: statut ???
+
+               tname=linkFileRequest.getPath();
+               loc=new FileLocation(tname);
+               hc=loc.getHash();
+
+               if (hc==null || !hc.equals(linkFileRequest.hash)) {
+                       log(Level.WARNING,"WARNING: file link request "+tname+" 
from client pointed to file with the wrong data !");
+                       return sock.send(CSResult.ERR);
+                       }
+
+               filename=prefs.getString("AFS","INDEX-DIRECTORY",null);
+               if (filename==null) {
+                       log(Level.WARNING,"WARNING: rejecting content-unindex 
request, INDEX-DIRECTORY option not set!");
+                       return sock.send(CSResult.ERR);
+                       }
+
+               prefix=new DirLocation(filename);
+               prefix.create();
+
+               filename=prefix.getPath()+"/"+linkFileRequest.hash.toHex();
+
+               f=new LinkLocation(filename);
+               if (f.setTarget(new FileLocation(tname))) {
+                       if (f.create()) {
+                               return sock.send(CSResult.OKAY);
+                               }
+                       }
+
+               log(Level.WARNING,"Could not create link from \""+tname+"\" to 
\""+f.getLabel()+"\".");
+               return sock.send(CSResult.ERR);
+       }
+
+       /**
+        * Process a client request to limit our super-query bloom
+        * filter.
+        * @param sock
+        * @param superIndexRequest
+        * @return
+        */
+
+       public boolean csHandleRequestUnindexSuper( CSSession sock, 
CSUnindexSuper superIndexRequest )
+       {
+               stat_cs_unindex_super_count.inc();
+
+               superBloomFilter.delete(superIndexRequest.superHash);
+               return sock.send(new 
CSResult(manager.removeContent(superIndexRequest.superHash,-1)));
+       }
+
+       /**
+        * @param sock
+        * @param insertRequest
+        * @return
+        *
+        */
+
+       public boolean csHandleRequestInsertSBlock( CSSession sock, 
CSInsertSBlock insertRequest )
+       {
+               ContentIndex    entry;
+               HashCode160             ns;
+               boolean                 ret;
+               boolean[]               dupe=new boolean[1];
+
+               stat_cs_insert_sblock_count.inc();
+
+               ns=insertRequest.getContent().getNameSpace();
+               debug("Received SBlock for namespace "+ns.toHex()+" with 
routing ID "+insertRequest.getContent().getIdentifier().toHex()+".");
+
+               entry=new ContentIndex();
+               entry.type= ContentIndex.LOOKUP_TYPE_SBLOCK;
+               entry.importance= insertRequest.getImportance();
+               entry.fileNameIndex= 0; /* database */
+               entry.fileOffset = 0; /* data/content */
+               entry.hash=(HashCode160) 
PersistentHelper.copy(insertRequest.getContent().getIdentifier());     //todo: 
copie utile ?
+               dupe[0] = false;
+               ret = 
manager.insertContent(entry,ContentBlock.SIZE,insertRequest.getContent(),null,dupe);
+
+               debug("Received SBlock insert is dupe: "+dupe+" (insert 
"+ret+")");
+
+               if (ret && !dupe[0]) {
+                       
singleBloomFilter.add(insertRequest.getContent().getIdentifier());
+                       }
+               return sock.send(new CSResult(ret));
+       }
+
+       /**
+        * @param sock
+        * @param queryRequest
+        * @return
+        *
+        */
+
+       public boolean csHandleRequestNSQuery( CSSession sock, CSNSQuery 
queryRequest )
+       {
+               int                             qp = 
Policy2.QUERY_ANSWER|Policy2.QUERY_FORWARD|Policy2.QUERY_INDIRECT|Policy2.QUERY_PRIORITY_BITMASK;
+               P2PNSQuery      msg;
+
+               stat_cs_nsquery_count.inc();
+
+               debug("Received NS query 
("+queryRequest.namespace.toHex()+"/"+queryRequest.identifier.toHex()+") with 
ttl "+queryRequest.ttl+" and priority "+queryRequest.priority+".");
+
+               msg = new P2PNSQuery(coreAPI.getIdentity());
+               msg.setPriorityAndTTL(queryRequest.priority,queryRequest.ttl);
+               msg.identifier=(HashCode160) 
PersistentHelper.copy(queryRequest.identifier);    //todo: copie utile ???
+               msg.namespace=(HashCode160) 
PersistentHelper.copy(queryRequest.namespace);      //todo: copie utile ???
+               routing.execQuery(qp, msg, sock);
+               return true;
+       }
+
+       public boolean handleNSQUERY( HostIdentity sender, P2PMessage msg )
+       {
+               int                             qp;
+               P2PNSQuery      qmsg;
+               int ttl;
+               int prio;
+               double preference;
+
+               if (msg.getByteSize() != P2PNSQuery.SIZEX) {
+                       log(Level.WARNING,"WARNING: nsquery received was 
malformed");
+                       return false;
+                       }
+               stat_p2p_nsquery_count.inc();
+               qmsg = (P2PNSQuery) msg;
+               /* decrement ttl */
+               ttl = qmsg.getTTL();
+
+               debug("DEBUG: received NS query for "+qmsg.identifier.toHex()+" 
with ttl "+ttl);
+
+               if (ttl < 0) {
+                       ttl = (int) (ttl - 2*TTL_DECREMENT - 
Crypto.nextLong(TTL_DECREMENT));
+                       if (ttl > 0)
+                               return true; /* just abort */
+                       }
+               else
+                       ttl = (int) (ttl - 2*TTL_DECREMENT - 
Crypto.nextLong(TTL_DECREMENT));
+
+               qp = policy.evaluateQuery(sender,qmsg.getPriority());
+               if ((qp & Policy2.QUERY_DROPMASK) == 0)
+                       return true; /* straight drop. */
+
+               preference =  (qp & Policy2.QUERY_PRIORITY_BITMASK);
+               if (preference < Policy2.QUERY_BANDWIDTH_VALUE)
+                       preference = Policy2.QUERY_BANDWIDTH_VALUE;
+               coreAPI.preferTrafficFrom(sender,preference);
+
+               /* adjust priority */
+               prio = qmsg.getPriority();
+               if ( (qp & Policy2.QUERY_PRIORITY_BITMASK) < prio) {
+                       prio = qp & Policy2.QUERY_PRIORITY_BITMASK;
+                       qmsg.setPriorityAndTTL(prio,qmsg.getTTL());
+               }
+
+               /* adjust TTL */
+               if ( (ttl > 0) &&
+                               (ttl > (prio+3)*TTL_DECREMENT) )
+                       ttl = (int) ((prio+3)*TTL_DECREMENT); /* bound! */
+               qmsg.setPriorityAndTTL(qmsg.getPriority(),ttl);
+
+               routing.execQuery(qp,qmsg, null);
+               return true;
+       }
+
+       /**
+        * @param sender
+        * @param msg
+        * @return
+        *
+        */
+
+       public boolean handleSBLOCK_CONTENT( HostIdentity sender, P2PMessage 
msg )
+       {
+               int                             prio;
+               P2PSBlockResult cmsg;
+               ContentIndex    ce=new ContentIndex();
+               boolean                 ret;
+               double                  preference;
+               boolean[]               dupe=new boolean[1];
+
+               if (msg.getByteSize() != P2PSBlockResult.SIZE) {
+                       log(Level.WARNING,"WARNING: signed content message 
received was malformed");
+                       return false;
+                       }
+               stat_p2p_sblock_replies.inc();
+               cmsg = (P2PSBlockResult) msg;
+
+               if (!cmsg.getResult().verify())
+                       return false;
+               new 
Pseudonym(prefs).addNamespace(cmsg.getResult().getNameSpace());
+
+               debug("DEBUG: received SBLOCK search result for 
"+cmsg.getResult().getIdentifier().toHex()+" from peer");
+
+               prio = 
routing.useContent(sender,cmsg.getResult().getIdentifier(),msg);
+               if (sender == null) { /* no migration, this is already content 
from the local node */
+                       debug("DEBUG: content migration not needed, content is 
local");
+                       return true;
+                       }
+
+               debug("DEBUG: content migration with preference "+prio);
+
+               preference = prio;
+               prio = 
policy.evaluateContent(cmsg.getResult().getIdentifier(),prio);
+               if (prio == -1) {
+                       debug("DEBUG: content not important enough, not 
replicated");
+                       return true; /* straight drop */
+                       }
+
+               debug("DEBUG: content replicated with total preference "+prio);
+
+               if (prio != -1)
+                       preference += prio;
+               if (preference < Policy2.CONTENT_BANDWIDTH_VALUE)
+                       preference = Policy2.CONTENT_BANDWIDTH_VALUE;
+               coreAPI.preferTrafficFrom(sender,preference);
+               ce.hash=(HashCode160) 
PersistentHelper.copy(cmsg.getResult().getIdentifier());  //todo: copie utile 
???
+               ce.importance    = prio;
+               ce.type          = ContentIndex.LOOKUP_TYPE_SBLOCK;
+               ce.fileNameIndex = 0;
+               ce.fileOffset    = 0;
+
+               ret = 
manager.insertContent(ce,ContentBlock.SIZE,cmsg.getResult(),sender,dupe);
+               if (ret && !dupe[0])
+                       singleBloomFilter.add(cmsg.getResult().getIdentifier());
+               return true;
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/IndexedFileNameCallback.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/IndexedFileNameCallback.java       
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/IndexedFileNameCallback.java       
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,23 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs;
+
+/**
+ *
+ */
+
+public interface IndexedFileNameCallback
+{
+       /**
+        * Callback for each indexed file.
+        *
+        * @param fn the name of the file
+        * @param idx the index of the file
+        * @param data opaque context pointer for the callee
+        * @return false if the file should be removed from the list
+        */
+
+       public boolean onFile( String fn, int idx, Object data );
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/IndirectionTableEntry.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/IndirectionTableEntry.java 
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/IndirectionTableEntry.java 
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,177 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs;
+
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.util.*;
+
+/**
+ * Indirection table entry. Lists what we're looking for,
+ * where to forward it, and how long to keep looking for it.
+ */
+
+public class IndirectionTableEntry extends Object
+{
+       /** what are we waiting for ? */
+       public HashCode160      hash;
+
+       /** Are we limited to a specific namespace ? Non-null if yes. */
+       public HashCode160      namespace;
+
+       /** when can we forget about this entry ? */
+       public long                     ttl;
+
+       /** How much is this query worth to us, that is, how much would this 
node be willing to "pay" for an answer that matches the
+        hash stored in this ITE? (This is NOT the inbound priority, it is the 
trust-adjusted inbound priority <B>divided</B> by the
+        number of queries (for a multi-query)). */
+       public int                      priority;
+
+       /** which replies have we already seen ? hashcodes of the encrypted (!) 
replies that we have forwarded so far */
+       private List                    seen;
+
+       /** How many hosts are waiting for an answer to this query / who are 
these hosts ? */
+       private List                    waiting;
+
+               /** How many tcpsocks are in use ? / local TCP clients to send 
the reply to, null if none */
+       private List                    tcpsocks;
+
+       /** Do we currently have a response in the delay loop (delays are 
introduced to make traffic analysis harder
+        and thus enable anonymity) ? This marker is set to avoid looking up 
content again before the first content
+        exits the delay loop.  Since this *not* looking up content again is 
not externally visible, it is ok to do this
+        optimization to reduce disk accesses (see Mantis bug #407). */
+       public boolean          successful_local_lookup_in_delay_loop;
+
+       /** Avoiding concurrent lookups for the same ITE: semaphore grants 
access to peers to perform a lookup that matches this ITE entry. */
+       public Object           lookup_exclusion;
+
+
+       public IndirectionTableEntry()
+       {
+               super();
+               seen=new ArrayList();
+               waiting=new ArrayList();
+               tcpsocks=new ArrayList();
+               hash=new HashCode160();
+       }
+
+       public String toString()
+       {
+               return "xxx";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public boolean hasSeen()
+       {
+               return seen.size()>0;
+       }
+
+       public int getSeenCount()
+       {
+               return seen.size();
+       }
+
+       public HashCode160 getSeen( int index )
+       {
+               return ((index>=0 && index<seen.size()) ? (HashCode160) 
seen.get(index) : null);
+       }
+
+       public void addSeen( HashCode160 h )
+       {
+               seen.add(PersistentHelper.copy(h));     //todo: copie utile ???
+       }
+
+       public void clearSeen()
+       {
+               seen.clear();
+       }
+
+       public boolean hasWaitingHost( HostIdentity hi )
+       {
+               return waiting.contains(hi);
+       }
+
+       public boolean hasWaitingHosts()
+       {
+               return waiting.size()>0;
+       }
+
+       public int getWaitingHostsCount()
+       {
+               return waiting.size();
+       }
+
+       public HostIdentity getWaitingHost( int index )
+       {
+               return ((index>=0 && index<waiting.size()) ? (HostIdentity) 
waiting.get(index) : null);
+       }
+
+       public void addWaitingHost( HostIdentity hi )
+       {
+               waiting.add(PersistentHelper.copy(hi)); //todo: copie utile ???
+       }
+
+       public void removeWaitingHost( HostIdentity hi )
+       {
+               while (waiting.remove(hi)) {}
+       }
+
+       public void clearWaitingHosts()
+       {
+               waiting.clear();
+       }
+
+       public boolean hasClients()
+       {
+               return tcpsocks.size()>0;
+       }
+
+       public boolean containsClient( CSSession c )
+       {
+               int     i;
+
+               for (i=0; i<tcpsocks.size(); i++) {
+                       if (tcpsocks.get(i)==c) {
+                               return true;
+                               }
+                       }
+               return false;
+       }
+
+       public int getClientsCount()
+       {
+               return tcpsocks.size();
+       }
+
+       public CSSession getClient( int index )
+       {
+               return ((index>=0 && index<tcpsocks.size()) ? (CSSession) 
tcpsocks.get(index) : null);
+       }
+
+       public void addClient( CSSession c )
+       {
+               tcpsocks.add(c);
+       }
+
+       public void removeClient( CSSession c )
+       {
+               int     i;
+
+               for (i=0; i<tcpsocks.size(); i++) {
+                       if (tcpsocks.get(i)==c) {
+                               tcpsocks.remove(i);
+                               i--;
+                               }
+                       }
+       }
+
+       public void clearClients()
+       {
+               tcpsocks.clear();
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/IterState.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/IterState.java     2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/IterState.java     2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,28 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs;
+
+import org.gnu.freeway.protocol.afs.esed2.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+
+import EDU.oswego.cs.dl.util.concurrent.*;
+
+/**
+ *
+ */
+
+public class IterState extends Object
+{
+       public boolean          hasNext;
+       public Semaphore                wsem;
+       public Semaphore                sem;
+       public HashCode160      next;
+       public ContentIndex     ce;
+       public int                      bucket;
+       public Object           data;
+       public int                      len;
+       public Task                     db_iterator;
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/LFS.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/LFS.java   2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/LFS.java   2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,325 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs;
+
+import org.gnu.freeway.protocol.afs.esed2.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.io.*;
+import org.gnu.freeway.util.net.*;
+
+import java.io.*;
+import java.nio.*;
+import java.nio.channels.*;
+import java.util.logging.*;
+
+/**
+ * Support for special handling of very large (3HASH) reply sets.
+ *
+ * The databases (gdbm in particular, but also the others) do not
+ * handle very large entries very well.  This is no problem for CHK,
+ * but there can be several thousand (!)  results for a very popular
+ * keyword, like a mime-type.  These 3HASH codes with more than
+ * VERY_LARGE_SIZE (16) results are thus stored in separate files.
+ *
+ * The reason is, that gdbm would grow quadratic when the file is
+ * build and that it would also be very slow: every read or write to
+ * these very large content entries would require reading and writing
+ * the *entire* 2 MB block (2 MB for 2,000 entries).  This API allows
+ * a random access to one of the results and the use of "append" to
+ * add a single entry.  It also does not suffer from the quadratic
+ * explosion in space consumption that gdbm has.  So essentially, this
+ * is a crapload of code that does not add any real functionality but
+ * overcomes problems with the crude database implementations that we
+ * would have to use otherwise (and that would be really bad for
+ * performance without this).
+ *
+ * Support for special handling of very large (3HASH) reply sets.
+ *
+ * The databases (gdbm in particular, but also the others) do not
+ * handle very large entries very well.  This is no problem for CHK,
+ * but there can be several thousand (!)  results for a very popular
+ * keyword, like a mime-type.  These 3HASH codes with more than
+ * VERY_LARGE_SIZE (16) results are thus stored in separate files.
+ *
+ * The reason is, that gdbm would grow quadratic when the file is
+ * build and that it would also be very slow: every read or write to
+ * these very large content entries would require reading and writing
+ * the *entire* 2 MB block (2 MB for 2,000 entries).  This API allows
+ * a random access to one of the results and the use of "append" to
+ * add a single entry.  It also does not suffer from the quadratic
+ * explosion in space consumption that gdbm has.  So essentially, this
+ * is a crapload of code that does not add any real functionality but
+ * overcomes problems with the crude database implementations that we
+ * would have to use otherwise (and that would be really bad for
+ * performance without this).
+ */
+
+public class LFS extends LoggedObject
+{
+       public static final String      DIR_EXT =       ".lfs";
+
+       private DirLocation     dir;
+       private Object          lock;
+       private StatusCallsService      status;
+
+
+       public LFS()
+       {
+               super(true);
+       }
+
+       public String toString()
+       {
+               return "LFS";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       /**
+        * Initialize the storage module.
+        * @param str the name of the directory/file
+        *        containing the content database
+        * @param sc
+        */
+
+       public void init( String str, StatusCallsService sc )
+       {
+               status=sc;
+
+               dir=new DirLocation(str+DIR_EXT);
+               if (!dir.create()) {
+                       log(Level.SEVERE,"Failed to open directory 
"+dir.getLabel()+" !");
+                       return;
+                       }
+
+               log(Level.INFO,"Database directory is located at 
\""+dir.getLabel()+"\".");
+
+               lock=new Object();
+       }
+
+       /**
+        * Remove the lfs database.
+        * Remove the lfs database.
+        */
+
+       public void delete()
+       {
+               if (!dir.delete()) {
+                       log(Level.SEVERE,"Could not remove 
\""+dir.getName()+"\" !");
+                       }
+               dir=null;
+               lock=null;
+       }
+
+       /**
+        * Clean shutdown of the storage module.
+        */
+
+       public void done()
+       {
+               dir=null;
+               lock=null;
+       }
+
+       /**
+        * Read the contents of a bucket to a buffer.
+        * @param query
+        *
+        * @return read blocks on success, null on failure
+        */
+
+       public ContentBlock[] read( HashCode160 query )
+       {
+               ContentBlock[]  blocks;
+               FileChannel             fd;
+               FileLocation                    fil;
+               int                             fsize,i;
+               ByteBuffer              buf;
+
+               fil=dir.getFile(query.toHex());
+               if (fil==null) {
+                       log(Level.WARNING,"File \""+query.toHex()+"\" does not 
exist !");
+                       return null;
+                       }
+
+               blocks=null;
+               synchronized(lock) {
+                       try {
+                               fd=new 
RandomAccessFile(fil.getPath(),"r").getChannel();
+                               try {
+                                       fsize=(int) fd.size();
+                                       if ((fsize % ContentBlock.SIZE)!=0) {
+                                               log(Level.WARNING,"LFS database 
corrupted (file \""+query.toHex()+"\" has bad length), trying to fix.");
+                                               fsize = (fsize / 
ContentBlock.SIZE) * ContentBlock.SIZE;
+                                               fd.truncate(fsize);
+                                               }
+
+                                       buf=ByteBuffer.allocateDirect(fsize);
+                                       if (fd.read(buf)!=fsize) {
+                                               return null;
+                                               }
+                                       buf.flip();
+
+                                       blocks= new 
ContentBlock[fsize/ContentBlock.SIZE];
+                                       for (i=0; i<blocks.length; i++) {
+                                               blocks[i]=new ContentBlock();
+                                               blocks[i].readBytes(buf,new 
ErrorReporter());
+                                               }
+                                       }
+                               finally {
+                                       fd.close();
+                                       }
+                               }
+                       catch( IOException x ) {
+                               err("Failed to read on \""+query.toHex()+"\" 
!",x);
+                               return null;
+                               }
+                       }
+               return blocks;
+       }
+
+       /**
+        * Read one random block from an entry
+        * Read the contents of a bucket to a buffer.
+        *
+        * @param query the hashcode representing the entry
+        * @param prio
+        * @return read blocks on success, null on failure
+        */
+
+       public ContentBlock[] randomRead( HashCode160 query, int prio )
+       {
+               ContentBlock[]  blocks;
+               int[]                   perm;
+               FileChannel             fd;
+               FileLocation                    fil;
+               int                             size,fsize,max,i;
+
+               max = (50-status.getNetworkLoadUp())*(prio+1);
+               if (max <= 0)
+                       max = 1;
+
+               fil=dir.getFile(query.toHex());
+               if (fil==null) {
+                       log(Level.WARNING,"File \""+query.toHex()+"\" does not 
exist !");
+                       return null;
+                       }
+
+               blocks=null;
+               synchronized(lock) {
+                       try {
+                               fd=new 
RandomAccessFile(fil.getPath(),"r").getChannel();
+                               try {
+                                       fsize=(int) fd.size();
+                                       if ((fsize % ContentBlock.SIZE)!=0) {
+                                               log(Level.WARNING,"LFS database 
corrupted (file \""+query.toHex()+"\" has bad length), trying to fix.");
+                                               fsize = (fsize / 
ContentBlock.SIZE) * ContentBlock.SIZE;
+                                               fd.truncate(fsize);
+                                               }
+
+                                       fsize = fsize / ContentBlock.SIZE;
+                                       if (fsize == 0) {
+                                               return null;
+                                               }
+                                       if (max > fsize) {
+                                               max = fsize;
+                                               }
+                                       log(Level.FINEST,"Received query, have 
"+fsize+" results, adding "+max+" to queue.");
+
+                                       blocks=new ContentBlock[max];
+
+                                       perm = Crypto.permute(fsize);
+                                       for (i=0; i<max; i++) {
+                                               blocks[i]=new ContentBlock();
+
+                                               
fd.position(perm[i]*ContentBlock.SIZE);
+
+                                               
size=fd.read(ByteBuffer.wrap(blocks[i].content));
+                                               if (size!=ContentBlock.SIZE) {
+                                                       return null;
+                                                       }
+                                               }
+                                       }
+                               finally {
+                                       fd.close();
+                                       }
+                               }
+                       catch( IOException x ) {
+                               err("Failed to read on \""+query.toHex()+"\" 
!",x);
+                               return null;
+                               }
+                       }
+               return blocks;
+       }
+
+       /**
+        * Append content to file.
+        *
+        * @param query the key for the entry
+        * @param data what to append/the data to store
+        * @return false on error, true if ok.
+        */
+
+       public boolean append( HashCode160 query, ContentBlock data )
+       {
+               FileChannel     fd;
+               FileLocation            fil;
+               int                     offset;
+
+               fil=dir.getFile(query.toHex());
+               fil.create();
+
+               synchronized(lock) {
+                       try {
+                               fd=new 
RandomAccessFile(fil.getPath(),"rw").getChannel();
+                               try {
+                                       fd.position(fd.size());
+                                       offset=(int) fd.position();
+                                       if ((offset % ContentBlock.SIZE)!=0) {
+                                               log(Level.WARNING,"LFS database 
corrupted (file \""+query.toHex()+"\" has bad length), trying to fix.");
+                                               
offset=(offset/ContentBlock.SIZE)*ContentBlock.SIZE;
+                                               fd.position(offset);
+                                               fd.truncate(offset);
+                                               }
+
+                                       fd.write(ByteBuffer.wrap(data.content));
+                                       }
+                               finally {
+                                       fd.close();
+                                       }
+                               }
+                       catch( IOException x ) {
+                               err("Failed to append on \""+query.toHex()+"\" 
!",x);
+                               return false;
+                               }
+                       }
+               return true;
+       }
+
+       /**
+        * Remove an entry.
+        * Free space in the database by removing one file
+        *
+        * @param query the key for the entry/the hashcode representing the 
entry
+        * @return false on error, true if ok.
+        */
+
+       public boolean remove( HashCode160 query )
+       {
+               FileLocation            f;
+
+               f=dir.getFile(query.toHex());
+               if (f==null) {
+                       log(Level.WARNING,"File \""+query.toHex()+"\" does not 
exist !");
+                       return false;
+                       }
+
+               synchronized(lock) {
+                       return f.delete();
+                       }
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/Manager.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/Manager.java       2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/Manager.java       2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,1015 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs;
+
+import org.gnu.freeway.protocol.afs.esed2.*;
+import org.gnu.freeway.server.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.io.*;
+import org.gnu.freeway.util.net.*;
+
+import EDU.oswego.cs.dl.util.concurrent.*;
+
+import java.io.*;
+import java.nio.*;
+import java.nio.channels.*;
+import java.util.*;
+import java.util.logging.*;
+
+/**
+ * This module is responsible to manage content, in particular it needs to 
decide what content to keep.
+ * This module is responsible for content management (what to
+ * keep, what to discard, content ageing, content migration).
+ *
+ * The manager.h header defines the external interface to the
+ * GNUnet databases.  The manager code is responsible for
+ * space management and on-demand encoding of blocks.  The
+ * high-level database abstraction defined in high_backend.h
+ * is responsible for lookup (3HASH and CHK) and block retrieval
+ * (ContentEntries and inserted blocks).
+ */
+
+public class Manager extends LoggedObject
+{
+       public static final String      VLS_DIR =       "large";
+       public static final String      AGEFILE =       "database.age";
+
+       public static final int         DB_DIRTY_AVAILABLE      =       
Integer.MIN_VALUE;
+
+
+       public static final boolean     TRACK_INDEXED_FILES     =       true;
+       public static final String      TRACKFILE                       =       
"indexed_requests.txt";
+
+
+       /** Entry length that indicates that the entry was too large for the 
usual DB and has been stored in a separate file instead. */
+       public static final int VERY_LARGE_FILE =       42;
+
+       /** How large is very large? (number of ContentEntries) Mysql seems to 
have some limit at 16k, so let's pick 15 to be on the good side for sure. */
+       public static final int VERY_LARGE_SIZE =       15;
+
+       /** The current base value for fresh content (used to time-out old 
content). */
+       private int                     MANAGER_age;
+
+       /** Is active migration allowed ? This is about us receiving data from 
the network,
+        actively pushing content out is always ok. */
+       private boolean         useActiveMigration;
+
+       /** Global database handle */
+//     private DatabaseAPI     dbAPI;
+       /** Handle of the database as returned by initContentDatabase() */
+       public DBHandle[]       dbHandles;
+
+       /** The number of buckets */
+       public int                      buckets;
+
+       /** cache estimated available blocks for each bucket */
+       public int[]            dbAvailableBlocks;
+
+       /** Large file handling. */
+       private LFS                     lfs;
+
+       /** Statistics handles */
+       private Stat    stat_handle_lookup_3hash;
+       private Stat    stat_handle_lookup_sblock;
+       private Stat    stat_handle_lookup_chk;
+       private Stat    stat_handle_lookup_ondemand;
+       private Stat    stat_handle_lookup_notfound;
+       private Stat    stat_handle_spaceleft;
+
+       private BloomFilter2    filter2;        //todo: VA PLANTER
+       private FileIndex               fileIndex;      //todo: VA PLANTER
+       private ContentEncoding encoding;       //todo: VA PLANTER
+
+       private Prefs           prefs;
+       private Statistics      stats;
+       private Scheduler               scheduler;
+       private ScheduledTask           reduceTask;
+
+
+       public Manager()
+       {
+               super(true);
+               lfs=new LFS();
+
+               reduceTask=new ScheduledTask("REDUCE-IMPORTANCE",new 
EvalAction(this,"cronReduceImportance"),Scheduler.HOURS_12);
+       }
+
+       public String toString()
+       {
+               return "Manager";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       /**
+        * Initialize the manager module.
+        * @param capi
+        */
+
+       public void init( CoreForProtocol capi )
+       {
+               String  dtype,afsdir;
+               int             delta,i;
+
+               prefs=capi.getApplication().getPreferences();
+               stats=capi.getApplication().getStatistics();
+
+               dtype = prefs.getString("AFS","DATABASETYPE",null);
+               initializeDatabaseAPI(dtype);
+               stat_handle_lookup_sblock=stats.getHandle("# lookup (SBlock, 
search results)");
+               stat_handle_lookup_3hash=stats.getHandle("# lookup (3HASH, 
search results)");
+               stat_handle_lookup_chk=stats.getHandle("# lookup (CHK, inserted 
or migrated content)");
+               stat_handle_lookup_ondemand=stats.getHandle("# lookup 
(ONDEMAND, indexed content)");
+               stat_handle_lookup_notfound=stats.getHandle("# lookup (data not 
found)");
+               stat_handle_spaceleft=stats.getHandle("# blocks AFS storage 
left (estimate)");
+
+               MANAGER_age = readAge();
+
+               useActiveMigration= 
prefs.testString("AFS","ACTIVEMIGRATION","YES");
+
+               scheduler=capi.getApplication().getScheduler();
+               scheduler.addJob(reduceTask,Scheduler.HOURS_6);
+
+               delta = estimateGlobalAvailableBlocks();
+               if (delta < 0) {
+                       int[]   perm = Crypto.permute(buckets);
+                       /* we permute to delete content in random order since
+                        users may interrupt the process (in particular at
+                        the beginning) and we want to make sure that the
+                        chances are distributed reasonably at random) */
+                       for (i=0; i<buckets; i++) {
+                               
dbHandles[perm[i]].deleteContent(16-delta/buckets,new EntryCallback() {
+                                       public void onEntry( HashCode160 key, 
ContentIndex ce, Object data, int dataLen, Object closure )
+                                       {
+                                               
filter2.bf_deleteEntryCallback(key,ce,data,dataLen,closure);
+                                       }
+                                       },null);
+                               dbAvailableBlocks[perm[i]]=DB_DIRTY_AVAILABLE;
+                               }
+                       delta = (16-delta/buckets)*buckets;
+                       }
+               stat_handle_spaceleft.set(delta);
+
+               afsdir = prefs.getString("AFS","AFSDIR",null);
+               if (afsdir==null) {
+                       trace("Configuration file must specify directory for 
storing AFS data in section AFS under AFSDIR !");
+                       return;
+                       }
+
+               lfs.init(afsdir+"/"+VLS_DIR,(StatusCallsService) 
capi.service(StatusCallsService.class));
+       }
+
+       /**
+        * Shutdown the manager module.
+        */
+
+       public void done()
+       {
+               int i;
+
+               scheduler.deleteJob(reduceTask);
+
+               for (i=0;i<buckets;i++)
+                       dbHandles[i].close();
+               dbHandles=null;
+               dbAvailableBlocks=null;
+               lfs.done();
+       }
+
+       /**
+        * calculates the global available space using
+        * cached bucket availability estimates
+        * @return
+        */
+
+       protected int estimateGlobalAvailableBlocks()
+       {
+               int i;
+               int ret = 0;
+               int perBucketQuota = prefs.getInt("AFS","DISKQUOTA",0) * 1024 / 
buckets;
+
+               for (i = 0; i < buckets; ++i) {
+                       if (dbAvailableBlocks[i] == DB_DIRTY_AVAILABLE) {
+                               dbAvailableBlocks[i] = 
dbHandles[i].estimateAvailableBlocks(perBucketQuota);
+                               }
+                       ret += dbAvailableBlocks[i];
+                       }
+               return ret;
+       }
+
+       /**
+        * Load the high-level database as specified by
+        * the given dtype.
+        * @param dtype
+        */
+
+       public void initializeDatabaseAPI( String dtype )
+       {
+               DBHandle        dbh;
+               String          odtype;
+               int                     i;
+
+               if (dtype == null) {
+                       log(Level.SEVERE,"AFS/DATABASETYPE not specified in 
config");
+                       return;
+                       }
+
+               odtype=prefs.getContentAsString("AFS-DATABASETYPE");
+               if (odtype==null) {
+                       prefs.putContent("AFS-DATABASETYPE",dtype);
+                       }
+               else if (!odtype.equals(dtype)) {
+                       log(Level.SEVERE,"FATAL: AFS database type was changed, 
run gnunet-convert");
+                       return;
+                       }
+
+               buckets= 4 *prefs.getInt("AFS","DISKQUOTA",0) / 1024; // one 
bucket per 256 MB
+               if (buckets == 0)
+                       buckets = 1; // at least 1 bucket!
+               dbHandles= new DBHandle[buckets];
+               dbAvailableBlocks=new int[buckets];
+
+               for (i=0;i<buckets;i++) {
+                       dbh=(DBHandle) new 
DynamicLibrary("freeway-afs-"+dtype+".jar").load(DBHandle.class,getClass().getClassLoader());
+                       if (dbh==null) {
+                               log(Level.SEVERE,"Failed to initialize AFS 
database "+i+" !");
+                               return;
+                               }
+                       if 
(!dbh.open(prefs,String.valueOf(i),String.valueOf(prefs.getInt("AFS","DISKQUOTA",0))))
 {
+                               dbh.close();
+                               trace("Should not happen !");
+                               return;
+                               }
+                       dbHandles[i]=dbh;
+
+                       if (dbHandles[i] == null) {
+                               log(Level.SEVERE,"Failed to initialize AFS 
database "+i+" !");
+                               return;
+                               }
+                       dbAvailableBlocks[i]=DB_DIRTY_AVAILABLE; /* not yet 
initialized */
+                       }
+       }
+
+       /**
+        * Store content (if the priority is high enough), potentially
+        * discarding less important content. If this method is called
+        * for indexed content, * data should be null and len==0 and
+        * fields of ce filled properly. For 3HASH inserts, 2HASH must
+        * be provided in ce.hash.
+        *
+        * @param ce the content entry describing the content
+        * @param len the length of the data in bytes/the length of the data in 
bytes, either 0 for no data (e.g. on-demand encoding
+        *         of indexed content or CONTENT_Block.SIZE for normal data.
+        * @param data the block itself
+        * @param sender from where does the content come? null for
+        *        from local client./from where does the content come? null for
+        *        from local client.
+        * @param duplicate output param, will be true if content was already 
there
+        * @return true if the block was stored, false if not
+        */
+
+       public boolean insertContent( ContentIndex ce, int len, byte[] data, 
HostIdentity sender, boolean[] duplicate )
+       {
+               byte[]                  old;
+               ContentIndex    oldce;
+               HashCode160             query;
+               int                             avail,importance;
+
+               if (ce.fileNameIndex>0) {
+                       log(Level.FINEST,"Using fileNameIndex 
"+ce.fileNameIndex);
+                       }
+
+               if (len!=0 && len!=ContentBlock.SIZE) {
+                       trace("Unexpected length "+len+" for insertContent !");
+                       return false;
+                       }
+
+               duplicate[0] = false;
+               if (sender!=null && !useActiveMigration) {
+                       return false; // forbidden!
+                       }
+               importance = ce.importance;
+               if (sender!=null && Crypto.nextInt(2 + importance)==0) {
+                       return false; // don't bother...
+                       }
+
+               ce.importance= importance + MANAGER_age;
+               switch (ce.type) {
+                       case ContentIndex.LOOKUP_TYPE_3HASH:
+                               
query=HashCode160.create(PersistentHelper.toBytes(ce.hash));
+                               break;
+                       case ContentIndex.LOOKUP_TYPE_CHK:
+                       case ContentIndex.LOOKUP_TYPE_CHKS:
+                       case ContentIndex.LOOKUP_TYPE_SUPER:
+                       case ContentIndex.LOOKUP_TYPE_SBLOCK:
+                               query=(HashCode160) 
PersistentHelper.copy(ce.hash);
+                               break;
+                       default:
+                               log(Level.WARNING,"Unexpected content type : 
"+ce.type);
+                               return false;
+                       }
+
+               oldce=(ContentIndex) PersistentHelper.copy(ce);
+
+               avail = estimateGlobalAvailableBlocks();
+               if (avail <= 0) {
+                       if (importance + MANAGER_age <= 
computeHighDB(query).getMinimumPriority())
+                               return false; // new content has such a low 
priority that we should not even bother!
+                       computeHighDB(query).deleteContent(16-avail,new 
EntryCallback() {
+                                       public void onEntry( HashCode160 key, 
ContentIndex ce2, Object data2, int dataLen, Object closure )
+                                       {
+                                               
filter2.bf_deleteEntryCallback(key,ce2,data2,dataLen,closure);
+                                       }
+                                       },null);
+                       stat_handle_spaceleft.set(16-avail);
+                       
dbAvailableBlocks[computeBucketGlobal(query)]=DB_DIRTY_AVAILABLE;
+                       }
+               else {
+                       stat_handle_spaceleft.set(avail);
+                       }
+
+               // try to read existing
+               old = computeHighDB(query).readContent(query,oldce,0);
+
+               // add the content
+               switch (ce.type) {
+                       case ContentIndex.LOOKUP_TYPE_3HASH:
+                               if (len != ContentBlock.SIZE ) {
+                                       trace("Unexpected length "+len+" for 
insertContent !");
+                                       return false;
+                                       }
+                               return 
handle3HSBInsert(query,ce,data,(old!=null ? old.length : 
-1),duplicate,len,old,oldce.importance);
+                       case ContentIndex.LOOKUP_TYPE_SBLOCK:
+                               if (len != ContentBlock.SIZE) {
+                                       trace("Unexpected length "+len+" for 
insertContent !");
+                                       return false;
+                                       }
+                               return 
handle3HSBInsert(query,ce,data,(old!=null ? old.length : 
-1),duplicate,len,old,oldce.importance);
+                       case ContentIndex.LOOKUP_TYPE_CHK:
+                       case ContentIndex.LOOKUP_TYPE_CHKS:
+                       case ContentIndex.LOOKUP_TYPE_SUPER:
+                       {
+                               boolean replace = false;
+                               /*
+                                * This is a bit messy. The intended idea is 
that missing blocks
+                                * are always replaced. Indexed blocks are 
replaced only if the
+                                * new one is indexed AND has a higher priority 
than the old one.
+                                * Nonindexed, existing blocks are replaced if 
the size differs OR if new
+                                * is more important OR if new is an indexed 
block. This scheme
+                                * should never replace an indexed block with a 
nonindexed block.
+                                * We are not setting *duplicate=YES in the 
true replace case because
+                                * we don't want the bloomfilters to be 
unnecessarily incremented
+                                * outside insertContent().
+                                */
+                               duplicate[0] = true;
+                               if (old == null) {
+                                       replace = true;
+                                       duplicate[0] = false;
+                                       }
+                               else if (oldce.fileNameIndex > 0) {
+                                       if (ce.fileNameIndex>0 && 
ce.importance>oldce.importance) {
+                                               replace = true;
+                                               }
+                                       else {
+                                               replace = false;
+                                               }
+                                       }
+                               else {
+                                       if ((old!=null ? old.length : -1) != 
len || ce.importance>oldce.importance || ce.fileNameIndex>0) {
+                                               replace = true;
+                                               }
+                                       else {
+                                               replace = false;
+                                               }
+                                       }
+
+                               if (!replace) {
+                                       return true;
+                                       }
+
+                               dbAvailableBlocks[computeBucketGlobal(query)]= 
DB_DIRTY_AVAILABLE;
+                               return 
computeHighDB(query).writeContent(ce,data,0,len);
+                               }
+//                             break;
+                       default:
+                               log(Level.WARNING,"Unexpected content type : 
"+ce.type);
+                               return false;
+                       }
+//             log(Level.SEVERE,"ERROR: insertContent code ended up where it 
never should");
+//             return false;
+       }
+
+       public boolean insertContent( ContentIndex ce, int len, Persistent 
data, HostIdentity sender, boolean[] duplicate )
+       {
+               return 
insertContent(ce,len,PersistentHelper.toBytes(data),sender,duplicate);
+       }
+
+       /**
+        * Locate content. This method locates the data matching the
+        * query.  The data is on-demand encrypted if it is
+        * indexed content or retrieved from the contentdatabase
+        * if it was inserted content.  The ContentIndex entry is
+        * filled with the appropriate values.
+        * Locate content.  This method locates the data matching the content
+        * entry.  The data is on-demand encrypted if it is indexed content or
+        * retrieved from the contentdatabase if it was inserted content.
+        *
+        * @param query         the CHK or the tripleHash key of the conten/the 
query for the content (CHK or 3HASH)
+        * @param ce            the content entry describing what to look 
for/the meta-data for the content
+        * @param prio          the amount to modify the priority of the 
entry/by how much should the priority of the content be changed (if it is 
found)?
+        * @param isLocal       is the request a local request? (true/false)
+        * @return                      the resulting content, null on error
+        */
+
+       public ContentBlock[] retrieveContent( HashCode160 query, ContentIndex 
ce, int prio, boolean isLocal )
+       {
+               ContentBlock[]  result;
+               byte[]                  tmp;
+               int                             len,i;
+
+               tmp=computeHighDB(query).readContent(query,ce,prio);
+               if (tmp==null) {
+                       stat_handle_lookup_notfound.inc();
+                       return null;
+                       }
+
+               if (tmp.length==VERY_LARGE_FILE) {
+                       result=(isLocal ? lfs.read(query) : 
lfs.randomRead(query,prio));
+                       if (result==null) {
+                               return null;
+                               }
+                       }
+               else {
+                       if ((tmp.length % ContentBlock.SIZE)!=0) {
+                               log(Level.SEVERE,"ERROR: retrieved content but 
size is not multiple of 1k!");
+                               return null;
+                               }
+
+                       result=new ContentBlock[tmp.length/ContentBlock.SIZE];
+                       for (i=0; i<result.length; i++) {
+                               result[i]=(ContentBlock) 
PersistentHelper.readFully(ContentBlock.class,ByteBuffer.wrap(tmp,i*ContentBlock.SIZE,ContentBlock.SIZE));
+                               }
+                       }
+
+               if (ce.fileNameIndex == 0) {
+                       switch (ce.type) {
+                               case ContentIndex.LOOKUP_TYPE_CHK:
+                               case ContentIndex.LOOKUP_TYPE_CHKS:
+                                       stat_handle_lookup_chk.inc();
+                                       break;
+                               case ContentIndex.LOOKUP_TYPE_3HASH:
+                                       stat_handle_lookup_3hash.inc();
+                                       break;
+                               case ContentIndex.LOOKUP_TYPE_SBLOCK:
+                                       stat_handle_lookup_sblock.inc();
+                                       break;
+                               case ContentIndex.LOOKUP_TYPE_SUPER:
+                                       // only gnunet-check will be doing 
this, don't bother to keep up stats
+                                       break;
+                               default:
+                                       log(Level.SEVERE,"Manager got 
unexpected content type : "+ce.type);
+                                       break;
+                               }
+                       return result;
+                       }
+
+               log(Level.SEVERE,"Retrieved content but index says on-demand 
encoded !");
+
+
+               stat_handle_lookup_ondemand.inc();
+
+               result=new ContentBlock[1];
+
+               len=encodeOnDemand(ce,result);
+               return (len>=0 ? result : null);
+       }
+
+       /**
+        * Explicitly delete content.  This method is not currently used
+        * (the manager discards data internally if we run out of space)
+        * but it could be used by a "gnunet-remove" application in the
+        * near future.
+        * <p>
+        *
+        * Explicitly remove some content from the database.
+        * Note that if multiple keywords correspond to the query, all are
+        * removed. To selectively remove one keyword, use retrieveContent and
+        * then insertContent if there are multiple results.
+        *
+        * @param query the query that corresponds to the block to remove
+        * @param bucket where to delete, <0 == autocompute/where to delete (<0 
== autocompute)
+        *               >=0 is used by gnunet-check.
+        * @return
+        */
+
+       public boolean removeContent( HashCode160 query, int bucket )
+       {
+               byte[]                  data;
+               ContentIndex    ce;
+               DBHandle                db;
+               boolean                 ok;
+
+               if (bucket < 0)
+                       db = computeHighDB(query);
+               else
+                       db = dbHandles[bucket];
+
+               ce=new ContentIndex();
+               data = db.readContent(query,ce,0);
+               if (data==null) {
+                       log(Level.WARNING,"removeContent ("+query.toHex()+") 
failed, readContent did not find content !");
+                       return false; /* not found! */
+                       }
+               if (data.length == VERY_LARGE_FILE) {
+                       // need to remove from VLS index, too -- this is 
unusual, but we can do it (should currently never happen in practice)
+                       ok = lfs.remove(query);
+                       if (!ok)
+                               log(Level.WARNING,"WARNING: removeContent 
failed on LFS content?");
+                       }
+
+               ok = db.unlink(query);
+               if (ok) {
+                       int delta;
+
+                       dbAvailableBlocks[computeBucketGlobal(query)] = 
DB_DIRTY_AVAILABLE;
+                       delta = estimateGlobalAvailableBlocks();
+                       if (delta < 0)  /* should not happen */
+                               delta = 0;
+                       stat_handle_spaceleft.set(delta);
+                       }
+               return ok;
+       }
+
+       /**
+        * Get some random contet.
+        * Return a random key from the database.
+        * @param ce output information about the key
+        * @return false on error, true if ok.
+        */
+
+       public boolean retrieveRandomContent( ContentIndex ce )
+       {
+               int     bucket;
+
+               bucket=Crypto.nextInt(buckets);
+               if (dbHandles[bucket] == null) {
+                       log(Level.SEVERE,"dbHandle at bucket "+bucket+" is 
null");
+                       return false;
+                       }
+               return dbHandles[bucket].getRandomContent(ce);
+       }
+
+       /**
+        * Iterator over all the queries in the database
+        * as needed by resizeBloomfilter.
+        *
+        * The idea is to use this code in the startup
+        * of the AFS module when the quota/memory limitations
+        * have changed and the bloomfilter needs to be
+        * resized. Note that the iterator is quite costly,
+        * but we can assume that the user is not going to
+        * change the configuration all the time :-).
+        * Iterator over all the queries in the database as needed by
+        * resizeBloomfilter (and gnunet-check).  Typical use:
+        * <code>
+        * state = makeDatabaseIteratorState();
+        * while (databaseIterator(state, x, y, z, t))
+        *   ...do something...
+        * </code>
+        *
+        * @param sss the iterator state as created by
+        *        makeDatabaseIteratorState
+        * @param hc next hash code (set)
+        * @param ce next content index (set)
+        * @param bucket where the data actually was (set)
+        * @param data corresponding data (set)
+        * @param datalen length of data (set)
+        * @return true if the iterator has filled in another element
+        *  from the database, false if there are no more elements
+        */
+
+       public boolean databaseIterator( IterState sss, HashCode160 hc, 
ContentIndex ce, int[] bucket, byte[][] data, int[] datalen )
+       {
+               IterState       st = sss;
+
+               try {
+                       st.sem.acquire();
+                       if (!st.hasNext) {
+                               st.sem=null;
+                               st.wsem=null;
+                               st.db_iterator.join();
+                               return false;
+                               }
+                       hc=(HashCode160) PersistentHelper.copy(st.next);
+                       ce=(ContentIndex) PersistentHelper.copy(st.ce);
+                       bucket[0] = st.bucket;
+                       data[0] = (byte[]) st.data;
+                       datalen[0] = st.len;
+                       st.wsem.release();
+                       return true;
+                       }
+               catch( InterruptedException x ) {
+                       err("",x);
+                       return false;
+                       }
+       }
+
+       /**
+        * Create the state required for a database iterator.
+        * Create the state required for a database iterator.  Calling this
+        * method requires to call databaseIterator with the state returned
+        * until "false" is returned.
+        * @return
+        */
+
+       public Object makeDatabaseIteratorState()
+       {
+               IterState  ret;
+
+               ret = new IterState();
+               ret.sem = new Semaphore(0);
+               ret.wsem = new Semaphore(1);
+
+               final IterState _ret = ret;
+
+               ret.db_iterator=new Task("DB-ITER",new AbstractAction() {
+                       public void perform()
+                       {
+                               iterator_helper(_ret);
+                       }
+                       });
+               ret.db_iterator.launch();
+               return ret;
+       }
+
+       /**
+        * Compute the database bucket id (for gnunet-check)
+        * This function is as crazy as it is since RIPE160 hashes do not seem
+        * to be quite random as we may want the to be...  So to get evenly
+        * distributed indices, we have to be a bit tricky. And no, there is
+        * high science but just a bit playing with the formula here.
+        * @param query
+        * @param maxBuckets
+        * @return
+        */
+
+       public int computeBucket( HashCode160 query, int maxBuckets )
+       {
+               HashCode160     qt;
+               long    r;
+
+               qt=HashCode160.create(PersistentHelper.toBytes(query));
+
+               r=((((query.getA()-qt.getA()) ^ (query.getB()-qt.getB()) ^ 
(query.getC()-qt.getC()) ^ (query.getD()-qt.getD()) ^ 
(query.getE()-qt.getE()))) >> 4);
+               r=(r & 0x00000000FFFFFFFFL);
+               return (int) (r % maxBuckets);
+       }
+
+       /**
+        * Use this, if initManager() has been executed and
+        * the global dbAPI has the correct bucket count
+        * @param query
+        * @return
+        */
+
+       public int computeBucketGlobal( HashCode160 query )
+       {
+               return computeBucket(query,
+                               buckets);
+       }
+
+       /**
+        * @param query
+        * @return
+        *
+        */
+
+       protected DBHandle computeHighDB( HashCode160 query )
+       {
+               return dbHandles[computeBucket(query,buckets)];
+       }
+
+       /**
+        * Open the AGE file and return the handle.
+        * @return
+        */
+
+       protected FileLocation getAgeFileHandle()
+       {
+               DirLocation             dir;
+               FileLocation            f;
+
+               dir=prefs.getDirLocation("AFS","AFSDIR");
+               if (dir==null) {
+                       log(Level.SEVERE,"Configuration file must specify 
directory for storage of AFS data in section AFS under AFSDIR.");
+                       return null;
+                       }
+               f=dir.getFile(AGEFILE);
+               f.create();
+               return f;
+       }
+
+       /**
+        * Cron-job that decreases the importance-level of all
+        * files by 1. Runs 'not very often'.
+        */
+
+       public void cronReduceImportance()
+       {
+               FileLocation    f;
+               MappedFile      m;
+
+               log(Level.INFO,"Enter cronReduceImportance");
+
+               MANAGER_age++;
+
+               f=getAgeFileHandle();
+               if (f==null) {
+                       log(Level.WARNING,"Could not open agefile !");
+                       return;
+                       }
+
+               m=f.openNew();
+               m.writeInt(MANAGER_age);
+               m.close();
+
+               log(Level.INFO,"Exit cronReduceImportance");
+       }
+
+       protected int readAge()
+       {
+               FileLocation    f;
+               MappedFile      m;
+               int                     age;
+
+               f=getAgeFileHandle();
+               if (f==null) {
+                       log(Level.WARNING,"Could not open agefile !");
+                       return 0;
+                       }
+
+               m=f.open();
+               age=m.asInt(0);
+               m.close();
+               return age;
+       }
+
+       /**
+        * Encode a block from a file on the drive, put the
+        * result in the result buffer (allocate) and return
+        * the size of the block.
+        * @param ce
+        * @param result
+        * @return
+        */
+
+       protected int encodeOnDemand( ContentIndex ce, ContentBlock[] result )
+       {
+               String fn;
+               int blen;
+               HashCode160 hc;
+               ContentBlock  iobuf;
+               FileChannel     fc;
+
+               // on-demand encoding mechanism
+               fn = fileIndex.getIndexedFileName(ce.fileNameIndex);
+               if (fn == null) {
+                       log(Level.SEVERE,"Database inconsistent! (index points 
to invalid offset ("+ce.fileNameIndex+")");
+                       return -1;
+                       }
+
+               iobuf = new ContentBlock();
+
+               try {
+                       fc=new RandomAccessFile(fn,"r").getChannel();
+
+                       if (TRACK_INDEXED_FILES) {
+                               PrintWriter     fp;
+                               FileLocation    scratch;
+                               DirLocation     afsDir;
+
+                               afsDir=prefs.getDirLocation("AFS","AFSDIR");
+                               if (afsDir!=null) {
+                                       scratch=afsDir.getFile(TRACKFILE);
+
+                                       fp=new PrintWriter(new 
FileOutputStream(scratch.getPath(),true),true);
+                                       fp.println(ce.fileNameIndex+" 
"+Scheduler.toSeconds(Scheduler.now()));
+                                       fp.close();
+                                       }
+                               else {
+                                       log(Level.SEVERE,"Configuration file 
must specify directory for storage of AFS data in section AFS under AFSDIR.");
+                                       }
+                               }
+
+                       try {
+                               fc.position(ce.fileOffset);
+                               if (fc.position()!=ce.fileOffset) {
+                                       log(Level.WARNING,"WARNING: unable to 
seek to "+ce.fileOffset+" in "+fn);
+                                       return -1;
+                                       }
+                               Arrays.fill(iobuf.content,(byte) 0);
+                               blen=fc.read(ByteBuffer.wrap(iobuf.content));
+                               if (blen <= 0) {
+                                       if(blen == 0)
+                                               log(Level.WARNING,"WARNING: 
read 0 bytes from "+fn);
+                                       else
+                                               log(Level.SEVERE,"ERROR: could 
not read file");
+                                       return -1;
+                                       }
+
+                               debug("Read "+blen+" bytes from "+fn+" for 
on-demand encoding at "+ce.fileOffset);
+                               }
+                       finally {
+                               fc.close();
+                               }
+                       }
+               catch( IOException x ) {
+                       err("I/O exception !",x);
+                       return -1;
+                       }
+
+               hc=HashCode160.create(PersistentHelper.toBytes(iobuf),0,blen);
+
+               result[0] = new ContentBlock();
+               if (!encoding.encryptContent(iobuf,hc,result[0])) {
+                       log(Level.SEVERE,"ERROR: encryption of content failed");
+                       return -1;
+                       }
+
+               hc=HashCode160.create(PersistentHelper.toBytes(result[0]));
+               debug("DEBUG: on-demand encoded content has query "+hc.toHex());
+               return ContentBlock.SIZE;
+       }
+
+       protected void iterator_helper_callback( HashCode160 key, ContentIndex 
ce, Object data, int dataLen, IterState sss )
+       {
+               try {
+                       sss.wsem.acquire();
+                       sss.next=(HashCode160) PersistentHelper.copy(key);
+                       sss.ce=(ContentIndex) PersistentHelper.copy(ce);
+                       sss.data = data;
+                       sss.len = dataLen;
+                       sss.sem.release();
+                       }
+               catch( InterruptedException x ) {
+                       err("",x);
+                       }
+       }
+
+       /**
+        * Thread that fetches the next entry from the database.
+        * The thread is created by makeDatabaseIteratorState
+        * and exits once we're through the database.
+        * @param sss
+        */
+
+       protected void iterator_helper( IterState sss )
+       {
+               int i;
+
+               try {
+                       sss.hasNext = true;
+                       for (i=0;i<buckets;i++) {
+                               sss.wsem.acquire();
+                               sss.bucket = i;
+                               sss.wsem.release();
+                               dbHandles[i].forEachEntry(new EntryCallback() {
+                                       public void onEntry( HashCode160 key, 
ContentIndex ce, Object data, int dataLen, Object closure )
+                                       {
+                                               
iterator_helper_callback(key,ce,data,dataLen,(IterState) closure);
+                                       }
+                                       },sss);
+                               }
+                       sss.wsem.acquire();
+                       sss.hasNext = false;
+                       sss.sem.release();
+                       }
+               catch( InterruptedException x ) {
+                       err("",x);
+                       }
+       }
+
+       protected boolean handleVLSResultSet( HashCode160 query, byte[] data, 
boolean[] duplicate )
+       {
+               ContentBlock[]  blocks;
+               int i,j;
+               int ret;
+
+               blocks=lfs.read(query);
+               if (blocks==null) {
+                       log(Level.WARNING,"lfs database inconsistent, trying to 
fix");
+                       if (computeHighDB(query).unlink(query)) {
+                               
dbAvailableBlocks[computeBucketGlobal(query)]=DB_DIRTY_AVAILABLE;
+                               }
+                       else {
+                               log(Level.WARNING,"Failed to fix lfs database 
inconsistency.");
+                               }
+                       return false;
+                       }
+
+               // check if the content is already present
+               ret=blocks.length;
+               for (i=0; i<ret; i++) {
+                       for (j=0; j<ContentBlock.SIZE && 
data[j]==blocks[i].content[j]; j++) {}
+
+                       if (j==ContentBlock.SIZE) {
+                               duplicate[0] = true;
+                               return true;
+                               }
+                       }
+               return lfs.append(query,(ContentBlock) 
PersistentHelper.readFully(ContentBlock.class,ByteBuffer.wrap(data)));
+       }
+
+       protected boolean migrateToVLS( byte[] old, int oldLen, HashCode160 
query, byte[] data, ContentIndex ce )
+       {
+               int             i;
+               boolean ret;
+               ContentBlock    block;
+
+               ret = true;
+
+               i = 0;
+               while ( (i < oldLen / ContentBlock.SIZE ) && ret) {
+                       block=(ContentBlock) 
PersistentHelper.readFully(ContentBlock.class,ByteBuffer.wrap(old,i*ContentBlock.SIZE,ContentBlock.SIZE));
+                       ret = lfs.append(query,block);
+                       i++;
+                       }
+
+               if (ret) {
+                       block=(ContentBlock) 
PersistentHelper.readFully(ContentBlock.class,ByteBuffer.wrap(data));
+                       ret = lfs.append(query,block);
+                       }
+
+               if (!ret) {
+                       lfs.remove(query);
+                       return ret;
+                       }
+               // put forwarding content (marked by size)
+               
ret=computeHighDB(query).writeContent(ce,data,0,VERY_LARGE_FILE); // data: 
random bits
+               
dbAvailableBlocks[computeBucketGlobal(query)]=DB_DIRTY_AVAILABLE;
+               return ret;
+       }
+
+       /**
+        * 3HASH and SBlock results require special treatment
+        * since multiple results are possible.
+        * @param query
+        * @param ce
+        * @param data
+        * @param oldLen
+        * @param duplicate
+        * @param len
+        * @param old
+        * @param oldImportance
+        * @return
+        */
+
+       protected boolean handle3HSBInsert( HashCode160 query, ContentIndex ce, 
byte[] data, int oldLen, boolean[] duplicate, int len, byte[] old, int 
oldImportance )
+       {
+               boolean ret;
+               byte[]  tmp;
+               int             n,i;
+
+               if (oldLen == -1) {
+                       // no old content, just write
+                       
dbAvailableBlocks[computeBucketGlobal(query)]=DB_DIRTY_AVAILABLE;
+                       return computeHighDB(query).writeContent(ce,data,0,len);
+                       }
+
+               // check if content is ALREADY in VLS store
+               if (oldLen == VERY_LARGE_FILE) {
+                       return handleVLSResultSet(query,data,duplicate);
+                       }
+
+               // Not VLS, check if the content is already present
+               for (n=0; n<oldLen/len; n++) {
+                       for (i=0; i<len && old[len*n+i]==data[i]; i++) {}
+                       if (i==len) {
+                               // content already there, abort
+                               duplicate[0] = true;
+                               return true;
+                               }
+                       }
+
+               // check if we need to *move* the content to VLS store
+               if (oldLen / ContentBlock.SIZE >= VERY_LARGE_SIZE) {
+                       return migrateToVLS(old, oldLen, query, data, ce);
+                       }
+
+               // else: default behavior: append
+
+               tmp = new byte[oldLen + len];
+               System.arraycopy(old,0,tmp,0,oldLen);
+               System.arraycopy(data,0,tmp,oldLen,len);
+               // Discussion: perhaps we should use eg max(a,b)? Otherwise n 
local inserts throws this through the ceiling...
+               // OTOH, if the same block was part of two files, it is really 
twice as important, so adding makes sense in some cases.
+               ce.importance = oldImportance + ce.importance;
+               ret = computeHighDB(query).writeContent(ce,tmp,0,oldLen+len);
+               
dbAvailableBlocks[computeBucketGlobal(query)]=DB_DIRTY_AVAILABLE;
+               return ret;
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/Migration.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/Migration.java     2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/Migration.java     2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,273 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs;
+
+import org.gnu.freeway.protocol.afs.esed2.*;
+import org.gnu.freeway.server.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import EDU.oswego.cs.dl.util.concurrent.*;
+
+import java.nio.*;
+import java.util.*;
+import java.util.logging.*;
+
+/**
+ * This module is responsible for pushing content out
+ * into the network.
+ */
+
+public class Migration extends LoggedObject
+{
+       /** use a 64-entry RCB buffer */
+       public static final int RCB_SIZE        =       128;
+
+       /** */
+       private CoreForProtocol coreAPI;
+
+       /** */
+       private StatusCallsService                      status;
+
+       /** */
+       private Stat            stat_handle_content_pushed;
+
+       /** Semaphore on which the RCB aquire thread waits if the RCB buffer is 
full. */
+       private Semaphore               aquireMoreSignal;
+
+       /** */
+       private Semaphore               doneSignal;
+
+       /** Lock for the RCB buffer. */
+       private Object                  lock;
+
+       /** Buffer with pre-fetched random content for migration. */
+       private ContentIndex[]  randomContentBuffer;
+
+       /** Highest index in RCB that is valid. */
+       private int                             rCBPos;
+
+       /** */
+       private Task                            gather_thread;
+
+       /** */
+       private Manager                 manager;
+
+
+       public Migration( Manager mgr )
+       {
+               super(true);
+               randomContentBuffer=new ContentIndex[RCB_SIZE];
+               manager=mgr;
+       }
+
+       public String toString()
+       {
+               return "xxx";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       /**
+        * Initialize the migration module.
+        * @param capi
+        */
+
+       public void init( CoreForProtocol capi )
+       {
+               Statistics      stats;
+
+               coreAPI=capi;
+
+               stats=capi.getApplication().getStatistics();
+               status=(StatusCallsService) 
capi.service(StatusCallsService.class);
+               stat_handle_content_pushed=stats.getHandle("# kb content pushed 
out as padding",Stat.VERBOSE);
+
+               Arrays.fill(randomContentBuffer,null);
+
+               aquireMoreSignal = new Semaphore(RCB_SIZE);
+               doneSignal = null;
+               lock=new Object();
+
+               gather_thread=new Task("MIGRATION-GATHER",new AbstractAction() {
+                       public void perform()
+                       {
+                               rcbAquire();
+                       }
+                       });
+               gather_thread.launch();
+
+               coreAPI.registerSendCallback(P2PChkResult.SIZE,new 
BufferFillCallback() {
+                       public boolean fillBuffer( HostIdentity receiver, 
ByteBuffer buf )
+                       {
+                               return activeMigrationCallback(receiver,buf);
+                       }
+                       });
+       }
+
+       /**
+        *
+        */
+
+       public void done()
+       {
+               coreAPI.unregisterSendCallback(P2PChkResult.SIZE,new 
BufferFillCallback() {
+                       public boolean fillBuffer( HostIdentity receiver, 
ByteBuffer buf )
+                       {
+                               return activeMigrationCallback(receiver,buf);
+                       }
+                       });
+
+               try {
+                       doneSignal = new Semaphore(0);
+                       aquireMoreSignal.release();
+                       doneSignal.acquire();
+                       aquireMoreSignal=null;
+                       doneSignal=null;
+                       lock=null;
+                       gather_thread.join();
+                       }
+               catch( InterruptedException x ) {
+                       err("",x);
+                       }
+       }
+
+       protected void rcbAquire()
+       {
+               ContentIndex    ce,cp;
+               boolean                 ok;
+
+               try {
+                       while (true) {
+                               aquireMoreSignal.acquire();
+                               if (doneSignal != null)
+                                       break;
+
+                               ce=new ContentIndex();
+                               ok = manager.retrieveRandomContent(ce);
+                               if (ok)
+                                       if 
(ce.type==ContentIndex.LOOKUP_TYPE_3HASH || 
ce.type==ContentIndex.LOOKUP_TYPE_SUPER)
+                                               ok = false;     // can not 
migrate these
+                               if (ok) {
+                                       cp=(ContentIndex) 
PersistentHelper.copy(ce);
+                                       synchronized(lock) {
+                                               randomContentBuffer[rCBPos++] = 
cp;
+                                               }
+                                       }
+                               else {
+                                       int load = status.getCPULoad();
+                                       if (load < 10)
+                                               load = 10;
+                                       
Scheduler.sleep(Scheduler.seconds(load/5));     // the higher the load, the 
longer the sleep, but at least 2 seconds
+                                       aquireMoreSignal.release();     // send 
myself signal to go again !
+                                       }
+                               }
+                       doneSignal.release();
+                       }
+               catch( InterruptedException x ) {
+                       err("",x);
+                       }
+       }
+
+       /**
+        * Select content for active migration.  Takes the best match from the
+        * randomContentBuffer (if the RCB is non-empty) and returns it.
+        * @param receiver
+        * @param ce
+        *
+        * @return false if the RCB is empty
+        */
+
+       protected boolean selectMigrationContent( HostIdentity receiver, 
ContentIndex ce )
+       {
+               int dist;
+               int minDist;
+               int minIdx;
+               int i;
+
+               minIdx = -1;
+               minDist = -1;   // max
+
+               synchronized(lock) {
+                       for (i=0;i<rCBPos;i++) {
+                               dist = 
receiver.distance(randomContentBuffer[i].hash);
+                               if (dist < minDist) {
+                                       minIdx = i;
+                                       minDist = dist;
+                                       }
+                               }
+                       if (minIdx == -1) {
+                               return false;
+                               }
+                       ce=(ContentIndex) 
PersistentHelper.copy(randomContentBuffer[minIdx]);
+                       randomContentBuffer[minIdx] = 
randomContentBuffer[--rCBPos];
+                       randomContentBuffer[rCBPos] = null;
+                       }
+               aquireMoreSignal.release();
+               return true;
+       }
+
+       /**
+        * Callback method for pushing content into the network.
+        * The method chooses either a "recently" deleted block
+        * or content that has a hash close to the receiver ID
+        * (randomized to guarantee diversity, unpredictability
+        * etc.).<p>
+        *
+        * @param receiver the receiver of the message
+        * @param buf
+        * @return the number of bytes written to
+        *   that buffer (must be a positive number).
+        */
+
+       protected boolean activeMigrationCallback( HostIdentity receiver, 
ByteBuffer buf )
+       {
+               ContentIndex    ce;
+
+               ce=new ContentIndex();
+               while (buf.remaining()>P2PChkResult.SIZE) {
+                       if (!selectMigrationContent(receiver,ce)) {
+                               return true;    // nothing selected, that's the 
end
+                               }
+
+                       // append it !
+                       if (!buildCHKReply(ce,buf)) {
+                               return false;   // abort early after any error
+                               }
+
+                       stat_handle_content_pushed.inc();
+                       }
+               return true;
+       }
+
+       /**
+        * Build a CHK reply message for some content
+        * selected for migration.
+        * @param ce
+        * @param buf
+        * @return OK on success, false on error
+        */
+
+       protected boolean buildCHKReply( ContentIndex ce, ByteBuffer buf )
+       {
+               ContentBlock[]  data;
+
+               if (ce.type==ContentIndex.LOOKUP_TYPE_3HASH || 
ce.type==ContentIndex.LOOKUP_TYPE_SUPER) {
+                       return false;
+                       }
+
+               data=manager.retrieveContent(ce.hash,ce,0,false);       // low 
prio! & should not matter for CHK anyway
+               if (data==null) {       // can happen if we're concurrently 
inserting, _should be_ rare but is OK !
+                       return false;
+                       }
+               if (data.length != ContentBlock.SIZE) {
+                       log(Level.WARNING,"BuildCHKReply got unsuitable block 
from db (len="+data.length+",type="+ce.type+")");
+                       return false;
+                       }
+               return PersistentHelper.write(data[0],buf);
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/MySQLHandle.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/MySQLHandle.java   2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/MySQLHandle.java   2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,1030 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs;
+
+import org.gnu.freeway.protocol.afs.esed2.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+import java.sql.*;
+import java.util.logging.*;
+
+/**
+ * Handle for a high-level database (mysql, simple)
+ * mysql wrapper
+ *
+ * API for the "high-level" database libraries.
+ * Equivalent to what is specified in high_backend.h.
+ * A header specifying the interfaces that each (high-level) database backend 
(gdbm, tdb, mysql, etc.) must provide.
+ * Database: MySQL
+ *
+ * HIGHLIGHTS
+ *
+ * Pros
+ * + On up-to-date hardware where mysql can be used comfortably, this
+ *   module will have better performance than the other db choices
+ *   (according to our tests).
+ * + Its often possible to recover the mysql database from internal
+ *   inconsistencies. The other db choices do not support repair
+ *   (gnunet-check cannot fix problems internal to the dbmgr!).
+ *   For example, we have seen several cases where power failure
+ *   has ruined a gdbm database beyond repair.
+ * Cons
+ * - Memory usage (Comment: "I have 1G and it never caused me trouble")
+ * - Manual setup
+ *
+ * MANUAL SETUP INSTRUCTIONS
+ *
+ * 1) in /etc/gnunet.conf, set
+ *    <pre>
+ *
+ *    DATABASETYPE = "mysql"
+ *
+ *    </pre>
+ * 2) Then access mysql as root,
+ *    <pre>
+ *
+ *    # mysql -u root -p
+ *
+ *    </pre>
+ *    and do the following. [You should replace $USER with the username
+ *    that will be running the gnunetd process].
+ *    <pre>
+ *
+         CREATE DATABASE gnunet;
+         GRANT select,insert,update,delete,create,alter,drop
+                ON gnunet.* TO address@hidden;
+         SET PASSWORD FOR address@hidden('$the_password_you_like');
+         FLUSH PRIVILEGES;
+ *
+ *    </pre>
+ * 3) In the $HOME directory of $USER, create a ".my.cnf" file
+ *    with the following lines
+ *    <pre>
+
+         [client]
+         user=$USER
+         password=$the_password_you_like
+
+ *    </pre>
+ *
+ * Thats it. Note that .my.cnf file is a security risk unless its on
+ * a safe partition etc. The $HOME/.my.cnf can of course be a symbolic
+ * link. Even greater security risk can be achieved by setting no
+ * password for $USER.  Luckily $USER has only priviledges to mess
+ * up GNUnet's tables, nothing else (unless you give him more,
+ * of course).<p>
+ *
+ * 4) Still, perhaps you should briefly try if the DB connection
+ *    works. First, login as $USER. Then use,
+ *
+ *    <pre>
+ *    # mysql -u $USER
+ *    mysql> use gnunet
+ *    </pre>
+ *
+ *    If you get the message &quot;Database changed&quot; it probably works.
+ *
+ *    [If you get &quot;ERROR 2002: Can't connect to local MySQL server
+ *     through socket '/tmp/mysql.sock' (2)&quot; it may be resolvable by
+ *     &quot;ln -s /var/run/mysqld/mysqld.sock /tmp/mysql.sock&quot;
+ *     so there may be some additional trouble depending on your mysql setup.]
+ *
+ * REPAIRING TABLES
+ *
+ * - Its probably healthy to check your tables for inconsistencies
+ *   every now and then.
+ * - If you get odd SEGVs on gnunetd startup, it might be that the mysql
+ *   databases have been corrupted.
+ * - The tables can be verified/fixed in two ways;
+ *   1) by shutting down mysqld (mandatory!) and running
+ *   # myisamchk -r *.MYI
+ *   in /var/lib/mysql/gnunet/ (or wherever the tables are stored).
+ *   Another repair command is "mysqlcheck". The usable command
+ *   may depend on your mysql build/version. Or,
+ *   2) by executing
+ *   mysql> REPAIR TABLE data1024of
+ *   for each table in the gnunet database (USE gnunet; SHOW TABLES;)
+ *
+ * If you have problems related to the mysql module, your best
+ * friend is probably the mysql manual. The first thing to check
+ * is that mysql is basically operational, that you can connect
+ * to it, create tables, issue queries etc.
+ *
+ * Database: MySQL
+ *
+ * NOTE: This db module does NOT work with mysql v3.23.49 due to a bug
+ * in mysql.  All later versions should be fine, including the 4.0.x
+ * series. Current devel version is 4.0.16-log on debian/unstable.
+ *
+ * HIGHLIGHTS
+ *
+ * Pros
+ * + On up-to-date hardware where mysql can be used comfortably, this
+ *   module will have better performance than the other db choices
+ *   (according to our tests).
+ * + Its often possible to recover the mysql database from internal
+ *   inconsistencies. The other db choices do not support repair
+ *   (gnunet-check cannot fix problems internal to the dbmgr!).
+ *   For example, we have seen several cases where power failure
+ *   has ruined a gdbm database beyond repair.
+ * Cons
+ * - Memory usage (Comment: "I have 1G and it never caused me trouble")
+ * - Manual setup
+ *
+ * MANUAL SETUP INSTRUCTIONS
+ *
+ * 1) in /etc/gnunet.conf, set
+ *    <pre>
+ *
+ *    DATABASETYPE = "mysql"
+ *
+ *    </pre>
+ * 2) Then access mysql as root,
+ *    <pre>
+ *
+ *    # mysql -u root -p
+ *
+ *    </pre>
+ *    and do the following. [You should replace $USER with the username
+ *    that will be running the gnunetd process].
+ *    <pre>
+ *
+ CREATE DATABASE gnunet;
+ GRANT select,insert,update,delete,create,alter,drop
+ ON gnunet.* TO address@hidden;
+ SET PASSWORD FOR address@hidden('$the_password_you_like');
+ FLUSH PRIVILEGES;
+ *
+ *    </pre>
+ * 3) In the $HOME directory of $USER, create a ".my.cnf" file
+ *    with the following lines
+ *    <pre>
+
+ [client]
+ user=$USER
+ password=$the_password_you_like
+
+ *    </pre>
+ *
+ * Thats it. Note that .my.cnf file is a security risk unless its on
+ * a safe partition etc. The $HOME/.my.cnf can of course be a symbolic
+ * link. Even greater security risk can be achieved by setting no
+ * password for $USER.  Luckily $USER has only priviledges to mess
+ * up GNUnet's tables, nothing else (unless you give him more,
+ * of course).<p>
+ *
+ * 4) Still, perhaps you should briefly try if the DB connection
+ *    works. First, login as $USER. Then use,
+ *
+ *    <pre>
+ *    # mysql -u $USER
+ *    mysql> use gnunet
+ *    </pre>
+ *
+ *    If you get the message &quot;Database changed&quot; it probably works.
+ *
+ *    [If you get &quot;ERROR 2002: Can't connect to local MySQL server
+ *     through socket '/tmp/mysql.sock' (2)&quot; it may be resolvable by
+ *     &quot;ln -s /var/run/mysqld/mysqld.sock /tmp/mysql.sock&quot;
+ *     so there may be some additional trouble depending on your mysql setup.]
+ *
+ * REPAIRING TABLES
+ *
+ * - Its probably healthy to check your tables for inconsistencies
+ *   every now and then.
+ * - If you get odd SEGVs on gnunetd startup, it might be that the mysql
+ *   databases have been corrupted.
+ * - The tables can be verified/fixed in two ways;
+ *   1) by shutting down mysqld (mandatory!) and running
+ *   # myisamchk -r *.MYI
+ *   in /var/lib/mysql/gnunet/ (or wherever the tables are stored).
+ *   Another repair command is "mysqlcheck". The usable command
+ *   may depend on your mysql build/version. Or,
+ *   2) by executing
+ *   mysql> REPAIR TABLE data1024of
+ *   for each table in the gnunet database (USE gnunet; SHOW TABLES;)
+ *
+ * EFFICIENCY ISSUES
+ *
+ * If you suffer from too slow index/insert speeds,
+ * you might try to define /etc/gnunet.conf option
+ *
+ *   [AFS]
+ *   MYSQL_DELAYED = YES
+ *
+ * for small efficiency boost. The option will let MySQL bundle multiple
+ * inserts before actually writing them to disk. You shouldn't use this
+ * option unless you're an (my)sql expert and really know what you're doing.
+ * Especially, if you run into any trouble due to this, you're on your own.
+ *
+ * PROBLEMS?
+ *
+ * If you have problems related to the mysql module, your best
+ * friend is probably the mysql manual. The first thing to check
+ * is that mysql is basically operational, that you can connect
+ * to it, create tables, issue queries etc.
+ */
+
+public class MySQLHandle extends LoggedObject implements DBHandle
+{
+       private static long                     ramdomSpentTime;
+       private static int                              randomCalls;
+
+       /** */
+       private Connection      dbf;
+
+       private String  tableName;
+       /** database index */
+//     private int;
+
+       /** total number of databases */
+//     private int;
+
+       /** */
+       private Object          DATABASE_Lock_;
+
+       /** which column contains the Avg_row_length in SHOW TABLE STATUS 
resultset */
+       private int                     avgLength_ID;
+
+       /** use potentially unsafe delayed inserts? */
+       private boolean         useDelayed;
+
+
+       /**
+        * Popularity of _known_ 3hash keywords can be tracked by creating
+        * the following table and enabling TRACK_3HASH_QUERIES. In addition,
+        * you'll have to fill the table with <name,hash> pairs yourself
+        * (not provided). Tracking is not generally recommended as
+        * it may harm your deniability.
+        *
+        USE gnunet;
+        CREATE TABLE `dictionary` (
+        `name` tinyblob NOT NULL,
+        `hash` varchar(40) binary NOT NULL default '',
+        `hits` smallint(5) unsigned NOT NULL default '0',
+        PRIMARY KEY  (`hash`),
+        UNIQUE KEY `unique_name` (`name`(32))
+        ) TYPE=MyISAM
+        *
+        */
+
+       public static final boolean TRACK_3HASH_QUERIES =       false;
+
+
+       public MySQLHandle()
+       {
+               super(true);
+               avgLength_ID=-1;
+       }
+
+       public String toString()
+       {
+               return "xxx";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public boolean open( Prefs prefs, String backend, String name )
+       {
+               Connection                      conn;
+               Statement                       stmt;
+               ResultSet                       rset;
+               ResultSetMetaData       meta;
+               StringBuffer            sql;
+               boolean                         found;
+               int     i;
+
+               tableName="data"+name+"of"+backend;
+
+               try {
+                       
Class.forName(prefs.getString("AFS","MYSQL_DRIVER",null));
+                       conn=DriverManager.getConnection(
+                               
"jdbc:mysql://"+prefs.getString("AFS","MYSQL_HOST",null)+"/gnunet",
+                               prefs.getString("AFS","MYSQL_USER",null),
+                               prefs.getString("AFS","MYSQL_PASSWORD",null)
+                               );
+                       conn.setAutoCommit(false);
+                       }
+               catch( SQLException x ) {
+                       err("Unable to init MySQL !",x);
+                       return false;
+                       }
+               catch( ClassNotFoundException x ) {
+                       err("Unable to init MySQL !",x);
+                       return false;
+                       }
+
+               dbf=conn;
+
+               useDelayed=prefs.testString("AFS","MYSQL_DELAYED","YES");
+               DATABASE_Lock_=new Object();
+
+               found=false;
+               try {
+                       stmt=dbf.createStatement();
+                       try {
+                               sql=new StringBuffer();
+                               sql.append("CREATE TABLE IF NOT EXISTS 
"+tableName+" (");
+                               sql.append("  hash tinyblob NOT NULL 
default'',");
+                               sql.append("  priority int(11) NOT NULL default 
0,");
+                               sql.append("  type tinyint NOT NULL default 
0,");
+                               sql.append("  fileIndex smallint NOT NULL 
default 0,");
+                               sql.append("  fileOffset int(11) NOT NULL 
default 0,");
+                               sql.append("  doubleHash tinyblob NOT NULL 
default '',");
+                               sql.append("  content mediumblob NOT NULL 
default '',");
+                               sql.append("  PRIMARY KEY (hash(20)),");
+                               sql.append("  KEY priority (priority)");
+                               sql.append(") TYPE=MyISAM");
+                               stmt.executeUpdate(sql.toString());
+
+                               // find out which column contains the avg row 
length field and assume
+                               // that mysqld always gives it in the same 
order across calls :)
+                               sql.setLength(0);
+                               sql.append("SHOW TABLE STATUS ");
+                               sql.append("FROM gnunet ");
+                               sql.append("LIKE '"+tableName+"'");
+
+                               rset=stmt.executeQuery(sql.toString());
+                               if (rset.next()) {
+                                       meta=rset.getMetaData();
+
+                                       for (i=0; i<meta.getColumnCount() && 
!found; i++) {
+                                               if 
(meta.getColumnName(i+1).equals("Avg_row_length")) {
+                                                       avgLength_ID=i;
+                                                       found=true;
+                                                       }
+                                               }
+                                       if (!found) {
+                                               log(Level.SEVERE,"ERROR: mysql 
Avg_row_length not found in SHOW TABLE STATUS");
+                                               return false;
+                                               }
+                                       }
+                               rset.close();
+
+                               if (avgLength_ID == -1) {
+                                       log(Level.SEVERE,"FATAL: could not find 
row avg_row_length");
+                                       return false;
+                                       }
+                               }
+                       finally {
+                               stmt.close();
+                               }
+                       }
+               catch( SQLException x ) {
+                       err("Failed to init !",x);
+                       return false;
+                       }
+               return true;
+       }
+
+       public void close()
+       {
+               DATABASE_Lock_=null;
+               try {
+                       dbf.close();
+                       dbf=null;
+                       }
+               catch( SQLException x ) {
+                       err("Failed to close connection !",x);
+                       }
+       }
+
+       public void drop()
+       {
+               Statement               stmt;
+               StringBuffer    sql;
+
+               try {
+                       sql=new StringBuffer();
+                       sql.append("DROP TABLE "+tableName);
+
+                       stmt=dbf.createStatement();
+                       try {
+                               stmt.executeUpdate(sql.toString());
+                               }
+                       finally {
+                               stmt.close();
+                               }
+                       }
+               catch( SQLException x ) {
+                       err("Failed to close connection !",x);
+                       }
+
+               close();
+       }
+
+       public byte[] readContent( HashCode160 query, ContentIndex ce, int prio 
)
+       {
+               byte[]                  tmp,result;
+               ResultSet               rset;
+               Statement               stmt;
+               StringBuffer    sql;
+               int                             _prio=-1;
+
+               result=null;
+               synchronized(DATABASE_Lock_) {
+                       sql=new StringBuffer();
+                       sql.append("SELECT content, type, priority, doubleHash, 
fileOffset, fileIndex ");
+                       sql.append("FROM "+tableName+" ");
+                       sql.append("WHERE hash='"+escape(query)+"'");
+
+                       try {
+                               
stmt=dbf.createStatement(ResultSet.TYPE_FORWARD_ONLY,ResultSet.CONCUR_UPDATABLE);
+                               try {
+                                       rset=stmt.executeQuery(sql.toString());
+
+                                       if (rset.next()) {
+                                               result=rset.getBytes(1);
+
+                                               ce.type = rset.getInt(2);
+                                               ce.importance = rset.getInt(3);
+                                               if 
(ce.type==ContentIndex.LOOKUP_TYPE_3HASH) {
+                                                       tmp=rset.getBytes(4);
+                                                       if (tmp!=null && 
tmp.length==HashCode160.SIZE) {
+                                                               
ce.hash=(HashCode160) 
PersistentHelper.readFully(HashCode160.class,ByteBuffer.wrap(tmp));
+                                                               }
+                                                       }
+                                               else {
+                                                       ce.hash=(HashCode160) 
PersistentHelper.copy(query);
+                                                       }
+                                               ce.fileOffset=rset.getInt(5);
+                                               ce.fileNameIndex=rset.getInt(6);
+
+                                               if (prio != 0) {
+                                                       //todo: debugguer le 
UPDATE qui devrait se faire ICI
+//                                                     
rset.updateInt(3,rset.getInt(3)+prio);
+//                                                     rset.updateRow();
+
+                                                       
_prio=rset.getInt(3)+prio;
+                                                       }
+                                               }
+
+                                       rset.close();
+                                       }
+                               finally {
+                                       stmt.close();
+                                       }
+
+                               if (TRACK_3HASH_QUERIES) {
+                                       if 
(ce.type==ContentIndex.LOOKUP_TYPE_3HASH) {
+                                               stmt=dbf.createStatement();
+                                               try {
+                                                       
stmt.executeUpdate("UPDATE dictionary SET hits=hits+1 WHERE 
hash='"+escape(query)+"'");
+                                                       }
+                                               finally {
+                                                       stmt.close();
+                                                       }
+                                               }
+                                       }
+
+                               //tempo (a supprimer apres debug)
+                               if (_prio!=0) {
+                                       sql=new StringBuffer();
+                                       sql.append("UPDATE "+tableName+" ");
+                                       sql.append("SET priority="+_prio+" ");
+                                       sql.append("WHERE 
hash='"+escape(query)+"'");
+
+                                       stmt=dbf.createStatement();
+                                       try {
+                                               
stmt.executeUpdate(sql.toString());
+                                               }
+                                       finally {
+                                               stmt.close();
+                                               }
+                                       }
+                               }
+                       catch( SQLException x ) {
+                               err("Failed to read content !",x);
+                               result=null;
+                               }
+                       }
+               return result;
+       }
+
+       public boolean writeContent( ContentIndex ce, byte[] block, int offset, 
int length )
+       {
+               Statement               stmt;
+               HashCode160             tripleHash;
+               StringBuffer    sql;
+               String                  doubleHash,escapedBlock,escapedHash;
+
+               synchronized(DATABASE_Lock_) {
+                       if (ce.type== ContentIndex.LOOKUP_TYPE_3HASH) {
+                               
tripleHash=HashCode160.create(PersistentHelper.toBytes(ce.hash));
+                               escapedHash=escape(tripleHash);
+                               doubleHash = escape(ce.hash);
+                               }
+                       else {
+                               doubleHash = null;
+                               escapedHash=escape(ce.hash);
+                               }
+
+                       escapedBlock=escape(block,offset,length);
+
+                       sql=new StringBuffer();
+                       sql.append("REPLACE "+(useDelayed ? "DELAYED" : "")+" 
INTO "+tableName+" ");
+                       
sql.append("(content,hash,priority,fileOffset,fileIndex,doubleHash,type)");
+                       sql.append(" VALUES (");
+                       sql.append("'"+(length > 0 ? escapedBlock : "")+"',");
+                       sql.append("'"+escapedHash+"',");
+                       sql.append("'"+ce.importance+"',");
+                       sql.append("'"+ce.fileOffset+"',");
+                       sql.append("'"+ce.fileNameIndex+"',");
+                       sql.append("'"+(doubleHash!=null ? doubleHash : 
"")+"',");
+                       sql.append(ce.type);
+                       sql.append(")");
+
+                       try {
+                               stmt=dbf.createStatement();
+                               try {
+                                       stmt.executeUpdate(sql.toString());
+                                       }
+                               finally {
+                                       stmt.close();
+                                       }
+                               }
+                       catch( SQLException x ) {
+                               err("Failed to write content !",x);
+
+                               //todo: a supprimer apres debug
+                               
log(Level.SEVERE,Utils.toString(block,offset,offset+length,"SEQ."));
+                               return false;
+                               }
+                       }
+               return true;
+       }
+
+       /**
+        * Get the number of entries in the database.
+        * Get the number of entries in the database.
+        *
+        * @return -1 on error, otherwise the number of entries
+        */
+
+       public int countContentEntries()
+       {
+               Statement               stmt;
+               ResultSet               rset;
+               StringBuffer    sql;
+               int                             count;
+
+               count=0;
+               synchronized(DATABASE_Lock_) {
+                       sql=new StringBuffer();
+                       sql.append("SELECT count(*) ");
+                       sql.append("FROM "+tableName);
+
+                       try {
+                               stmt=dbf.createStatement();
+                               try {
+                                       rset=stmt.executeQuery(sql.toString());
+                                       if (rset.next()) {
+                                               count=rset.getInt(1);
+                                               }
+                                       rset.close();
+                                       }
+                               finally {
+                                       stmt.close();
+                                       }
+                               }
+                       catch( SQLException x ) {
+                               err("Failed to get minimum priority !",x);
+                               count=-1;
+                               }
+                       }
+               return count;
+       }
+
+       /**
+        * Get the lowest priority of content in the DB.
+        * Get the lowest priority of content in the store.
+        * Get the lowest priority value of all content in the store.
+        *
+        * @return the lowest priority
+        */
+
+       public int getMinimumPriority()
+       {
+               Statement               stmt;
+               ResultSet               rset;
+               StringBuffer    sql;
+               int                             minPrio;
+
+               minPrio=0;
+               synchronized(DATABASE_Lock_) {
+                       sql=new StringBuffer();
+                       sql.append("SELECT MIN(priority) ");
+                       sql.append("FROM "+tableName);
+
+                       try {
+                               stmt=dbf.createStatement();
+                               try {
+                                       rset=stmt.executeQuery(sql.toString());
+                                       if (rset.next()) {
+                                               minPrio=rset.getInt(1);
+                                               }
+                                       rset.close();
+                                       }
+                               finally {
+                                       stmt.close();
+                                       }
+                               }
+                       catch( SQLException x ) {
+                               err("Failed to get minimum priority !",x);
+                               }
+                       }
+               return minPrio;
+       }
+
+       /**
+        * Call a method for each key in the database and
+        * call the callback method on it.
+        * Call a method for each key in the database and
+        * call the callback method on it.
+        *
+        * @param callback the callback method
+        * @param data second argument to all callback calls
+        * @return the number of items stored in the content database
+        */
+
+       public int forEachEntry( EntryCallback callback, Object data )
+       {
+               byte[]                  result,tmp;
+               Statement               stmt;
+               ResultSet               rset;
+               ContentIndex    ce;
+               HashCode160             h;
+               StringBuffer    sql;
+               int                             count;
+
+               count=0;
+               synchronized(DATABASE_Lock_) {
+                       sql=new StringBuffer();
+                       sql.append("SELECT content, type, priority, doubleHash, 
fileOffset, fileIndex, hash ");
+                       sql.append("FROM "+tableName);
+
+                       try {
+                               stmt=dbf.createStatement();
+                               try {
+                                       rset=stmt.executeQuery(sql.toString());
+                                       while (rset.next()) {
+                                               ce=new ContentIndex();
+
+                                               result=rset.getBytes(1);
+                                               ce.type=rset.getInt(2);
+                                               ce.importance=rset.getInt(3);
+                                               if 
(ce.type==ContentIndex.LOOKUP_TYPE_3HASH) {
+                                                       tmp=rset.getBytes(4);
+                                                       if (tmp!=null && 
tmp.length==HashCode160.SIZE) {
+                                                               
ce.hash=(HashCode160) 
PersistentHelper.readFully(HashCode160.class,ByteBuffer.wrap(tmp));
+                                                               }
+                                                       }
+                                               else {
+                                                       tmp=rset.getBytes(7);
+                                                       ce.hash=(HashCode160) 
PersistentHelper.readFully(HashCode160.class,ByteBuffer.wrap(tmp));
+                                                       }
+                                               ce.fileOffset=rset.getInt(5);
+                                               ce.fileNameIndex=rset.getInt(6);
+
+                                               tmp=rset.getBytes(7);
+                                               h=(HashCode160) 
PersistentHelper.readFully(HashCode160.class,ByteBuffer.wrap(tmp));
+
+                                               
callback.onEntry(h,ce,result,(result!=null ? result.length : 0),data);
+                                               count++;
+                                               }
+                                       rset.close();
+                                       }
+                               finally {
+                                       stmt.close();
+                                       }
+                               }
+                       catch( SQLException x ) {
+                               err("Failed to iterate over entries !",x);
+                               count=-1;
+                               }
+                       }
+               return count;
+       }
+
+       /**
+        * Return a random key from the database (just the key, not the
+        * content!).
+        * Get a random content block from MySQL database.
+        * Tries to use indexes efficiently.
+        *
+        * Code supplied by H. Pagenhardt
+        *
+        * @param ce the meta-data of the random content (set)/output 
information about the key
+        * @return true on success, false on error
+        */
+
+       public boolean getRandomContent( ContentIndex ce )
+       {
+               byte[]                  tmp;
+               Statement               stmt;
+               ResultSet               rset;
+               HashCode160             hash;
+               StringBuffer    sql;
+               long                    startTime,endTime;
+               boolean                 found;
+
+               startTime=Scheduler.now();
+
+               synchronized(DATABASE_Lock_) {
+                       found=false;
+
+                       hash=HashCode160.random();
+
+                       sql=new StringBuffer();
+                       sql.append("SELECT hash, type, priority, fileOffset, 
fileIndex ");
+                       sql.append("FROM "+tableName+" ");
+                       sql.append("WHERE hash >= '"+escape(hash)+"' ");
+                       sql.append("AND (type = 
"+ContentIndex.LOOKUP_TYPE_CHK+" OR type = "+ContentIndex.LOOKUP_TYPE_CHKS+") 
");
+                       sql.append("LIMIT 1");
+
+                       try {
+                               stmt=dbf.createStatement();
+                               try {
+                                       rset=stmt.executeQuery(sql.toString());
+
+                                       found=rset.next();
+                                       if (!found) {
+                                               rset.close();
+
+                                               sql.setLength(0);
+                                               sql.append("SELECT 
hash,type,priority,fileOffset,fileIndex ");
+                                               sql.append("FROM "+tableName+" 
");
+                                               sql.append("WHERE hash >= '' ");
+                                               sql.append("AND (type = 
"+ContentIndex.LOOKUP_TYPE_CHK+" OR type = "+ContentIndex.LOOKUP_TYPE_CHKS+") 
");
+                                               sql.append("LIMIT 1");
+
+                                               
rset=stmt.executeQuery(sql.toString());
+                                               found=rset.next();
+                                               }
+
+                                       if (found) {
+                                               tmp=rset.getBytes(1);
+                                               ce.hash=(HashCode160) 
PersistentHelper.readFully(HashCode160.class,ByteBuffer.wrap(tmp));
+                                               ce.type=rset.getInt(2);
+                                               ce.importance=rset.getInt(3);
+                                               ce.fileOffset=rset.getInt(4);
+                                               ce.fileNameIndex=rset.getInt(5);
+                                               }
+
+                                       rset.close();
+                                       }
+                               finally {
+                                       stmt.close();
+                                       }
+                               }
+                       catch( SQLException x ) {
+                               err("Failed to get random content !",x);
+                               found=false;
+                               }
+                       }
+
+               endTime=Scheduler.now();
+               randomCalls++;
+               ramdomSpentTime+=(endTime-startTime);
+               debug("getRandomContent spent total 
"+Scheduler.toMillis(ramdomSpentTime)+"ms / "+randomCalls+" calls.");
+
+               if (!found) {
+                       log(Level.FINEST,"MySQL random didn't find anything !");
+                       }
+               return found;
+       }
+
+       /**
+        * Delete low-priority content from the database
+        * Deletes some least important content
+        *
+        * @param count the number of entries to delete/the number of 1kb 
blocks to free
+        * @param callback method to call on each deleted entry/method to call 
on each deleted item
+        * @param closure extra argument to callback
+        * @return true on success, false on error
+        */
+
+       public boolean deleteContent( int count, EntryCallback callback, Object 
closure )
+       {
+               HashCode160[]   deleteThese;
+               byte[]                  data,tmp;
+               ContentIndex    ce;
+               Statement               stmt;
+               ResultSet               rset;
+               StringBuffer    sql;
+               int                             k;
+
+               synchronized(DATABASE_Lock_) {
+                       try {
+                               stmt=dbf.createStatement();
+                               try {
+                                       // collect hashes to delete
+                                       sql=new StringBuffer();
+                                       sql.append("SELECT hash FROM 
"+tableName+" ");
+                                       sql.append("ORDER BY priority ASC LIMIT 
"+count);
+
+                                       deleteThese=new HashCode160[count];
+
+                                       k=0;
+                                       rset=stmt.executeQuery(sql.toString());
+                                       while (rset.next()) {
+                                               tmp=rset.getBytes(1);
+                                               deleteThese[k++]=(HashCode160) 
PersistentHelper.readFully(HashCode160.class,ByteBuffer.wrap(tmp));
+                                               }
+                                       rset.close();
+                                       }
+                               finally {
+                                       stmt.close();
+                                       }
+
+                               // delete collected hashes
+                               count=k;
+                               for(k=0; k<count; k++) {
+                                       ce=new ContentIndex();
+
+                                       data=readContent(deleteThese[k],ce,0);
+                                       if (data!=null && callback!=null) {
+                                               
callback.onEntry(deleteThese[k],ce,data,data.length,closure);
+                                               }
+
+                                       sql.setLength(0);
+                                       sql.append("DELETE FROM "+tableName+" 
");
+                                       sql.append("WHERE 
hash='"+escape(deleteThese[k])+"'");
+
+                                       stmt=dbf.createStatement();
+                                       try {
+                                               
stmt.executeUpdate(sql.toString());
+                                               }
+                                       finally {
+                                               stmt.close();
+                                               }
+                                       }
+                               }
+                       catch( SQLException x ) {
+                               err("Failed to delete content !",x);
+                               return false;
+                               }
+                       }
+               return true;
+       }
+
+       /**
+        * Estimate how many blocks can be stored in the DB
+        * before the quota is reached.
+        * Estimate how many blocks can be stored in the DB
+        * before the quota is reached.
+        *
+        * NOTE: this function can not be performed relying on
+        * Data_length+Index_length from "SHOW TABLE STATUS" because
+        * those values seem not to be decreasing in real time.
+        * On mysql 4.0.16, Avg_row_len seems to be updating in real
+        * time w.r.t. insertions and deletions.
+        *
+        * @param quota the number of kb available for the DB
+        * @return number of blocks left
+        */
+
+       public int estimateAvailableBlocks( int quota )
+       {
+               Statement               stmt;
+               ResultSet               rset;
+               StringBuffer    sql;
+               long                    avgRowLen,rowsInTable;
+               int                             kbUsed;
+
+               avgRowLen=-1;
+               rowsInTable=0;
+               synchronized(DATABASE_Lock_) {
+                       try {
+                               stmt=dbf.createStatement();
+                               try {
+                                       // find out average row length in bytes
+                                       // FIXME: probably unnecessary to check 
avg row length every time
+                                       sql=new StringBuffer();
+                                       sql.append("SHOW TABLE STATUS ");
+                                       sql.append("FROM gnunet ");
+                                       sql.append("LIKE '"+tableName+"'");
+
+                                       rset=stmt.executeQuery(sql.toString());
+                                       if (!rset.next()) {
+                                               log(Level.SEVERE,"Unable to 
estimate available blocks (query \""+sql+"\" had no results).");
+                                               return 0;
+                                               }
+
+                                       if 
(avgLength_ID>=rset.getMetaData().getColumnCount() || avgLength_ID<0) {
+                                               log(Level.SEVERE,"Unable to 
estimate available blocks (query \""+sql+"\" had 
"+rset.getMetaData().getColumnCount()+" rows, expected > "+avgLength_ID+").");
+                                               return 0;
+                                               }
+
+                                       avgRowLen=rset.getLong(avgLength_ID+1);
+                                       if (rset.wasNull()) {
+                                               avgRowLen=-1;
+                                               }
+                                       
debug("******************************************* avgRowLen = "+avgRowLen);
+                                       rset.close();
+
+                                       if (avgRowLen<0) {
+                                               log(Level.SEVERE,"FATAL: mysql 
claimed avgRowLen<0 (or failed)");
+                                               return 0;
+                                               }
+
+                                       // find number of entries (rows)
+                                       sql.setLength(0);
+                                       sql.append("SELECT count(*) ");
+                                       sql.append("FROM "+tableName);
+
+                                       rset=stmt.executeQuery(sql.toString());
+
+                                       //todo: if (!rset.next()) {}
+                                       if (rset.next()) {
+                                               rowsInTable=rset.getLong(1);
+                                               }
+                                       rset.close();
+                                       }
+                               finally {
+                                       stmt.close();
+                                       }
+                               }
+                       catch( SQLException x ) {
+                               err("Failed to estimate available blocks !",x);
+                               return 0;
+                               }
+                       }
+
+               kbUsed=(int) ((rowsInTable * avgRowLen) / 1024);
+               debug("Estimate content available 
(q="+quota+",u="+kbUsed+",rem="+(quota-kbUsed)+")");
+               return quota - kbUsed;
+       }
+
+       /**
+        * Free space in the database by removing an entry.
+        *
+        * @param name the key of the entry to remove/the query of the entry to 
remove/hashcode for the block to be deleted
+        * @return false on error, true if ok.
+        * Free space in the database by removing one block
+        */
+
+       public boolean unlink( HashCode160 name )
+       {
+               Statement               stmt;
+               StringBuffer    sql;
+
+               synchronized(DATABASE_Lock_) {
+                       sql=new StringBuffer();
+                       sql.append("DELETE FROM "+tableName+" ");
+                       sql.append("WHERE hash='"+escape(name)+"'");
+
+                       try {
+                               stmt=dbf.createStatement();
+                               try {
+                                       stmt.executeUpdate(sql.toString());
+                                       }
+                               finally {
+                                       stmt.close();
+                                       }
+                               }
+                       catch( SQLException x ) {
+                               err("Failed to unlink "+name.toHex()+" !",x);
+                               return false;
+                               }
+                       }
+               return true;
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public static String escape( HashCode160 h )
+       {
+               return escape(PersistentHelper.toBytes(h));
+       }
+
+       public static String escape( byte[] b )
+       {
+               return escape(b,0,b.length);
+       }
+
+       public static String escape( byte[] b, int offset, int length )
+       {
+               StringBuffer    buf;
+               int                             i;
+               char                    c;
+
+               buf=new StringBuffer();
+               for (i=0; i<length; i++) {
+                       c=(char) (b[offset+i] & 0x000000ff);
+                       switch (c) {
+                               case '\'': buf.append("''"); break;
+                               case '\\': buf.append("\\\\"); break;
+                               default: buf.append(c); break;
+                               }
+                       }
+               return buf.toString();
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/Policy2.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/Policy2.java       2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/Policy2.java       2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,117 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs;
+
+import org.gnu.freeway.server.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+
+/**
+ * Policy interface. This is the interface to the C part of the policy.
+ * resource allocation (storage space, routing) implementation
+ */
+
+public class Policy2 extends Object
+{
+       /** Drop the query if & with this bitmask is 0 */
+       public static final int QUERY_DROPMASK  =       0x00FF0000;
+
+       /** Send answer if local files match */
+       public static final int QUERY_ANSWER    =       0x00020000;
+
+       /** Forward the query, priority is encoded in QUERY_PRIORITY_BITMASK */
+       public static final int QUERY_FORWARD   =       0x00040000;
+
+       /** Indirect the query (use this as the originating node) */
+       public static final int QUERY_INDIRECT  =       0x00080000;
+
+       /** Maximum priority to use (apply this bitmask to the QueryPolicy) */
+       public static final int QUERY_PRIORITY_BITMASK  =       0x0000FFFF;
+
+       /** Bandwidth value of an (effectively) 0-priority query. */
+       public static final double      QUERY_BANDWIDTH_VALUE   =       0.01;
+
+       /** Bandwidth value of a 0-priority content (must be fairly high 
compared to query since content is
+        typically significantly larger -- and more valueable since it can take 
many queries to get one piece of content). */
+       public static final double      CONTENT_BANDWIDTH_VALUE =       0.8;
+
+       private CoreForProtocol coreAPI;
+
+
+       public Policy2( CoreForProtocol capi )
+       {
+               super();
+               coreAPI=capi;
+       }
+
+       public String toString()
+       {
+               return "xxx";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       /**
+        * A query has been received. The question is, if it should be forwarded
+        * and if with which priority. Routing decisions(to whom) are to be 
taken elsewhere.
+        * <p>
+        *
+        * @param sender the host sending us the query
+        * @param priority the priority the query had when it came in, may be 
an arbitrary number if the
+        *        sender is malicious! Cap by trustlevel first!
+        * @return binary encoding: QUERY_XXXX constants
+        */
+
+       public int evaluateQuery( HostIdentity sender, int priority )
+       {
+               int netLoad = ((StatusCallsService) 
coreAPI.service(StatusCallsService.class)).getNetworkLoadUp();
+
+               if (netLoad < 50)
+                       return 0 /* minimum priority */ |
+                       QUERY_ANSWER | QUERY_FORWARD | QUERY_INDIRECT;
+               /* charge! */
+               priority = - coreAPI.changeTrust(sender, -priority);
+               if (netLoad < 100 + priority)
+                       return ((priority) & QUERY_DROPMASK) |
+                       QUERY_ANSWER | QUERY_FORWARD | QUERY_INDIRECT;
+               else if (netLoad < 100 + 10 * priority)
+                       return ((priority) & QUERY_DROPMASK) |
+                       QUERY_ANSWER | QUERY_FORWARD;
+               else if (netLoad < 100)
+                       return ((priority) & QUERY_DROPMASK) | QUERY_ANSWER;
+               else
+                       return 0; /* drop entirely */
+       }
+
+       /**
+        * Some content dropped by. We may want to store it locally, or not.
+        * The policy adjusts the priority and returns the effective
+        * importance for the content.
+        *
+        * @param hc the query
+        * @param priority of the original query
+        * @return -1 if the content should be dropped, the
+        *   priority for keeping it otherwise/-1 if the content should not be 
replicated,
+        *   otherwise the new priority for the lookup database
+        */
+
+       public int evaluateContent( HashCode160 hc, int priority )
+       {
+               int distance;
+               int j;
+
+               distance = coreAPI.getIdentity().distance(hc);
+               /* compute 'log' of distance */
+               j = 16;
+               while (distance > 0) {
+                       distance = distance>>1;
+                       j--;
+               }
+               if (j < 0)
+                       return -1;
+               return priority * j;
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/QueryManager.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/QueryManager.java  2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/QueryManager.java  2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,694 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs;
+
+import org.gnu.freeway.protocol.afs.esed2.*;
+import org.gnu.freeway.server.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * The query manager is responsible for queueing queries
+ * forwarding of queries
+ *
+ * The query manager is responsible for queueing queries.  Queued
+ * queries are used to fill buffers (instead of using noise). The QM
+ * is also responsible for selecting the initial set of nodes that
+ * will receive the query. For a good choice, it keeps track of which
+ * nodes were recently hot in answering queries. Some randomness is
+ * preserved to ensure that we potentially find a better path.<p>
+ *
+ * Routing is an incredibly hard problem, so please consider
+ * consulting with other gnunet-developers before making any
+ * significant changes here, even if you have CVS write access.
+ */
+
+public class QueryManager extends LoggedObject implements AFSConstants
+{
+       /** Set to 'YES' to play with 0.6.2b (and earlier) behavior. */
+       private static final boolean    TRADITIONAL_SELECTION   =       false;
+
+       /** of how many outbound queries do we simultaneously keep track ? */
+       public static final int QUERY_RECORD_COUNT      =       512;
+
+       public static final double      FINAL_GOAL      =       3.0;
+
+       /** how many peers should be selected on average? */
+       public static final double      SELECT_GOAL     =       2.0;
+
+       /** How much is a query worth 'in general' (even if there is no trust 
relationship between the peers !).
+        Multiplied by the number of queries in the request.  20 is for '20 
bytes / hash', so this is kind of the base unit. */
+       public static final int BASE_QUERY_PRIORITY     =       20;
+
+       /** Array of the queries we are currently sending out. */
+       private QueryRecord[]   queries;
+       private int                             zePos;
+
+       /** Mutex for all query manager structures. */
+       private Object                  queryManagerLock;
+
+       private int             Xaverage        = 0;
+       private double  Xweight = 4.0;
+       private double  Yweight = 4.0;
+       private Scheduler               scheduler;
+       private ScheduledTask           ageTask;
+       private CoreForProtocol coreAPI;
+
+       /** Linked list tracking reply statistics.  Synchronize access using 
the queryManagerLock! */
+       private ReplyTrackData  rtdList;
+
+
+       public QueryManager()
+       {
+               super(true);
+               queries=new QueryRecord[QUERY_RECORD_COUNT];
+               ageTask=new ScheduledTask("AGE-RTD",new 
EvalAction(this,"ageRTD"),Scheduler.MINUTES_2);
+       }
+
+       public String toString()
+       {
+               return "Query manager";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       /**
+        * Initialize the query management.
+        * @param capi
+        */
+
+       public void init( CoreForProtocol capi )
+       {
+               int     i;
+
+               coreAPI=capi;
+               scheduler=capi.getApplication().getScheduler();
+
+               for (i=0;i<QUERY_RECORD_COUNT;i++) {
+                       queries[i]=new QueryRecord();
+                       queries[i].expires = 0; /* all expired */
+                       queries[i].msg = null;
+               }
+               queryManagerLock = coreAPI.getConnectionModuleLock();
+               coreAPI.registerSendCallback(P2PQuery.SIZE+HashCode160.SIZE,new 
BufferFillCallback() {
+                       public boolean fillBuffer( HostIdentity receiver, 
ByteBuffer buf )
+                       {
+                               return fillInQuery(receiver,buf);
+                       }
+               });
+
+               scheduler.addJob(ageTask,Scheduler.MINUTES_2);
+       }
+
+       /**
+        * Shutdown query management.
+        */
+
+       public void done()
+       {
+               int     i;
+
+               scheduler.deleteJob(ageTask);
+
+               for (i=0; i<QUERY_RECORD_COUNT; i++) {
+                       queries[i]=null;
+                       }
+
+               
coreAPI.unregisterSendCallback(P2PQuery.SIZE+HashCode160.SIZE,new 
BufferFillCallback() {
+                       public boolean fillBuffer( HostIdentity receiver, 
ByteBuffer buf )
+                       {
+                               return fillInQuery(receiver,buf);
+                       }
+               });
+       }
+
+       /**
+        * Take a query and forward it to the appropriate number of nodes 
(depending on load, queue, etc).
+        *
+        * @param msg   the query
+        * @param origin        where did the query come from?
+        * @param client        where did the query come from? (if it was a 
client)
+        */
+
+       public void forwardQuery( P2PQuery msg, HostIdentity origin, CSSession 
client )
+       {
+               long now;
+               QueryRecord  qr;
+               QueryRecord             dummy=new QueryRecord();
+               long oldestTime;
+               long expirationTime;
+               int oldestIndex;
+               int i;
+               boolean noclear = false;
+
+               debug("DEBUG: forwarding query for "+msg.getQuery(0).toHex()+" 
with ttl "+msg.getTTL());
+
+               now=Scheduler.now();
+
+               synchronized(queryManagerLock) {
+                       oldestIndex = -1;
+                       expirationTime = now + msg.getTTL();
+                       oldestTime = expirationTime;
+
+                       for (i=0; i<QUERY_RECORD_COUNT; i++) {
+                               if (queries[i].expires<oldestTime) {
+                                       oldestTime=queries[i].expires;
+                                       oldestIndex=i;
+                                       }
+
+                               if (queries[i].msg==null) {
+                                       continue;
+                                       }
+
+                               if (    queries[i].msg.sameQueries(msg)) {
+                                       /* We have exactly this query pending 
already. Replace existing query! */
+
+                                       oldestIndex = i;
+                                       if ( (queries[i].expires > now - 4 * 
TTL_DECREMENT) && /* not long expired */ (Crypto.nextInt(4) != 0) ) {
+                                               /* do not clear the bitmap 
describing which peers we have
+                                                forwarded the query to 
already; but do this only with high
+                                                probability since we may want 
to try again if the query is
+                                                retransmitted lots (this can 
happen if this is the only
+                                                query; we may forward it to 
all connected peers and get no
+                                                reply.  If the initiator keeps 
retrying, we want to
+                                                eventually forward it again.
+
+                                                Note that the initial 
probability here (0.6.0/0.6.1) was
+                                                very low (1:64), which is far 
too low considering that the
+                                                clients do an exponential 
back-off.  The rule is a pure
+                                                optimization, and as such the 
probability that we
+                                                eventually forward must be 
significant.  25% seems to work
+                                                better... (extra-note: in 
small testbeds, the problem
+                                                is bigger than in a larger 
network where the case that
+                                                a query stays in the QM 
indefinitely might be much more
+                                                rare; so don't just trust a 
micro-scale benchmark when
+                                                trying to figure out an 
'optimal' threshold). */
+                                               noclear = true;
+
+                                               debug("DEBUG: QM noclear rule 
applied!\n");
+                                               }
+                                       break; /* this is it, do not scan for 
other 'oldest' entries */
+                                       }
+                               }
+
+                       if (oldestIndex == -1) {
+                               debug("DEBUG: keeping track of 
"+QUERY_RECORD_COUNT+" queries already, will not manage this one");
+                               qr = dummy;
+                               }
+                       else {
+                               qr = queries[oldestIndex];
+                               qr.msg = null;
+                               }
+                       qr.expires = expirationTime;
+                       qr.transmissionCount = 0;
+                       qr.msg=(P2PQuery) PersistentHelper.copy(msg);
+
+                       if (!noclear) {
+                               qr.clearBits();
+                               qr.noTarget=(origin != null ? origin : 
coreAPI.getIdentity());
+                               qr.localClient=client;
+                               qr.totalDistance=0;
+
+                               if (TRADITIONAL_SELECTION) {
+                                       
qr.activeConnections=coreAPI.forAllConnectedNodes(new PerNodeCallback() {
+                                               public void callback( 
HostIdentity identity, Object data )
+                                               {
+                                                       
selectActiveNodes(identity,(QueryRecord) data);
+                                               }
+                                               },qr);
+
+                                       if (qr.activeConnections>0) {
+                                               selectActiveNodes(null, qr); /* 
give SAN chance to adjust weight at the end of the iteration! */
+
+                                               for 
(i=BITMAP_SIZE*4/qr.activeConnections;i>=0;i--) {
+                                                       
qr.setBit(Crypto.nextInt(BITMAP_SIZE)*8); /* select 4 random nodes */
+                                                       }
+
+                                               
coreAPI.forAllConnectedNodes(new PerNodeCallback() {
+                                                       public void callback( 
HostIdentity identity, Object data )
+                                                       {
+                                                               
selectRandomNodes(identity,(QueryRecord) data);
+                                                       }
+                                                       },qr);
+                                               }
+                                       }
+                               else {
+                                       qr.rankings = new int[8*BITMAP_SIZE];
+                                       
qr.activeConnections=coreAPI.forAllConnectedNodes(new PerNodeCallback() {
+                                               public void callback( 
HostIdentity identity, Object data )
+                                               {
+                                                       
newSelectCode(identity,(QueryRecord) data);
+                                               }
+                                               },qr);
+
+                                       /* actual selection, proportional to 
rankings assigned by newSelectCode ... */
+                                       {
+                                               int j;
+                                               long rankingSum = 0;
+                                               for (i=0;i<8*BITMAP_SIZE;i++)
+                                                       rankingSum += 
qr.rankings[i];
+
+                                               if ( (rankingSum != 0) && /* 
doppelt haelt besser */ (qr.activeConnections > 0) ) {
+                                                       /* select 4 peers for 
forwarding */
+                                                       for (i=0;i<4;i++) {
+                                                               long sel;
+                                                               long pos;
+                                                               sel = 
Crypto.nextLong(rankingSum);
+                                                               pos = 0;
+                                                               for 
(j=0;j<8*BITMAP_SIZE;j++) {
+                                                                       pos += 
qr.rankings[j];
+                                                                       if (pos 
> sel) {
+                                                                               
qr.setBit(j);
+                                                                               
break;
+                                                                               
}
+                                                                       }
+                                                               }
+                                                       }
+                                               else {
+                                                       /* no bias available, 
go random! */
+                                                       if 
(qr.activeConnections > 0) {
+                                                               for 
(i=4*BITMAP_SIZE*8/qr.activeConnections-1;i>=0;i--) {
+                                                                       
qr.setBit(Crypto.nextInt(BITMAP_SIZE)*8); /* select 4 random nodes */
+                                                                       }
+                                                               }
+                                                       }
+                                       }
+
+                                       qr.rankings = null;
+                                       }
+                               /* now forward to a couple of selected nodes */
+                               coreAPI.forAllConnectedNodes(new 
PerNodeCallback() {
+                                       public void callback( HostIdentity 
identity, Object data )
+                                       {
+                                               
sendToSelected(identity,(QueryRecord) data);
+                                       }
+                                       },qr);
+                               }
+                       }
+       }
+
+       /**
+        * Stop transmitting a certain query (we don't route it anymore or we 
have learned the answer).
+        * @param query
+        */
+
+       public void dequeueQuery( HashCode160 query )
+       {
+               int                     i,j;
+               QueryRecord     qr;
+
+               synchronized(queryManagerLock) {
+                       for (i=0;i<QUERY_RECORD_COUNT;i++) {
+                               qr = queries[i];
+                               if( qr.msg != null ) {
+                                       for 
(j=NUMBER_OF_QUERIES(qr.msg)-1;j>=0;j--) {
+                                               if 
(query.equals(qr.msg.getQuery(j))) {
+                                                       qr.expires = 0; /* 
expire NOW! */
+                                                       break;
+                                               }
+                                       }
+                               }
+                       }
+               }
+       }
+
+       /**
+        * How many queries are in the given query (header given).
+        * @param p
+        * @return
+        */
+
+       protected int NUMBER_OF_QUERIES( Persistent p )
+       {
+               return ((p.getByteSize()-P2PQuery.SIZE)/HashCode160.SIZE);
+       }
+
+       /**
+        * Map the id to an index into the bitmap array.
+        * @param id
+        * @return
+        */
+
+       protected int getIndex( HostIdentity id )
+       {
+               int index;
+
+               index = coreAPI.computeIndex(id);
+               if (index > 8*BITMAP_SIZE)
+                       index = index & (8*BITMAP_SIZE-1);
+               return index;
+       }
+
+       /**
+        * Callback method for filling buffers. This method is invoked by the
+        * core if a message is about to be send and there is space left for a
+        * 3QUERY.  We then search the pending queries and fill one (or more)
+        * in if possible.
+        *
+        * Note that the same query is not transmitted twice to a peer and that
+        * queries are not queued more frequently than 2 TTL_DECREMENT.
+        *
+        * @param receiver the receiver of the message
+        * @param buf
+        * @return the number of bytes written to
+        *   that buffer (must be a positive number).
+        */
+
+       protected boolean fillInQuery( HostIdentity receiver, ByteBuffer buf )
+       {
+               long    now;
+               int             start,delta;
+
+               now=Scheduler.now();
+               synchronized(queryManagerLock) {
+                       start = zePos;
+                       delta = 0;
+                       while (buf.remaining()>P2PQuery.SIZE+HashCode160.SIZE) {
+                               if (queries[zePos].expires>now &&
+                                               
!queries[zePos].getBit(getIndex(receiver)) &&
+                                               buf.remaining()>= 
queries[zePos].msg.getByteSize()
+                               ) {
+
+                                       debug("Adding 
"+NUMBER_OF_QUERIES(queries[zePos].msg)+" queries 
("+queries[zePos].msg.getQuery(0).toHex()+") to outbound buffer of 
"+receiver.getName());
+
+                                       
queries[zePos].setBit(getIndex(receiver));
+                                       if 
(!PersistentHelper.write(queries[zePos].msg,buf)) {
+                                               return false;
+                                       }
+                                       queries[zePos].sendCount++;
+                                       delta += 
queries[zePos].msg.getByteSize();
+                               }
+                               zePos++;
+                               if (zePos >= QUERY_RECORD_COUNT)
+                                       zePos = 0;
+                               if (zePos == start)
+                                       break;
+                       }
+               }
+               return true;
+       }
+
+       /**
+        * A "PerNodeCallback" method that selects the most
+        * active nodes for forwarding (with some randomness).
+        *
+        * Also computes the sum of the distances of the
+        * other node IDs as a side-effect.
+        * @param id
+        * @param qr
+        */
+
+       protected void selectActiveNodes( HostIdentity id, QueryRecord qr )
+       {
+               int trf;
+
+               if (id==null) {
+                       // special call for weight adjustment
+                       
Xweight=Xweight/Math.sqrt((qr.transmissionCount+1.0)/(SELECT_GOAL+1.0));
+                       return;
+               }
+
+               trf = coreAPI.queryBPMfromPeer(id);
+               Xaverage  = (Xaverage * 15 + trf) / 16; /* approximate average 
over time...*/
+
+               debug("Selecting from active nodes "+id.getName()+" as 
rand("+(trf+1)+") > rand("+(Xaverage+1)+")*"+Xweight+".");
+
+               /* Forward the query to peers that have on average lots of 
bandwidth
+                assigned to them (and are thus recently very productive...) */
+               if (Crypto.nextInt(trf+1)>Crypto.nextInt(Xaverage+1)*Xweight) {
+                       qr.setBit(getIndex(id));
+                       qr.transmissionCount++;
+
+                       debug("Node selected for forwarding due to activity: 
"+id.getName()+".");
+               }
+               else {
+                       qr.totalDistance+=id.distance(qr.msg.getQuery(0));
+               }
+       }
+
+       /**
+        * A "PerNodeCallback" method that selects some
+        * random nodes (biased according to proximity).
+        * @param id
+        * @param qr
+        */
+
+       protected void selectRandomNodes( HostIdentity id, QueryRecord qr )
+       {
+               int avgDist;
+               int peerDist;
+
+               if (id == null) {
+                       Yweight = Yweight / Math.sqrt( 
(qr.transmissionCount+1.0) / (FINAL_GOAL+1.0) );
+                       return;
+               }
+
+               if (qr.totalDistance==0 || qr.activeConnections==0) {
+                       return; /* activeConnections should never be 0, if 
totalDistance is 0, this is caused by us selectAcitveNodes selecting all nodes 
already */
+               }
+
+               if (qr.getBit(getIndex(id))) {
+                       return;
+               }
+
+               avgDist = (int) (qr.totalDistance / qr.activeConnections);
+               peerDist = id.distance(qr.msg.getQuery(0));
+
+               debug("Selecting at random from active nodes "+id.getName()+" 
using rand("+(peerDist+1)+")*"+qr.transmissionCount+"*"+Yweight+" < 
rand("+(avgDist+1)+").");
+
+               /* Select 2 random nodes for forwarding. Give preference
+                to nodes that are close.  Division by 4 to ensure
+                that we are in the range of a signed int. */
+               if (Crypto.nextInt(1+peerDist) * qr.transmissionCount * Yweight 
< Crypto.nextInt(1+avgDist) ) {
+                       debug("Node selected for forwarding from random set: 
"+id.getName()+".");
+
+                       qr.setBit(getIndex(id));
+                       qr.transmissionCount++;
+               }
+       }
+
+       protected void newSelectCode( HostIdentity id, QueryRecord qr )
+       {
+               ReplyTrackData  pos;
+               ResponseList  rp;
+               int ranking = 0;
+               int distance;
+
+               pos = rtdList;
+               while (pos != null) {
+                       if ((qr.localClient==null && 
pos.queryOrigin.equals(qr.noTarget)) || qr.localClient == pos.localQueryOrigin) 
{
+                               break;
+                               }
+                       pos=pos.next;
+                       }
+
+               if (pos != null) {
+                       rp = pos.responseList;
+                       while (rp != null) {
+                               if (rp.responder.equals(id)) {
+                                       break;
+                                       }
+                               rp = rp.next;
+                               }
+
+                       if (rp != null) {
+                               if (rp.responseCount < 0xFFFF)
+                                       ranking = 0x7FFF * rp.responseCount;
+                               else
+                                       ranking = 0x7FFFFFF;
+                               }
+                       }
+
+               distance=id.distance(qr.msg.getQuery(0));
+               if (distance <= 0)
+                       distance = 1;
+
+               ranking += 0xFFFF / (1 + Crypto.nextInt(distance));
+               ranking += Crypto.nextInt(0xFF); /* small random chance for 
everyone */
+               qr.rankings[getIndex(id)]=ranking;
+       }
+
+       /**
+        * A "PerNodeCallback" method that forwards
+        * the query to the selected nodes.
+        * @param id
+        * @param qr
+        */
+
+       protected void sendToSelected( HostIdentity id, QueryRecord qr )
+       {
+               if (id.equals(qr.noTarget)) {
+                       return;
+                       }
+
+               if (qr.getBit(getIndex(id))) {
+                       debug("Queueing query "+qr.msg.getQuery(0).toHex()+" in 
buffer of selected node "+id.getName()+".");
+
+                       coreAPI.sendToNode(id,
+                                       qr.msg,
+                                       BASE_QUERY_PRIORITY 
*(qr.msg.getPriority()*2+NUMBER_OF_QUERIES(qr.msg)),
+                                       (int) TTL_DECREMENT);
+                       }
+       }
+
+       /**
+        * Cron job that ages the RTD data and that frees memory for entries 
that reach 0.
+        */
+
+       public void ageRTD()
+       {
+               ReplyTrackData  pos;
+               ReplyTrackData  prev;
+               ResponseList  rpos;
+               ResponseList  rprev;
+
+               synchronized(queryManagerLock) {
+                       prev = null;
+                       pos = rtdList;
+                       while (pos != null) {
+                               /* after 10 minutes, always discard everything 
*/
+                               if (pos.lastReplyReceived < 
Scheduler.toSeconds(Scheduler.now()) - 600) {
+                                       while (pos.responseList != null) {
+                                               rpos = pos.responseList;
+                                               pos.responseList = rpos.next;
+                                       }
+                               }
+                               /* otherwise, age reply counts */
+                               rprev = null;
+                               rpos = pos.responseList;
+                               while (rpos != null) {
+                                       rpos.responseCount = rpos.responseCount 
/ 2;
+                                       if (rpos.responseCount == 0) {
+                                               if (rprev == null)
+                                                       pos.responseList = 
rpos.next;
+                                               else
+                                                       rprev.next = rpos.next;
+                                               if (rprev == null)
+                                                       rpos = pos.responseList;
+                                               else
+                                                       rpos = rprev.next;
+                                               continue;
+                                       }
+                               }
+                               /* if we have no counts for a peer anymore,
+                                free pos entry */
+                               if (pos.responseList == null) {
+                                       if (prev == null)
+                                               rtdList = pos.next;
+                                       else
+                                               prev.next = pos.next;
+                                       if (prev == null)
+                                               pos = rtdList;
+                                       else
+                                               pos = prev.next;
+                                       continue;
+                               }
+                               prev = pos;
+                               pos = pos.next;
+                               }
+                       }
+       }
+
+       /**
+        * We received a reply from 'responder' to a query received from
+        * 'origin' (or 'localOrigin').  Update reply track data!
+        *
+        * @param origin only valid if localOrigin == null
+        * @param localOrigin origin if query was initiated by local client
+        * @param responder peer that send the reply
+        */
+
+       public void updateResponseData( HostIdentity origin, CSSession 
localOrigin, HostIdentity responder )
+       {
+               ReplyTrackData  pos;
+               ReplyTrackData  prev;
+               ResponseList  rpos;
+               ResponseList  rprev;
+
+               if (responder == null)
+                       return; /* we don't track local responses */
+
+               synchronized(queryManagerLock) {
+                       pos = rtdList;
+                       prev = null;
+                       while (pos != null) {
+                               if (pos.localQueryOrigin==localOrigin && 
(localOrigin!=null || origin.equals(pos.queryOrigin))) {
+                                       break; /* found */
+                                       }
+                               prev = pos;
+                               pos = pos.next;
+                       }
+                       if (pos == null) {
+                               pos = new ReplyTrackData();
+                               pos.next = null;
+                               pos.localQueryOrigin = localOrigin;
+                               if (localOrigin == null)
+                                       pos.queryOrigin = origin;
+                               pos.responseList = null;
+                               if (prev == null)
+                                       rtdList = pos;
+                               else
+                                       prev.next = pos;
+                       }
+                       pos.lastReplyReceived=(int) 
Scheduler.toSeconds(Scheduler.now());
+                       rpos = pos.responseList;
+                       rprev = null;
+                       while (rpos != null) {
+                               if (responder.equals(rpos.responder)) {
+                                       rpos.responseCount++;
+                                       return;
+                                       }
+                               rprev = rpos;
+                               rpos = rpos.next;
+                               }
+
+                       rpos = new ResponseList();
+                       rpos.responseCount = 1;
+                       rpos.responder = responder;
+                       rpos.next = null;
+                       if (rprev == null)
+                               pos.responseList = rpos;
+                       else
+                               rprev.next = rpos;
+                       }
+       }
+}
+
+/**
+ * Linked list of peer ids with number of replies received.
+ */
+
+class ResponseList extends Object
+{
+       public HostIdentity     responder;
+       public int                      responseCount;
+       public ResponseList     next;
+}
+
+/**
+ * Structure for tracking from which peer we got valueable replies for which 
clients / other peers.
+ */
+
+class ReplyTrackData extends Object
+{
+       /** For which client does this entry track replies ? Only valid if 
localQueryOrigin == null! */
+       public HostIdentity             queryOrigin;
+
+       /** For which client does this entry track replies ? */
+       public CSSession                localQueryOrigin;
+
+       /** Time at which we received the last reply for this client (in 
seconds). Used to discard old entries eventually. */
+       public int                              lastReplyReceived;
+
+       /** Linked list of peers that responded, with number of responses. */
+       public ResponseList             responseList;
+
+       /** Linked list. */
+       public ReplyTrackData           next;
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/QueryRecord.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/QueryRecord.java   2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/QueryRecord.java   2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,91 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs;
+
+import org.gnu.freeway.protocol.afs.esed2.*;
+
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.CSSession;
+
+import java.util.*;
+
+/**
+ * In this struct, we store information about a
+ * query that is being send from the local node to
+ * optimize the sending strategy.
+ */
+
+public class QueryRecord extends Object implements AFSConstants
+{
+       /** How often did we send this query so far ? */
+       public int                              sendCount;
+
+       /** The message that we are sending. */
+       public P2PQuery msg;
+
+       /** Bit-map marking the hostIndices (computeIndex) of nodes that have 
received this query already.
+        Note that the bit-map has a maximum size, if the index is 
out-of-bounds, it is hashed into
+        the smaller size of the bitmap. There may thus be nodes with identical 
indices, in that case, only one of
+        the nodes will receive the query. */
+       public byte[]                   bitmap;
+
+       /** When do we stop forwarding (!) this query ? */
+       public long                             expires;
+
+       /** How many nodes were connected when we initated sending this query ? 
*/
+       public int                              activeConnections;
+
+       /** What is the total distance of the query to the connected nodes ? */
+       public long                             totalDistance;
+
+       /** To how many peers has / will this query be transmitted ? */
+       public int                              transmissionCount;
+
+       /** To which peer will we never send this message ? */
+       public HostIdentity             noTarget;
+
+       /** Sender identity, for a local client. */
+       public CSSession                localClient;
+
+       /** How important would it be to send the message to all peers in this 
bucket ? */
+       public int[]                            rankings;
+
+
+       public QueryRecord()
+       {
+               super();
+               bitmap=new byte[BITMAP_SIZE];
+               msg=new P2PQuery();
+       }
+
+       public String toString()
+       {
+               return "Query record";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public boolean getBit( int bit )
+       {
+               byte    theBit;
+
+               theBit=(byte) (1 << (bit & 7));
+               return (bitmap[bit>>3] & theBit)!=0;
+       }
+
+       public void setBit( int bit )
+       {
+               byte    theBit;
+
+               theBit=(byte) (1 << (bit & 7));
+               bitmap[bit>>3] |= theBit;
+       }
+
+       public void clearBits()
+       {
+               Arrays.fill(bitmap,(byte) 0);
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/Routing.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/Routing.java       2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/Routing.java       2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,1335 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs;
+
+import org.gnu.freeway.*;
+import org.gnu.freeway.protocol.afs.esed2.*;
+import org.gnu.freeway.server.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.io.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+import java.util.logging.*;
+
+/**
+ * Routing interface. This is the interface that does the routing.
+ * routing of AFS queries and replies
+ *
+ * The routing code is responsible for deciding which replies
+ * need to be forwarded to which peers. While the querymanager
+ * decides where to forward queries, it needs to negotiate with
+ * the routing code which queries can be forwarded since we may
+ * not be able to keep track of all queries.
+ */
+
+public class Routing extends LoggedObject implements AFSConstants
+{
+       private static final boolean    DEBUG_WRITE_INDTABLE    =       true;
+
+       /** How much is a response worth 'in general'. Since replies are 
roughly 1k and should be much (factor of 4)
+        preferred over queries (which have a base priority of 20, which yields 
a base unit of roughly 1 per byte).
+        Thus if we set this value to 4092 we'd rather send a reply instead of 
a query unless the queries have
+        (on average) a priority that is more than double the reply priority 
(note that querymanager multiplies the
+        query priority with 2 to compute the scheduling priority). */
+
+       public static final int BASE_REPLY_PRIORITY     =       4092;
+
+       /**
+        * minimum indirection table size, defaults to 8192 entries, reduce if
+        * you have very little memory, enlarge if you start to overflow often
+        * and have memory available.<p>
+        *
+        * If the average query lives for say 1 minute (10 hops), and you have
+        * a 56k connection (= 420 kb/minute, or approximately 8000
+        * queries/minute) the maximum reasonable routing table size would
+        * thus be 8192 entries.  Every entry takes about 68 bytes.<p>
+        *
+        * The larger the value is that you pick here, the greater your
+        * anonymity can become.  It also can improve your download speed.<p>
+        *
+        * Memory consumption:
+        * <ul>
+        * <li>8192 => 560k indirection table => approx. 6 MB gnunetd</li>
+        * <li>65536 => 4456k indirection table => approx. 10 MB gnuentd</li>
+        * </ul>
+        * <p>
+        * THE VALUE YOU PICK MUST BE A POWER OF 2, for example:
+        * 128, 256, 512, 1024, 2048, 4092, 8192, 16384, 32768, 65536
+        */
+
+       public static final int MIN_INDIRECTION_TABLE_SIZE      =       8192;
+
+       /**
+        * Under certain cirumstances, two peers can interlock in their
+        * routing such that both have a slot that is blocked exactly until
+        * the other peer will make that slot available.  This is the
+        * probability that one will give in.  And yes, it's a hack.  It
+        * may not be needed anymore once we add collision-resistance to
+        * the routing hash table.
+        */
+
+       public static final int TIE_BREAKER_CHANCE      =       4;
+
+       /** ITE modes for addToSlot. */
+       public static final int ITE_REPLACE     =       0;
+       public static final int ITE_GROW        =       1;
+
+       /** Size of the indirection table specified in gnunet.conf */
+       private int     indirectionTableSize;
+
+       /** The routing table. This table has entries for all queries that we 
have recently send out.
+        It helps GNUnet to route the replies back to the respective sender. */
+
+       private IndirectionTableEntry[] ROUTING_indTable_;
+
+       /** Stats handle for how much content replies we have send back to 
clients. */
+       private Stat    stat_cs_reply_content_out;
+
+       /** Stats handles about incoming blocks */
+       private Stat    stat_content_in_ok;
+       private Stat    stat_content_in_dupe;
+       private Stat    stat_content_in_orphan;
+       private Stat    stat_routingFull;
+       private Stat    stat_routingReplaced;
+       private Stat    stat_routingPresent;
+       private Stat    stat_p2p_query_out;
+       private Stat    stat_concurrent_route_replacement;
+       private Stat    stat_delaytime_route_replacement;
+
+       private int                     random_qsel;
+
+       private CoreForProtocol         coreAPI;
+       private Prefs   prefs;
+       private Statistics              stats;
+       private Scheduler                       scheduler;
+       private int             round = 0;
+
+       public Policy           policy;
+       public Manager          manager;
+       public QueryManager     queryManager;
+       public BloomFilter      superBloomFilter;
+       public BloomFilter      singleBloomFilter;
+
+       private ScheduledTask           writeTask;
+
+
+       public Routing()
+       {
+               super(true);
+               coreAPI=null;
+
+               writeTask=new ScheduledTask("WRITE-ID-TABLE",new 
EvalAction(this,"writeIDtable"),Scheduler.MINUTES_1);
+       }
+
+       public String toString()
+       {
+               return "xxx";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       /**
+        * This function will write numeric entries of the indirectiontable
+        * to a textfile. With idtablesize of (8192*8), it will generate
+        * >1200 kb per run. The output will not be an accurate snapshot
+        * of the idtable at any moment as the locking is done on per-entry
+        * basis. Naturally this function might also twist the routing
+        * itself a little by locking the entries. The hope is that analyzing
+        * this data in eg octave or matlab might help understand the
+        * routing behaviour better. Or not.
+        */
+
+       public void writeIDtable()
+       {
+               IndirectionTableEntry           ite;
+               MappedFile                              fp;
+               FileLocation                            f;
+               DirLocation                             dir;
+               long                                            now;
+               int                                             i;
+
+               now=Scheduler.now();
+
+               dir=prefs.getDirLocation("","GNUNETD_HOME");
+               if (dir==null) {
+                       dir=new DirLocation("~");
+                       }
+
+               f=dir.getFile("gn_idstats.txt");
+               f.create();
+
+               fp=f.open();
+               fp.seekEnd();
+
+               for(i=0; i<indirectionTableSize; i++) {
+                       ite=ROUTING_indTable_[i];
+                       synchronized(ite.lookup_exclusion) {
+                               fp.writeString(round+" "+i+" 
"+(ite.namespace!=null ? 1 : 0)+" "+
+                                       (ite.ttl == 0 ? 0 : 
Scheduler.toSeconds(ite.ttl-now))+" "+ite.priority+" "+
+                                       ite.getSeenCount()+" 
"+ite.getWaitingHostsCount()+" "+ite.getClientsCount()+"\n");
+                               }
+                       }
+
+               fp.close();
+               round++;
+       }
+
+       /**
+        * Initialize routing module.
+        * Initialize routing module (initializes indirection table)
+        * @param capi
+        */
+
+       public void init( CoreForProtocol capi )
+       {
+               int i;
+
+               coreAPI=capi;
+               prefs=capi.getApplication().getPreferences();
+               stats=capi.getApplication().getStatistics();
+               scheduler=capi.getApplication().getScheduler();
+
+               random_qsel = Crypto.nextInt(HashCode160.SIZE/4);
+               stat_cs_reply_content_out=stats.getHandle("# kb downloaded by 
clients",Stat.VERBOSE);
+               stat_delaytime_route_replacement=stats.getHandle("# routing 
entry replaced during delaytime",Stat.VERBOSE);
+               stat_concurrent_route_replacement=stats.getHandle("# routing 
entry replaced during lookup",Stat.VERBOSE);
+
+               stat_content_in_ok=stats.getHandle("# kb ok content in");
+               stat_content_in_dupe=stats.getHandle("# kb dupe content in");
+               stat_content_in_orphan=stats.getHandle("# kb orphan or pushed 
content in");
+               stat_routingFull=stats.getHandle("# routing table full");
+               stat_routingReplaced=stats.getHandle("# routing table entry 
replaced");
+               stat_routingPresent=stats.getHandle("# routing table entry 
already in place");
+               stat_p2p_query_out=stats.getHandle("# p2p queries sent");
+               indirectionTableSize 
=prefs.getInt("AFS","INDIRECTIONTABLESIZE",0);
+               if (indirectionTableSize < MIN_INDIRECTION_TABLE_SIZE)
+                       indirectionTableSize = MIN_INDIRECTION_TABLE_SIZE;
+               i = 1;
+               while (i < indirectionTableSize)
+                       i*=2;
+               indirectionTableSize = i; // make sure it's a power of 2
+
+               debug("Set indirection table size to 
"+indirectionTableSize+".");
+
+               ROUTING_indTable_= new 
IndirectionTableEntry[indirectionTableSize];
+               for (i=0;i<indirectionTableSize;i++) {
+                       ROUTING_indTable_[i]=new IndirectionTableEntry();
+                       ROUTING_indTable_[i].namespace = null;
+                       ROUTING_indTable_[i].ttl = 0; // expired / free
+                       
ROUTING_indTable_[i].successful_local_lookup_in_delay_loop = false;
+                       ROUTING_indTable_[i].lookup_exclusion=new Object();
+                       }
+               ((Server) coreAPI.getApplication()).registerCSExitHandler(new 
ClientExitHandler() {
+                       public void handle( CSSession client )
+                       {
+                               cancelTCP_routing(client);
+                       }
+                       });
+
+               if (DEBUG_WRITE_INDTABLE) {
+                       scheduler.addJob(writeTask,0);
+                       }
+       }
+
+       /**
+        * Shutdown the routing module.
+        */
+
+       public void done()
+       {
+               ((Server) coreAPI.getApplication()).unregisterCSExitHandler(new 
ClientExitHandler() {
+                       public void handle( CSSession client )
+                       {
+                               cancelTCP_routing(client);
+                       }
+                       });
+               ROUTING_indTable_=null;
+
+               if (DEBUG_WRITE_INDTABLE) {
+                       scheduler.deleteJob(writeTask);
+                       }
+       }
+
+       /**
+        * Print the current routing table.
+        * Print the routing table.
+        */
+
+       public void printRoutingTable()
+       {
+               int i;
+               IndirectionTableEntry  ite;
+               long now;
+
+               now=Scheduler.now();
+               log(Level.INFO,"Routing TABLE:");
+               for (i=0;i<indirectionTableSize;i++) {
+                       ite = ROUTING_indTable_[i];
+                       synchronized(ite.lookup_exclusion) {
+                               // if (ite.ttl >= now)
+                               log(Level.FINEST,i+": hash "+ite.hash.toHex()+" 
ttl "+Scheduler.toSeconds(ite.ttl - now)+"s hostsWaiting 
"+ite.getWaitingHostsCount()+" prio "+ite.priority+" seenIndex: 
"+ite.getSeenCount());
+                               }
+                       }
+       }
+
+       /**
+        * Execute the query. <p>
+        *
+        * Execute means to test if we can route the query (or, in the case
+        * of a multi-query, any of the sub-queries). If yes, we lookup the
+        * content locally and potentially route it deferred. Regardless if
+        * the content was found or not, the queries that we can route are
+        * forwarded to other peers (by the querymanager code).<p>
+        *
+        * The decision if we can route is made by "needsForwarding". Note that
+        * queries that we are already routing do not "need forwarding". If
+        * we do route the query, execQuery decides if we are going to do source
+        * rewriting or not.<p>
+        *
+        * If we route a query, execSingleQuery will use the bloom filters and
+        * the databases to locate the content and queue a cron job that will
+        * pass the response to "useContent" as if it came from another peer.
+        * Note that if the query originated from a local client, the response
+        * is instant (no cron job scheduled).
+        *
+        * @param qp the polciy (priority) for the query
+        * @param msg the query message (with host identity for the reply)
+        * @param sock the TCP socket to send the answer to if it is
+        *        a query from the local host, otherwise null.
+        * @return true if the query was routed (at least in part), false if it 
was dropped
+        */
+
+       public boolean execQuery( int qp, P2PQuery msg, CSSession sock )
+       {
+               HostIdentity  sender;
+               HostIdentity senderCpy;
+               int prio;
+               int count;
+               int routeCount;
+
+               count = (msg.getByteSize()-P2PQuery.SIZE) / HashCode160.SIZE;
+               prio = msg.getPriority() / count; // per-query priority
+
+               // source rewriting (or not...)
+               if (sock == null) {
+                       if (msg.getReturnTo().equals(coreAPI.getIdentity()))
+                               return false; // A to B, B sends back to A 
without (!) source rewriting, in this case, A must just drop
+                       senderCpy=(HostIdentity) 
PersistentHelper.copy(msg.getReturnTo());      //todo: copie utile ???
+                       sender = senderCpy;
+               } else {
+                       sender = null;
+                       senderCpy=(HostIdentity) 
PersistentHelper.copy(coreAPI.getIdentity());  //todo: copie utile ???
+               }
+               if ((qp & Policy2.QUERY_INDIRECT) != 0) {
+                       msg.setReturnTo((HostIdentity) 
PersistentHelper.copy(coreAPI.getIdentity()));   //todo: copie utile ???
+                       }
+               else {
+                       msg.setPriorityAndTTL(0,msg.getTTL());
+                       }
+
+               debug("Received "+count+"-query "+msg.getQuery(0).toHex()+" 
with ttl "+msg.getTTL()+" and priority "+msg.getPriority()+".");
+
+               if (msg instanceof P2PNSQuery) {
+                       if 
(execNSQuery(sender,sock,prio,msg.getTTL(),((P2PNSQuery) 
msg).identifier,((P2PNSQuery) msg).namespace))
+                               routeCount = 2; // NAMESPACE + IDENTIFIER!
+                       else
+                               routeCount = 0;
+                       }
+               else {
+                       if (count > 1) { // MULTI-QUERY, take apart for 
individual routing, but reassemble for forwarding
+                               int i;
+                               boolean superBF;
+
+                               superBF = 
superBloomFilter.test(msg.getQuery(0));
+                               routeCount = 1;
+                               for (i=1;i<count;i++) {
+                                       if 
(execSingleQuery(sender,sock,prio,msg.getTTL(),msg.getQuery(i),superBF)) {
+                                               // route this query !
+                                               
msg.setQuery(routeCount,msg.getQuery(i));
+                                               routeCount++;
+                                               }
+                                       }
+                               if (routeCount == 1)
+                                       routeCount = 0; // nothing to forward
+                               }
+                       else { // single query or 3hash search
+                               if 
(execSingleQuery(sender,sock,prio,msg.getTTL(),msg.getQuery(0),false))
+                                       routeCount = 1;
+                               else
+                                       routeCount = 0;
+                               }
+                       }
+
+               if (routeCount >= 1) {
+                       stat_p2p_query_out.add(routeCount);
+                       queryManager.forwardQuery(msg,((sock == null) ? sender 
: null),sock);
+
+                       debug("Slots free in routing table for "+((sock==null) 
? "remote" : "LOCAL")+
+                               " query "+msg.getQuery(0).toHex()+"; forwarded 
"+routeCount+" out of "+count+" queries.");
+                       return true;
+                       }
+
+               debug("0 slots free in routing table for "+((sock==null) ? 
"remote" : "LOCAL")+
+                       " query "+msg.getQuery(0).toHex()+" with "+count+" hash 
codes, none forwarded.");
+               return false;
+       }
+
+       /**
+        * Content has arrived. We must decide if we want to
+        *   a) forward it to our clients
+        *   b) indirect it to other nodes.
+        * The routing module should know what to do.
+        *
+        * This method checks the routing table if we have a matching route and 
if yes queues the reply.
+        * It also makes sure that we do not send the same reply back on the 
same route more than once.
+        *
+        * @param hostId        who sent the message
+        * @param queryHash     the hashcode from the query
+        * @param msg           the p2p message we received, good for 
indirecting, must potentially be turned into the adequate CS message.
+        * @return                      how good this content was (priority of 
the original request)
+        */
+
+       public int useContent( HostIdentity hostId, HashCode160 queryHash, 
P2PMessage msg )
+       {
+               int i;
+               ContentBlock  content;
+               HashCode160 contentHC;
+               IndirectionTableEntry  ite;
+               int prio = -1;
+
+               //log(Level.FINEST,"DEBUG: useContent - prints routing table");
+               //printRoutingTable();
+
+               debug("Received content "+queryHash.toHex()+" from peer 
"+((hostId == null) ? "self" : hostId.getName())+".");
+
+               ite = ROUTING_indTable_[computeRoutingIndex(queryHash)];
+
+               synchronized(ite.lookup_exclusion) {
+                       if (!ite.hash.equals(queryHash) ) {
+                               stat_content_in_orphan.inc();
+                               debug("No matching query pending for content 
"+queryHash.toHex()+" (not indirected).");
+                               return 0; // no indirection pending: was useless
+                               }
+
+                       if (msg instanceof P2P3HashResult) {
+                               content=new ContentBlock(((P2P3HashResult) 
msg).getResult());
+                               if (ite.namespace != null) {
+                                       return 0;
+                                       }
+                               }
+                       else if (msg instanceof P2PChkResult) {
+                               queryManager.dequeueQuery(ite.hash);
+                               content = ((P2PChkResult)msg).result;
+                               if (ite.namespace != null) {
+                                       return 0;
+                                       }
+
+                               // remove the sender from the waiting list (if 
the sender was waiting for a response)
+                               if (hostId != null) {
+                                       ite.removeWaitingHost(hostId);
+                                       }
+                               }
+                       else if (msg instanceof P2PSBlockResult) {
+                               content=(ContentBlock) 
PersistentHelper.readFully(ContentBlock.class,PersistentHelper.toBuffer(((P2PSBlockResult)msg).getResult()));
+                               if (ite.namespace == null) {
+                                       return 0;
+                                       }
+                               HashCode160 hc;
+                               
hc=((P2PSBlockResult)msg).getResult().getNameSpace();
+                               if (! ite.namespace.equals(hc) ) {
+                                       return 0;
+                                       }
+                               }
+                       else {
+                               log(Level.WARNING,"Unexpected p2p result : 
"+msg);
+                               return 0;
+                               }
+
+                       
contentHC=HashCode160.create(PersistentHelper.toBytes(content));
+
+                       for (i=0;i<ite.getSeenCount();i++) {
+                               if (contentHC.equals(ite.getSeen(i))) {
+                                       stat_content_in_dupe.inc();
+                                       debug("Content is not new (slot: 
"+computeRoutingIndex(queryHash)+").");
+                                       return 0; // seen before, useless
+                                       }
+                               }
+                       // new reply, adjust credits !
+                       if (hostId != null) // if we are the sender, hostId 
will be null
+                               coreAPI.changeTrust(hostId, ite.priority);
+                       prio = ite.priority;
+                       ite.priority = 0; // no priority for further replies, 
because we don't get paid for those...
+
+                       debug("Indirecting new content matching query 
"+ite.hash.toHex()+".");
+
+                       for (i=0; i<ite.getClientsCount(); i++) {
+                               
queryManager.updateResponseData(null,ite.getClient(i),hostId);
+                               }
+
+                       for (i=0; i<ite.getWaitingHostsCount(); i++) {
+                               
queryManager.updateResponseData(ite.getWaitingHost(i),null,hostId);
+                               }
+
+                       sendReply(ite,msg);
+                       ite.addSeen(contentHC);
+                       stat_content_in_ok.inc();
+                       }
+               return prio;
+       }
+
+       /**
+        * Compute the hashtable index of a host id.
+        * @param query
+        * @return
+        */
+
+       protected int computeRoutingIndex( HashCode160 query )
+       {
+               int     res;
+
+               switch (random_qsel) {
+                       case 0: res=query.getA(); break;
+                       case 1: res=query.getB(); break;
+                       case 2: res=query.getC(); break;
+                       case 3: res=query.getD(); break;
+                       default: res=query.getE(); break;
+                       }
+               res=(res & (indirectionTableSize-1));
+               if (res>=indirectionTableSize) {
+                       trace("Indirection table size not power of 2 ? 
("+indirectionTableSize+")");
+                       }
+               return res;
+       }
+
+       /**
+        * Queue a CHK reply with cron to simulate
+        * another peer returning the response with
+        * some latency (and then route as usual).
+        *
+        * @param sender the next hop
+        * @param result the content that was found
+        */
+
+       protected void queueCHKReply( HostIdentity sender, ContentBlock result )
+       {
+               final P2PChkResult      _msg;
+               IndirectionTableEntry   ite;
+               HashCode160                             hc;
+
+               hc=HashCode160.create(PersistentHelper.toBytes(result));
+
+               ite=ROUTING_indTable_[computeRoutingIndex(hc)];
+               if (!ite.hash.equals(hc)) {
+                       debug("Concurrent route replacement : "+hc.toHex()+".");
+
+                       stat_concurrent_route_replacement.inc();
+                       return;
+                       }
+               if (ite.successful_local_lookup_in_delay_loop) {
+                       debug("Unexpected concurrent CHK lookup of 
"+hc.toHex()+".");
+                       return; // wow, really bad concurrent DB lookup and 
processing for the same query.  Well, at least we should not also queue the 
delayed reply twice...
+                       }
+               ite.successful_local_lookup_in_delay_loop = true;
+
+               _msg=new P2PChkResult();
+               _msg.result=(ContentBlock) PersistentHelper.copy(result);       
//todo: copie utile ???
+
+               // delay reply, delay longer if we are busy (makes it harder to 
predict / analyze, too).
+               scheduler.addJob(new ScheduledTask("DELAYED-REPLY",new 
AbstractAction() {
+                       public void perform()
+                       {
+                               HashCode160                             hc2;
+                               IndirectionTableEntry           ite2;
+
+                               
hc2=HashCode160.create(PersistentHelper.toBytes(_msg.result));
+
+                               ite2 = 
ROUTING_indTable_[computeRoutingIndex(hc2)];
+                               synchronized(ite2.lookup_exclusion) {
+                                       if (hc2.equals(ite2.hash)) {
+                                               
ite2.successful_local_lookup_in_delay_loop = false;
+                                               }
+                                       else {
+                                               
stat_delaytime_route_replacement.inc();
+                                               }
+                                       }
+                               useContent(null,hc2,_msg);
+                       }
+                       }),Crypto.nextLong(TTL_DECREMENT));
+       }
+
+       /**
+        * Queue an SBLOCK reply with cron to simulate
+        * another peer returning the response with
+        * some latency (and then route as usual).
+        *
+        * @param sender the next hop
+        * @param result the content that was found
+        */
+
+       protected void queueSBLOCKReply( HostIdentity sender, EncryptedSBlock 
result )
+       {
+               final P2PSBlockResult   _msg;
+
+               _msg=new P2PSBlockResult(result);
+
+               // delay reply, delay longer if we are busy (makes it harder to 
predict / analyze, too).
+               scheduler.addJob(new ScheduledTask("SBLOCK-REPLY",new 
AbstractAction() {
+                       public void perform()
+                       {
+                               
useContent(null,_msg.getResult().getIdentifier(),_msg);
+                       }
+                       }),Crypto.nextLong(TTL_DECREMENT));
+       }
+
+       /**
+        * Queue a 3Hash reply with cron to simulate
+        * another peer returning the response with
+        * some latency (and then route as usual).
+        *
+        * @param sender the next hop
+        * @param hc the double (!) hash
+        * @param result the content that was found
+        */
+
+       protected void queue3HashReply( HostIdentity sender, HashCode160 hc, 
ContentBlock result )
+       {
+               final P2P3HashResult    _msg;
+
+               _msg=new P2P3HashResult(hc,result);
+
+               // delay reply, delay longer if we are busy (makes it harder to 
predict / analyze, too).
+               scheduler.addJob(new ScheduledTask("3HASH-REPLY",new 
AbstractAction() {
+                       public void perform()
+                       {
+                               useContent(null,_msg.getTripleHash(),_msg);
+                       }
+                       }),Crypto.nextLong(TTL_DECREMENT));
+       }
+
+       /**
+        * Hand a CHK reply to the client.
+        * @param sock the client socket
+        * @param result the response
+        */
+
+       protected void tellClientCHKReply( CSSession sock, ContentBlock result )
+       {
+               CSResultChk  reply;
+               HashCode160 hc;
+
+               hc=HashCode160.create(PersistentHelper.toBytes(result));
+
+               debug("Sending client response to CHK query "+hc.toHex()+".");
+
+               reply = new CSResultChk();
+               reply.result=(ContentBlock) PersistentHelper.copy(result);      
//todo: copie utile ???
+
+               stat_cs_reply_content_out.inc();
+
+               sock.send(reply);
+       }
+
+       /**
+        * Hand an SBLOCK reply to the client.
+        * @param sock the client socket
+        * @param result the response
+        */
+
+       protected void tellClientSBLOCKReply( CSSession sock, EncryptedSBlock 
result )
+       {
+               CSResultSBlock  reply;
+
+               reply=new CSResultSBlock(result);
+
+               stat_cs_reply_content_out.inc();
+
+               sock.send(reply);
+       }
+
+       /**
+        * Hand a 3Hash reply to the client.
+        * @param sock the client socket
+        * @param hc the double hash
+        * @param result the response
+        */
+
+       protected void tellClient3HashReply( CSSession sock, HashCode160 hc, 
ContentBlock result )
+       {
+               CSResult3Hash  reply;
+
+               reply = new CSResult3Hash(hc,result);
+
+               stat_cs_reply_content_out.inc();
+
+               sock.send(reply);
+       }
+
+       /**
+        * Add an entry to the routing table. The lock on the ite
+        * must be held and is being released.
+        *
+        * @param mode replace or extend an existing entry?
+        * @param ite slot in the routing table that is manipulated
+        * @param query the query to look for
+        * @param namespace the namespace to look in (null for global namespace)
+        * @param ttl how long to keep the new entry, relative ttl
+        * @param priority how important is the new entry
+        * @param sender for which node is the entry (null for local client)
+        * @param sock for which local client is the entry (null for peer)
+        * @return true if sock or sender was added, false if both are null or 
existed already
+        *            in the queue
+        */
+
+       protected boolean addToSlot( int mode, IndirectionTableEntry ite, 
HashCode160 query, HashCode160 namespace, int ttl, int priority, HostIdentity 
sender, CSSession sock )
+       {
+               long    now;
+               boolean ret = false;
+
+               // namespace handling: always override with the new value 
(query collisions are supposed to be 'impossible',
+               // so this should always be correct.  Either we replace the 
existing slot with something new, or it
+               // should not make a difference since the old and the new 
namespace will be the same.
+               if (ite.namespace != null) {
+                       if (namespace == null) {
+                               ite.namespace = null;
+                       } else {
+                               ite.namespace=(HashCode160) 
PersistentHelper.copy(namespace);   //todo: copie utile ???
+                       }
+               } else {
+                       if (namespace != null) {
+                               ite.namespace=(HashCode160) 
PersistentHelper.copy(namespace);   //todo: copie utile ???
+                       }
+               }
+               now=Scheduler.now();
+               if (mode == ITE_REPLACE) {
+                       ite.clearSeen();
+                       if (query.equals(ite.hash)) {
+                               stat_routingPresent.inc();
+                               ite.ttl = now + ttl;
+                               ite.priority = priority;
+                               }
+                       else {
+                               if (ite.hasClients() && sender==null && 
!ite.hasSeen()) {
+                                       debug("Replacing local query 
"+query.toHex()+" without results with foreign query !");
+                                       }
+
+                               ite.successful_local_lookup_in_delay_loop = 
false;
+                               // different request, flush pending queues
+                               stat_routingReplaced.inc();
+                               queryManager.dequeueQuery(ite.hash);
+                               ite.hash=(HashCode160) 
PersistentHelper.copy(query);    //todo: copie utile ???
+                               ite.clearWaitingHosts();
+                               ite.clearClients();
+                               ite.ttl = now + ttl;
+                               ite.priority = priority;
+                       }
+               } else { // GROW mode
+                       if (!query.equals(ite.hash)) {
+                               trace("Assert failed !");
+                               return false;
+                               }
+                       if (sender != null) {
+                               if (ite.hasWaitingHost(sender)) {
+                                       sender=null;
+                                       }
+                               }
+                       stat_routingPresent.inc();
+
+                       if (sock!=null && ite.containsClient(sock)) {
+                               sock=null;
+                               }
+
+                       if ( (sock == null) &&
+                                       (sender == null) ) {
+                               return ret; // already there!
+                       }
+                       // extend lifetime
+                       if (ite.ttl < now + ttl)
+                               ite.ttl = now + ttl;
+                       ite.priority += priority;
+                       }
+
+               if (sock!=null) {
+                       if (ite.containsClient(sock)) {
+                               sock=null;
+                               }
+
+                       if (sock != null) {
+                               ite.addClient(sock);
+                               ite.clearSeen(); // new listener, flush "seen" 
list
+                               ret = true;
+                               }
+                       }
+
+               if (sender!=null && ite.hasWaitingHost(sender)) {
+                       sender=null;
+                       }
+
+               if (sender!=null) {
+                       ite.addWaitingHost(sender);
+                       ret = true;
+                       // again: new listener, flush seen list
+                       ite.clearSeen();
+                       }
+               return ret;
+       }
+
+       /**
+        * Find out, if this query is already pending. If the ttl of
+        * the new query is higher than the ttl of an existing query,
+        * false is returned since we should re-send the query.<p>
+        *
+        * If true is returned, the slot is also marked as used by
+        * the query and the sender (HostId or socket) is added.<p>
+        *
+        * This method contains a heuristic that attempts to do its best to
+        * route queries without getting too many cycles, send a query and
+        * then drop it from the routing table without sending a response,
+        * etc.  Before touching this code, definitely consult Christian
+        * (address@hidden) who has put more bugs in these five lines
+        * of code than anyone on this planet would think is possible.
+        *
+        *
+        * @param query the hash to look for
+        * @param namespace the namespace to look in, null for the global 
namespace
+        * @param ttl how long would the new query last
+        * @param priority the priority of the query
+        * @param sender which peer transmitted the query? (null for this peer)
+        * @param sock which client transmitted the query? (null for other peer)
+        * @param isRouted set to true if we can route this query, false if we 
can not
+        * @param doForward is set to true if we should forward the query, 
false if not
+        * @return a case ID for debugging
+        */
+
+       protected int needsForwarding( HashCode160 query, HashCode160 
namespace, int ttl, int priority, HostIdentity sender, CSSession sock, 
boolean[] isRouted, boolean[] doForward )
+       {
+               IndirectionTableEntry  ite;
+               long now;
+
+               now=Scheduler.now();
+               ite = ROUTING_indTable_[computeRoutingIndex(query)];
+               // released either in here or by addToSlot!
+
+               if ( ( ite.ttl < now - TTL_DECREMENT * 10) &&
+                               ( ttl > - TTL_DECREMENT * 5) ) {
+                       addToSlot(ITE_REPLACE, ite, query, namespace, ttl, 
priority, sender, sock);
+                       isRouted[0] = true;
+                       doForward[0] = true;
+                       return 21;
+                       }
+               if (ttl<0 && query.equals(ite.hash)) {
+                       // if ttl is "expired" and we have the exact query 
pending, route replies but do NOT forward _again_!
+
+                       debug("GROW: ttl < 0 and existing query is equal 
("+ttl+", "+(ite.ttl-now)+").");
+
+                       addToSlot(ITE_GROW, ite, query, namespace, ttl, 
priority, sender, sock);
+                       // don't go again, we are not even going to reset the 
seen list, so why bother looking locally again, if we would find
+                       //something, the seen list would block sending the 
reply anyway since we're not resetting that (ttl too small!)!
+                       isRouted[0] = false;
+                       doForward[0] = false;
+                       return 0;
+               }
+
+               if ( (ite.ttl + (TTL_DECREMENT * coreAPI.estimateNetworkSize()) 
<
+                               (now + ttl)) &&
+                               (ite.ttl < now) ) {
+                       // expired AND is significantly (!) longer expired than 
new query
+
+                       debug("REPLACE and reset SEEN: existing query expired 
and older than new query ("+ttl+", "+(ite.ttl-now)+").");
+
+                       // previous entry relatively expired, start using the 
slot -- and kill the old seen list!
+                       ite.clearSeen();
+                       if ( query.equals(ite.hash) &&
+                                       (ite. 
successful_local_lookup_in_delay_loop) ) {
+                               isRouted[0] = false;
+                               doForward[0] = false;
+                               addToSlot(ITE_GROW, ite, query, namespace, ttl, 
priority, sender, sock);
+                               return 1;
+                               }
+                       isRouted[0] = true;
+                       doForward[0] = true;
+                       addToSlot(ITE_REPLACE, ite, query, namespace, ttl, 
priority, sender, sock);
+                       return 2;
+                       }
+
+               if (query.equals(ite.hash) ) {
+                       if (!ite.hasSeen()) {
+                               // can not tell if CHK/3HASH/NSQUERY
+                               if (ite.ttl + TTL_DECREMENT < (now + ttl)) { // 
ttl of new is SIGNIFICANTLY     longer?
+                                       // query again
+
+                                       debug("REPLACE (seen was empty): 
existing query and TTL higher ("+(ite.ttl-now)+", "+ttl+").");
+
+                                       addToSlot(ITE_REPLACE, ite, query, 
namespace, ttl, priority, sender, sock);
+                                       if 
(ite.successful_local_lookup_in_delay_loop) {
+                                               isRouted[0] = false; // don't 
go again, we are already processing a local lookup!
+                                               doForward[0] = false;
+                                               return 3;
+                                               }
+                                       isRouted[0] = true;
+                                       doForward[0] = true;
+                                       return 4;
+                                       }
+
+                               // new TTL is lower than the old one, thus just 
wait for the reply that may come back
+
+                               debug("GROW - equal existing query exists 
without replies ("+(ite.ttl-now)+", "+ttl+").");
+
+                               if (addToSlot(ITE_GROW, ite, query, namespace, 
ttl, priority, sender, sock)) {
+                                       if 
(ite.successful_local_lookup_in_delay_loop) {
+                                               isRouted[0] = false; // don't 
go again, we are already processing a local lookup!
+                                               doForward[0] = false;
+                                               return 5;
+                                               }
+                                       isRouted[0] = true;
+                                       doForward[0] = false;
+                                       return 6;
+                                       }
+
+                               // same query with _higher_ TTL has already 
been processed FOR THE SAME recipient! Do NOT do
+                               // the lookup *again*.
+                               isRouted[0] = false;
+                               doForward[0] = false;
+                               return 7;
+                               }
+                       // ok, seen reply before, can judge type of query!
+
+                       // pending == new!
+                       if ( ite.hash.equals(ite.getSeen(0)) &&
+                                       (ite.namespace == null) ) { // CHK
+                               if (ite.ttl < (now + ttl)) { // ttl of new is 
longer?
+                                       // go again
+                                       ite.clearSeen();
+
+                                       debug("REPLACE and reset SEEN: existing 
query equal but we've seen the response already ("+(ite.ttl-now)+", "+ttl+").");
+
+                                       addToSlot(ITE_REPLACE, ite, query, 
namespace, ttl, priority, sender, sock);
+                                       if 
(ite.successful_local_lookup_in_delay_loop) {
+                                               isRouted[0] = false; // don't 
go again, we are already processing a local lookup!
+                                               doForward[0] = false;
+                                               return 8;
+                                               }
+                                       isRouted[0] = true;
+                                       // only forward if new TTL is 
significantly higher
+                                       if (ite.ttl + TTL_DECREMENT < (now + 
ttl))
+                                               doForward[0] = true;
+                                       else
+                                               doForward[0] = false;
+                                       return 9;
+                                       }
+
+                               // new TTL is lower than the old one, thus just 
wait for the reply that may come back
+                               debug("GROW - equal existing query exists 
without replies ("+(ite.ttl-now)+", "+ttl+").");
+
+                               if (addToSlot(ITE_GROW, ite, query, namespace, 
ttl, priority, sender, sock)) {
+                                       if 
(ite.successful_local_lookup_in_delay_loop) {
+                                               isRouted[0] = false;
+                                               doForward[0] = false;
+                                               return 10;
+                                               }
+                                       isRouted[0] = true;
+                                       doForward[0] = false;
+                                       return 11;
+                                       }
+                               isRouted[0] = false;
+                               doForward[0] = false;
+                               return 12;
+                               }
+
+                       // 3HASH or SQUERY, multiple results possible!
+                       // It's a pending 3HASH or SQUERY that can have 
multiple replies.  Do not re-send, just forward the
+                       // answers that we get from now on to this additional 
receiver
+                       boolean isttlHigher;
+
+                       debug("GROW - equal existing query exists without 
replies ("+(ite.ttl-now)+", "+ttl+").");
+
+                       if (ite.ttl <  now+ttl)
+                               isttlHigher = false;
+                       else
+                               isttlHigher = true;
+                       if (addToSlot(ITE_GROW, ite, query, namespace, ttl, 
priority, sender, sock)) {
+                               isRouted[0] = true;
+                               doForward[0] = false;
+                               return 13;
+                               }
+                       // receiver is the same as the one that already got the 
answer, do not bother to do this again,
+                       // IF the TTL is not higher!
+                       isRouted[0] = isttlHigher;
+                       doForward[0] = false;
+                       return 14;
+                       }
+               // a different query that is expired a bit longer is using the 
slot; but if it is a CHK query that has received
+               // a response already, we can eagerly throw it out anyway, 
since the request has been satisfied completely
+               if ( (ite.ttl + TTL_DECREMENT < (now + ttl) ) &&
+                               (ite.ttl < now) &&
+                               (ite.getSeenCount() == 1) &&
+                               (ite.namespace == null) &&
+                               (ite.hash.equals(ite.getSeen(0))) ) {
+                       // is CHK and we have seen the answer, get rid of it 
early
+
+                       debug("CHK "+ite.hash.toHex()+" with reply already 
seen, replacing eagerly ("+(ite.ttl-now)+", "+ttl+").");
+
+                       addToSlot(ITE_REPLACE, ite, query, namespace, ttl, 
priority, sender, sock);
+                       isRouted[0] = true;
+                       doForward[0] = true;
+                       return 15;
+               }
+               // Another still valid query is using the slot.  Now we need a 
_really_ good reason to discard it...
+               if (ttl < 0) {
+                       isRouted[0] = false;
+                       doForward[0] = false;
+                       return 16; // if new ttl is "expired", don't bother 
with priorities
+               }
+
+               // Finally try to find a _strong_ reason looking at 
priority/ttl relationships to replace the existing query. A low ttl with high
+               // priority should be preferred, so we do a 
cross-multiplication (!). Also, we want a _strong_ reason, so we add a "magic" 
factor
+               // of 10 for the additional work that the replacement would 
make (the network needs a certain amount of resilience to changes in
+               // the routing table, otherwise it might happen that query A 
replaces query B which replaces query A which could happen so
+               // quickly that no response to either query ever makes it 
through...
+
+               if ( ((ite.ttl - now) * priority) >
+                                10 * (ttl * ite.priority) ) {
+
+                       debug("Priority of new query is much higher, overriding 
("+(ite.ttl-now)+", "+ttl+").");
+
+                       addToSlot(ITE_REPLACE, ite, query, namespace, ttl, 
priority, sender, sock);
+                       isRouted[0] = true;
+                       doForward[0] = true;
+                       return 17;
+               }
+
+               if (Crypto.nextInt(TIE_BREAKER_CHANCE) == 0) {
+                       debug("TIE-BREAKER.  Overriding ("+(ite.ttl-now)+", 
"+ttl+").");
+
+                       addToSlot(ITE_REPLACE, ite, query, namespace, ttl, 
priority, sender, sock);
+                       isRouted[0] = true;
+                       doForward[0] = true;
+                       return 20;
+                       }
+
+               // sadly, the slot is busy with something else; we can not even 
add ourselves to the reply set
+               stat_routingFull.inc();
+               isRouted[0] = false;
+               doForward[0] = false;
+
+               debug("Existing "+(!ite.hasClients() ? "remote" : "LOCAL")+
+                       " query "+ite.hash.toHex()+" 
("+computeRoutingIndex(ite.hash)+") is more important (EP: "+
+                       ite.priority+", ET: "+(ite.ttl-now)+"; NP: 
"+priority+", NT: "+ttl+")");
+               return 18;
+       }
+
+       /**
+        * Send a reply to a host.  Distinguishes between local and remote
+        * delivery, converts the reply into the appropriate format and sends
+        * it out.
+        *
+        * @param ite the matching slot in the indirection table
+        * @param msg the message to route
+        */
+
+       protected void sendReply( IndirectionTableEntry ite, P2PMessage msg )
+       {
+               int j;
+               int maxDelay;
+               long now;
+
+               now=Scheduler.now();
+               if (now < ite.ttl)
+                       maxDelay = (int) (ite.ttl - now);
+               else
+                       maxDelay = (int) TTL_DECREMENT; // for expired queries
+
+               // send to peers
+               for (j=0; j<ite.getWaitingHostsCount(); j++) {
+                       
coreAPI.sendToNode(ite.getWaitingHost(j),msg,BASE_REPLY_PRIORITY 
*(ite.priority+1),maxDelay);// weigh priority
+                       }
+
+               for (j=0; j<ite.getClientsCount(); j++) {
+                       if (msg instanceof P2P3HashResult) {
+                               tellClient3HashReply(ite.getClient(j),
+                                               ((P2P3HashResult) 
msg).getDoubleHash(),
+                                               new 
ContentBlock(((P2P3HashResult) msg).getResult())
+                                               );
+                               }
+                       else if (msg instanceof P2PChkResult) {
+                               
tellClientCHKReply(ite.getClient(j),((P2PChkResult)msg).result);
+                               }
+                       else if (msg instanceof P2PSBlockResult) {
+                               
tellClientSBLOCKReply(ite.getClient(j),((P2PSBlockResult) msg).getResult());
+                               }
+                       else {
+                               log(Level.WARNING,"Unexpected p2p result : 
"+msg);
+                               }
+                       }
+       }
+
+       /**
+        * TCP connection is shut down, cancel all replies to that client.
+        * @param sock
+        */
+
+       protected void cancelTCP_routing( CSSession sock )
+       {
+               IndirectionTableEntry   ite;
+               int                                             i;
+
+               for (i=0; i<indirectionTableSize; i++) {
+                       ite=ROUTING_indTable_[i];
+                       synchronized(ite.lookup_exclusion) {
+                               ite.removeClient(sock);
+                               }
+                       }
+       }
+
+       /**
+        * Execute a single query. Tests if the query can be routed. If yes,
+        * the query is added to the routing table and the content is looked
+        * for locally. If the content is available locally, a deferred
+        * response is simulated with a cron job and the local content is
+        * marked as valueable. The method returns true if the query should
+        * subsequently be routed to other peers.
+        *
+        * @param sender next hop in routing of the reply
+        * @param sock client socket if we are ultimate receiver
+        * @param prio the effective priority of the query
+        * @param ttl the relative ttl of the query
+        * @param query the query itself
+        * @param superHash true if the super-hash test has indicated that we#
+        *        have a reply locally available
+        * @return true if the query should be routed further, false if not.
+        */
+
+       protected boolean execSingleQuery( HostIdentity sender, CSSession sock, 
int prio, int ttl, HashCode160 query, boolean superHash )
+       {
+               ContentIndex                    ce=new ContentIndex();
+               ContentBlock[]                  result;
+               int                                             len;
+               boolean[]                               isRouted=new boolean[1];
+               boolean[]                               doForward=new 
boolean[1];
+               IndirectionTableEntry   ite;
+               int                                             nfCase,i,rcount;
+               HashCode160                             hc;
+
+               ite = ROUTING_indTable_[computeRoutingIndex(query)];
+               synchronized(ite.lookup_exclusion) {
+                       nfCase = needsForwarding(query,
+                                       null,
+                                       ttl,
+                                       prio,
+                                       sender,
+                                       sock,
+                                       isRouted,
+                                       doForward);
+
+                       debug("Needs forwarding decided for "+((sock == null) ? 
"remote" : "LOCAL")+
+                               " query "+query.toHex()+" 
("+computeRoutingIndex(query)+", ttl "+ttl+
+                               ", pri "+prio+"): case "+nfCase+" 
("+(doForward[0] ? "FWD" : "")+", "+(isRouted[0] ? "ROUTE" : "")+")");
+
+                       if ( (sender != null) && !isRouted[0]) {
+                               return false; // if we can't route, forwarding 
never makes any sense
+                       }
+
+                       if (!superHash && !singleBloomFilter.test(query)) {
+                               return doForward[0]; // content not available 
locally,  just route
+                               }
+
+                       result = manager.retrieveContent(query,ce,prio,sender 
== null);
+                       if (result==null) {
+                               return doForward[0]; // bloomfilter was wrong, 
content not there
+                               }
+
+                       len=result.length;
+                       if (len == ContentBlock.SIZE) {
+                               
hc=HashCode160.create(PersistentHelper.toBytes(result[0]));
+                               if (ite.hasSeen()) {
+                                       if (hc.equals(ite.getSeen(0))) {
+                                               log(Level.SEVERE,"Lookup 
produced result already seen. Case: "+nfCase+".");
+                                               }
+                                       }
+                               }
+
+                       if (sender != null) {
+                               if (ce.type == ContentIndex.LOOKUP_TYPE_3HASH) {
+                                       if 
(!policy.checkAnonymityPolicy(CSMessage.IS_RESULT_3HASH,P2P3HashResult.SIZE)) {
+                                               return doForward[0]; // policy 
says: no direct response, but routing is ok!
+                                               }
+                                       }
+                               else {
+                                       if 
(!policy.checkAnonymityPolicy(CSMessage.IS_RESULT_CHK,P2PChkResult.SIZE)) {
+                                               return doForward[0]; // policy 
says: no direct response, but routing is ok
+                                               }
+                                       }
+                               }
+
+                       switch (ce.type) {
+                               case ContentIndex.LOOKUP_TYPE_CHK:
+                               case ContentIndex.LOOKUP_TYPE_CHKS:
+                                       if (len != ContentBlock.SIZE) {
+                                               log(Level.WARNING,"Local CHK 
content had bad size : "+len+".");
+                                               break;
+                                               }
+                                       if (sock != null) {
+                                               
tellClientCHKReply(sock,result[0]);
+                                               doForward[0]=false; // purely 
local handling!
+                                               }
+                                       if (sender!=null) {
+                                               queueCHKReply(sender,result[0]);
+                                               }
+                                       doForward[0]=false; // we have the one 
and only answer
+                                       break;
+
+                               case ContentIndex.LOOKUP_TYPE_3HASH:
+                                       byte[]  
tmp=PersistentHelper.toBytes(result[0]);
+                                       ContentBlock    res;
+
+                                       rcount = len / ContentBlock.SIZE;
+                                       if (rcount * ContentBlock.SIZE != len) {
+                                               log(Level.WARNING,"Database 
returned a 3HASH result that is not multiple of 1k ("+len+") ! Database 
corrupted ?");
+                                               break;
+                                               }
+
+                                       if (sock!=null) {
+                                               for (i=0; i<rcount; i++) {
+                                                       res=(ContentBlock) 
PersistentHelper.readFully(ContentBlock.class,ByteBuffer.wrap(tmp,i*ContentBlock.SIZE,ContentBlock.SIZE));
+                                                       
tellClient3HashReply(sock,ce.hash,res);
+                                                       }
+                                               }
+
+                                       if (sender!=null) {
+                                               for (i=0; i<rcount; i++) {
+                                                       res=(ContentBlock) 
PersistentHelper.readFully(ContentBlock.class,ByteBuffer.wrap(tmp,i*ContentBlock.SIZE,ContentBlock.SIZE));
+                                                       
queue3HashReply(sender,ce.hash,res);
+                                                       }
+                                               }
+                                       break;
+
+                               default:
+                                       log(Level.FINEST,"Query lookup produced 
unexpected type "+ce.type+" !");
+                                       break;
+                               }
+                       }
+               return doForward[0];
+       }
+
+       /**
+        * Execute a namespace query. Tests if the query can be routed. If yes,
+        * the query is added to the routing table and the content is looked
+        * for locally. If the content is available locally, a deferred
+        * response is simulated with a cron job and the local content is
+        * marked as valueable. The method returns true if the query should
+        * subsequently be routed to other peers.
+        *
+        * @param sender next hop in routing of the reply
+        * @param sock client socket if we are ultimate receiver
+        * @param prio the effective priority of the query
+        * @param ttl the relative ttl of the query
+        * @param query the query itself
+        * @param namespace for which namespace
+        * @return true if the query should be routed further, false if not.
+        */
+
+       protected boolean execNSQuery( HostIdentity sender, CSSession sock, int 
prio, int ttl, HashCode160 query, HashCode160 namespace )
+       {
+               ContentIndex                    ce=new ContentIndex();
+               ContentBlock[]                  result;
+               int                                             len;
+               HashCode160                             hc;
+               boolean[]                               isRouted=new boolean[1];
+               boolean[]                               doForwarding=new 
boolean[1];
+               int                                             k;
+               IndirectionTableEntry   ite;
+
+               debug("Received NS query for 
"+namespace.toHex()+"/"+query.toHex()+".");
+
+               ite = ROUTING_indTable_[computeRoutingIndex(query)];
+               synchronized(ite.lookup_exclusion) {
+                       
needsForwarding(query,namespace,ttl,prio,sender,sock,isRouted,doForwarding);
+                       }
+               if (!isRouted[0]) {
+                       return false;
+                       }
+               if (!singleBloomFilter.test(query)) {
+                       debug("Bloomfilter test says content not available 
locally.");
+                       return doForwarding[0]; // content not available 
locally, just route
+                       }
+
+               result=manager.retrieveContent(query,ce,prio,sender == null);
+               if (result==null) {
+                       debug("Bloomfilter test wrong, DB lookup failed.");
+                       return doForwarding[0]; // bloomfilter was wrong, 
content not there
+                       }
+
+               len=result.length;
+               if (ce.type != ContentIndex.LOOKUP_TYPE_SBLOCK) {
+                       return doForwarding[0];
+                       }
+
+               if (sender != null) {
+                       if 
(!policy.checkAnonymityPolicy(CSMessage.IS_RESULT_SBLOCK,P2PSBlockResult.SIZE)) 
{
+                               debug("Anonymity policy denies reply at this 
time.");
+                               return doForwarding[0]; // policy says: no 
direct response, but routing is ok
+                               }
+                       }
+
+               if (0!=(len % ContentBlock.SIZE)) {
+                       log(Level.WARNING,"Local SBLOCK content had bad size 
"+len+".");
+                       return doForwarding[0];
+                       }
+
+               EncryptedSBlock sblock;
+               byte[]  tmp;
+
+               for (k=(len/ContentBlock.SIZE)-1; k>=0; k--) {
+                       tmp=PersistentHelper.toBytes(result[k]);
+                       sblock=(EncryptedSBlock) 
PersistentHelper.readFully(EncryptedSBlock.class,ByteBuffer.wrap(tmp,k*ContentBlock.SIZE,ContentBlock.SIZE));
+
+                       hc=sblock.getNameSpace();
+                       if (! namespace.equals(hc)) {
+                               log(Level.WARNING,"WARNING: namespace mismatch 
(should be rare but can theoretically happen).");
+                               return doForwarding[0];
+                               }
+                       if (sender != null) {
+                               queueSBLOCKReply(sender,sblock);
+                               }
+                       if (sock != null) {
+                               tellClientSBLOCKReply(sock,sblock);
+                               doForwarding[0] = false;
+                               }
+                       }
+               return doForwarding[0];
+       }
+
+       /**
+        * Handle query for current average routing priority.
+        *
+        * @param sock
+        * @param msg
+        * @return
+        */
+
+       public boolean csHandleRequestAvgPriority( CSSession sock, 
CSGetAvgPriority msg )
+       {
+               int i;
+               IndirectionTableEntry  ite;
+               int j = 0;
+               long priSum = 0;
+
+               for (i=0; i<MIN_INDIRECTION_TABLE_SIZE; i++) {
+                       ite = ROUTING_indTable_[i];
+                       synchronized(ite.lookup_exclusion) {
+                               if (ite.ttl!=0 && ite.hasWaitingHosts() && 
!ite.hasClients() ) {
+                                       /* only count entries that do NOT 
correspond to local requests in any way... */
+                                       priSum += ite.priority;
+                                       j++;
+                                       }
+                               }
+                       }
+               if (j > 0)
+                       priSum = priSum / j;
+               return sock.send(new CSResult((int) priSum));
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/esed2/AFSConstants.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/esed2/AFSConstants.java    
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/esed2/AFSConstants.java    
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,87 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.esed2;
+
+import org.gnu.freeway.util.*;
+
+/**
+ *
+ */
+
+public interface AFSConstants
+{
+       public static final String      GNUNET_URI_PREFIX_AFS   =       
"gnunet://afs/";
+
+       /* major/minor format versions (current) */
+       public static final int ROOT_MINOR_VERSION              =       0;
+       public static final int ROOT_MAJOR_VERSION              =       1;
+
+       /* major/minor format versions (current) */
+       public static final int SBLOCK_MINOR_VERSION    =       0;
+       public static final int SBLOCK_MAJOR_VERSION    =       2;
+
+       /** Fixed SBlock updateInterval codes. Positive values are interpreted 
as durations (in seconds) for periodical updates. */
+       public static final int SBLOCK_UPDATE_SPORADIC  =       -1;
+       public static final int SBLOCK_UPDATE_NONE              =       0;
+
+
+       /** Size of the Blocks we slice file data into (DBlocks and IBlocks). 
Never change this ! */
+       public static final int CONTENT_SIZE    =       1024;
+
+
+       /** Block is freshly created, nothing has been done. */
+       public static final int BLOCK_CREATED                           =       
0;
+
+       /** We know the correct block data and is is on the drive (and in 
memory if data != null) */
+       public static final int BLOCK_PRESENT                           =       
1;
+
+       /** We do not know the correct data, but we have not done a request yet.
+        It may be that we can construct the data from the children (if they 
are present). */
+       public static final int BLOCK_NOT_PRESENT                       =       
2;
+
+       /** We have a request pending for this block (either with the parent if 
parent != null)
+        or a direct request if parent == null. */
+       public static final int BLOCK_PENDING                           =       
3;
+
+       /** This block is present and all children (transitively) are also 
present. */
+       public static final int BLOCK_CHILDREN_PRESENT          =       4;
+
+       /** This iblock has a super-query pending. */
+       public static final int BLOCK_SUPERQUERY_PENDING        =       5;
+
+       /** This block is done (about to be freed). */
+       public static final int BLOCK_DONE                                      
=       6;
+
+       /** This block shall not be freed, even if all children are dead. */
+       public static final int BLOCK_PERSISTENT                        =       
7;
+
+
+       /** by which amount do we decrement the TTL for simple forwarding / 
indirection of the query; in milli-seconds.
+        Set somewhat in accordance to your network latency (above the time 
it'll take you to send a packet and get a reply). */
+       public static final long        TTL_DECREMENT   =       
Scheduler.SECS_5;
+
+       /** Highest TTL allowed ? (equivalent of 25-50 HOPS distance !) */
+       public static final long        MAX_TTL                 =       (100 * 
TTL_DECREMENT);
+
+       /** After how many retries do we print a warning ? */
+       public static final int         MAX_TRIES               =       50;
+
+       /* what is the context in which a root-node was discovered? */
+       public static final int DIR_CONTEXT_SEARCH              =       1;
+       public static final int DIR_CONTEXT_INSERT              =       2;
+       public static final int DIR_CONTEXT_DIRECTORY   =       4;
+       public static final int DIR_CONTEXT_INSERT_SB   =       8;
+
+       public static final int DIR_CONTEXT_ALL                 =       
(DIR_CONTEXT_SEARCH|DIR_CONTEXT_INSERT|DIR_CONTEXT_DIRECTORY|DIR_CONTEXT_INSERT_SB);
+
+       /* see also: http://www.w3.org/TR/PNG#R.PNG-file-signature */
+       public static final String      GNUNET_DIRECTORY_MAGIC  =       
"\211GND\r\n\032\n";
+       public static final String      GNUNET_DIRECTORY_EXT    =       ".gnd";
+       public static final String      GNUNET_DIRECTORY_MIME   =       
"application/gnunet-directory";
+
+       /** default size of the query record bitmap: 16 byte = 128 bit */
+       public static final int BITMAP_SIZE     =       16;
+
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/esed2/AFSUtils.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/esed2/AFSUtils.java        
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/esed2/AFSUtils.java        
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,121 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.esed2;
+
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+import java.util.*;
+import java.util.logging.*;
+
+/**
+ *
+ */
+
+public class AFSUtils extends Object
+{
+       private static Logger   logger;
+
+       static {
+               logger=Logger.getLogger(AFSUtils.class.getName());
+               }
+
+
+       /**
+        * Parse the keywords (join at spaces, separate at AND).
+        * @param keywords      the list of keywords
+        * @return                      the hashcodes of the keywords
+        */
+
+       public static HashCode160[] parseKeywords( String[] keywords )
+       {
+               HashCode160[]   h;
+               List                    list;
+               StringBuffer    buf;
+               String                  str;
+               int                             i;
+
+               list=new ArrayList();
+
+               buf=new StringBuffer();
+               for (i=0; i<keywords.length; i++) {
+                       buf.setLength(0);
+                       while (i<keywords.length && (!keywords[i].equals("AND") 
|| i==keywords.length-1)) {
+                               buf.append(keywords[i++]);
+                               buf.append(" ");
+                               }
+
+                       if (buf.length()>0) {
+                               buf.setLength(buf.length()-1);
+                               list.add(buf.toString());
+                               }
+                       }
+
+               h=new HashCode160[list.size()];
+               for (i=0; i<h.length; i++) {
+                       str=(String) list.get(i);
+
+                       logger.log(Level.FINEST,"Keyword : \""+str+"\"");
+                       h[i]=HashCode160.create(str);
+                       }
+               return h;
+       }
+
+       /**
+        * Build an initial set of query messages from the list of keywords.
+        *
+        * @param keywords      the keywords (or keys)
+        * @return                      the resulting query messages
+        */
+
+       public static CSQuery[] buildMessages( HashCode160[] keywords )
+       {
+               HashCode160     doubleHash;
+               CSQuery[]       messages;
+               int                     i;
+
+               messages=new CSQuery[keywords.length];
+               for (i=0; i<messages.length; i++) {
+                       messages[i]=new CSQuery();
+                       messages[i].setPriorityAndTTL(
+                               5+Crypto.nextInt(20),
+                               (int) 
(AFSConstants.TTL_DECREMENT*4+Crypto.nextInt((int) 
Scheduler.seconds(messages.length*5)))
+                               );
+
+                       
doubleHash=HashCode160.create(PersistentHelper.toBytes(keywords[i]));
+                       
messages[i].addQuery(HashCode160.create(PersistentHelper.toBytes(doubleHash)));
+                       }
+               return messages;
+       }
+
+       public static String get( ByteBuffer buf, int len )
+       {
+               byte[]  b;
+               int             n;
+
+               b=new byte[len];
+               buf.get(b);
+
+               for (n=0; n<b.length && b[n]!=0; n++) {}
+               return new String(b,0,n);
+       }
+
+       public static void put( String str, ByteBuffer buf, int len )
+       {
+               byte[]  b;
+               int             n;
+
+               b=str.getBytes();
+               n=Math.min(b.length,len-1);
+
+               buf.put(b,0,n);
+               while (n<len) {
+                       buf.put((byte) 0);
+                       n++;
+                       }
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/esed2/Block.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/esed2/Block.java   2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/esed2/Block.java   2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,552 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.esed2;
+
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+import java.util.*;
+import java.util.logging.*;
+
+/**
+ * Shared structure used in the internal objectish representation
+ * of all blocks (DBlocks and IBlocks) in the merkle-tree.
+ * Merkle-tree-CHK file encoding for anonymous file sharing
+ *
+ * Note that the current implementation no longer uses the exact
+ * scheme from the ESED paper. Extensive documentation is forthcoming,
+ * for now see http://www.ovmj.org/GNUnet/encoding.php3
+ */
+
+public abstract class Block extends LoggedObject implements AFSConstants
+{
+       /** The parent node in the file-tree, null for the node on top of the 
file-tree. */
+       private IBlock                  parent;
+
+       /** The total size of the file. */
+       private int                             filesize;
+
+       /** Position of the block relative to the beginning of the file. */
+       private int                             pos;
+
+       /** How many bytes in data are actual data (not padding)? Set to 0 to 
indicate that the download of this block is complete. */
+       private int                             len;
+
+       /** Pointer to the data of this block, null if the data is not yet 
available. */
+       private Persistent              data;
+
+       /** Hashes of the plaintext block (key) and the encrypted block 
(query). */
+       private ChkHashes               chk;
+
+       /** See BLOCK_XXX constants. */
+       private int                             status;
+
+
+       protected Block( int size, int length )
+       {
+               super(true);
+               parent=null;
+               filesize=size;
+               pos=0;
+               len=length;
+               data=null;
+               chk=null;
+               status=BLOCK_CREATED;
+
+               assert(length>0 && length<=ContentBlock.SIZE) : "Invalid block 
size ("+length+").";
+       }
+
+       protected Block( Block b, int offset, int length )
+       {
+               this(b.filesize,length);
+               parent=(IBlock) b;
+               pos=offset;
+       }
+
+       protected Block( Block b )
+       {
+               super(true);
+               filesize=b.filesize;
+               pos=b.pos;
+               chk=(ChkHashes) PersistentHelper.copy(b.chk);
+               len=b.len;
+               data=PersistentHelper.copy(b.data);
+               parent=b.parent;
+               status=b.status;
+       }
+
+       public String toString()
+       {
+               return "Block";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int getDataLength()
+       {
+               return len;
+       }
+
+       public boolean hasData()
+       {
+               return data!=null;
+       }
+
+       public byte[] getRawData()
+       {
+               //todo: bufferiser
+               return (data!=null ? PersistentHelper.toBytes(data) : new 
byte[] {});
+       }
+
+       public int getDataCRC()
+       {
+               return Crypto.crc32(getRawData(),0,len);
+       }
+
+       public Persistent getData()
+       {
+               return data;
+       }
+
+       public Persistent getData( Class c )
+       {
+               //todo: optimiser
+               return 
PersistentHelper.readFully(c,ByteBuffer.wrap(getRawData()));
+       }
+
+       public void setData( Persistent p )
+       {
+               data=p;
+               //todo: len par rapport a p.getByteSize() ???
+       }
+
+       public void clearData()
+       {
+               data=null;
+       }
+
+       protected boolean loadData( NodeContext nc, boolean warning )
+       {
+               int     n;
+               ContentBlock    tmp;
+
+               tmp=new ContentBlock();
+
+               n=nc.ioc.read(getDepth(),pos,tmp.content,len);
+//debug("... load data (pos="+pos+",len="+len+") = "+n);
+               if (n!=len) {
+                       if (warning) {
+                               log(Level.WARNING,"Read from file did not 
return expected size "+len+", but "+n+".");
+                               }
+                       return false;
+                       }
+               data=tmp;
+               return true;
+       }
+
+       public boolean writeData( NodeContext nc )
+       {
+               int     n;
+
+               n=nc.ioc.write(getDepth(),pos,getRawData(),len);
+               if (n!=len) {
+                       log(Level.SEVERE,"Writing to file failed 
("+len+","+n+") !");
+                       return false;
+                       }
+               return true;
+       }
+
+       public IBlock getParent()
+       {
+               return parent;
+       }
+
+       public void detach()
+       {
+               parent=null;
+       }
+
+       public boolean checkTopCRC( int crc )
+       {
+               return Crypto.crc32(getRawData(),0,len)==crc;
+       }
+
+       public HashCode160 getKey()
+       {
+               return chk.key;
+       }
+
+       public HashCode160 getQuery()
+       {
+               return chk.query;
+       }
+
+       public void setKeyAndQuery( ChkHashes c )
+       {
+               chk=(ChkHashes) PersistentHelper.copy(c);
+       }
+
+       public void setKeyAndQuery( FileIdentifier fid )
+       {
+               chk=new ChkHashes();
+               chk.query=fid.getFileQuery();
+               chk.key=fid.getFileKey();
+       }
+
+       public ChkHashes getKeyAndQuery()
+       {
+               return chk;
+       }
+
+       public boolean isStatus( int s )
+       {
+               return status==s;
+       }
+
+       public int getStatus()
+       {
+               return status;
+       }
+
+       public void setStatus( int s )
+       {
+               status=s;
+       }
+
+       public abstract int getDepth();
+
+       public int getFilePosition()
+       {
+               return pos;
+       }
+
+       public int getFileSize()
+       {
+               return filesize;
+       }
+
+       /**
+        * Encrypt this block and initialize
+        * this.chk and return the encrpyted data (edata)
+        * @return
+        */
+
+       protected ContentBlock encrypt()
+       {
+               ContentBlock    edata;
+               byte[]                  bdata;
+
+               bdata=getRawData();
+//System.out.println("CHK="+chk+" len = "+len);
+               chk=new ChkHashes();
+               chk.key=HashCode160.create(bdata,0,len);
+
+               Arrays.fill(bdata,len,ContentBlock.SIZE,(byte) 0);
+               edata = new ContentBlock();
+               if (!new ContentEncoding().encryptContent(bdata,chk.key,edata)) 
{
+                       trace("Encryption failed !?");
+                       return null;
+                       }
+               
chk.query=HashCode160.create(PersistentHelper.toBytes(edata),0,ContentBlock.SIZE);
+               return edata;
+       }
+
+       /**
+        * This block has received a CHK reply for a block. Decrypt.
+        *
+        * @param query the query for which reply is the answer
+        * @param reply the reply
+        * @return OK if the reply was valid, false on error
+        */
+
+       protected boolean receivedChk( HashCode160 query, CSResultChk reply )
+       {
+               byte[]          tmp;
+               HashCode160     hc;
+
+               assert(chk.query.equals(query)) : "invoked with reply for a 
different block. This should not be.";
+
+               tmp=reply.decrypt(chk.key);
+               if (tmp==null) {
+                       trace("Decryption failed !?");
+                       return false;
+                       }
+               setData(new ContentBlock(tmp));
+
+               hc=HashCode160.create(getRawData(),0,len);
+               if (!chk.key.equals(hc)) {
+                       data = null;
+                       log(Level.SEVERE,"Decrypted content does not match key. 
This is either a bug or a maliciously inserted file. Download aborted.");
+                       return false;
+                       }
+               return true;
+       }
+
+       /**
+        * Delete a block (send appropriate message to gnunetd).
+        *
+        * @param nc the context
+        * @param sock the socket to talk to gnunetd
+        * @return OK on success, false on error
+        */
+
+       public boolean delete( NodeContext nc, CSSession sock )
+       {
+               ContentBlock            edata;
+               CSUnindexBlock  request;
+               CSResult                        rv;
+
+               edata = encrypt();
+               if (sock == null) {
+                       return true;    // fake insert only
+                       }
+
+               if (nc.index != 0) {
+                       request=new CSUnindexBlock();
+                       request.contentIndex.importance= nc.priority;
+                       request.contentIndex.type= 
ContentIndex.LOOKUP_TYPE_CHKS;
+                       request.contentIndex.fileNameIndex= nc.index;
+                       request.contentIndex.fileOffset= pos;
+                       request.contentIndex.hash=(HashCode160) 
PersistentHelper.copy(chk.query);
+                       if (!sock.send(request)) {
+                               log(Level.WARNING,"Could not send unindex 
information to gnunetd. Is gnunetd running ?");
+                               return false;
+                               }
+
+                       rv=(CSResult) sock.receive(CSResult.class);
+                       if (rv==null) {
+                               log(Level.WARNING,"Server did not send 
confirmation of deletion.");
+                               return false;
+                               }
+                       if (!rv.isOkay()) {
+                               log(Level.WARNING,"Server could not perform 
unindexing (content already removed ?).");
+                               return false;
+                               }
+                       return true;
+                       }
+               return deleteCHKBlock(sock,edata,nc.priority);
+       }
+
+       /**
+        * Insert a CHK block (insert, not index!)
+        *
+        * @param eblock the block to insert
+        * @param priority the priority to use
+        * @param sock the socket to talk to gnunetd
+        * @return OK on success, false on error
+        */
+
+       protected boolean insertCHKBlock( CSSession sock, ContentBlock eblock, 
int priority )
+       {
+               CSInsertChk     request;
+               CSResult                rv;
+
+               if (sock == null)
+                       return true;    // fake insert
+
+               request = new CSInsertChk(priority,eblock.content);
+
+               if (!sock.send(request)) {
+                       log(Level.WARNING,"Could not send index information to 
gnunetd. Is gnunetd running ?");
+                       return false;
+                       }
+
+               rv=(CSResult) sock.receive(CSResult.class);
+               if (rv==null) {
+                       log(Level.WARNING,"Server did not send confirmation of 
insertion.");
+                       return false;
+                       }
+               if (!rv.isOkay()) {
+                       log(Level.WARNING,"Server could not perform 
insertion.");
+                       return false;
+                       }
+               return true;
+       }
+
+       /**
+        * Delete a CHK block.
+        *
+        * @param eblock the block to insert
+        * @param priority the priority to use
+        * @param sock the socket to talk to gnunetd
+        * @return OK on success, false on error
+        */
+
+       protected boolean deleteCHKBlock( CSSession sock, ContentBlock eblock, 
int priority )
+       {
+               CSDeleteChk     request;
+               CSResult                rv;
+
+               if (sock == null)
+                       return true;    // fake insert
+
+               request =new CSDeleteChk();
+               request.importance = priority;
+               
System.arraycopy(eblock.content,0,request.content,0,ContentBlock.SIZE);
+
+               if (!sock.send(request)) {
+                       log(Level.WARNING,"Could not send delete information to 
gnunetd. Is gnunetd running ?");
+                       return false;
+                       }
+
+               rv=(CSResult) sock.receive(CSResult.class);
+               if (rv==null) {
+                       log(Level.WARNING,"Server did not send confirmation of 
deletion.");
+                       return false;
+                       }
+
+               if (!rv.isOkay()) {
+                       log(Level.WARNING,"Server could not perform deletion.");
+                       return false;
+                       }
+               return true;
+       }
+
+       /**
+        * Send a single query via the RequestManager to gnunetd.
+        *
+        * @param rm the rm used to issue the query
+        * @param receiver the receiver to call on the reply
+        * @param nc the context
+        * @param query the query to perform
+        */
+
+       protected void issueQuery( RequestManager rm, Listener receiver, 
NodeContext nc, HashCode160 query )
+       {
+               CSQuery msg;
+
+               msg = new CSQuery();
+               msg.setPriorityAndTTL(1,1);
+               msg.addQuery(query);
+
+debug("issueQuery +++ CSQuery (2): "+msg);
+
+               rm.request(this,receiver,nc,msg);
+       }
+
+       /**
+        * Print the block summary (for debugging)
+        * @param indent
+        */
+
+       public abstract void print( int indent );
+
+       /**
+        * Free the associated resources of this Block. DOES ALSO free the
+        * memory occupied by the Block struct itself!
+        *
+        * @param rm reference to the RequestManager for requests
+        */
+
+       public void destroy( RequestManager rm )
+       {
+               /* better make sure that we have no request pending... */
+               if (rm != null) {       /* rm == null for gnunet-insert! */
+                       rm.assertDead(this);
+                       if (rm.top == this) {
+                               rm.top=null;
+                               }
+                       }
+
+               if (parent != null) {
+                       if (parent.childrenDestroyed(this)) {
+                               parent.destroy(rm);
+                               }
+                       }
+               data=null;
+       }
+
+       /**
+        * Check if this block is already present, if yes, loads it.
+        * @param nc the context
+        * @return YES if the block is present, NO if not
+        */
+
+       public abstract boolean check( NodeContext nc );
+
+       /**
+        * Download this node (and the children below). Note that the
+        * processing is asynchronous until the pmodel is called with position
+        * == total (and thus no more requests are pending) or the request
+        * manager is aborted by the user.
+        * @param nc the context
+        * @param rm the request manager
+        */
+
+       public abstract void download( NodeContext nc, RequestManager rm );
+
+       /**
+        * Insert the current block into the network. Implementations
+        * are also responsible for updating the corresponding fields
+        * of the parent node (of course, except if the parent is
+        * null in the case of the top-node in the tree).<p>
+        * Inner nodes first call the respective inserter methods for
+        * their children.<p>
+        *
+        * Insert a block (send appropriate message to gnunetd).
+        * This method encrypts the block and then sends an
+        * index or insertion request to gnunetd, depending on
+        * the configuration.
+        *
+        * @param nc the node context/the context (gives us the priority)
+        * @param sock the socket to use to talk to the core/the socket to talk 
to gnunetd, null if
+        *        we just do a "fake" insert to compute the tree in memory
+        * @return OK on success, false on error
+        */
+
+       public boolean insert( NodeContext nc, CSSession sock )
+       {
+               ContentBlock    edata;
+               CSIndexBlock    request;
+               CSResult                rv;
+               ContentIndex    index;
+
+               edata = encrypt();
+               if (sock == null) {
+                       return true;    // fake insert only
+                       }
+
+               if (nc.index != 0) {
+                       index=new ContentIndex();
+                       index.importance=nc.priority;
+                       index.type=ContentIndex.LOOKUP_TYPE_CHKS;
+                       index.fileNameIndex=nc.index;
+                       index.fileOffset=pos;
+                       index.hash=(HashCode160) 
PersistentHelper.copy(chk.query);
+
+                       request=new CSIndexBlock(index);
+                       if (!sock.send(request)) {
+                               log(Level.WARNING,"Could not send index 
information to gnunetd. Is gnunetd running ?");
+                               return false;
+                               }
+                       rv=(CSResult) sock.receive(CSResult.class);
+                       if (rv==null) {
+                               log(Level.WARNING,"Server did not send 
confirmation of insertion.");
+                               return false;
+                               }
+                       if (!rv.isOkay()) {
+                               log(Level.WARNING,"Server could not perform 
indexing.");
+                               return false;
+                               }
+                       return true;
+                       }
+               return insertCHKBlock(sock,edata,nc.priority);
+       }
+
+       public abstract boolean receive( HashCode160 query, CSResultChk reply, 
RequestManager rm, NodeContext nc );
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public static Block create( int size )
+       {
+               assert(size>0) : "size ("+size+") not allowed to be <= 0";
+
+               return (size>ContentBlock.SIZE ? (Block) new IBlock(size) : 
(Block) new DBlock(size));
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/esed2/CSDelete3Hash.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/esed2/CSDelete3Hash.java   
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/esed2/CSDelete3Hash.java   
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,75 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.esed2;
+
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * Structure for an incoming request messages from the local TCP link
+ * to add content to the node.
+ */
+
+public class CSDelete3Hash extends CSMessage
+{
+       public static final int SIZE    =       
8+HashCode160.SIZE+ContentBlock.SIZE;
+
+       /** The (initial) priority of the data. */
+       public int                              importance;
+
+       /** The doubleHash of the plaintext. */
+       public HashCode160              doubleHash;
+
+       /** The data to insert */
+       public ContentBlock             content;
+
+
+       public CSDelete3Hash()
+       {
+               super(IS_DELETE_3HASH);
+               importance=0;
+               doubleHash=new HashCode160();
+               content=new ContentBlock();
+       }
+
+       public String toString()
+       {
+               return "xxx";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int getByteSize()
+       {
+               return SIZE;
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               int     size,type;
+
+               size=buf.getShort() & 0x0000ffff;
+               err.reportIf(size!=SIZE,"bad size");
+
+               type=buf.getShort() & 0x0000ffff;
+               err.reportIf(type!=IS_DELETE_3HASH,"bad type !");
+
+               importance=buf.getInt();
+               doubleHash.readBytes(buf,err);
+               content.readBytes(buf,err);
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               buf.putShort((short) SIZE);
+               buf.putShort((short) IS_DELETE_3HASH);
+               buf.putInt(importance);
+               doubleHash.writeBytes(buf);
+               content.writeBytes(buf);
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/esed2/CSDeleteChk.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/esed2/CSDeleteChk.java     
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/esed2/CSDeleteChk.java     
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,68 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.esed2;
+
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * Structure for an incoming request messages from the local TCP link
+ * to add content to the node.
+ */
+
+public class CSDeleteChk extends CSMessage
+{
+       public static final int SIZE    =       8+ContentBlock.SIZE;
+
+       /** The (initial) priority of the data */
+       public int                      importance;
+
+       /** The data to insert */
+       public ContentBlock     content;
+
+
+       public CSDeleteChk()
+       {
+               super(IS_DELETE_CHK);
+               importance=0;
+               content=new ContentBlock();
+       }
+
+       public String toString()
+       {
+               return "xxx";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int getByteSize()
+       {
+               return SIZE;
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               int     size,type;
+
+               size=buf.getShort() & 0x0000ffff;
+               err.reportIf(size!=SIZE,"bad size");
+
+               type=buf.getShort() & 0x0000ffff;
+               err.reportIf(type!=IS_DELETE_CHK,"bad type !");
+
+               importance=buf.getInt();
+               content.readBytes(buf,err);
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               buf.putShort((short) SIZE);
+               buf.putShort((short) IS_DELETE_CHK);
+               buf.putInt(importance);
+               content.writeBytes(buf);
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/esed2/CSGetAvgPriority.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/esed2/CSGetAvgPriority.java        
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/esed2/CSGetAvgPriority.java        
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,53 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.esed2;
+
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ */
+
+public class CSGetAvgPriority extends CSMessage
+{
+       public static final int SIZE            =       4;
+
+
+       public CSGetAvgPriority()
+       {
+               super(IS_GET_AVG_PRIORITY);
+       }
+
+       public String toString()
+       {
+               return "Get average priority message";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int getByteSize()
+       {
+               return SIZE;
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               int     size,type;
+
+               size=buf.getShort() & 0x0000ffff;
+               err.reportIf(size!=SIZE,"invalid size !");
+
+               type=buf.getShort() & 0x0000ffff;
+               err.reportIf(type!=IS_GET_AVG_PRIORITY,"invalid type !");
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               buf.putShort((short) SIZE);
+               buf.putShort((short) IS_GET_AVG_PRIORITY);
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/esed2/CSIndexBlock.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/esed2/CSIndexBlock.java    
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/esed2/CSIndexBlock.java    
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,73 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.esed2;
+
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * Structure for an incoming request messages from the local TCP link
+ * to add content to the INDEX of the node.
+ */
+
+public class CSIndexBlock extends CSMessage
+{
+       public static final int SIZE    =       4+ContentIndex.SIZE;
+
+       /** indexing information */
+       private ContentIndex    contentIndex;
+
+
+       public CSIndexBlock()
+       {
+               super(IS_INDEX_BLOCK);
+               contentIndex=new ContentIndex();
+       }
+
+       public CSIndexBlock( ContentIndex idx )
+       {
+               this();
+               contentIndex=idx;
+       }
+
+       public String toString()
+       {
+               return "Client/server index block 
[contentIndex="+contentIndex+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public ContentIndex getContentIndex()
+       {
+               return contentIndex;
+       }
+
+       public int getByteSize()
+       {
+               return SIZE;
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               int     size,type;
+
+               size=buf.getShort() & 0x0000ffff;
+               err.reportIf(size!=SIZE,"bad size");
+
+               type=buf.getShort() & 0x0000ffff;
+               err.reportIf(type!=IS_INDEX_BLOCK,"bad type !");
+
+               contentIndex.readBytes(buf,err);
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               buf.putShort((short) SIZE);
+               buf.putShort((short) IS_INDEX_BLOCK);
+               contentIndex.writeBytes(buf);
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/esed2/CSIndexFile.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/esed2/CSIndexFile.java     
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/esed2/CSIndexFile.java     
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,79 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.esed2;
+
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.io.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * Structure for an incoming request messages from the local TCP link
+ * to add a filename to the list of directly shared files
+ */
+
+public class CSIndexFile extends CSMessage
+{
+       public static final int SIZE            =       8+HashCode160.SIZE;
+
+       /** Size of the file in bytes. */
+       public long                     filesize;
+
+       /** RIPE160 hash of the entire file (to avoid duplicates !). */
+       public HashCode160      hash;
+
+
+       public CSIndexFile()
+       {
+               super(IS_INDEX_FILE);
+               filesize=0;
+               hash=new HashCode160();
+       }
+
+       public CSIndexFile( FileLocation f )
+       {
+               this();
+               filesize=f.getSize();
+               hash=f.getHash();
+       }
+
+       public String toString()
+       {
+               return "Client/server index file [filesize="+filesize+", 
hash="+hash+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int getByteSize()
+       {
+               return SIZE;
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               int     size,type;
+
+               size=buf.getShort() & 0x0000ffff;
+               err.reportIf(size!=SIZE,"bad size !");
+
+               type=buf.getShort() & 0x0000ffff;
+               err.reportIf(type!=IS_INDEX_FILE,"bad type !");
+
+               filesize=buf.getInt();  //fixme: convert to a long value
+
+               hash=new HashCode160();
+               hash.readBytes(buf,err);
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               buf.putShort((short) SIZE);
+               buf.putShort((short) IS_INDEX_FILE);
+               buf.putInt((int) filesize);     //fixme: convert to a long value
+               hash.writeBytes(buf);
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/esed2/CSIndexSuper.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/esed2/CSIndexSuper.java    
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/esed2/CSIndexSuper.java    
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,87 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.esed2;
+
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * Structure for an incoming request messages from the local TCP link
+ * to add a super-query to the bloom filter.
+ */
+
+public class CSIndexSuper extends CSMessage
+{
+       public static final int SIZE    =       HashCode160.SIZE+8;
+
+       /** The super-hash for the bloom-filter. */
+       private HashCode160     superHash;
+
+       /** The (initial) priority of the data. */
+       private int                     importance;
+
+
+       public CSIndexSuper()
+       {
+               super(IS_INDEX_SUPER);
+               superHash=new HashCode160();
+               importance=0;
+       }
+
+       public CSIndexSuper( HashCode160 h, int pri )
+       {
+               this();
+               superHash=(HashCode160) PersistentHelper.copy(h);
+               importance=pri;
+       }
+
+       public String toString()
+       {
+               return "Client/server index super [superHash="+superHash+", 
importance="+importance+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public HashCode160 getSuperHash()
+       {
+               return superHash;
+       }
+
+       public int getImportance()
+       {
+               return importance;
+       }
+
+       public int getByteSize()
+       {
+               return SIZE;
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               int     size,type;
+
+               size=buf.getShort() & 0x0000ffff;
+               err.reportIf(size!=SIZE,"bad size");
+
+               type=buf.getShort() & 0x0000ffff;
+               err.reportIf(type!=IS_INDEX_SUPER,"bad type !");
+
+               superHash.readBytes(buf,err);
+
+               importance=buf.getInt();
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               buf.putShort((short) SIZE);
+               buf.putShort((short) IS_INDEX_SUPER);
+               superHash.writeBytes(buf);
+               buf.putInt(importance);
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/esed2/CSInsert3Hash.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/esed2/CSInsert3Hash.java   
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/esed2/CSInsert3Hash.java   
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,98 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.esed2;
+
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * Structure for an incoming request messages from the local TCP link
+ * to add content to the node.
+ */
+
+public class CSInsert3Hash extends CSMessage
+{
+       public static final int SIZE    =       
HashCode160.SIZE+ContentBlock.SIZE+8;
+
+       /** The (initial) priority of the data  (network byte order) */
+       private int                             importance;
+
+       /** The doubleHash of the plaintext. */
+       private HashCode160             doubleHash;
+
+       /** The data to insert */
+       private ContentBlock    content;
+
+
+       public CSInsert3Hash()
+       {
+               super(IS_INSERT_3HASH);
+               importance=0;
+               doubleHash=new HashCode160();
+               content=new ContentBlock();
+       }
+
+       public CSInsert3Hash( int prio, HashCode160 h, ContentBlock c )
+       {
+               this();
+               importance=prio;
+               doubleHash=h;
+               content=c;
+       }
+
+       public String toString()
+       {
+               return "Insert 3-hash (c/s) [importance="+importance+", 
doubleHash="+doubleHash+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int getImportance()
+       {
+               return importance;
+       }
+
+       public HashCode160 getDoubleHash()
+       {
+               return doubleHash;
+       }
+
+       public ContentBlock getContent()
+       {
+               return content;
+       }
+
+       public int getByteSize()
+       {
+               return SIZE;
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               int     size,type;
+
+               size=buf.getShort() & 0x0000ffff;
+               err.reportIf(size!=SIZE,"bad size");
+
+               type=buf.getShort() & 0x0000ffff;
+               err.reportIf(type!=IS_INSERT_3HASH,"bad type !");
+
+               importance=buf.getInt();
+               doubleHash.readBytes(buf,err);
+               content.readBytes(buf,err);
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               buf.putShort((short) SIZE);
+               buf.putShort((short) IS_INSERT_3HASH);
+               buf.putInt(importance);
+               doubleHash.writeBytes(buf);
+               content.writeBytes(buf);
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/esed2/CSInsertChk.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/esed2/CSInsertChk.java     
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/esed2/CSInsertChk.java     
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,87 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.esed2;
+
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * Structure for an incoming request messages from the local TCP link
+ * to add content to the node.
+ */
+
+public class CSInsertChk extends CSMessage
+{
+       private static final int        SIZE    =       ContentBlock.SIZE+8;
+
+       /** The (initial) priority of the data. */
+       private int                             importance;
+
+       /** The data to insert */
+       private ContentBlock    content;
+
+
+       public CSInsertChk()
+       {
+               super(IS_INSERT_CHK);
+               importance=0;
+               content=new ContentBlock();
+       }
+
+       public CSInsertChk( int prio, byte[] b )
+       {
+               this();
+               importance=prio;
+               assert(b.length==ContentBlock.SIZE);
+               System.arraycopy(b,0,content.content,0,b.length);
+       }
+
+       public String toString()
+       {
+               return "Client/server insert chk [importance="+importance+", 
content="+content+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int getImportance()
+       {
+               return importance;
+       }
+
+       public ContentBlock getContent()
+       {
+               return content;
+       }
+
+       public int getByteSize()
+       {
+               return SIZE;
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               int     size,type;
+
+               size=buf.getShort() & 0x0000ffff;
+               err.reportIf(size!=SIZE,"bad size");
+
+               type=buf.getShort() & 0x0000ffff;
+               err.reportIf(type!=IS_INSERT_CHK,"bad type !");
+
+               importance=buf.getInt();
+
+               content.readBytes(buf,err);
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               buf.putShort((short) SIZE);
+               buf.putShort((short) IS_INSERT_CHK);
+               buf.putInt(importance);
+               content.writeBytes(buf);
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/esed2/CSInsertSBlock.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/esed2/CSInsertSBlock.java  
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/esed2/CSInsertSBlock.java  
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,85 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.esed2;
+
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * Structure for an incoming request messages from the local TCP link
+ * to add content to the node.
+ */
+
+public class CSInsertSBlock extends CSMessage
+{
+       public static final int SIZE    =       SBlock.SIZE+8;
+
+       /** The (initial) priority of the data. */
+       private int                             importance;
+
+       /** The data to insert. */
+       private EncryptedSBlock content;
+
+
+       public CSInsertSBlock()
+       {
+               super(IS_INSERT_SBLOCK);
+               importance=0;
+               content=null;
+       }
+
+       public CSInsertSBlock( int i, EncryptedSBlock eb )
+       {
+               this();
+               importance=i;
+               content=eb;
+       }
+
+       public String toString()
+       {
+               return "C/S insert SBlock [importance="+importance+", 
content="+content+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int getImportance()
+       {
+               return importance;
+       }
+
+       public EncryptedSBlock getContent()
+       {
+               return content;
+       }
+
+       public int getByteSize()
+       {
+               return SIZE;
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               int     size,type;
+
+               size=buf.getShort() & 0x0000ffff;
+               err.reportIf(size!=SIZE,"bad size");
+
+               type=buf.getShort() & 0x0000ffff;
+               err.reportIf(type!=IS_INSERT_SBLOCK,"bad type !");
+
+               importance=buf.getInt();
+               content.readBytes(buf,err);
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               buf.putShort((short) SIZE);
+               buf.putShort((short) IS_INSERT_SBLOCK);
+               buf.putInt(importance);
+               content.writeBytes(buf);
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/esed2/CSLinkFile.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/esed2/CSLinkFile.java      
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/esed2/CSLinkFile.java      
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,89 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.esed2;
+
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.io.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * AFS message sent for linking to a file.
+ */
+
+public class CSLinkFile extends CSMessage
+{
+       public static final int SIZE            =       4+HashCode160.SIZE;
+
+       /** RIPE160 hash of the entire file (to avoid duplicates !) */
+       public HashCode160      hash;
+
+       /** Path of the file. */
+       public String           data;
+
+
+       public CSLinkFile()
+       {
+               super(IS_LINK_FILE);
+               hash=new HashCode160();
+               data="";
+       }
+
+       public CSLinkFile( FileLocation f )
+       {
+               this();
+               hash=f.getHash();
+               data=f.getPath();
+       }
+
+       public String toString()
+       {
+               return "Client/server link file [hash="+hash+", data="+data+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public String getPath() //todo: use FileLocation
+       {
+               return data;
+       }
+
+       public int getByteSize()
+       {
+               return SIZE+data.length();
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               byte[]  b;
+               int             size,type;
+
+               size=buf.getShort() & 0x0000ffff;
+               err.reportIf(size<SIZE,"bad size !");
+
+               type=buf.getShort() & 0x0000ffff;
+               err.reportIf(type!=IS_LINK_FILE,"bad type !");
+
+               hash=new HashCode160();
+               hash.readBytes(buf,err);
+
+               //fixme: character set encoding
+               b=new byte[size-SIZE];
+               buf.get(b);
+               data=new String(b);
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               buf.putShort((short) getByteSize());
+               buf.putShort((short) IS_LINK_FILE);
+               hash.writeBytes(buf);
+
+               //fixme: character set encoding
+               buf.put(data.getBytes());
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/esed2/CSNSQuery.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/esed2/CSNSQuery.java       
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/esed2/CSNSQuery.java       
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,60 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.esed2;
+
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * peer-to-peer message containing a namespace-query
+ */
+
+public class CSNSQuery extends CSMessage
+{
+       public static final int SIZE    =       HashCode160.SIZE*2+12;
+
+       /** how important is this request (network byte order) */
+       public int                      priority;
+
+       /** time to live in cronMILLIS (network byte order) */
+       public int                      ttl;
+
+       /** ID of the Namespace that we are searching in */
+       public HashCode160      namespace;
+
+       /** ID (in the namespace) that we're looking for */
+       public HashCode160      identifier;
+
+
+       public CSNSQuery()
+       {
+               super(IS_NSQUERY);
+       }
+
+       public String toString()
+       {
+               return "xxx";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int getByteSize()
+       {
+               return SIZE;
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               throw new IllegalStateException();
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               throw new IllegalStateException();
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/esed2/CSQuery.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/esed2/CSQuery.java 2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/esed2/CSQuery.java 2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,151 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.esed2;
+
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+import java.util.*;
+
+/**
+ * peer-to-peer message containing a set of queries.
+ */
+
+public class CSQuery extends CSMessage
+{
+       public static final int SIZE    =       12;
+
+       /** */
+       private int             size;
+
+       /** How important is this request ? */
+       private int             priority;
+
+       /** Time to live in milliseconds. */
+       private int             ttl;
+
+       /** Hashcodes of the files we're looking for.
+        If multiple queries are given, the first query is the super-query for 
the bloom filter. */
+       private List            queries;
+
+
+       public CSQuery()
+       {
+               super(IS_QUERY);
+               size=SIZE;
+               priority=0;
+               ttl=0;
+               queries=new ArrayList();
+       }
+
+       public String toString()
+       {
+               return "Client/server query [priority="+priority+", 
ttl="+ttl+", queries.count="+queries.size()+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int getPriority()
+       {
+               return priority;
+       }
+
+       public void setPriority( int pri )
+       {
+               priority=pri;
+       }
+
+       public int getTTL()
+       {
+               return ttl;
+       }
+
+       public void setTTL( int time )
+       {
+               ttl=time;
+       }
+
+       public void setPriorityAndTTL( int pri, int time )
+       {
+               priority=pri;
+               ttl=time;
+       }
+
+       public int getQueriesCount()
+       {
+               return queries.size();
+       }
+
+       public HashCode160 getQuery( int index )
+       {
+               return ((index>=0 && index<queries.size()) ? (HashCode160) 
queries.get(index) : null);
+       }
+
+       public void addQuery( HashCode160 h )
+       {
+               queries.add(PersistentHelper.copy(h));
+               size+=HashCode160.SIZE;
+       }
+
+       public HashCode160[] getQueries()
+       {
+               return (HashCode160[]) queries.toArray(new 
HashCode160[queries.size()]);
+       }
+
+       public void setQueries( HashCode160[] q )
+       {
+               queries.clear();
+               queries.addAll(Arrays.asList(q));
+               size=SIZE+q.length*HashCode160.SIZE;
+       }
+
+       public void clearQueries()
+       {
+               queries.clear();
+               size=SIZE;
+       }
+
+       public int getByteSize()
+       {
+               return SIZE+queries.size()*HashCode160.SIZE;
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               HashCode160     h;
+               int                     type,i;
+
+               size=buf.getShort() & 0x0000ffff;
+               err.reportIf((size<SIZE || ((size-SIZE) % 
HashCode160.SIZE)!=0),"bad size");
+
+               type=buf.getShort() & 0x0000ffff;
+               err.reportIf(type!=IS_QUERY,"bad type !");
+
+               priority=buf.getInt();
+               ttl=buf.getInt();
+
+               queries.clear();
+               for (i=(size-SIZE)/HashCode160.SIZE; i>0; i--) {
+                       h=new HashCode160();
+                       h.readBytes(buf,err);
+                       queries.add(h);
+                       }
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               int     i;
+
+               buf.putShort((short) size);
+               buf.putShort((short) IS_QUERY);
+               buf.putInt(priority);
+               buf.putInt(ttl);
+               for (i=0; i<queries.size(); i++) {
+                       ((HashCode160) queries.get(i)).writeBytes(buf);
+                       }
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/esed2/CSResult3Hash.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/esed2/CSResult3Hash.java   
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/esed2/CSResult3Hash.java   
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,104 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.esed2;
+
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * TCP communication: search result content send back by gnunetd
+ */
+
+public class CSResult3Hash extends CSMessage implements AFSConstants
+{
+       public static final int SIZE    =       HashCode160.SIZE+CONTENT_SIZE+4;
+
+       /** The double-hash. */
+       private HashCode160     hash;
+
+       /** The search result. */
+       private byte[]          result;
+
+
+       public CSResult3Hash()
+       {
+               super(IS_RESULT_3HASH);
+               hash=new HashCode160();
+               result=new byte[CONTENT_SIZE];
+       }
+
+       public CSResult3Hash( HashCode160 h, ContentBlock block )
+       {
+               this();
+               hash=(HashCode160) PersistentHelper.copy(h);    //todo: copie 
utile ???
+               System.arraycopy(block.content,0,result,0,CONTENT_SIZE);
+       }
+
+       public String toString()
+       {
+               return "C/S 3-hash result [hash="+hash+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public HashCode160 getDoubleHash()
+       {
+               return hash;
+       }
+
+       public HashCode160 getTripleHash()
+       {
+               return HashCode160.create(PersistentHelper.toBytes(hash));
+       }
+
+       /**
+        * Returns the content of the node in bytes.
+        * @param keyword
+        * @return
+        */
+
+       public byte[] decrypt( HashCode160 keyword )
+       {
+               byte[]          iv,buf;
+               SessionKey      skey;
+
+               // get key and init value from the hash code
+               iv=new byte[SessionKey.BLOWFISH_BLOCK_LENGTH];
+               skey=keyword.extractKey(iv);
+
+               buf=skey.decrypt(result,iv);
+               return buf;
+       }
+
+       public int getByteSize()
+       {
+               return SIZE;
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               int     size,type;
+
+               size=buf.getShort() & 0x0000ffff;
+               err.reportIf(size!=SIZE,"bad size !");
+
+               type=buf.getShort() & 0x0000ffff;
+               err.reportIf(type!=IS_RESULT_3HASH,"bad type !");
+
+               hash.readBytes(buf,err);
+               buf.get(result);
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               buf.putShort((short) SIZE);
+               buf.putShort((short) IS_RESULT_3HASH);
+               hash.writeBytes(buf);
+               buf.put(result);
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/esed2/CSResultChk.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/esed2/CSResultChk.java     
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/esed2/CSResultChk.java     
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,95 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.esed2;
+
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * client-server message for search results
+ *
+ * Used in the CS-TCP communication: search result content send back
+ * by gnunetd
+ */
+
+public class CSResultChk extends CSMessage
+{
+       public static final int SIZE    =       ContentBlock.SIZE+4;
+
+       /** The search result. */
+       public ContentBlock     result;
+
+
+       public CSResultChk()
+       {
+               super(IS_RESULT_CHK);
+               result=null;
+       }
+
+       public String toString()
+       {
+               return "xxx";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       /**
+        * Decrypts inner data block.
+        *
+        * @param keyword       represents the key concatenated with the 
initial value used in the alg
+        * @return                      decrypted block on success, null on 
error
+        */
+
+       public byte[] decrypt( HashCode160 keyword )
+       {
+               byte[]          iv;
+               SessionKey      skey;
+
+               // get key and init value from the hash code
+               iv=new byte[SessionKey.BLOWFISH_BLOCK_LENGTH];
+               skey=keyword.extractKey(iv);
+
+               return skey.decrypt(result.content,iv);
+       }
+
+       public int getByteSize()
+       {
+               return SIZE;
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               int     size,type;
+
+               size=buf.getShort() & 0x0000ffff;
+               err.reportIf(size!=SIZE,"bad size !");
+
+               type=buf.getShort() & 0x0000ffff;
+               err.reportIf(type!=IS_RESULT_CHK,"bad type !");
+
+               result=new ContentBlock();
+               result.readBytes(buf,err);
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               int     i;
+
+               buf.putShort((short) SIZE);
+               buf.putShort((short) IS_RESULT_CHK);
+
+               if (result!=null) {
+                       result.writeBytes(buf);
+                       }
+               else {
+                       for (i=0; i<ContentBlock.SIZE; i++) {
+                               buf.put((byte) 0);
+                               }
+                       }
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/esed2/CSResultSBlock.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/esed2/CSResultSBlock.java  
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/esed2/CSResultSBlock.java  
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,75 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.esed2;
+
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * client-server message for SBlock results
+ *
+ * Used in the CS-TCP communication: SBlock result content send back
+ * by gnunetd
+ */
+
+public class CSResultSBlock extends CSMessage
+{
+       public static final int SIZE    =       4+EncryptedSBlock.SIZE;
+
+       /** The search result. */
+       private EncryptedSBlock result;
+
+
+       public CSResultSBlock()
+       {
+               super(IS_RESULT_SBLOCK);
+               result=new EncryptedSBlock();
+       }
+
+       public CSResultSBlock( EncryptedSBlock esb )
+       {
+               this();
+               result=(EncryptedSBlock) PersistentHelper.copy(esb);    //todo: 
copie utile ???
+       }
+
+       public String toString()
+       {
+               return "C/S result SBlock [result="+result+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public EncryptedSBlock getResult()
+       {
+               return result;
+       }
+
+       public int getByteSize()
+       {
+               return SIZE;
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               int     size,type;
+
+               size=buf.getShort() & 0x0000ffff;
+               err.reportIf(size!=SIZE,"bad size");
+
+               type=buf.getShort() & 0x0000ffff;
+               err.reportIf(type!=IS_RESULT_SBLOCK,"bad type !");
+
+               result.readBytes(buf,err);
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               buf.putShort((short) SIZE);
+               buf.putShort((short) IS_RESULT_SBLOCK);
+               result.writeBytes(buf);
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/esed2/CSUnindexBlock.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/esed2/CSUnindexBlock.java  
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/esed2/CSUnindexBlock.java  
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,51 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.esed2;
+
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * Structure for an incoming request messages from the local TCP link
+ * to add content to the INDEX of the node.
+ */
+
+public class CSUnindexBlock extends CSMessage
+{
+       public static final int SIZE    =       ContentIndex.SIZE+4;
+
+       /** indexing information */
+       public ContentIndex     contentIndex;
+
+
+       public CSUnindexBlock()
+       {
+               super(IS_UNINDEX_BLOCK);
+       }
+
+       public String toString()
+       {
+               return "xxx";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int getByteSize()
+       {
+               return SIZE;
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               throw new IllegalStateException();
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               throw new IllegalStateException();
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/esed2/CSUnindexFile.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/esed2/CSUnindexFile.java   
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/esed2/CSUnindexFile.java   
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,79 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.esed2;
+
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.io.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * Structure for an incoming request messages from the local TCP link
+ * to add a filename to the list of directly shared files
+ */
+
+public class CSUnindexFile extends CSMessage
+{
+       public static final int SIZE            =       8+HashCode160.SIZE;
+
+       /** Size of the file. */
+       public long                     filesize;
+
+       /** RIPE160 hash of the entire file (to avoid duplicates !). */
+       public HashCode160      hash;
+
+
+       public CSUnindexFile()
+       {
+               super(IS_UNINDEX_FILE);
+               filesize=0;
+               hash=new HashCode160();
+       }
+
+       public CSUnindexFile( FileLocation f )
+       {
+               this();
+               filesize=f.getSize();
+               hash=f.getHash();
+       }
+
+       public String toString()
+       {
+               return "Client/server unindex file [filesize="+filesize+", 
hash="+hash+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int getByteSize()
+       {
+               return SIZE;
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               int     size,type;
+
+               size=buf.getShort() & 0x0000ffff;
+               err.reportIf(size!=SIZE,"bad size !");
+
+               type=buf.getShort() & 0x0000ffff;
+               err.reportIf(type!=IS_UNINDEX_FILE,"bad type !");
+
+               filesize=buf.getInt();  //fixme: convert to a long value
+
+               hash=new HashCode160();
+               hash.readBytes(buf,err);
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               buf.putShort((short) SIZE);
+               buf.putShort((short) IS_UNINDEX_FILE);
+               buf.putInt((int) filesize);     //fixme: convert to a long value
+               hash.writeBytes(buf);
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/esed2/CSUnindexSuper.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/esed2/CSUnindexSuper.java  
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/esed2/CSUnindexSuper.java  
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,55 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.esed2;
+
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * Structure for an incoming request messages from the local TCP link
+ * to add a super-query to the bloom filter.
+ */
+
+public class CSUnindexSuper extends CSMessage
+{
+       public static final int SIZE    =       HashCode160.SIZE+8;
+
+       /** The super-hash for the bloom-filter. */
+       public HashCode160      superHash;
+
+       /** The (initial) priority of the data  (network byte order) */
+       public int                      importance;
+
+
+       public CSUnindexSuper()
+       {
+               super(IS_UNINDEX_SUPER);
+       }
+
+       public String toString()
+       {
+               return "xxx";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int getByteSize()
+       {
+               return SIZE;
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               throw new IllegalStateException();
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               throw new IllegalStateException();
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/esed2/CSUploadFile.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/esed2/CSUploadFile.java    
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/esed2/CSUploadFile.java    
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,125 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.esed2;
+
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.io.*;
+import org.gnu.freeway.util.net.*;
+
+import java.io.*;
+import java.nio.*;
+import java.nio.channels.*;
+import java.util.logging.*;
+
+/**
+ * AFS message sent for uploading a file.
+ */
+
+public class CSUploadFile extends CSMessage
+{
+       public static final int SIZE            =       8+HashCode160.SIZE;
+
+       /** Position in the file. */
+       public long                     pos;
+
+       /** RIPE160MD hash of the entire file (to avoid duplicates !). */
+       public HashCode160      hash;
+
+       /** Chunk of binary content. */
+       public byte[]           data;
+
+
+       public CSUploadFile()
+       {
+               super(IS_UPLOAD_FILE);
+               pos=0;
+               hash=new HashCode160();
+               data=new byte[0];
+       }
+
+       public CSUploadFile( FileLocation f )
+       {
+               this();
+               hash=f.getHash();
+       }
+
+       public String toString()
+       {
+               return "Client/server upload file [pos="+pos+", hash="+hash+", 
data="+data+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public byte[] getChunk()
+       {
+               return data;
+       }
+
+       public int copyChunk( FileChannel fc ) throws IOException       //todo: 
with MappedFile
+       {
+               Logger  logger;
+               int             copied,read;
+
+               pos=(int) fc.position();
+
+               copied=Math.min(65532-SIZE,(int) (fc.size()-fc.position()));
+
+               data=new byte[copied];
+               read=fc.read(ByteBuffer.wrap(data));
+               if (read!=copied) {
+                       logger=Logger.getLogger(getClass().getName());
+                       logger.log(Level.SEVERE,"Unable to read "+copied+" 
bytes from "+fc+" (read "+read+") !");
+                       return -1;
+                       }
+               return read;
+       }
+
+       public int copyChunk( byte[] b, int offset )
+       {
+               int     copied;
+
+               pos=offset;
+
+               copied=Math.min(65532-SIZE,b.length-offset);
+
+               data=new byte[copied];
+               System.arraycopy(b,offset,data,0,copied);
+               return copied;
+       }
+
+       public int getByteSize()
+       {
+               return SIZE+data.length;
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               int     size,type;
+
+               size=buf.getShort() & 0x0000ffff;
+               err.reportIf(size<SIZE,"bad size !");
+
+               type=buf.getShort() & 0x0000ffff;
+               err.reportIf(type!=IS_UPLOAD_FILE,"bad type !");
+
+               pos=buf.getInt();       //fixme: convert to a long value
+
+               hash=new HashCode160();
+               hash.readBytes(buf,err);
+
+               data=new byte[size-SIZE];
+               buf.get(data);
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               buf.putShort((short) getByteSize());
+               buf.putShort((short) IS_UPLOAD_FILE);
+               buf.putInt((int) pos);  //fixme: convert to a long value
+               hash.writeBytes(buf);
+               buf.put(data);
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/esed2/ChkHashes.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/esed2/ChkHashes.java       
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/esed2/ChkHashes.java       
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,114 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.esed2;
+
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * Pair of Hashcodes for CHK encoded blocks.
+ *
+ * Every DBlock and IBlock is represented by two
+ * hashcodes, one is the key used to encrypt or
+ * decrypt the block; the other one is used to
+ * search for the block without reveiling the key.
+ * See also Freenet's CHK keys.<p>
+ *
+ * Note that GNUnet uses a different encoding for
+ * the RBlocks (root-nodes) in order to make searches
+ * possible.
+ */
+
+public class ChkHashes extends Object implements Persistent
+{
+       public static final int SIZE    =       
HashCode160.SIZE+HashCode160.SIZE;
+
+       /** The hash of the plaintext is the key to decrypt. */
+       public HashCode160      key;
+
+       /** The hash of the encrypted block is the query. */
+       public HashCode160      query;
+
+
+       public ChkHashes()
+       {
+               super();
+               key=new HashCode160();
+               query=new HashCode160();
+       }
+
+       public String toString()
+       {
+               return "Check hashes [key="+key+", query="+query+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public HashCode160 getKeyHash()
+       {
+               return key;
+       }
+
+       public HashCode160 getQueryHash()
+       {
+               return query;
+       }
+
+       public int hashCode()
+       {
+               return (key.hashCode()<<1)^query.hashCode();
+       }
+
+       public boolean equals( Object obj )
+       {
+               ChkHashes       c;
+
+               if (!(obj instanceof ChkHashes)) {
+                       return false;
+                       }
+               c=(ChkHashes) obj;
+               return (c.key.equals(key) && c.query.equals(query));
+       }
+
+       public int getByteSize()
+       {
+               return SIZE;
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               key=new HashCode160();
+               key.readBytes(buf,err);
+
+               query=new HashCode160();
+               query.readBytes(buf,err);
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               int     i;
+
+               if (key!=null) {
+                       key.writeBytes(buf);
+                       }
+               else {
+                       for (i=0; i<HashCode160.SIZE; i++) {
+                               buf.put((byte) 0);
+                               }
+                       }
+
+               if (query!=null) {
+                       query.writeBytes(buf);
+                       }
+               else {
+                       for (i=0; i<HashCode160.SIZE; i++) {
+                               buf.put((byte) 0);
+                               }
+                       }
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/esed2/ContentBlock.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/esed2/ContentBlock.java    
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/esed2/ContentBlock.java    
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,63 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.esed2;
+
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+import java.util.*;
+
+/**
+ * basic transmission unit for content in GNUnet
+ *
+ * A CONTENT_Block, representative of the structure
+ * of the leaf nodes (a simple chunk of 1 kb of data)
+ */
+
+public class ContentBlock extends Object implements Persistent, AFSConstants
+{
+       public static final int SIZE    =       1024;
+
+
+       public byte[]   content;
+
+//TODO: SUPPRIMER, NE SERT A RIEN !!!!!!!!!!!!
+       public ContentBlock()
+       {
+               super();
+               content=new byte[CONTENT_SIZE];
+               Arrays.fill(content,(byte) 0);  // not necessary, but...
+       }
+
+       public ContentBlock( byte[] b )
+       {
+               this();
+               assert(b.length==CONTENT_SIZE);
+               System.arraycopy(b,0,content,0,b.length);
+       }
+
+       public String toString()
+       {
+               return "Content block";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int getByteSize()
+       {
+               return SIZE;
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               buf.get(content);
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               buf.put(content);
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/esed2/ContentEncoding.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/esed2/ContentEncoding.java 
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/esed2/ContentEncoding.java 
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,65 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.esed2;
+
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+
+/**
+ * Encryption and decryption of blocks for deniability.
+ */
+
+public class ContentEncoding extends LoggedObject
+{
+       public ContentEncoding()
+       {
+               super(true);
+       }
+
+       public String toString()
+       {
+               return "xxx";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       /**
+        * Encrypts a given data block
+        *
+        * @param data represents the data block
+        * @param hashcode represents the key concatenated with the initial
+        *        value used in the alg
+        * @param result where to store the result (encrypted block)
+        * @return OK on success, false on error
+        */
+
+       public boolean encryptContent( ContentBlock data, HashCode160 hashcode, 
ContentBlock result )
+       {
+               return encryptContent(data.content,hashcode,result);
+       }
+
+       public boolean encryptContent( byte[] data, HashCode160 hashcode, 
ContentBlock result )
+       {
+               byte[]          iv,res;
+               SessionKey      skey;
+
+               if (data==null || hashcode==null || result==null) {
+                       trace("Aborting encryptContent: null in arguments.");
+                       return false;
+                       }
+
+               // get key and init value from the hash code
+               iv=new byte[SessionKey.BLOWFISH_BLOCK_LENGTH];
+               skey=hashcode.extractKey(iv);
+
+               res=skey.encrypt(data,0,ContentBlock.SIZE,iv);
+               if (res==null) {
+                       return false;
+                       }
+               System.arraycopy(res,0,result.content,0,res.length);
+               return true;
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/esed2/ContentIndex.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/esed2/ContentIndex.java    
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/esed2/ContentIndex.java    
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,136 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.esed2;
+
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * Type of the content index file entries. The size of this
+ * struct dominates the database size, so keep it as small
+ * as possible. 32 byte should be enough!
+ *
+ * This structure is also used as a convenience struct to
+ * pass arguments around the db. Perhaps not a good idea.
+ */
+
+public class ContentIndex extends Object implements Persistent
+{
+       public static final int SIZE    =       HashCode160.SIZE+12;
+
+       /** Free entry. Historical. */
+       public static final int LOOKUP_TYPE_FREE        =       0;
+
+       /** Historical. */
+       public static final int LOOKUP_TYPE_DELETED     =       1;
+
+       /** (migrated) CHK content. */
+       public static final int LOOKUP_TYPE_CHK         =       2;
+
+       /** Search result, never indexed (always inserted). */
+       public static final int LOOKUP_TYPE_3HASH       =       3;
+
+       /** Super-query. Add to superBloomFilter, does not refer to any content 
in particular. */
+       public static final int LOOKUP_TYPE_SUPER       =       4;
+
+       /** CHK content covered by super-query (treat like CHK except do not 
add to singleBloomFilter). */
+       public static final int LOOKUP_TYPE_CHKS        =       5;
+
+       /** SBlock content. */
+       public static final int LOOKUP_TYPE_SBLOCK      =       6;
+
+       /** The double-hash (hash of the hash of the plaintext) of this entry 
for 3HASH entries, or the CHK query hash
+        (hash of the encrypted content) for CHK entries. Which is the case can 
be determined by looking at fileNameIndex and fileOffset. */
+       public HashCode160      hash;
+
+       /** The current rating of this content (in network byte order). */
+       public int                      importance;
+
+       /** The type of the entry. See LOOKUP_TYPE_XXX. The field is always in 
network byte order. */
+       public int                      type;
+
+       /** This field gives the index of the file into the file-index module 
if the value is >0. If the
+        value is 0, the file is in the contentdatabase. The field is always in 
network byte order. */
+       public int                      fileNameIndex;
+
+       /** The offset in the file for on-demand-encoded files where 
fileNameIndex is >0. */
+       public long                     fileOffset;
+
+
+       public ContentIndex()
+       {
+               super();
+               hash=null;
+               importance=0;
+               type=0;
+               fileNameIndex=0;
+               fileOffset=0;
+       }
+
+       public String toString()
+       {
+               return "Content index";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int hashCode()
+       {
+               return type;
+       }
+
+       public boolean equals( Object obj )
+       {
+               ContentIndex    ce;
+
+               if (!(obj instanceof ContentIndex)) {
+                       return false;
+                       }
+
+               ce=(ContentIndex) obj;
+               return ((ce.hash!=null ? ce.hash.equals(hash) : hash==null) &&
+                       ce.importance==importance &&
+                       ce.type==type &&
+                       ce.fileNameIndex==fileNameIndex &&
+                       ce.fileOffset==fileOffset);
+       }
+
+       public int getByteSize()
+       {
+               return SIZE;
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               hash=new HashCode160();
+               hash.readBytes(buf,err);
+
+               importance=buf.getInt();
+               type=buf.getShort() & 0x0000ffff;
+               fileNameIndex=buf.getShort() & 0x0000ffff;
+               fileOffset=buf.getInt();        //fixme: long value
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               int     i;
+
+               if (hash!=null) {
+                       hash.writeBytes(buf);
+                       }
+               else {
+                       for (i=0; i<HashCode160.SIZE; i++) {
+                               buf.put((byte) 0);
+                               }
+                       }
+               buf.putInt(importance);
+               buf.putShort((short) type);
+               buf.putShort((short) fileNameIndex);
+               buf.putInt((int) fileOffset);   //fixme: long value
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/esed2/DBlock.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/esed2/DBlock.java  2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/esed2/DBlock.java  2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,284 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.esed2;
+
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.util.logging.*;
+
+/**
+ * Leaf (level-zero node) in the merkle-tree.
+ */
+
+public class DBlock extends Block
+{
+       /**
+        * Create a top-DBlock for files <= 1k where there is no parent IBlock.
+        *
+        * @param size the size of the file
+        */
+
+       public DBlock( int size )
+       {
+               super(size,size);
+       }
+
+       /**
+        * Create a DBlock. Note that this method can NOT be used for files <=
+        * 1k since parent may not be null (which it would be for the
+        * top-block). Use createTopDBlock for files <= 1k.
+        *
+        * @param parent        the parent block
+        * @param pos2          the offset of b block in the file
+        */
+
+       public DBlock( IBlock parent, int pos2 )
+       {
+               
super(parent,pos2,Math.min(ContentBlock.SIZE,parent.getFileSize()-pos2));
+       }
+
+       public String toString()
+       {
+               return "Data block [offset="+getFilePosition()+", 
size="+getFileSize()+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int getDepth()
+       {
+               return 0;
+       }
+
+       /**
+        * Insert the current block into the network. Implementations
+        * are also responsible for updating the corresponding fields
+        * of the parent node (of course, except if the parent is
+        * null in the case of the top-node in the tree).<p>
+        *
+        * Inner nodes first call the respective inserter methods for
+        * their children.<p>
+        *
+        * @param nc the context (gives us the priority)
+        * @param sock the socket to use to talk to the core
+        * @return OK on success, false on error
+        */
+
+       public boolean insert( NodeContext nc, CSSession sock )
+       {
+               boolean res;
+
+//debug("DI1 "+hasData());
+               if (hasData()) {
+                       return true;
+                       }
+
+//debug("will load");
+               if (!loadData(nc,sock!=null)) {
+                       return false;
+                       }
+
+               debug("Read "+getDataLength()+" bytes from IOC for insertion 
(2).");
+
+               nc.stats.progress += getDataLength();
+               if (nc.pmodel != null) {
+                       nc.pmodel.progress(nc.stats,nc.data);
+                       }
+
+               res = super.insert(nc,sock);
+               debug("Inserting dblock "+getFilePosition()+" of len 
"+getDataLength()+" under query "+getQuery().toHex()+".");
+               return res;
+       }
+
+       /**
+        * Delete the current block from the local peer. Works just
+        * like insert.
+        *
+        * @param nc the context (gives us the priority)
+        * @param sock the socket to use to talk to the core
+        * @return OK on success, false on error
+        */
+
+       public boolean delete( NodeContext nc, CSSession sock )
+       {
+               boolean res;
+
+               if (hasData()) {
+                       return true;
+                       }
+
+               if (!loadData(nc,sock!=null)) {
+                       return false;
+                       }
+               debug("Loaded "+getDataLength()+" bytes for deletion.");
+
+               nc.stats.progress += getDataLength();
+               if (nc.pmodel != null) {
+                       nc.pmodel.progress(nc.stats,nc.data);
+                       }
+               res = super.delete(nc,sock);
+               debug("Deleting dblock "+getFilePosition()+" of len 
"+getDataLength()+" under query "+getQuery().toHex());
+               return res;
+       }
+
+       /**
+        * Check if this dblock is already present on the drive.
+        * If the block is present, the parent and the
+        * ProgressModel are notified.
+        *
+        * @param nc the context (gives us the priority)
+        * @return YES if present, NO if not.
+        */
+
+       public boolean check( NodeContext nc )
+       {
+               HashCode160     hc;
+
+               debug("check: "+this);
+
+               /* first check if its already present */
+
+               if (loadData(nc,false)) {
+                       hc=HashCode160.create(getRawData(),0,getDataLength());
+                       if (hc.equals(getKey())) {
+                               setStatus(BLOCK_PRESENT);
+                               nc.stats.filesize = getFileSize();
+                               nc.stats.progress += getDataLength();
+                               nc.pmodel.progress(nc.stats,nc.data);
+                               return true;
+                               }
+                       }
+               clearData();
+               return false;
+       }
+
+       /**
+        * Download this node (and the children below). Note that the
+        * processing is asynchronous until the pmodel is called with position
+        * == total (and thus no more requests are pending) or the request
+        * manager is aborted by the user.
+        *
+        * @param nc the context (gives us the priority)
+        * @param rm the request manager
+        */
+
+       public void download( NodeContext nc, RequestManager rm )
+       {
+               debug("download");
+
+               if (check(nc)) {
+                       if (getParent() != null) {
+                               getParent().childDownloadCompleted(this,nc,rm);
+                               }
+                       /* leaf node, we're done when present */
+                       setStatus(BLOCK_DONE);
+                       destroy(rm);
+                       return;
+                       }
+               // not present, either request ourselves or let the parent do 
it automagically when we return...
+               setStatus(BLOCK_PENDING);
+               if (getParent() == null) {
+                       issueQuery(rm,new Listener() {
+                               public boolean listen( HashCode160 query, 
CSResultChk reply, RequestManager rmg, NodeContext data )
+                               {
+                                       return receive(query,reply,rmg,data);
+                               }
+                               },nc,getQuery());
+                       }
+       }
+
+       /**
+        * Function that is called when a message matching a
+        * request for a DBlock is received. Decrypts the received
+        * block and writes it to the file. Notifies the parent
+        * and the ProgressModel.
+        *
+        * @param query the query that was sent out
+        * @param reply the reply that was received
+        * @param rm the handle for the request manager
+        * @param nc the context (gives us the priority)
+        * @return false the request manager should abort the download,
+        *         OK if everything is fine
+        */
+
+       public boolean receive( HashCode160 query, CSResultChk reply, 
RequestManager rm, NodeContext nc )
+       {
+               ProgressStats   pstats;
+               int                             i;
+
+               debug("dblxock_download_receive : "+this);
+
+               if (!isStatus(BLOCK_PENDING)) {
+                       trace("dblxock_download_receive called, but no request 
was pending !");
+                       return false;
+                       }
+               if (!receivedChk(query,reply)) {
+                       pstats=new ProgressStats();
+                       nc.pmodel.progress(pstats,nc.data);
+                       return false;
+                       }
+               if (!writeData(nc)) {
+                       pstats=new ProgressStats();
+                       nc.pmodel.progress(pstats,nc.data);
+                       log(Level.SEVERE,"Writing to file failed !");
+                       return false;
+                       }
+
+               for (i=0; i<10; i++) {
+                       if (nc.stats.progress * 10000L > nc.stats.filesize * 
(10000L-(1024>>i)) &&
+                               (nc.stats.progress-getDataLength()) * 10000L <= 
nc.stats.filesize * (10000L - (1024 >> i))
+                               ) {
+                               // end-game boundary crossed, slaughter TTLs
+                               rm.endGame();
+                               }
+                       }
+
+               setStatus(BLOCK_PRESENT);
+               /* request satisfied, remove from RM */
+
+               nc.stats.progress += getDataLength();
+               if (getParent() != null) {
+                       /* child, must tell parent to adjust requests */
+                       getParent().childDownloadCompleted(this,nc,rm);
+                       getParent().doSuperRequest(nc,rm);
+                       }
+               else {
+                       /* top block, must cancel my own request */
+                       rm.update(this,null);
+                       }
+               // leaf, done when download complete
+               setStatus(BLOCK_DONE);
+               destroy(rm);
+               nc.pmodel.progress(nc.stats,nc.data);
+               return true;
+       }
+
+       /**
+        * Print a block to log.
+        * @param indent
+        */
+
+       public void print( int indent )
+       {
+               debug("print: "+this);
+
+               log(Level.FINEST,Utils.repeat('*',indent)+" DBLOCK (0) 
"+getFilePosition()+" "+getQuery().toHex());
+       }
+
+       /**
+        * Free the associated resources of this Block. DOES ALSO free the
+        * memory occupied by the Block struct itself!
+        *
+        * @param rm reference to the RequestManager for requests
+        */
+
+       public void destroy( RequestManager rm )
+       {
+               debug("destroy");
+               super.destroy(rm);
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/esed2/DeleteURI.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/esed2/DeleteURI.java       
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/esed2/DeleteURI.java       
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,29 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.esed2;
+
+/**
+ */
+
+public class DeleteURI extends GeneralURI
+{
+//     public int              action;
+       public String   filename;
+
+
+       public DeleteURI()
+       {
+               super();
+       }
+
+       public String toString()
+       {
+               return "xxx";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/esed2/DeleteUtil.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/esed2/DeleteUtil.java      
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/esed2/DeleteUtil.java      
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,116 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.esed2;
+
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.io.*;
+import org.gnu.freeway.util.net.*;
+
+import java.util.logging.*;
+
+/**
+ * deleteutil, helper methods for file deletion.
+ * Break file that is deleted into blocks and encrypts
+ * them according to the CHK-triple-hash-tree scheme.
+ * Then sends delete-requests to gnunetd.
+ */
+
+public class DeleteUtil extends LoggedObject
+{
+       public DeleteUtil()
+       {
+               super(true);
+       }
+
+       public String toString()
+       {
+               return "xxx";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       /**
+        * Ask gnunetd for an index that matches the filename
+        * @param sock
+        * @param filename
+        * @return the index, -1 on error
+        */
+
+       protected int askDeleteFilename( CSSession sock, String filename )
+       {
+               CSUnindexFile   request;
+               CSResult        rv;
+               FileLocation            f;
+
+               f=new FileLocation(filename);
+               if (!f.exists()) {
+                       return -1;
+                       }
+
+               request=new CSUnindexFile(f);
+
+               if (!sock.send(request)) {
+                       log(Level.WARNING,"Could not send data to gnunetd. Is 
gnunetd running ?");
+                       return -1;
+                       }
+
+               rv=(CSResult) sock.receive(CSResult.class);
+               if (rv==null) {
+                       log(Level.WARNING,"Could not receive data from gnunetd. 
Is gnunetd running ?");
+                       return -1;
+                       }
+               return rv.getResult();
+       }
+
+       /**
+        * Deletes a file under the given name from the local GNUnet node.
+        *
+        * @param sock the socket to use to talk to gnunetd
+        * @param filename the name of the file to delete
+        * @param model the delete model used to
+        *        update status information; points to null if
+        *        no status updates shall be given, otherwise
+        *        to a method that takes two size_t arguments
+        *        (retrieved so far, total).
+        * @param model_data pointer that is passed to the model method
+        * @return OK on success, false on error
+        */
+
+       public boolean deleteFile( CSSession sock, String filename, 
ProgressModel model, Object model_data )
+       {
+               NodeContext     nc;
+               Block           top;
+               int                     filesize,ret;
+
+               filename = new FileLocation(filename).getPath();
+               filesize = (int) new FileLocation(filename).getSize();
+
+               nc=new NodeContext();
+               nc.pmodel = model;
+               nc.data = model_data;
+               nc.stats.filesize = filesize;
+               nc.priority = 0;
+
+               ret=askDeleteFilename(sock, filename);
+               if (ret <= 0) {
+                       return false;
+                       }
+               nc.index = ret;
+               if (!nc.ioc.init(filesize,filename,true)) {
+                       return false;
+                       }
+
+               top = Block.create(filesize);
+               if (!top.delete(nc,sock)) {
+                       top.destroy(null);
+                       nc.ioc.free(false);
+                       return false;
+                       }
+               nc.ioc.free(false);
+               top.destroy(null);
+               return true;
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/esed2/DownloadURI.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/esed2/DownloadURI.java     
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/esed2/DownloadURI.java     
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,31 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.esed2;
+
+/**
+ */
+
+public class DownloadURI extends GeneralURI
+{
+//     public int                              action;
+       public FileIdentifier           fid;
+       public String                   filename;
+
+
+       public DownloadURI()
+       {
+               super();
+       }
+
+       public String toString()
+       {
+               return "xxx";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/esed2/DownloadUtil.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/esed2/DownloadUtil.java    
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/esed2/DownloadUtil.java    
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,111 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.esed2;
+
+import org.gnu.freeway.*;
+
+/**
+ * Helper functions for downloading.
+ * Download helper methods (which do the real work).
+ */
+
+public class DownloadUtil extends Object
+{
+
+       public DownloadUtil()
+       {
+               super();
+       }
+
+       public String toString()
+       {
+               return "xxx";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       protected void pModelWrap( ProgressStats stats, PMWrap wrap )
+       {
+               if (wrap.userModel!=null) {
+                       wrap.userModel.progress(stats,wrap.userData);
+                       }
+               if (stats.progress == stats.filesize) {
+                       wrap.nc.ioc.free(stats.progress!=0);
+                       }
+       }
+
+       /**
+        * Download a file.
+        *
+        * @param app
+        * @param p
+        * @param pppp
+        * @param fi the file identification (CHK, crc32, size) of the file
+        * @param fileName the name of the file
+        * @param model the download model used to
+        *        update status information; points to null if
+        *        no status updates shall be given, otherwise
+        *        to a method that takes two size_t arguments
+        *        (retrieved so far, total).
+        * @param data pointer that is passed to the model method
+        * @return a request manager that can be used to abort on success, null 
on error
+        */
+
+       public RequestManager downloadFile( AbstractClient app, Policy p, 
Priority pppp, FileIdentifier fi, String fileName, ProgressModel model, Object 
data )
+       {
+               NodeContext             nc;
+               Block                   top;
+               RequestManager  rm;
+               PMWrap                  wrap;
+
+               nc=new NodeContext();
+
+               rm=new RequestManager(app,p,pppp);
+               if (rm==null) {
+                       return null;
+                       }
+
+               if (!nc.ioc.init((int) fi.getFileLength(),fileName,false)) {    
//fixme: long value
+                       rm.destroy();
+                       return null;
+                       }
+
+               wrap=new PMWrap();
+               wrap.userModel = model;
+               wrap.userData = data;
+               wrap.nc = nc;
+
+               final PMWrap    _wrap = wrap;
+
+               nc.priority = 0; /* unused */
+               nc.index = 0; /* unused */
+               nc.pmodel = new ProgressModel() {
+                       public void progress( ProgressStats stats, Object o )
+                       {
+                               pModelWrap(stats,_wrap);
+                       }
+                       };
+               nc.data = wrap;
+               nc.stats=new ProgressStats();
+               nc.stats.filesize=(int) fi.getFileLength();     //fixme: long 
value
+
+               top=Block.create((int) fi.getFileLength());     //fixme: long 
value
+               top.setKeyAndQuery(fi);
+
+               rm.topCrc32=fi.getFileCRC();
+               rm.top=top;
+
+               top.download(nc,rm);
+               return rm;
+       }
+}
+
+class PMWrap extends Object
+{
+       protected ProgressModel userModel;
+       protected Object                userData;
+       protected NodeContext   nc;
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/esed2/EncryptedSBlock.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/esed2/EncryptedSBlock.java 
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/esed2/EncryptedSBlock.java 
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,130 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.esed2;
+
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ *
+ */
+
+public class EncryptedSBlock extends LoggedObject implements Persistent
+{
+       public static final int SIZE                            =       1024;
+       public static final int ENCRYPTED_SIZE  =       484;
+       public static final int SIGNED_SIZE             =       504;
+
+       /** */
+       private byte[]          encryptedData;
+
+       /** */
+       private HashCode160     identifier;
+
+       /** */
+       private Signature       signature;
+
+       /** */
+       private PublicKey       subspace;
+
+
+       public EncryptedSBlock()
+       {
+               super(true);
+               encryptedData=new byte[ENCRYPTED_SIZE];
+               identifier=new HashCode160();
+               signature=new Signature();
+               subspace=new PublicKey();
+       }
+
+       public EncryptedSBlock( byte[] b, HashCode160 id, Signature sig, 
PublicKey pub )
+       {
+               this();
+
+               assert(b.length==ENCRYPTED_SIZE);
+               System.arraycopy(b,0,encryptedData,0,ENCRYPTED_SIZE);
+               identifier=(HashCode160) PersistentHelper.copy(id);     //todo: 
copie utile ???
+               signature=(Signature) PersistentHelper.copy(sig);               
//todo: copie utile ???
+               subspace=(PublicKey) PersistentHelper.copy(pub);                
//todo: copie utile ???
+       }
+
+       public String toString()
+       {
+               return "xxx";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       /**
+        * Verify that a given encryted SBlock is well-formed.
+        * @return
+        */
+
+       public boolean verify()
+       {
+               byte[]  raw;
+
+               raw=PersistentHelper.toBytes(this);
+               return subspace.verify(signature,raw,0,SIGNED_SIZE);
+       }
+
+       /**
+        * @param key
+        * @return
+        */
+
+       public SBlock decrypt( HashCode160 key )
+       {
+               byte[]          raw,iv,tmp;
+               SessionKey      skey;
+
+               raw=PersistentHelper.toBytes(this);
+
+               iv=new byte[SessionKey.BLOWFISH_BLOCK_LENGTH];
+               skey=key.extractKey(iv);
+
+               tmp=skey.decrypt(raw,0,ENCRYPTED_SIZE,iv);
+               if (tmp==null || tmp.length!=ENCRYPTED_SIZE) {
+                       return null;
+                       }
+               System.arraycopy(tmp,0,raw,0,ENCRYPTED_SIZE);
+               return (SBlock) 
PersistentHelper.readFully(SBlock.class,ByteBuffer.wrap(raw));
+       }
+
+       public HashCode160 getIdentifier()
+       {
+               return identifier;
+       }
+
+       public HashCode160 getNameSpace()
+       {
+               return HashCode160.create(PersistentHelper.toBytes(subspace));
+       }
+
+       public int getByteSize()
+       {
+               return SIZE;
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               buf.put(encryptedData);
+               identifier.writeBytes(buf);
+               signature.writeBytes(buf);
+               subspace.writeBytes(buf);
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               buf.get(encryptedData);
+               identifier.readBytes(buf,err);
+               signature.readBytes(buf,err);
+               subspace.readBytes(buf,err);
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/esed2/Extractor.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/esed2/Extractor.java       
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/esed2/Extractor.java       
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,26 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.esed2;
+
+/**
+ *
+ */
+
+public class Extractor extends Object
+{
+       public Extractor()
+       {
+               super();
+       }
+
+       public String toString()
+       {
+               return "xxx";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/esed2/FileIdentifier.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/esed2/FileIdentifier.java  
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/esed2/FileIdentifier.java  
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,187 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.esed2;
+
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+import java.util.logging.*;
+import java.util.regex.*;
+
+/**
+ * information required to download a file from GNUnet
+ *
+ * A FileIdentifier groups the information
+ * required to download (and check) a file.
+ */
+
+public class FileIdentifier extends Object implements Persistent, AFSConstants
+{
+       public static final int SIZE            =       48;
+
+       /** Total size of the file in bytes. */
+       private long                    fileLength;
+
+       /** Top CRC of the tree-encoding. */
+       private int                     fileCRC;
+
+       /** Query and key of the top IBlock. */
+       private ChkHashes       chk;
+
+
+       public FileIdentifier()
+       {
+               super();
+               fileLength=0;
+               fileCRC=0;
+               chk=new ChkHashes();
+       }
+
+       public FileIdentifier( Block b )
+       {
+               this();
+               chk=(ChkHashes) PersistentHelper.copy(b.getKeyAndQuery());      
//todo: copie utile ???
+               fileCRC=b.getDataCRC();
+               fileLength=b.getFileSize();
+       }
+
+       public FileIdentifier( long length, int crc, ChkHashes h )
+       {
+               this();
+               fileLength=length;
+               fileCRC=crc;
+               chk=(ChkHashes) PersistentHelper.copy(h);       //todo: copie 
utile ???
+       }
+
+       public String toString()
+       {
+               return "File identifier";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public long getFileLength()
+       {
+               return fileLength;
+       }
+
+       public int getFileCRC()
+       {
+               return fileCRC;
+       }
+
+       public HashCode160 getFileQuery()
+       {
+               return chk.query;
+       }
+
+       public HashCode160 getFileKey()
+       {
+               return chk.key;
+       }
+
+       /**
+        * Convert a fileIdentifier to an URI string
+        * (to display it to the user).
+        *
+        * @return string containing the url (must be freed by caller)
+        */
+
+       public String toURI()
+       {
+               StringBuffer    buf;
+               String          str;
+
+               buf=new StringBuffer();
+               buf.append(GNUNET_URI_PREFIX_AFS);
+               buf.append(chk.key.toHex());
+               buf.append(".");
+               buf.append(chk.query.toHex());
+               buf.append(".");
+
+               str="00000000000000000000000"+Utils.toHex(fileCRC);//todo: 
mieux faire !!!
+               buf.append(str.substring(str.length()-8));
+
+               buf.append(".");
+               buf.append(fileLength);
+               return buf.toString();
+       }
+
+       public int getByteSize()
+       {
+               return SIZE;
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               int     i;
+
+               buf.putInt((int) fileLength);   //fixme: long value
+               buf.putInt(fileCRC);
+               if (chk!=null) {
+                       chk.writeBytes(buf);
+                       }
+               else {
+                       for (i=0; i<ChkHashes.SIZE; i++) {
+                               buf.put((byte) 0);
+                               }
+                       }
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               fileLength=buf.getInt();
+               fileCRC=buf.getInt();
+               chk=new ChkHashes();
+               chk.readBytes(buf,err);
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       /**
+        * Converts an AFS uri to a FileIdentifier
+        * FIXME: Not much error handling. Doesn't recognize
+        * a namespace URL. We'd need something more general,
+        * or a mechanism that recognizes uri type and routes
+        * it to the right function.
+        *
+        * @param str the URI of a fileIdentifier
+        * @return the fileidentifier (caller must free)
+        */
+
+       public static FileIdentifier parseURI( String str )
+       {
+               FileIdentifier  fid;
+               Logger                  logger;
+               Matcher                 m;
+
+               assert(str!=null) : "null string";
+
+               if (!str.startsWith(GNUNET_URI_PREFIX_AFS)) {
+                       logger=Logger.getLogger(FileIdentifier.class.getName());
+                       logger.log(Level.SEVERE,"Malformed URI :"+str);
+                       return null;
+                       }
+
+               
m=Pattern.compile("([0-9A-Za-z]{40})\\.([0-9A-Za-z]{40})\\.([0-9A-Fa-f]+)\\.([0-9]+)").matcher("");
+               m.reset(str.substring(GNUNET_URI_PREFIX_AFS.length()));
+               if (!m.matches()) {
+                       logger=Logger.getLogger(FileIdentifier.class.getName());
+                       logger.log(Level.SEVERE,"Malformed URI :"+str);
+                       return null;
+                       }
+
+               fid=new FileIdentifier();
+               fid.chk.key=HashCode160.parse(m.group(1));
+               fid.chk.query=HashCode160.parse(m.group(2));
+               fid.fileCRC=(int) Long.parseLong(m.group(3),16);        //car 
parseInt ne prend pas en compte si >2^31 en non signe
+               fid.fileLength=Long.parseLong(m.group(4));
+               return fid;
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/esed2/GNDirectory.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/esed2/GNDirectory.java     
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/esed2/GNDirectory.java     
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,172 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.esed2;
+
+import org.gnu.freeway.util.net.*;
+
+import java.io.*;
+import java.nio.*;
+import java.util.*;
+import java.util.logging.*;
+
+/**
+ * Format of a GNUnet directory (both in memory and on the drive).
+ * functions for building directories
+ *
+ * Helper functions for building directories.
+ *
+ * Directories are an add-on mechanism on top of the ESED II.  As
+ * such, gnunetd has no notion of directories.  Thus, this code is
+ * NEVER run inside of gnunetd but always by the various AFS tools.
+ * Since multiple AFS tools may concurrently access the directories
+ * from different processes, IPC is required to synchronize the
+ * access.
+ *
+ */
+
+public class GNDirectory extends Object implements Persistent, AFSConstants
+{
+       public static final int SIZE    =       RootNode.SIZE;  // 1024
+
+       /** */
+       public byte[]           MAGIC;
+
+       /** in network byte order */
+       public int                      version;
+
+       /** number of files in the directory */
+       public int                      number_of_files;
+
+       /** description/filename of the directory */
+       public String           description;
+
+       /** must be zero for now */
+       public byte[]           reserved;
+
+       /** number_of_files root-nodes */
+       public RootNode[]       contents;
+
+
+       public GNDirectory()
+       {
+               super();
+               MAGIC=new byte[8];
+               Arrays.fill(MAGIC,(byte) 0);
+               version=0;
+               number_of_files=0;
+               description="";
+               reserved=new byte[RootNode.SIZE - 256 - 16];
+               Arrays.fill(reserved,(byte) 0);
+               contents=new RootNode[] {};
+       }
+
+       /**
+        * Build a GNUnet directory in memory.
+        *
+        * @param numberOfEntries how many files are in the directory
+        * @param name what is the name of the directory
+        * @param entries the entries in the directory
+        */
+
+       public GNDirectory( int numberOfEntries, String name, RootNode[] 
entries )
+       {
+               this();
+
+               int     i;
+               System.arraycopy(GNUNET_DIRECTORY_MAGIC.getBytes(),0,MAGIC,0,8);
+               number_of_files = numberOfEntries;
+               if (!name.endsWith(File.separator)) {
+                       name+="/";
+                       }
+               description=name;
+
+               contents=new RootNode[numberOfEntries];
+               for (i=0; i<contents.length; i++) {
+                       contents[i]=entries[i];
+                       }
+       }
+
+       public String toString()
+       {
+               return "xxx";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public String getDescription()
+       {
+               return description;
+       }
+
+       public int getByteSize()
+       {
+               return SIZE+number_of_files*RootNode.SIZE;
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               int     i;
+
+               buf.put(MAGIC);
+               buf.putInt(version);
+               buf.putInt(number_of_files);
+               AFSUtils.put(description,buf,256);
+               buf.put(reserved);
+
+               for (i=0; i<number_of_files; i++) {
+                       contents[i].writeBytes(buf);
+                       }
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               int     i;
+
+               buf.get(MAGIC);
+               version=buf.getInt();
+               number_of_files=buf.getInt();
+               description=AFSUtils.get(buf,256);
+               buf.get(reserved);
+
+               err.reportIf(version!=0,"bad version");
+               
err.reportIf(!Arrays.equals(MAGIC,GNUNET_DIRECTORY_MAGIC.getBytes()),"bad 
magic");
+
+               contents=new RootNode[number_of_files];
+               for (i=0; i<number_of_files; i++) {
+                       contents[i]=new RootNode();
+                       contents[i].readBytes(buf,err);
+                       }
+
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       /**
+        * Appends a suffix ".gnd" to a given string if the suffix
+        * doesn't exist already. Existing suffix '/' is replaced if
+        * encountered.
+        *
+        * @param dn the directory name (string)
+        * @return the converted name on success
+        */
+
+       public static String expandDirectoryName( String dn )
+       {
+               if (dn==null) {
+                       
Logger.getLogger("").log(Level.SEVERE,"expandDirectoryName called with null 
argument");
+                       return null;
+                       }
+
+               if (dn.endsWith(File.separator)) {
+                       dn=dn.substring(0,dn.length()-File.separator.length());
+                       }
+               if (!dn.endsWith(GNUNET_DIRECTORY_EXT)) {
+                       dn+=GNUNET_DIRECTORY_EXT;
+                       }
+               return dn;
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/esed2/GNDirectoryDatabase.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/esed2/GNDirectoryDatabase.java     
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/esed2/GNDirectoryDatabase.java     
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,229 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.esed2;
+
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.io.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+import java.util.*;
+import java.util.logging.*;
+
+/**
+ * The "state" database (see include/util/state.h) is used
+ * to store the data.  Note that state does not do any locking,
+ * and that it in particular can not do any locking for us since
+ * it is IPC!
+ */
+
+public class GNDirectoryDatabase extends LoggedObject implements AFSConstants
+{
+       /** */
+       private Prefs   prefs;
+
+
+       public GNDirectoryDatabase( Prefs p )
+       {
+               super(true);
+               prefs=p;
+       }
+
+       public String toString()
+       {
+               return "GN directory database";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       /**
+        * Makes a root-node available for directory building.
+        *
+        * This function is called whenever a root-node is encountered.  This
+        * can either be because the user inserted a file locally; because we
+        * received a search result or because the user retrieved a directory
+        * with root-nodes.  From which context the root node was encountered
+        * is specified in the context parameters.<p>
+        *
+        * makeRootNodeAvailable adds the node to the list of files that
+        * we can build a directory from later.  The context is used to allow
+        * the user to filter on root-node sources.
+        *
+        * @param root the file identifier that was encountered
+        * @param context the context in which the identifier was encountered 
(may not be a bitmask)
+        */
+
+       public void makeRootNodeAvailable( Node root, int context )
+       {
+               byte[]          b;
+               RootNode        nd;
+               MappedFile      sem;
+               ByteBuffer      buf;
+               int                     ret;
+
+               if (!prefs.testString("AFS","COLLECT-FILE-IDENTIFIERS","YES") ) 
{
+                       log(Level.FINEST,"Collecting file identifiers 
disabled.");
+                       return;
+                       }
+
+               sem=createLock();
+               if (sem==null) {
+                       return;
+                       }
+
+               try {
+                       b=PersistentHelper.toBytes(root);
+
+                       buf=prefs.getContent("dir"+context);
+                       if (buf!=null) {
+                               // if size is not a multiple of the RootNode 
size, try to fix DB by truncating !
+                               ret=buf.limit();
+                               if (ret % RootNode.SIZE != 0) {
+                                       ret -= ret % RootNode.SIZE;
+
+                                       buf.position(ret);
+                                       buf.limit(ret);
+                                       prefs.putContent("dir"+context,buf);
+                                       buf.position(0);
+                                       }
+
+                               ret = ret / RootNode.SIZE;
+                               while (ret > 0) {
+                                       ret--;
+
+                                       nd=(RootNode) 
PersistentHelper.read(RootNode.class,buf);
+                                       if 
(Arrays.equals(b,PersistentHelper.toBytes(nd))) {//todo: 'equals' sur RootNode, 
NNode, SBlock
+                                               return; // already present
+                                               }
+                                       }
+                               }
+                       prefs.appendContent("dir"+context,b);
+                       }
+               finally {
+                       releaseLock(sem);
+                       }
+       }
+
+       /**
+        * Remove all of the root-nodes of a particular type
+        * from the directory database.
+        *
+        * @param contexts bitmask of the databases that should be emptied.
+        */
+
+       public void emptyDirectoryDatabase( int contexts )
+       {
+               MappedFile      sem;
+               int                     i;
+
+               sem=createLock();
+               if (sem==null) {
+                       return;
+                       }
+
+               try {
+                       i=1;
+                       while (contexts > 0) {
+                               if ((contexts & i) > 0) {
+                                       contexts -= i;
+                                       prefs.unlink("dir"+i);
+                                       }
+                               i*=2;
+                               }
+                       }
+               finally {
+                       releaseLock(sem);
+                       }
+       }
+
+       /**
+        * Iterate over all entries that match the given context
+        * mask.
+        *
+        * @param contexts context bitmask for the entries to iterate over
+        * @param callback function to call on each entry, may be null
+        * @param closure extra argument to the callback
+        * @return number of entries found
+        */
+
+       public int iterateDirectoryDatabase( int contexts, RootNodeCallback 
callback, Object closure )
+       {
+               RootNode        nd;
+               MappedFile      sem;
+               ByteBuffer      buf;
+               int                     ret,rval,i;
+
+               sem=createLock();
+               if (sem==null) {
+                       return 0;
+                       }
+
+               rval=0;
+               try {
+                       i = 1;
+                       while (contexts > 0) {
+                               if ((contexts & i) > 0) {
+                                       contexts -= i;
+
+                                       buf=prefs.getContent("dir"+i);
+                                       if (buf!=null) {
+                                               // if size is not a multiple of 
the RootNode size, try to fix DB by truncating !
+                                               ret=buf.limit();
+                                               if (ret % RootNode.SIZE != 0) {
+                                                       ret -= ret % 
RootNode.SIZE;
+
+                                                       buf.position(ret);
+                                                       buf.limit(ret);
+                                                       
prefs.putContent("dir"+i,buf);
+                                                       buf.position(0);
+                                                       }
+
+                                               ret = ret / RootNode.SIZE;
+                                               while (ret > 0) {
+                                                       ret--;
+                                                       nd=(RootNode) 
PersistentHelper.read(RootNode.class,buf);
+                                                       if (callback != null) {
+                                                               
callback.rootNode(nd,closure);
+                                                               }
+                                                       rval++;
+                                                       }
+                                               }
+                                       }
+                               i*=2;
+                               }
+                       }
+               finally {
+                       releaseLock(sem);
+                       }
+               return rval;
+       }
+
+       protected MappedFile createLock()
+       {
+               FileLocation            loc;
+               MappedFile              f;
+
+               
loc=prefs.getDirLocation("","GNUNET_HOME").getFile("directory_ipc_lock");
+
+               f=loc.openNew();
+               if (f==null) {
+                       return null;
+                       }
+               if (f.lock()) {
+                       return f;
+                       }
+               f.close();
+               return null;
+       }
+
+       protected void releaseLock( MappedFile f )
+       {
+               f.unlock();
+               f.close();
+
+               f.getLocation().delete();
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/esed2/GeneralURI.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/esed2/GeneralURI.java      
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/esed2/GeneralURI.java      
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,31 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.esed2;
+
+/**
+ * Header prefix for whatever is in an AFS URI.
+ * "action" is the identifier of the URI purpose/context.
+ */
+
+public class GeneralURI extends Object
+{
+       public int              action;
+//     public Object   data;
+
+
+       public GeneralURI()
+       {
+               super();
+       }
+
+       public String toString()
+       {
+               return "xxx";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/esed2/IBlock.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/esed2/IBlock.java  2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/esed2/IBlock.java  2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,757 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.esed2;
+
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+import java.util.logging.*;
+
+/**
+ * An inner node in the merkle-tree.
+ */
+
+public class IBlock extends Block
+{
+       /** Number of CHK_Hashes per IBlock. The value must be 25 since 
25*40+20+4 is 1024.
+        The other values are 40=CHK_Hashes.SIZE, 20=HashCode160.SIZE for the 
super-hash
+        and 4=int.SIZE for the CRC32. */
+       public static final int CHK_PER_INODE   =       25;
+
+
+       /** The depth of this node in the file tree. At depth 0 we have the 
leaves, since this is an IBlock, depth is always > 0. */
+       private int             depth;
+
+       /** CRC (if (data != null): ((IBlockData)data).crc32). */
+       private int             crc32;
+
+       /** Number of children [1-CHK_PER_INODE] of this node. */
+       private int             childcount;
+
+       /** References to the children (IBlocks or DBlocks, depending on if 
depth > 1 or not). */
+       private Block[] children;
+
+       /** CRC of each of the children. */
+       private int[]   crcs;
+
+
+       /**
+        * Create a top-IBlock for the root of the file tree.
+        * Note that you must set the chk field before calling
+        * download.
+        * @param filesize2 the size of the file
+        */
+
+       public IBlock( int filesize2 )
+       {
+               
super(filesize2,computeDataLength(filesize2,0,IOContext.computeDepth(filesize2)));
+               depth=IOContext.computeDepth(filesize2);
+               childcount=0;
+               children=null;//new Block[CHK_PER_INODE];
+               crcs=new int[CHK_PER_INODE];
+
+               init();
+       }
+
+       /**
+        * Create an IBlock. Use createTopIBlxock for the
+        * node on top of the file-tree.
+        *
+        * @param pos2          the position of the IBlock in the file
+        * @param parent2       the parent block
+        */
+
+       public IBlock( IBlock parent2, int pos2 )
+       {
+               
super(parent2,pos2,computeDataLength(parent2.getFileSize(),pos2,parent2.depth - 
1));
+               depth=parent2.depth-1;
+               childcount=0;
+               children=null;//new Block[CHK_PER_INODE];
+               crcs=new int[CHK_PER_INODE];
+
+               init();
+       }
+
+       public IBlock( IBlock b )
+       {
+               super(b);
+               depth=b.depth;
+               childcount=b.childcount;
+               children=null;//new Block[CHK_PER_INODE];
+               System.arraycopy(b.children,0,children,0,b.children.length);
+               crcs=new int[CHK_PER_INODE];
+               System.arraycopy(b.crcs,0,crcs,0,b.crcs.length);
+
+               init();
+       }
+
+       public String toString()
+       {
+               return "Inner block [offset="+getFilePosition()+", 
size="+getFileSize()+", childcount="+childcount+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       /**
+        * Initialize IBlock fields
+        */
+
+       protected void init()
+       {
+               
childcount=computeChildCount(getFileSize(),getFilePosition(),depth);
+               children=null;
+       }
+
+       /**
+        * Allocate space for children.
+        */
+
+       protected void allocateChildren()
+       {
+               int     cover,i;
+
+               if (children!=null)
+                       return;
+
+               children=new Block[childcount];
+
+               // create child nodes
+               cover=computeChildCover(depth);
+               for (i=0; i<childcount; i++) {
+                       if (depth>1) {
+                               children[i]=new 
IBlock(this,getFilePosition()+i*cover);
+                               }
+                       else {
+                               children[i]=new 
DBlock(this,getFilePosition()+i*cover);
+                               }
+                       }
+       }
+
+       public int getDepth()
+       {
+               return depth;
+       }
+
+       public boolean childrenDestroyed( Block b )
+       {
+               int     live,i;
+
+               live=0;
+               if (children!=null) {
+                       for (i=0; i<childcount;i++) {
+                               if (children[i] == b)
+                                       children[i] = null;
+                               if (children[i] != null)
+                                       live++;
+                               }
+                       }
+               return (live==0 && !isStatus(BLOCK_PERSISTENT));
+       }
+
+       /**
+        * A child has been completely downloaded. Perform the
+        * appropriate CRC checks in the parent node and free associated
+        * resources if possible. Since the only errors are either
+        * bugs or hash-crc-collisions (probability 1:2^160), we
+        * always die on errors (return values do not work well for
+        * async calls anyway).<p>
+        *
+        * Note that the leaves update the ProgressModel, so we do
+        * not have to worry about that.
+        * Note that the leaves update the ProgressModel, so we do
+        * not have to worry about that. If all children of a node
+        * are complete, this method calls itself recursively to
+        * notify the parent of the parent.
+        *
+        * @param child the completed child block
+        * @param nc the context (IO, priority, etc.)
+        * @param rm request manager to schedule queries
+        */
+
+       public void childDownloadCompleted( Block child, NodeContext nc, 
RequestManager rm )
+       {
+               int     i,pendingChildren;
+
+               debug("childDownloadxComplete "+this+" "+child);
+
+               assert(children!=null);
+
+               for (i=0;i<childcount;i++)
+                       if (children[i] == child)
+                               break;
+
+               assert (i!=childcount) : "childxDownloadCompleted called on 
node that does not know that child ! ("+child+", "+this+")";
+
+               crcs[i] = Crypto.crc32(child.getRawData(), 
0,child.getDataLength());
+
+               pendingChildren = 0;
+               for (i=0;i<childcount;i++)
+                       if ( (children[i] != null) && 
!children[i].isStatus(BLOCK_PRESENT))
+                               pendingChildren++;
+
+               // check if this IBlock is complete, if yes, go to our parent 
and notify that we are done !
+               if (getParent() != null) {
+                       if (pendingChildren == 0) {
+                               if (fuckedCRC32(crcs,childcount)!=crc32) {
+                                       log(Level.SEVERE,"File corrupted (or 
bug), crc mismatch in block "+depth+
+                                               " "+getFilePosition()+
+                                               ": 
"+fuckedCRC32(crcs,childcount)+
+                                               " != "+crc32);
+                                       }
+                               getParent().childDownloadCompleted(this,nc,rm);
+                               }
+                       }
+               else {  // parent == null
+                       if (pendingChildren == 0) {
+                               if (fuckedCRC32(crcs,childcount)!=crc32 ||
+                                       !checkTopCRC(rm.topCrc32)) {
+                                       log(Level.SEVERE,"File corrupted (or 
bug), top CRC mismatch in block "+depth+
+                                               " "+getFilePosition()+
+                                               ": 
"+Utils.toHex(fuckedCRC32(crcs,childcount))+
+                                               " != "+Utils.toHex(crc32)+
+                                               " or 
"+checkTopCRC(rm.topCrc32));
+
+                                       return;
+                                       }
+                               }
+                       }
+
+               // free memory as early as possible !
+               if (pendingChildren == 0) {
+                       clearData();
+                       }
+       }
+
+       public static int fuckedCRC32( int[] p, int count )
+       {
+               byte[]          b;
+               ByteBuffer      buf;
+               int                     i,crc;
+
+               b=new byte[count*4];
+               buf=ByteBuffer.wrap(b);
+               buf.order(ByteOrder.LITTLE_ENDIAN);//hum, hum...
+               for (i=0; i<count; i++) {
+                       buf.putInt(p[i]);
+                       }
+               crc=Crypto.crc32(b);
+               return ((crc & 0x000000ff)<<24) |
+                       ((crc & 0x0000ff00)<<8) |
+                       ((crc>>8) & 0x0000ff00) |
+                       ((crc>>24) & 0x000000ff)
+                       ;
+       }
+
+       /**
+        * Insert the current block into the network. Implementations
+        * are also responsible for updating the corresponding fields
+        * of the parent node (of course, except if the parent is
+        * null in the case of the top-node in the tree).<p>
+        *
+        * Inner nodes first call the respective inserter methods for
+        * their children.<p>
+        *
+        * @param nc the context (gives us the priority)
+        * @param sock the socket to use to talk to the core
+        * @return OK on success, false on error
+        */
+
+       public boolean insert( NodeContext nc, CSSession sock )
+       {
+               Block           child;
+               IBlockData      ibd;
+               CSIndexSuper    req;
+               ContentBlock    edata;
+               CSResult                rv;
+               int                     ui,childCover;
+
+               debug("insert");
+
+               setStatus(BLOCK_PERSISTENT);
+               ibd = new IBlockData();
+               setData(ibd);
+
+               childCover = ContentBlock.SIZE;
+               for (ui=0;ui<depth-1;ui++)
+                       childCover *= CHK_PER_INODE;
+
+               allocateChildren();
+
+               for (ui=0; ui<childcount; ui++) {
+                       child = children[ui];
+                       if (!child.insert(nc,sock)) {
+                               if (sock != null)
+                                       log(Level.WARNING,"Child insertion 
failed on level "+depth+", pos "+child.getFilePosition()+", aborting !");
+                               return false; /* abort! */
+                               }
+
+                       crcs[ui] = 
Crypto.crc32(child.getRawData(),0,child.getDataLength());
+                       ibd.chks[ui]=(ChkHashes) 
PersistentHelper.copy(child.getKeyAndQuery());
+                       child.destroy(null);
+                       children[ui] = null;
+                       }
+
+               
ibd.superHash=HashCode160.create(PersistentHelper.toBytes(ibd.chks),0,ChkHashes.SIZE
 * childcount);
+
+               if (nc.index!=0 && sock!=null) {
+                       req=new CSIndexSuper(ibd.superHash,nc.priority);
+
+                       if (sock.send(req)) {
+                               rv=(CSResult) sock.receive(CSResult.class);
+                               if (rv==null) {
+                                       log(Level.WARNING,"Server did not send 
confirmation of insertion.");
+                                       return false;
+                                       }
+                               if (!rv.isOkay()) {
+                                       log(Level.WARNING,"Server could not 
perform insertion.");
+                                       return false;
+                                       }
+                               }
+                       else {
+                               log(Level.WARNING,"Could not send super-index 
information to gnunetd. Is gnunetd running ?");
+                               return false;
+                               }
+                       }
+
+               ibd.crc32 = fuckedCRC32(crcs,childcount);
+               crc32 = ibd.crc32;
+               edata = encrypt();
+               return insertCHKBlock(sock,edata,nc.priority);
+       }
+
+       /**
+        * Remove the current block from the local AFS storage.
+        *
+        * @param nc the context (gives us the priority)
+        * @param sock the socket to use to talk to the core
+        * @return OK on success, false on error
+        */
+
+       public boolean delete( NodeContext nc, CSSession sock )
+       {
+               int                             ui,childCover;
+               Block                   child;
+               IBlockData              ibd;
+               CSUnindexSuper  req;
+               ContentBlock            edata;
+               CSResult                        rv;
+
+               setStatus(BLOCK_PERSISTENT);
+               ibd = new IBlockData();
+               setData(ibd);
+               childCover = ContentBlock.SIZE;
+               for (ui=0;ui<depth-1;ui++)
+                       childCover *= CHK_PER_INODE;
+
+               allocateChildren();
+
+               for (ui=0;ui<childcount;ui++) {
+                       child = children[ui];
+                       if (!child.delete(nc,sock)) {
+                               if (sock != null) {
+                                       log(Level.WARNING,"Failed to delete 
child on level "+depth+", pos "+child.getFilePosition()+". Will continue.");
+                                       }
+                               }
+                       crcs[ui] = 
Crypto.crc32(child.getRawData(),0,child.getDataLength());
+                       ibd.chks[ui]=(ChkHashes) 
PersistentHelper.copy(child.getKeyAndQuery());
+                       child.destroy(null);
+                       children[ui] = null;
+                       }
+
+               
ibd.superHash=HashCode160.create(PersistentHelper.toBytes(ibd.chks),0,ChkHashes.SIZE
 * childcount);
+               if (sock != null) {
+                       req=new CSUnindexSuper();
+                       req.importance = nc.priority;
+                       req.superHash=(HashCode160) 
PersistentHelper.copy(ibd.superHash);
+
+                       if (sock.send(req)) {
+                               rv=(CSResult) sock.receive(CSResult.class);
+                               if (rv==null) {
+                                       log(Level.WARNING,"Server did not send 
confirmation of deletion.");
+                                       return false;
+                                       }
+                               if (!rv.isOkay()) {
+                                       // super blocks don't matter !
+                                       }
+                               }
+                       else {
+                               log(Level.WARNING,"Could not send super-unindex 
information to gnunetd. Is gnunetd running ?");
+                               return false;
+                               }
+                       }
+
+               ibd.crc32 = fuckedCRC32(crcs,childcount);
+               edata = encrypt();
+               return deleteCHKBlock(sock,edata,nc.priority);
+       }
+
+       /**
+        * The request manager got a reply for one of the childs
+        * we were looking after. Update the RM query, call
+        * receive on the appropriate child, etc.
+        *
+        * @param query the query that was sent out
+        * @param reply the reply that was received
+        * @param rm the handle for the request manager
+        * @param nc the context (gives us the priority)
+        * @return false the request manager should abort the download
+        */
+
+       protected boolean receiveChild( HashCode160 query, CSResultChk reply, 
RequestManager rm, NodeContext nc )
+       {
+               int                     i;
+               IBlockData      ibd;
+
+               debug("iblock_download_receivec_child "+this);
+
+               if (!isStatus(BLOCK_SUPERQUERY_PENDING)) {
+                       trace("No superquery is pending !");
+                       return false;
+                       }
+
+               debug("iblock "+this+" receives message for child");
+
+               ibd=(IBlockData) getData(IBlockData.class);
+
+               allocateChildren();
+
+               for (i=0; i<childcount; i++) {
+                       if (query.equals(ibd.chks[i].query)) {
+                               if (children[i]!=null && 
children[i].isStatus(BLOCK_PENDING)) {
+                                       return 
children[i].receive(query,reply,rm,nc);
+                                       }
+                               }
+                       }
+               return true;    // we may receive replies twice, just ignore 
those
+       }
+
+       /**
+        * Call download on the children to test if they are present.
+        *
+        * @param nc the context (gives us the priority)
+        * @param rm the request manager
+        */
+
+       protected void downloadChildren( NodeContext nc, RequestManager rm )
+       {
+               int                     i;
+               IBlockData      ibd;
+               Block           child;
+
+               debug("iblock_download_childcren "+this);
+
+               assert(childcount<=CHK_PER_INODE) : "iblock "+this+" has 
"+childcount+" children !";
+
+               ibd=(IBlockData) getData(IBlockData.class);
+
+               allocateChildren();
+
+               for (i=0; i<childcount; i++) {
+                       child=children[i];
+                       if (child!=null) {
+                               child.setKeyAndQuery(ibd.chks[i]);
+                               child.download(nc,rm);
+                               }
+                       }
+       }
+
+       /**
+        * Send the super-request that groups the queries for all
+        * child-nodes in one large query. Note that recursion and
+        * updates are checked by the "superState" field of IBlock.
+        *
+        * @param rm reference to the RequestManager for requests
+        * @param nc the context (gives us the priority)
+        */
+
+       protected void doSuperRequest( NodeContext nc, RequestManager rm )
+       {
+               IBlockData              ibd;
+               CSQuery msg;
+               int                             liveChildren,i;
+
+               debug("ibclock_do_superrequest "+this);
+
+               liveChildren = 0;
+               allocateChildren();
+               for (i=0;i<childcount;i++)
+                       if (children[i] != null)
+                               if (children[i].isStatus(BLOCK_PENDING))
+                                       liveChildren++;
+
+               if (liveChildren == 0) {
+                       debug("iblock "+this+" cancels request, all children 
done ("+getStatus()+")");
+
+                       /* finally drop remaining requests, all satisfied! */
+                       if (isStatus(BLOCK_SUPERQUERY_PENDING)) {
+                               rm.update(this,null);
+                               }
+                       setStatus(BLOCK_CHILDREN_PRESENT);
+                       return; /* we are done here! */
+                       }
+
+               ibd=(IBlockData) getData(IBlockData.class);
+
+               msg=new CSQuery();
+               msg.setPriorityAndTTL(1,1);
+               msg.addQuery(ibd.superHash);
+//trace("CSQuery (1): "+msg);
+
+               liveChildren = 0;
+               allocateChildren();
+               for (i=0;i<childcount;i++) {
+                       if (children[i] != null) {
+                               if (children[i].isStatus(BLOCK_PENDING)) {
+                                       msg.addQuery(ibd.chks[i].query);
+                                       liveChildren++;
+                                       }
+                               }
+                       }
+
+               if (isStatus(BLOCK_SUPERQUERY_PENDING)) {
+                       debug("iblock "+this+" updates request, 
"+liveChildren+" children pending");
+
+                       rm.update(this,msg);
+                       }
+               else {
+                       debug("iblock "+this+" starts request, "+liveChildren+" 
children pending");
+
+                       setStatus(BLOCK_SUPERQUERY_PENDING);
+                       rm.request(this,new Listener() {
+                               public boolean listen( HashCode160 query, 
CSResultChk reply, RequestManager rmg, NodeContext data )
+                               {
+                                       return 
receiveChild(query,reply,rmg,data);
+                               }
+                               },nc,msg);
+                       }
+       }
+
+       /**
+        * Type of a method that is called by the RequestManager
+        * whenever a reply to a query has been received.
+        *
+        * @param query the query that was sent out
+        * @param reply the reply that was received
+        * @param rm the handle for the request manager
+        * @param nc the context (gives us the priority)
+        * @return false the request manager should abort the download
+        */
+
+       public boolean receive( HashCode160 query, CSResultChk reply, 
RequestManager rm, NodeContext nc )
+       {
+               ProgressStats   pstats;
+
+               debug("receive");
+
+               if (!isStatus(BLOCK_PENDING)) {
+                       /* As far as I can tell, this should never happen */
+                       log(Level.WARNING,"IBlock "+this+" receives reply, but 
we are already done !");
+                       return true;
+                       }
+
+               debug("iblock "+this+" receives reply");
+
+               if (!receivedChk(query,reply)) {
+                       pstats=new ProgressStats();
+                       nc.pmodel.progress(pstats,nc.data);
+                       return false;
+                       }
+
+               if (!writeData(nc)) {
+                       pstats=new ProgressStats();
+                       nc.pmodel.progress(pstats,nc.data);
+                       log(Level.SEVERE,"Write to temporary IBlock file 
failed, aborting.");
+                       return false;
+                       }
+
+               crc32=((IBlockData) getData()).crc32;
+
+               setStatus(BLOCK_PRESENT);
+               if (getParent() == null) {
+                       /* our request, stop doing it */
+                       rm.update(this,null);
+                       }
+               else {
+                       getParent().childDownloadCompleted(this,nc,rm);
+                       getParent().doSuperRequest(nc,rm);
+                       }
+               setStatus(BLOCK_PERSISTENT);
+               downloadChildren(nc,rm);
+               doSuperRequest(nc,rm);
+               return true;
+       }
+
+       /**
+        * Download this node (and the children below). Note that the
+        * processing is asynchronous until the pmodel is called with position
+        * == total (and thus no more requests are pending) or the request
+        * manager is aborted by the user.
+        *
+        * @param rm the request manager
+        * @param nc the context (gives us the priority)
+        */
+
+       public void download( NodeContext nc, RequestManager rm )
+       {
+               NodeContext     fakeContext;
+               IBlock          fakeThis;
+               boolean         isPresent;
+
+               debug("download");
+debug("S1");
+               isPresent=check(nc);
+debug("S11 "+isPresent);
+               if (!isPresent) {
+                       fakeContext=new NodeContext();
+                       fakeContext.ioc=new IOContext(nc.ioc);
+                       fakeContext.priority=0;
+                       fakeContext.index=-1;
+                       fakeContext.pmodel=ProgressModel.NO_MODEL;
+                       fakeContext.data = null;
+                       fakeContext.stats.progress = 0;
+
+                       fakeThis=new IBlock(this);
+
+                       fakeThis.detach();
+                       fakeThis.setStatus(BLOCK_PERSISTENT);
+
+                       if (fakeThis.insert(fakeContext,null)) {
+                               if 
(fakeThis.getKeyAndQuery().equals(getKeyAndQuery())) {
+                                       setStatus(BLOCK_PRESENT);
+                                       setData(fakeThis.getData());
+                                       crc32=fakeThis.crc32;
+                                       fakeThis.clearData();
+
+                                       isPresent = true;
+                                       }
+                               }
+                       fakeThis.destroy(null);
+                       }
+
+               if (isPresent) {
+                       if (getParent() != null) {
+                               getParent().childDownloadCompleted(this,nc,rm);
+                               getParent().doSuperRequest(nc,rm);
+                               }
+                       setStatus(BLOCK_PERSISTENT);
+
+                       downloadChildren(nc,rm);
+                       doSuperRequest(nc,rm);
+                       return;
+                       }
+               // not present, either request ourselves or let the parent do 
it automagically when we return...
+               setStatus(BLOCK_PENDING);
+               if (getParent() == null) {
+                       issueQuery(rm,new Listener() {
+                               public boolean listen( HashCode160 query, 
CSResultChk reply, RequestManager rmg, NodeContext data )
+                               {
+                                       return receive(query,reply,rmg,data);
+                               }
+                               },nc,getQuery());
+                       }
+       }
+
+       /**
+        * Check if an IBlock is already present.
+        *
+        * @param nc the context (gives us the priority)
+        * @return YES if it is present, NO if not.
+        */
+
+       public boolean check( NodeContext nc )
+       {
+               HashCode160 hc;
+
+               debug("check");
+
+               /* first check if its already present */
+               if (loadData(nc,false)) {
+debug("check 2");
+                       hc=HashCode160.create(getRawData(),0,getDataLength());
+                       if (hc.equals(getKey())) {
+                               crc32=((IBlockData) getData()).crc32;
+                               return true;
+                               }
+                       }
+debug("check 3");
+               clearData();
+               return false;
+       }
+
+       public void print( int indent )
+       {
+               int     i;
+
+               log(Level.FINEST,Utils.repeat('*',indent)+" IBLOCK ("+depth+") 
"+getFilePosition()+" "+getQuery().toHex()+" ("+childcount+" children)");
+
+               if (children!=null) {
+                       for (i=0; i<childcount; i++) {
+                               if (children[i]!=null) {
+                                       children[i].print(indent+2);
+                                       }
+                               }
+                       }
+       }
+
+       /**
+        * Free the associated resources of this Block. DOES ALSO free the
+        * memory occupied by the Block struct itself!
+        *
+        * @param rm reference to the RequestManager for requests
+        */
+
+       public void destroy( RequestManager rm )
+       {
+               int     i;
+
+               debug("destroy");
+               setStatus(BLOCK_PERSISTENT); /* last child would otherwise call 
destroy on us! */
+               if (children!=null) {
+                       for (i=0; i<childcount; i++) {
+                               if (children[i]!=null) {
+                                       children[i].destroy(rm);
+                                       }
+                               }
+                       children=null;
+                       }
+               super.destroy(rm);
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public static int computeChildCover( int depth )
+       {
+               int     cover,i;
+
+               cover=ContentBlock.SIZE;
+               for (i=0; i<depth-1; i++) {
+                       cover*=CHK_PER_INODE;
+                       }
+               return cover;
+       }
+
+       public static int computeChildCount( int size, int pos, int depth )
+       {
+               int     myCover,cover,count;
+
+               cover=computeChildCover(depth);
+
+               myCover=Math.min(size-pos,CHK_PER_INODE*cover);
+               for (count=0; myCover>0; count++) {
+                       myCover-=cover;
+                       }
+               return count;
+       }
+
+       public static int computeDataLength( int size, int pos, int depth )
+       {
+               // superhash + crc + chkhashes * children #
+               return 
computeChildCount(size,pos,depth)*ChkHashes.SIZE+HashCode160.SIZE+4;
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/esed2/IBlockData.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/esed2/IBlockData.java      
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/esed2/IBlockData.java      
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,88 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.esed2;
+
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * format of an IBlock.
+ */
+
+public class IBlockData extends Object implements Persistent
+{
+       public static final int SIZE    =       
HashCode160.SIZE+4+ChkHashes.SIZE*IBlock.CHK_PER_INODE;
+
+       /** The super-Hashcode for retrieving all CHK_PER_INODE sub-nodes in 
one big lookup.
+        This hash is the hash of the concatenation of all encrypted 
CHK_PER_INODE children of this node. */
+       public HashCode160      superHash;
+
+       /** The CRC32 checksum of the sub-blocks (crc32N of the concatenation 
of the individual crc32N's over
+        the plaintext-data (without padding) of each block). */
+       public int                      crc32;
+
+       /** The keys and queries for the nodes one level below. This entry must 
be at the end since it is variable size ! */
+       public ChkHashes[]      chks;
+
+
+       public IBlockData()
+       {
+               super();
+               chks=new ChkHashes[IBlock.CHK_PER_INODE];
+               for (int i=0; i<chks.length; i++) {
+                       chks[i]=new ChkHashes();
+                       }
+       }
+
+       public String toString()
+       {
+               return "Inner block data";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int getByteSize()
+       {
+               return SIZE;
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               int     i;
+
+               superHash=new HashCode160();
+               superHash.readBytes(buf,err);
+
+               crc32=buf.getInt();
+
+               for (i=0; i<chks.length; i++) {
+                       chks[i]=new ChkHashes();
+                       chks[i].readBytes(buf,err);
+                       }
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               int     i;
+
+               if (superHash!=null) {
+                       superHash.writeBytes(buf);
+                       }
+               else {
+                       for (i=0; i<HashCode160.SIZE; i++) {
+                               buf.put((byte) 0);
+                               }
+                       }
+
+               buf.putInt(crc32);
+
+               for (i=0; i<chks.length; i++) {
+                       chks[i].writeBytes(buf);
+                       }
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/esed2/IOContext.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/esed2/IOContext.java       
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/esed2/IOContext.java       
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,320 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.esed2;
+
+import org.gnu.freeway.util.*;
+
+import EDU.oswego.cs.dl.util.concurrent.*;
+
+import java.io.*;
+import java.nio.*;
+import java.nio.channels.*;
+import java.util.*;
+import java.util.logging.*;
+
+/**
+ * IO context for reading-writing AFS file blocks.
+ *
+ * encapsulation of IO
+ *
+ * In GNUnet, files are stored in the form of a balanced tree, not
+ * unlike INodes in unix filesystems. When we download files, the
+ * inner nodes of the tree are stored under FILENAME.X (where X
+ * characterizes the level of the node in the tree). If the download
+ * is aborted and resumed later, these .X files can be used to avoid
+ * downloading the inner blocks again.  The successfully received leaf
+ * nodes in FILENAME (the target file) are of course also not
+ * downloaded again.<p>
+ *
+ * The IOContext struct presents an easy api to access the various
+ * dot-files. It uses function pointers to allow implementors to
+ * provide a different mechanism (other than files on the drive) to
+ * cache the IBlocks.
+ */
+
+public class IOContext extends LoggedObject implements AFSConstants
+{
+       /** The base-filename */
+       private String                  filename;
+
+       /** The depth of the file-tree. */
+       private int                             treeDepth;
+
+       /** A lock for each file-handle for synchronizing access. */
+       private Mutex[]                 locks;
+
+       /** The file handles for each level in the tree. */
+       private FileChannel[]           handles;
+
+
+       public IOContext()
+       {
+               super(true);
+               filename="";
+               treeDepth=0;
+               locks=null;
+               handles=null;
+       }
+
+       public IOContext( IOContext ioc )
+       {
+               this();
+               init(ioc);
+       }
+
+       public String toString()
+       {
+               return "xxx";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       protected void init( IOContext ioc )
+       {
+               filename=ioc.filename;
+               treeDepth=ioc.treeDepth;
+
+               locks=new Mutex[ioc.locks.length];
+               System.arraycopy(ioc.locks,0,locks,0,ioc.locks.length);
+
+               handles=new FileChannel[ioc.handles.length];
+               System.arraycopy(ioc.handles,0,handles,0,ioc.handles.length);
+       }
+
+       public String getFilename()
+       {
+               return filename;
+       }
+
+       /**
+        * Initialize an IOContext.
+        *
+        * @param filesize the size of the file
+        * @param str the name of the level-0 file
+        * @param rdOnly use YES for read-only IOC
+        * @return OK on success, false on failure
+        */
+
+       public boolean init( int filesize, String str, boolean rdOnly )
+       {
+               FileChannel     fc;
+               String          fn;
+               int                     i;
+
+               log(Level.FINEST,"Init on '"+str+"' ("+filesize+" bytes) 
\""+(rdOnly ? "r" : "r/w")+"\" mode.");
+
+               treeDepth = computeDepth(filesize);
+               locks = new Mutex[treeDepth+1];
+               handles = new FileChannel[treeDepth+1];
+               Arrays.fill(handles,null);
+               filename = str;
+
+               if (!rdOnly) {
+                       try {
+                               // if exists and oversized, truncate
+                               fc=new 
RandomAccessFile(filename,"rw").getChannel();
+                               try {
+                                       if (fc.size()>filesize) {
+                                               fc.truncate(filesize);
+                                               }
+                                       }
+                               catch( IOException x ) {
+                                       err("Unable to truncate "+filename+" 
!",x);
+                                       return false;
+                                       }
+                               }
+                       catch( FileNotFoundException x ) {
+                               }
+
+                       }
+
+               for (i=0; i<=treeDepth; i++) {
+                       locks[i]=new Mutex();
+
+                       fn=filename+(i>0 ? "."+(char) ('A'+i) : "");
+                       try {
+                               handles[i]=new RandomAccessFile(fn,(rdOnly ? 
"r" : "rw")).getChannel();
+                               }
+                       catch( IOException x ) {
+                               handles[i]=null;
+
+                               if (!rdOnly || i==0) {
+                                       err("Could not open file "+fn+" !",x);
+                                       free(false);
+                                       return false;
+                                       }
+                               }
+                       }
+               return true;
+       }
+
+       /**
+        * Read method.
+        *
+        * @param level level in the tree to read/write at
+        * @param pos position where to read or write
+        * @param buf where to read from or write to
+        * @param len how many bytes to read or write
+        * @return number of bytes read or written, -1 on error
+        */
+
+       public int read( int level, int pos, byte[] buf, int len )
+       {
+               return read(level,pos,ByteBuffer.wrap(buf,0,len));
+       }
+
+       public int read( int level, int pos, ByteBuffer buf )
+       {
+               int ret;
+               int lpos;
+
+
+//log(Level.FINEST,">>> read level="+level+", pos="+pos);
+
+               lpos=pos;
+               for (ret=0; ret<level; ret++) {
+                       lpos/=IBlock.CHK_PER_INODE;
+                       }
+
+               try {
+                       locks[level].acquire();
+                       try {
+                               handles[level].position(lpos);
+                               ret=handles[level].read(buf);
+                               }
+                       finally {
+                               locks[level].release();
+                               }
+                       }
+               catch( IOException x ) {
+                       err("Failed to read "+buf.remaining()+" bytes on 
"+handles[level]+" !",x);
+                       ret=-1;
+                       }
+               catch( InterruptedException x ) {
+                       err("Failed to read "+buf.remaining()+" bytes on 
"+handles[level]+" !",x);
+                       ret=-1;
+                       }
+               return ret;
+       }
+
+       /**
+        * Write method.
+        *
+        * @param level level in the tree to read/write at
+        * @param pos position where to read or write
+        * @param buf where to read from or write to
+        * @param len how many bytes to read or write
+        * @return number of bytes read or written, -1 on error
+        */
+
+       public int write( int level, int pos, byte[] buf, int len )
+       {
+               return write(level,pos,ByteBuffer.wrap(buf,0,len));
+       }
+
+       public int write( int level, int pos, ByteBuffer buf )
+       {
+               int ret,lpos,len;
+
+//log(Level.FINEST,">>> write level="+level+", pos="+pos);
+
+               lpos = pos;
+               for (ret=0; ret<level; ret++) {
+                       lpos /= IBlock.CHK_PER_INODE;
+                       }
+
+               try {
+                       locks[level].acquire();
+                       try {
+                               handles[level].position(lpos);
+                               len=buf.remaining();
+                               ret = handles[level].write(buf);
+                               if (ret != len) {
+                                       
log(Level.FINEST,"Write("+handles[level]+", "+buf+", "+len+") failed !");
+                                       }
+                               }
+                       finally {
+                               locks[level].release();
+                               }
+                       }
+               catch( IOException x ) {
+                       err("Failed to write "+buf.remaining()+" bytes on 
"+handles[level]+" !",x);
+                       ret=-1;
+                       }
+               catch( InterruptedException x ) {
+                       err("Interrupted !",x);
+                       ret=-1;
+                       }
+               return ret;
+       }
+
+       /**
+        * Close the files in the IOContext and free
+        * the associated resources. Does NOT free
+        * the memory occupied by the IOContext struct
+        * itself.
+        *
+        * @param unlinkTreeFiles if YES, the non-level 0 files
+        *     are unlinked (removed), set to NO if the download
+        *     is not complete and may be resumed later.
+        */
+
+       public void free( boolean unlinkTreeFiles )
+       {
+               int             i;
+               String  fn;
+
+               log(Level.FINEST,"Free (unlink: "+unlinkTreeFiles+").");
+               for (i=0;i<=treeDepth;i++) {
+                       if (handles[i] != null) {
+                               try {
+                                       handles[i].close();
+                                       }
+                               catch( IOException x ) {
+                                       err("Failed to close "+handles[i]+" 
!",x);
+                                       }
+                               handles[i] = null;
+                               }
+                       locks[i]=null;
+                       }
+
+               if (unlinkTreeFiles) {
+                       for (i=1; i<=treeDepth; i++) {
+                               fn=filename+"."+(char) ('A'+i);
+                               if (!new File(fn).delete()) {
+                                       log(Level.WARNING,"Could not unlink 
temporary file "+fn+".");
+                                       }
+                               }
+                       }
+
+               filename=null;
+               handles=null;
+               locks=null;
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       /**
+        * Compute the depth of the tree.
+        * @param size file length for which to compute the depth
+        * @return depth of the tree
+        */
+
+       public static int computeDepth( int size )
+       {
+               int     depth,n;
+
+               depth=0;
+
+               n=CONTENT_SIZE;
+               while (n<size) {
+                       depth++;
+                       n*=IBlock.CHK_PER_INODE;
+                       }
+               return depth;
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/esed2/InsertURI.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/esed2/InsertURI.java       
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/esed2/InsertURI.java       
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,33 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.esed2;
+
+/**
+ */
+
+public class InsertURI extends GeneralURI
+{
+//     public int              action;
+       public String   filename;
+       public String[] keywords;
+       public String   pseudonym;
+       public String   password;
+       public int              keycount;
+
+
+       public InsertURI()
+       {
+               super();
+       }
+
+       public String toString()
+       {
+               return "xxx";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/esed2/InsertUtil.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/esed2/InsertUtil.java      
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/esed2/InsertUtil.java      
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,660 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.esed2;
+
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.io.*;
+import org.gnu.freeway.util.net.*;
+
+import java.io.*;
+import java.nio.channels.*;
+import java.util.*;
+import java.util.logging.*;
+
+/**
+ * Insertutil, helper methods for file insertion.
+ * Break file that is inserted into blocks and encrypts them according to the 
CHK-triple-hash-tree scheme (ESED II).
+ *
+ * @see "http://www.ovmj.org/GNUnet/encoding.php3";
+ */
+
+public class InsertUtil extends LoggedObject implements AFSConstants
+{
+       /** Default priority for locally indexed content ("infty") */
+       public static final int LOCAL_INDEXED_CONTENT_PRIO      =       0xFFFF;
+
+       private Prefs   prefs;
+
+
+       public InsertUtil( Prefs p )
+       {
+               super(true);
+               prefs=p;
+       }
+
+       public String toString()
+       {
+               return "xxx";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       /**
+        * Insert the SBlock
+        *
+        * @param esb
+        * @param sock
+        * @return OK on success, false on error
+        */
+
+       public boolean insert( EncryptedSBlock esb, CSSession sock )
+       {
+               CSInsertSBlock  msg;
+               CSResult        rv;
+
+               msg=new 
CSInsertSBlock(prefs.getInt("GNUNET-INSERT","CONTENT-PRIORITY",0),esb);
+
+               if (!sock.send(msg)) {
+                       log(Level.WARNING,"Server did not seem to be alive.");
+                       return false;
+                       }
+
+               rv=(CSResult) sock.receive(CSResult.class);
+               if (rv==null) {
+                       log(Level.WARNING,"Server did not send confirmation of 
insertion.");
+                       return false;
+                       }
+               if (!rv.isOkay()) {
+                       log(Level.WARNING,"Server could not perform 
insertion.");
+                       return false;
+                       }
+               return true;
+       }
+
+       /**
+        * Ask gnunetd to receive and store a file in
+        * on the server side.
+        *
+        * @param sock connection to gnunetd
+        * @param filename the name to add to fileindex.c
+        * @return the index, -1 on error
+        */
+
+       protected int transferFile( CSSession sock, String filename )
+       {
+               CSIndexFile             request;
+               CSUploadFile            upload;
+               CSResult                        rv;
+               int                             index,pos,fsize,delta;
+               FileChannel             handle;
+               FileLocation            f;
+
+               f=new FileLocation(filename);
+               if (!f.exists()) {
+                       return -1;
+                       }
+
+               /* first: request index */
+               request=new CSIndexFile(f);
+
+               if (!sock.send(request)) {
+                       log(Level.WARNING,"Could not request or receive data 
from gnunetd. Is gnunetd running ?");
+                       return -1;
+                       }
+
+               rv=(CSResult) sock.receive(CSResult.class);
+               if (rv==null) {
+                       log(Level.WARNING,"Could not request or receive data 
from gnunetd. Is gnunetd running ?");
+                       return -1;
+                       }
+
+               index=rv.getResult();
+               if (index == -1) {
+                       log(Level.WARNING,"gnunetd refused to index file (no 
space left ?)");
+                       return -1;
+                       }
+
+               assert(index!=0) : "gnunetd violated protocol (returned 0)";
+
+               if (prefs.testString("GNUNET-INSERT","LINK","YES")) {
+                       CSLinkFile      req;
+
+                       req=new CSLinkFile(f);
+
+                       if (sock.send(req)) {
+                               rv=(CSResult) sock.receive(CSResult.class);
+                               if (rv!=null && rv.isOkay()) {
+                                       /* link successful */
+                                       return index;
+                                       }
+                               }
+
+                       log(Level.WARNING,"WARNING: link request to gnunetd 
failed. Trying to, make copy instead.");
+                       }
+
+               // do not create link: transfer the file !
+               try {
+                       fsize=(int) f.getSize();
+
+                       upload=new CSUploadFile(f);
+
+                       handle=new 
RandomAccessFile(f.getPath(),"r").getChannel();
+                       try {
+                               pos     = 0;
+                               while (pos < fsize) {
+                                       delta=upload.copyChunk(handle);
+                                       if (delta<0) {
+                                               log(Level.SEVERE,"could not 
read file");
+                                               index=-1;
+                                               break;
+                                               }
+
+                                       if (!sock.send(upload)) {
+                                               log(Level.WARNING,"Could not 
send data to gnunetd. Is gnunetd running ?");
+                                               index=-1;
+                                               break;
+                                               }
+
+                                       rv=(CSResult) 
sock.receive(CSResult.class);
+                                       if (rv==null) {
+                                               log(Level.WARNING,"Could not 
receive data from gnunetd. Is gnunetd running ?");
+                                               index=-1;
+                                               break;
+                                               }
+                                       if (!rv.isOkay()) {
+                                               index=-1;
+                                               break;
+                                               }
+
+                                       pos+=delta;
+                                       }
+                               }
+                       finally {
+                               handle.close();
+                               }
+                       }
+               catch( IOException x ) {
+                       err("Could not upload file \""+f.getLabel()+"\" !",x);
+                       return -1;
+                       }
+               return index;
+       }
+
+       /**
+        * Inserts a file under the given name into the local GNUnet node.
+        * Insert (or index) a file under the given name into the local GNUnet
+        * node.
+        *
+        * @param sock the socket to use to talk to gnunetd/connection to 
gnunetd
+        * @param filename the name of the (incoming/source) file/the name of 
the file to insert
+        * @param model the insert model used to
+        *        update status information; points to null if
+        *        no status updates shall be given, otherwise
+        *        to a method that takes two integer arguments
+        *        (retrieved so far, total).
+        * @param model_data pointer that is passed to the model method
+        * @return top IBlock on success, null on error/null on error, 
otherwise the top block
+        */
+
+       public Block insertFile( CSSession sock, String filename, ProgressModel 
model, Object model_data )
+       {
+               NodeContext     nc;
+               int                     filesize;
+               Block           top;
+               FileLocation            f;
+               String  restore;
+               int                     ret;
+
+               f=new FileLocation(filename);
+               if (!f.exists()) {
+                       log(Level.WARNING,"Could not insert, file 
'"+f.getPath()+"' does not exist !");
+                       return null;
+                       }
+
+               filename=f.getPath();
+               filesize = (int) f.getSize();
+
+               restore = prefs.getString("GNUNET-INSERT","INDEX-CONTENT",null);
+               if (filesize <= ContentBlock.SIZE) {
+                       prefs.setString("GNUNET-INSERT","INDEX-CONTENT","NO");
+                       }
+
+               if (filesize==0) {
+                       log(Level.INFO,"File '"+filename+"' is empty, can't 
insert it.");
+                       
prefs.setString("GNUNET-INSERT","INDEX-CONTENT",restore);
+                       return null;
+                       }
+
+               nc=new NodeContext();
+               nc.pmodel = model;
+               nc.data = model_data;
+               nc.stats=new ProgressStats();
+               nc.stats.filesize = filesize;
+               nc.priority = 
prefs.getInt("GNUNET-INSERT","CONTENT-PRIORITY",0);
+               if (nc.priority == 0) {
+                       nc.priority = LOCAL_INDEXED_CONTENT_PRIO;
+                       }
+
+               if (prefs.testString("GNUNET-INSERT","INDEX-CONTENT","YES")) {
+                       ret = transferFile(sock, filename);
+                       assert(ret!=0);
+
+                       if (ret == -1) {
+                               log(Level.WARNING,"Adding to index list failed, 
trying insertion !");
+                               nc.index = 0;
+                               }
+                       else {
+                               nc.index = ret;
+                               }
+                       }
+               else {
+                       nc.index = 0; /* 0: no indexing */
+                       }
+
+               if (!nc.ioc.init(filesize,filename,true)) {
+                       
prefs.setString("GNUNET-INSERT","INDEX-CONTENT",restore);
+                       return null;
+                       }
+
+               top = Block.create(filesize);
+               if (!top.insert(nc,sock)) {
+                       top.destroy(null);
+                       nc.ioc.free(false);
+                       
prefs.setString("GNUNET-INSERT","INDEX-CONTENT",restore);//todo: le mettre en 
finally
+                       return null;
+                       }
+               nc.ioc.free(false);
+               prefs.setString("GNUNET-INSERT","INDEX-CONTENT",restore);
+               return top;
+       }
+
+       /**
+        * Insert a root-block into GNUnet.
+        *
+        * @param sock connection to gnunetd
+        * @param top the top block of the file
+        * @param description description to use
+        * @param filenameRoot filename to use
+        * @param mimetype mimetype to use
+        * @param keywords the keywords that shall be used to retrieve the 
file/to be associated with the file
+        * @return the root node on success, null on error
+        */
+
+       public RootNode insertRoot( CSSession sock, Block top, String 
description, String filenameRoot, String mimetype, String[] keywords )
+       {
+               int                             i,priority;
+               RootNode                rn;
+               boolean                 res;
+               FileIdentifier  fid;
+
+               priority = prefs.getInt("GNUNET-INSERT","CONTENT-PRIORITY",0);
+
+               fid=new FileIdentifier(top);
+
+               rn=new RootNode(fid);
+               rn.setDescription(description);
+               rn.setFileName(filenameRoot);
+               rn.setMimeType(mimetype);
+
+               res = true;
+               for (i=0; i<keywords.length; i++) {
+                       if 
(!insertRootWithKeyword(sock,rn,keywords[i],priority)) {
+                               res = false;
+                               }
+                       }
+
+               // directory support...
+               new 
GNDirectoryDatabase(prefs).makeRootNodeAvailable(rn,DIR_CONTEXT_INSERT);
+               return (res ? (RootNode) PersistentHelper.copy(rn) : null);     
        //todo: copie utile ???
+       }
+
+       /**
+        * Creates root node for the tree and writes the top-level tree node.
+        *
+        * @param sock connection to gnunetd
+        * @param rn the RootNode to insert
+        * @param keyword the keyword under which the rn is inserted
+        * @param contentPriority priority of the inserted content
+        * @return
+        */
+
+       public boolean insertRootWithKeyword( CSSession sock, RootNode rn, 
String keyword, int contentPriority )
+       {
+               HashCode160             hc;
+               CSInsert3Hash   msg;
+               CSResult        rv;
+               ContentBlock    block;
+
+               hc=HashCode160.create(keyword);
+
+               block=new ContentBlock();
+               if (!new 
ContentEncoding().encryptContent(PersistentHelper.toBytes(rn),hc,block)) {
+                       log(Level.SEVERE,"Encryption failed.");
+                       return false;
+                       }
+
+               msg = new CSInsert3Hash(contentPriority,hc,block);
+               if (!sock.send(msg)) {
+                       log(Level.WARNING,"WARNING: could not send data to 
gnunetd. Is gnunetd running ?");
+                       return false;
+                       }
+
+               rv=(CSResult) sock.receive(CSResult.class);
+               if (rv==null) {
+                       log(Level.WARNING,"WARNING: server did not send 
confirmation of insertion");
+                       return false;
+                       }
+               if (!rv.isOkay()) {
+                       log(Level.WARNING,"WARNING: server could not perform 
insertion");
+                       return false;
+                       }
+               return true;
+       }
+
+       /**
+        * Inserts a directory. Sets the file-identifier that can afterwards be 
used to retrieve the directory.
+        * Does <em>not</em> insert any RBlocks or SBlocks.
+        *
+        * @param sock
+        * @param nodeCount how many rootNodes in the directory
+        * @param rootNodes the actual nodes
+        * @param dirName name of this directory
+        * @param model
+        * @param modelArg
+        * @return null on failure, resulting file identifier for the directory 
on success
+        */
+
+       public FileIdentifier insertDirectory( CSSession sock, int nodeCount, 
RootNode[] rootNodes, String dirName, ProgressModel model, Object modelArg )
+       {
+               GNDirectory     dir;
+               FileLocation    temp;
+               Block           top;
+               String          oldval;
+               FileIdentifier  fid;
+               MappedFile      f;
+               boolean         res;
+
+               dir=new GNDirectory(nodeCount,dirName,rootNodes);
+
+               temp=FileLocation.temporary();
+
+               f=temp.openNew();
+               res=f.writePersistent(dir);
+               f.close();
+
+               if (!res) {
+                       log(Level.WARNING,"Could not write directory to 
temporary file \""+temp.getLabel()+"\".");
+                       temp.delete();
+                       return null;
+                       }
+
+               /* ok, insert the directory */
+               oldval = prefs.getString("GNUNET-INSERT",       
"INDEX-CONTENT",null);
+               prefs.setString("GNUNET-INSERT",        "INDEX-CONTENT","NO");
+               top = insertFile(sock,temp.getPath(),model,modelArg);
+
+               temp.delete();
+
+               prefs.setString("GNUNET-INSERT","INDEX-CONTENT",        oldval);
+               if (top == null) {
+                       log(Level.SEVERE,"Error inserting directory 
"+temp.getPath()+".\nYou may want to check whether or not you are out of 
space.\nRun gnunet-stats | grep \"AFS storage left\" to check.\n");
+                       return null;
+                       }
+
+               fid=new FileIdentifier(top);
+               top.destroy(null);
+               return fid;
+       }
+
+       /**
+        * Build an RBlock for the given file and insert
+        * it into GNUnet under all applicable keywords.
+        *
+        * @param sock
+        * @param fid the identifier for the file
+        * @param filename the full filename (complete path)
+        * @param gloKeywords
+        * @param gloKeywordCnt
+        * @param extractors_
+        * @return the RootNode
+        */
+
+       protected RootNode buildFileRBlock( CSSession sock, FileIdentifier fid, 
String filename, String[] gloKeywords, int gloKeywordCnt, List extractors_ )
+       {
+               RootNode        result;
+               int index,i;
+               String  description;
+               String  mimetype;
+               String  shortFN;
+               boolean nodirectindex;
+               String[]        keywords;
+               Extractor[]             extractors = (Extractor[]) 
extractors_.toArray(new Extractor[extractors_.size()]);
+
+               mimetype = prefs.getString("GNUNET-INSERT","MIMETYPE",null);
+               description = 
prefs.getString("GNUNET-INSERT","DESCRIPTION",null);
+               nodirectindex = 
prefs.testString("GNUNET-INSERT","ADDITIONAL-RBLOCKS","NO");
+
+               shortFN = prefs.getString("GNUNET-INSERT","FILENAME",null);
+               if (shortFN == null) {
+                       index=filename.replace('\\','/').lastIndexOf('/');
+                       shortFN=filename.substring(index+1);
+                       }
+
+               keywords=new String[] {};
+               if (!prefs.testString("GNUNET-INSERT","EXTRACT-KEYWORDS","NO")) 
{
+                       keywords=new 
KeyWords().extractKeywordsMulti(filename,description,mimetype,extractors);
+                       }
+
+               if (mimetype == null)
+                       mimetype = "unknown";
+               if (description == null)
+                       description = shortFN;
+
+               result=new RootNode(fid);
+               result.setDescription(description);
+               result.setFileName(shortFN);
+               result.setMimeType(mimetype);
+
+               for (i=0;i<gloKeywordCnt;i++)
+                       if (!insertRootWithKeyword(sock,
+                                       result,
+                                       gloKeywords[i],
+                                       
prefs.getInt("GNUNET-INSERT","CONTENT-PRIORITY",0))) {
+                               log(Level.SEVERE,"Failed to insert RBlock. Is 
gnunetd running and space available ?");
+                               break;
+                       }
+
+               for (i=0; i<keywords.length; i++) {
+                       if (!nodirectindex) {
+                               if 
(!insertRootWithKeyword(sock,result,keywords[i],prefs.getInt("GNUNET-INSERT","CONTENT-PRIORITY",0)))
 {
+                                       log(Level.SEVERE,"Failed to insert 
RBlock. Is gnunetd running and space available ?");
+                                       }
+                               }
+                       }
+               return result;
+       }
+
+       /**
+        * Build an RBlock for a directory (and insert the RBlock
+        * into GNUnet under all applicable keywords).
+        * @param sock
+        *
+        * @param fid the identifier for the file
+        * @param dirName the name of the last component of the path to the 
directory
+        * @param description the description for the file
+        * @param gloKeywords
+        * @param gloKeywordCnt
+        * @return the RBlock
+        */
+
+       public RootNode buildDirectoryRBlock( CSSession sock, FileIdentifier 
fid, String dirName, String description, String[] gloKeywords, int 
gloKeywordCnt )
+       {
+               RootNode        result;
+               int i;
+               String  dn;
+
+               dn=dirName;
+               if (!dn.endsWith(GNUNET_DIRECTORY_EXT)) {
+                       dn+=GNUNET_DIRECTORY_EXT;
+                       }
+
+               result=new RootNode(fid);
+               result.setDescription(description);
+               result.setFileName(dn);
+               result.setMimeType(GNUNET_DIRECTORY_MIME);
+
+               for (i=0;i<gloKeywordCnt;i++) {
+                       if 
(!insertRootWithKeyword(sock,result,gloKeywords[i],prefs.getInt("GNUNET-INSERT","CONTENT-PRIORITY",0)))
 {
+                               log(Level.SEVERE,"Failed to insert RBlock. Is 
gnunetd running and space available ?");
+                               }
+                       }
+               return result;
+       }
+
+       /**
+        * Index or insert a file or directory.  Creates and inserts RootNodes
+        * for the file if applicable.  Recursively processes directory if
+        * applicable.  If directories are build or if filename refers to a
+        * single file, a plaintext RootNode that identifies the inserted
+        * object is returned and the FileIdentifier fid is set.  If we do not
+        * create directories and a directory is given or if there was an
+        * error, null is returned.  Every file encountered is inserted with
+        * all specified global keywords and (if applicable) additional keywords
+        * are extracted with the extractors.
+        *
+        * @param sock
+        * @param filename the name of the file or directory
+        * @param gloKeywords
+        * @param gloKeywordCnt
+        * @param extractors_
+        * @param model
+        * @param modelArg
+        * @param insert callback used to insert individual (leaf) files
+        * @param insertArg
+        * @return RootNode that identifies the single file or directory or
+        *      null on error or null if filename is a directory and we don't
+        *      create directories.
+        */
+
+       public RootNode insertRecursively( CSSession sock, String filename, 
String[] gloKeywords, int gloKeywordCnt, List extractors_, ProgressModel model, 
Object modelArg, InsertWrapper insert, Object insertArg )
+       {
+               DECData                         dec;
+               DirLocation             dir;
+               FileIdentifier  fid;
+               String                  dirName;
+               int                             index;
+               boolean                 builddir,processRecursive;
+
+               dir=new DirLocation(filename);
+               if (!dir.exists()) {
+                       fid=insert.insert(sock,filename,insertArg);
+                       if (fid==null) {
+                               return null;
+                               }
+                       return 
buildFileRBlock(sock,fid,filename,gloKeywords,gloKeywordCnt,extractors_);
+                       }
+
+               processRecursive = 
prefs.testString("GNUNET-INSERT","RECURSIVE","YES");
+               if (processRecursive) {
+                       builddir = 
prefs.testString("GNUNET-INSERT","BUILDDIR","YES");
+
+                       dec=new DECData();
+                       dec.identifiers.clear();
+                       dec.nodes.clear();
+                       dec.sock = sock;
+                       dec.gloKeywords = gloKeywords;
+                       dec.gloKeywordCnt = gloKeywordCnt;
+                       dec.extractors_ = extractors_;
+                       dec.model = model;
+                       dec.model_arg = modelArg;
+                       dec.insert = insert;
+                       dec.insert_arg = insertArg;
+
+                       final DECData   _dec = dec;
+
+                       dir.traverse(new Traverser() {
+                               public boolean examine( Location node, int 
depth )
+                               {
+                                       dirEntryCallback(node,_dec);
+                                       return true;
+                               }
+                               });
+
+                       assert(dec.nodes.size() == dec.identifiers.size()) : 
"ERROR: assertion violated";
+
+                       if (builddir) {
+                               
index=filename.replace('\\','/').lastIndexOf('/');
+                               dirName=filename.substring(index+1);
+
+                               
fid=insertDirectory(sock,dec.nodes.size(),dec.getNodes(),dirName,model,modelArg);
+                               dec.identifiers.clear();
+                               dec.nodes.clear();
+                               return 
buildDirectoryRBlock(sock,fid,dirName,dirName,gloKeywords,gloKeywordCnt);
+                               }
+                       dec.identifiers.clear();
+                       dec.nodes.clear();
+                       return null;
+                       }
+               return null;
+       }
+
+       protected void dirEntryCallback( Location node, DECData data )
+       {
+               String  fn;
+               RootNode        rb;
+
+               data.identifiers.add(null);
+               data.nodes.add(null);
+
+               fn = node.getName();
+               rb = insertRecursively(data.sock,
+                               fn,
+                               data.gloKeywords,
+                               data.gloKeywordCnt,
+                               data.extractors_,
+                               data.model,
+                               data.model_arg,
+                               data.insert,
+                               data.insert_arg);
+
+               if (rb != null) {
+                       
data.identifiers.set(data.identifiers.size()-1,rb.getFileIdentifier());
+                       data.nodes.set(data.nodes.size()-1,rb);
+                       }
+               else {
+                       data.identifiers.remove(data.identifiers.size()-1);
+                       data.nodes.remove(data.nodes.size()-1);
+                       }
+       }
+}
+
+class DECData extends Object
+{
+       public List                     identifiers;
+       public List                     nodes;
+       public CSSession        sock;
+       public String[]         gloKeywords;
+       public int                      gloKeywordCnt;
+       public List                     extractors_;
+       public ProgressModel    model;
+       public Object           model_arg;
+       public InsertWrapper    insert;
+       public Object           insert_arg;
+
+
+       public DECData()
+       {
+               super();
+               identifiers=new ArrayList();
+               nodes=new ArrayList();
+       }
+
+       public RootNode[] getNodes()
+       {
+               return (RootNode[]) nodes.toArray(new RootNode[nodes.size()]);
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/esed2/InsertWrapper.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/esed2/InsertWrapper.java   
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/esed2/InsertWrapper.java   
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,27 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.esed2;
+
+import org.gnu.freeway.util.net.*;
+
+/**
+ *
+ */
+
+public interface InsertWrapper
+{
+       /**
+        * Wrapper around insertFile that gives the user the appropriate
+        * feedback.  The insertWrapper is expected to return fid at the
+        * end of the insertion.  See "gnunet-insert.c::doFile()" for
+        * a possible implementation.
+        * @param sock
+        * @param filename
+        * @param closure
+        * @return
+        */
+
+       public FileIdentifier insert( CSSession sock, String filename, Object 
closure );
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/esed2/KeyWords.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/esed2/KeyWords.java        
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/esed2/KeyWords.java        
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,90 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.esed2;
+
+import java.io.*;
+
+/**
+ * Layer to encapsulate the keyword extraction API and
+ * make it accessible to gnunet-insert.
+ * Layer to encapsulate the keyword extraction API and make it
+ * accessible to gnunet-insert.
+ *
+ * TODO: extractors à implémenter
+ */
+
+public class KeyWords extends Object
+{
+       public KeyWords()
+       {
+               super();
+       }
+
+       public String toString()
+       {
+               return "xxx";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       /**
+        * Extract keywords, mime-type and description from a file
+        *
+        * @param filename the name of the file
+        * @param description the description (the user may have
+        *        supplied a description already (*description != null),
+        *        in that case, append, mind the maximum size!
+        * @param mimetype the mimetype, again, the user may
+        *        have supplied one
+        * @return the list of keywords
+        */
+
+       public String[] extractKeywords( File filename, StringBuffer 
description, StringBuffer mimetype )
+       {
+               //todo: à implémenter
+               return new String[] {};
+       }
+
+       public String[] extractKeywords( File filename, StringBuffer 
description, StringBuffer mimetype, Extractor[] xxx )
+       {
+               //todo: à implémenter
+               return new String[] {};
+       }
+
+       /**
+        * Load the extractors as specified by the configuration.
+        *
+        * @return linked list of extractrs
+        */
+
+       public Extractor[] getExtractors()
+       {
+               //todo: à implémenter
+               return new Extractor[] {};
+       }
+
+       /**
+        * Extract keywords, mime-type and description from a file
+        *
+        * @param filename the name of the file
+        * @param description the description (the user may have
+        *        supplied a description already (*description != null),
+        *        in that case, append, mind the maximum size!
+        * @param mimetype the mimetype, again, the user may
+        *        have supplied one
+        * @param exList the list of extractors/list of libextractor plugins, 
null if
+        *        libextractor is not used.  Of type EXTRACTOR_ExtractorList*
+        * @return keywords the list of keywords, allocate space at
+        *        another location if required, copy existing keywords
+        *        over to that space! Do NEVER free *keywords!
+        */
+
+       public String[] extractKeywordsMulti( String filename, String 
description, String mimetype, Extractor[] exList )
+       {
+               //todo: à implémenter
+               return new String[] {};
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/esed2/Listener.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/esed2/Listener.java        
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/esed2/Listener.java        
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,29 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.esed2;
+
+import org.gnu.freeway.util.crypto.*;
+
+/**
+ * type of callback used by nodes in the merkle tree to receive
+ * content-arrived notifications from the RequestManager
+ */
+
+public interface Listener
+{
+       /**
+        * Type of a method that is called by the RequestManager
+        * whenever a reply to a query has been received.
+        *
+        * @param query the query that was sent out
+        * @param reply the reply that was received
+        * @param rm the handle for the request manager
+        * @param data an opaque handle that is passed along,
+        *        typically used to pass the NodeContext
+        * @return false the request manager should abort the download
+        */
+
+       public boolean listen( HashCode160 query, CSResultChk reply, 
RequestManager rm, NodeContext data );
+}

Added: 
freeway/src/org/gnu/freeway/protocol/afs/esed2/NSSearchResultCallback.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/esed2/NSSearchResultCallback.java  
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/esed2/NSSearchResultCallback.java  
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,21 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.esed2;
+
+/**
+ */
+
+public interface NSSearchResultCallback
+{
+       /**
+        * Type of a callback method for results that have
+        * been received.
+        *
+        * @param sb the plaintext of the SBlock that has been received
+        * @param data the opaque handle (context for the callee)
+        */
+
+       public void searchResult( SBlock sb, Object data );
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/esed2/Node.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/esed2/Node.java    2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/esed2/Node.java    2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,116 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.esed2;
+
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.net.*;
+
+/**
+ *
+ */
+
+public abstract class Node extends LoggedObject implements Persistent, 
AFSConstants
+{
+       /** Major format version. */
+       protected int                   majorVersion;
+
+       /** Minor format version. */
+       protected int                   minorVersion;
+
+       /** Information required for the download. */
+       protected FileIdentifier        fileIdentifier;
+
+       /** Description of the content. */
+       protected String                        description;
+
+       /** Suggested filename. */
+       protected String                        fileName;
+
+       /** Mime-type (as claimed by insertion !). */
+       protected String                        mimeType;
+
+
+       protected Node( int major, int minor )
+       {
+               super(true);
+               majorVersion=major;
+               minorVersion=minor;
+               fileIdentifier=new FileIdentifier();
+               description="";
+               fileName="";
+               mimeType="";
+       }
+
+       public String toString()
+       {
+               return "Node [majorVersion="+majorVersion+", 
minorVersion="+minorVersion+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int getMajorVersion()
+       {
+               return majorVersion;
+       }
+
+       public int getMinorVersion()
+       {
+               return minorVersion;
+       }
+
+       public FileIdentifier getFileIdentifier()
+       {
+               return fileIdentifier;
+       }
+
+       public String getDescription()
+       {
+               return description;
+       }
+
+       public void setDescription( String str )
+       {
+               description=str;
+       }
+
+       public String getFileName()
+       {
+               return fileName;
+       }
+
+       public void setFileName( String str )
+       {
+               fileName=str;
+       }
+
+       public String getMimeType()
+       {
+               return mimeType;
+       }
+
+       public void setMimeType( String str )
+       {
+               mimeType=str;
+       }
+
+       public abstract String toLabel();
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public static int extractMajorVersion( byte[] b )
+       {
+               return (b.length>=2 ? ((b[0] & 0x000000ff)<<8) | (b[1] & 
0x000000ff) : -1);
+       }
+
+       public static int extractMinorVersion( byte[] b )
+       {
+               if (b.length<4) {
+                       return -1;
+                       }
+               return (b.length>=4 ? ((b[2] & 0x000000ff)<<8) | (b[3] & 
0x000000ff) : -1);
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/esed2/NodeContext.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/esed2/NodeContext.java     
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/esed2/NodeContext.java     
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,55 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.esed2;
+
+/**
+ * Context information for the merkle-tree objects
+ *
+ * The NodeContext groups the IOC and the progress model
+ * into a single struct.
+ */
+
+public class NodeContext extends Object
+{
+       /** The IO context for IO operations. */
+       public IOContext                ioc;
+
+       /** Priority */
+       public int                              priority;
+
+       /** Index of the file that we are indexing, 0 for insertion. */
+       public int                              index;
+
+       /** The ProgressModel to communicate status updates. */
+       public ProgressModel    pmodel;
+
+       /** Data argument to the ProgressModel. */
+       public Object                   data;
+
+       /** Current progress so far. */
+       public ProgressStats    stats;
+
+
+       public NodeContext()
+       {
+               super();
+               ioc=new IOContext();
+               priority=0;
+               index=0;
+               pmodel=null;
+               data=null;
+               stats=new ProgressStats();
+       }
+
+       public String toString()
+       {
+               return "xxx";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/esed2/P2P3HashResult.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/esed2/P2P3HashResult.java  
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/esed2/P2P3HashResult.java  
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,91 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.esed2;
+
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * A return message for search query with double-hash proof.
+ */
+
+public class P2P3HashResult extends P2PMessage implements AFSConstants
+{
+       public static final int SIZE    =       HashCode160.SIZE+CONTENT_SIZE+4;
+
+       /** The double-hash. */
+       private HashCode160     hash;
+
+       /** The search result. */
+       private byte[]          result;
+
+
+       public P2P3HashResult()
+       {
+               super(IS_3HASH_RESULT);
+               hash=new HashCode160();
+               result=new byte[CONTENT_SIZE];
+       }
+
+       public P2P3HashResult( HashCode160 h, ContentBlock block )
+       {
+               this();
+               hash=h;
+               System.arraycopy(block.content,0,result,0,CONTENT_SIZE);
+       }
+
+       public String toString()
+       {
+               return "P2P 3-hash result [hash="+hash+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public HashCode160 getDoubleHash()
+       {
+               return hash;
+       }
+
+       public HashCode160 getTripleHash()
+       {
+               return HashCode160.create(PersistentHelper.toBytes(hash));
+       }
+
+       public byte[] getResult()
+       {
+               return result;
+       }
+
+       public int getByteSize()
+       {
+               return SIZE;
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               int     size,type;
+
+               size=buf.getShort() & 0x0000ffff;
+               err.reportIf(size!=SIZE,"bad size !");
+
+               type=buf.getShort() & 0x0000ffff;
+               err.reportIf(type!=IS_3HASH_RESULT,"bad type !");
+
+               hash.readBytes(buf,err);
+
+               buf.get(result);
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               buf.putShort((short) SIZE);
+               buf.putShort((short) IS_3HASH_RESULT);
+               hash.writeBytes(buf);
+               buf.put(result);
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/esed2/P2PChkResult.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/esed2/P2PChkResult.java    
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/esed2/P2PChkResult.java    
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,71 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.esed2;
+
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * Return message for content download (CHK style)
+ */
+
+public class P2PChkResult extends P2PMessage
+{
+       public static final int SIZE    =       4+ContentBlock.SIZE;
+
+       /** The search result. */
+       public ContentBlock     result;
+
+
+       public P2PChkResult()
+       {
+               super(IS_CHK_RESULT);
+               result=null;
+       }
+
+       public String toString()
+       {
+               return "P2P chk result [result="+result+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int getByteSize()
+       {
+               return SIZE;
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               int     size,type;
+
+               size=buf.getShort() & 0x0000ffff;
+               err.reportIf(size!=SIZE,"bad size !");
+
+               type=buf.getShort() & 0x0000ffff;
+               err.reportIf(type!=IS_CHK_RESULT,"bad type !");
+
+               result=new ContentBlock();
+               result.readBytes(buf,err);
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               int     i;
+
+               buf.putShort((short) SIZE);
+               buf.putShort((short) IS_CHK_RESULT);
+               if (result!=null) {
+                       result.writeBytes(buf);
+                       }
+               else {
+                       for (i=0; i<ContentBlock.SIZE; i++) {
+                               buf.put((byte) 0);
+                               }
+                       }
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/esed2/P2PNSQuery.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/esed2/P2PNSQuery.java      
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/esed2/P2PNSQuery.java      
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,61 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.esed2;
+
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * Request for content from a namespace.
+ */
+
+public class P2PNSQuery extends P2PQuery
+{
+       public static final int SIZEX   =       
P2PQuery.SIZE+HashCode160.SIZE*2;
+
+       /** Namespace that we are restricted to */
+       public HashCode160      namespace;
+
+       /** Identifier that we are looking for. */
+       public HashCode160      identifier;
+
+
+       public P2PNSQuery( HostIdentity hi )
+       {
+               this();
+               //todo: copie de 'hi' utile ???
+               throw new IllegalStateException("A CORRIGER");
+       }
+
+       public P2PNSQuery()
+       {
+               super(IS_NSQUERY);
+       }
+
+       public String toString()
+       {
+               return "xxx";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int getByteSize()
+       {
+               return SIZEX;
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               throw new IllegalStateException();
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               throw new IllegalStateException();
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/esed2/P2PQuery.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/esed2/P2PQuery.java        
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/esed2/P2PQuery.java        
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,180 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.esed2;
+
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+import java.util.*;
+
+/**
+ * Request for content. The number of queries can
+ * be determined from the header size.
+ */
+
+public class P2PQuery extends P2PMessage
+{
+       public static final int SIZE    =       12+HostIdentity.SIZE;
+
+       /** How important is this request ? */
+       private int                             priority;
+
+       /** Time to live in milliseconds. */
+       private int                             ttl;
+
+       /** To whom to return results ? */
+       private HostIdentity    returnTo;
+
+       /** Hashcodes of the file(s) we're looking for. If multiple queries are 
given, the first query
+        is the super-query for the bloom filter. If only one query is given, 
the bloom filter should NOT
+        be used since it does not contain summaries for simple 1k blocks. It 
is not possible to group
+        multiple queries with this message type if they are not dominated by 
the same super-query. */
+       private List                    queries;
+
+
+       public P2PQuery()
+       {
+               this(IS_QUERY);
+       }
+
+       public P2PQuery( HostIdentity hi )
+       {
+               this();
+               returnTo=(HostIdentity) PersistentHelper.copy(hi);      //todo: 
copie utile ?
+       }
+
+       protected P2PQuery( int t )
+       {
+               super(t);
+               priority=0;
+               ttl=0;
+               returnTo=null;
+               queries=new ArrayList();
+       }
+
+       public String toString()
+       {
+               return "AFS p2p query";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int getPriority()
+       {
+               return priority;
+       }
+
+       public int getTTL()
+       {
+               return ttl;
+       }
+
+       public void setPriorityAndTTL( int p, int t )
+       {
+               priority=p;
+               ttl=t;
+       }
+
+       public HostIdentity getReturnTo()
+       {
+               return returnTo;
+       }
+
+       public void setReturnTo( HostIdentity hi )
+       {
+               returnTo=(HostIdentity) PersistentHelper.copy(hi);
+       }
+
+       public boolean sameQueries( P2PQuery q )
+       {
+               return queries.equals(q.queries);
+       }
+
+       public HashCode160 getQuery( int index )
+       {
+               return ((index>=0 && index<queries.size()) ? (HashCode160) 
queries.get(index) : null);
+       }
+
+       public void addQuery( HashCode160 h )
+       {
+               queries.add(PersistentHelper.copy(h));
+       }
+
+       public void setQuery( int index, HashCode160 h )
+       {
+               queries.set(index,PersistentHelper.copy(h));    //todo: copie 
utile ???
+       }
+
+       public HashCode160[] getQueries()
+       {
+               return (HashCode160[]) queries.toArray(new 
HashCode160[queries.size()]);
+       }
+
+       public void setQueries( HashCode160[] q )
+       {
+               queries.clear();
+               queries.addAll(Arrays.asList(q));
+       }
+
+       public void clearQueries()
+       {
+               queries.clear();
+       }
+
+       public int getByteSize()
+       {
+               return SIZE+queries.size()*HashCode160.SIZE;
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               HashCode160     h;
+               int                     size,type,i;
+
+               size=buf.getShort() & 0x0000ffff;
+               err.reportIf(size<SIZE,"bad size");
+
+               type=buf.getShort() & 0x0000ffff;
+               err.reportIf(type!=IS_QUERY,"bad type !");
+
+               priority=buf.getInt();
+               ttl=buf.getInt();
+
+               returnTo=new HostIdentity();
+               returnTo.readBytes(buf,err);
+
+               queries.clear();
+               for (i=(size-SIZE)/HashCode160.SIZE; i>0; i--) {
+                       h=new HashCode160();
+                       h.readBytes(buf,err);
+                       queries.add(h);
+                       }
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               HashCode160     h;
+               int                     i;
+
+               buf.putShort((short) getByteSize());
+               buf.putShort((short) IS_QUERY);
+               buf.putInt(priority);
+               buf.putInt(ttl);
+               if (returnTo!=null) {
+                       returnTo.writeBytes(buf);
+                       }
+               else {
+                       for (i=0; i<HostIdentity.SIZE; i++) {
+                               buf.put((byte) 0);
+                               }
+                       }
+               for (i=0; i<queries.size(); i++) {
+                       h=(HashCode160) queries.get(i);
+                       h.writeBytes(buf);
+                       }
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/esed2/P2PSBlockResult.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/esed2/P2PSBlockResult.java 
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/esed2/P2PSBlockResult.java 
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,72 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.esed2;
+
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * Return message for SBlock download
+ */
+
+public class P2PSBlockResult extends P2PMessage
+{
+       public static final int SIZE    =       4+EncryptedSBlock.SIZE;
+
+       /** The search result. */
+       private EncryptedSBlock result;
+
+
+       public P2PSBlockResult()
+       {
+               super(IS_SBLOCK_RESULT);
+               result=new EncryptedSBlock();
+       }
+
+       public P2PSBlockResult( EncryptedSBlock esb )
+       {
+               this();
+               result=(EncryptedSBlock) PersistentHelper.copy(esb);    //todo: 
copie utile ???
+       }
+
+       public String toString()
+       {
+               return "P2P result SBlock [result="+result+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public EncryptedSBlock getResult()
+       {
+               return result;
+       }
+
+       public int getByteSize()
+       {
+               return SIZE;
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               int     size,type;
+
+               size=buf.getShort() & 0x0000ffff;
+               err.reportIf(size!=SIZE,"bad size");
+
+               type=buf.getShort() & 0x0000ffff;
+               err.reportIf(type!=IS_SBLOCK_RESULT,"bad type !");
+
+               result.readBytes(buf,err);
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               buf.putShort((short) SIZE);
+               buf.putShort((short) IS_SBLOCK_RESULT);
+               result.writeBytes(buf);
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/esed2/Policy.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/esed2/Policy.java  2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/esed2/Policy.java  2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,373 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.esed2;
+
+import org.gnu.freeway.server.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.net.*;
+
+import java.util.logging.*;
+
+/**
+ * The code in this module is responsible for enforcing
+ * the anonymity policy set by the user.
+ */
+
+public class Policy extends LoggedObject implements AFSConstants
+{
+       /** Socket to communicate with gnunetd. */
+       private CSSession               sock;
+
+       /** CoreAPI, null if we are using the socket ! */
+       private CoreForProtocol coreAPI;
+
+       /** Which value did the user specify for the sendPolicy ? */
+       private int                             sendPolicy;
+
+       /** Which value did the user specify for the receivePolicy ? */
+       private int                             receivePolicy;
+
+       /** Last time traffic information was obtained. */
+       private long                            lastPoll;
+
+       /** Mutex for synchronizing access. */
+       private Object                  lock;
+
+       /** Number of peers that were active at the last poll (since this is 
always type sensitive, not totals). */
+       private int                             chkPeers;
+
+       /** */
+       private int                             hashPeers;
+
+       /** */
+       private int                             queryPeers;
+
+       /** Number of bytes of unmatched bytes of traffic at the last poll 
(totals and separate for the 3 message types). */
+       private int                             totalReceiveBytes;
+
+       /** */
+       private int                             totalCHKBytes;
+
+       /** */
+       private int                             total3HASHBytes;
+
+       /** */
+       private int                             totalQueryBytes;
+
+
+       protected Policy( Prefs prefs )
+       {
+               super(true);
+               sock=null;
+               coreAPI=null;
+               sendPolicy=prefs.getInt("AFS","ANONYMITY-SEND",0);
+               receivePolicy=prefs.getInt("AFS","ANONYMITY-RECEIVE",0);
+               lastPoll=0;
+               lock=new Object();
+               chkPeers=0;
+               hashPeers=0;
+               queryPeers=0;
+               totalReceiveBytes=0;
+               totalCHKBytes=0;
+               total3HASHBytes=0;
+               totalQueryBytes=0;
+       }
+
+       /**
+        * Initialize the module / from server.
+        *
+        * @param prefs
+        * @param capi the GNUnet core API
+        */
+
+       public Policy( Prefs prefs, CoreForProtocol capi )
+       {
+               this(prefs);
+               coreAPI=capi;
+       }
+
+       /**
+        * Initialize the module / we are a client.
+        *
+        * @param prefs
+        * @param session
+        */
+
+       public Policy( Prefs prefs, CSSession session )
+       {
+               this(prefs);
+
+               assert(session!=null);
+               sock=session;
+       }
+
+       public String toString()
+       {
+               return "xxx";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       /**
+        * Shutdown the module.
+        */
+
+       public void doneAnonymityPolicy()
+       {
+               if (sock!=null) {
+                       sock.disconnect();
+                       sock=null;
+                       }
+               coreAPI=null;
+       }
+
+       /**
+        * Check if the anonymity policy will be violated if
+        * a message of the given type will be send.
+        * @param type the request type of the message that will be
+        *        transmitted
+        * @param size the size of the message that will be
+        *        transmitted
+        * @return true if this is ok for the policy, false if not
+        */
+
+       public boolean checkAnonymityPolicy( int type, int size )
+       {
+               if (sock==null && coreAPI==null) {
+                       return true;
+                       }
+
+               if (sock == null)
+                       pollCAPI();
+               else
+                       pollSocket();
+
+               switch (type) {
+                       case CSMessage.IS_QUERY:
+                               return checkPolicy(receivePolicy, type, size);
+                       case CSMessage.IS_RESULT_3HASH:
+                       case CSMessage.IS_RESULT_CHK:
+                               return checkPolicy(sendPolicy, type, size);
+                       default:
+                               return true;
+                       }
+       }
+
+       /**
+        * Poll gnunetd via TCP about traffic information.
+        * Note that we only send the request if we would
+        * otherwise potentially have to refuse messages.
+        */
+
+       protected void pollSocket()
+       {
+               long                            now;
+               CSTrafficInfo           info;
+               CSTrafficRequest        req;
+               TrafficCounter          tc;
+               int                                     i;
+
+               now=Scheduler.now();
+
+               synchronized(lock) {
+                       if (now - lastPoll < TTL_DECREMENT) {
+                               return; // poll at most every ttl-decrement 
time units
+                               }
+
+                       lastPoll = now;
+
+                       req=new CSTrafficRequest();
+                       req.timePeriod=(int) TTL_DECREMENT;
+
+                       if (!sock.send(req)) {
+                               log(Level.WARNING,"WARNING: could not query 
gnunetd about traffic conditions");
+                               return;
+                               }
+
+                       info = (CSTrafficInfo) 
sock.receive(CSTrafficInfo.class);
+                       if (info==null) {
+                               log(Level.WARNING,"WARNING: did not receive 
reply from gnunetd about traffic conditions");
+                               return;
+                               }
+
+                       for (i=info.count-1; i>=0; i--) {
+                               tc=info.counters[i];
+                               if ((tc.flags & TrafficCounter.TC_TYPE_MASK) == 
TrafficCounter.TC_RECEIVED) {
+                                       totalReceiveBytes += tc.count * 
tc.avrg_size;
+                                       switch (tc.type) {
+                                               case P2PMessage.IS_QUERY:
+                                                       totalQueryBytes += 
tc.count * tc.avrg_size;
+                                                       queryPeers += (tc.flags 
& TrafficCounter.TC_DIVERSITY_MASK);
+                                                       break;
+                                               case P2PMessage.IS_3HASH_RESULT:
+                                                       total3HASHBytes += 
tc.count * tc.avrg_size;
+                                                       hashPeers += (tc.flags 
& TrafficCounter.TC_DIVERSITY_MASK);
+                                                       break;
+                                               case P2PMessage.IS_CHK_RESULT:
+                                                       totalCHKBytes += 
tc.count * tc.avrg_size;
+                                                       chkPeers += (tc.flags & 
TrafficCounter.TC_DIVERSITY_MASK);
+                                                       break;
+                                               default:
+                                                       break;
+                                               }
+                                       }
+                               }
+                       }
+       }
+
+       /**
+        * Poll gnunet core via coreapi about traffic information.
+        */
+
+       protected void pollCAPI()
+       {
+               long    now;
+               int             
avgMessageSize,messageCount,peerCount,messageType;
+               int[]   counts;
+
+               now=Scheduler.now();
+               synchronized(lock) {
+                       if (now - lastPoll < TTL_DECREMENT) {
+                               return; // don't bother
+                               }
+                       lastPoll = now;
+
+                       for 
(messageType=0;messageType<P2PMessage.IS_MAX;messageType++) {
+                               
counts=coreAPI.getTrafficStats(messageType,TrafficCounter.TC_RECEIVED,(int) 
TTL_DECREMENT);
+                               avgMessageSize=counts[0];
+                               messageCount=counts[1];
+                               peerCount=counts[2];
+
+                               totalReceiveBytes += messageCount * 
avgMessageSize;
+                               switch (messageType) {
+                                       case P2PMessage.IS_QUERY:
+                                               totalQueryBytes += messageCount 
* avgMessageSize;
+                                               queryPeers += peerCount;
+                                               break;
+
+                                       case P2PMessage.IS_3HASH_RESULT:
+                                               total3HASHBytes += messageCount 
* avgMessageSize;
+                                               hashPeers += peerCount;
+                                               break;
+
+                                       case P2PMessage.IS_CHK_RESULT:
+                                               totalCHKBytes += messageCount * 
avgMessageSize;
+                                               chkPeers += peerCount;
+                                               break;
+
+                                       default:
+                                               break;
+                                       }
+                               }
+                       }
+       }
+
+       /**
+        * Test if the required number of peers were active.
+        *
+        * @param port which protocol are we interested in
+        * @param peerCount how many peers are required
+        * @return true if we had sufficient amounts of traffic, false if not
+        */
+
+       protected boolean checkPeerPolicy( int port, int peerCount )
+       {
+               switch (port) {
+                       case P2PMessage.IS_QUERY:
+                               if (queryPeers >= peerCount)
+                                       return true;
+                               return false;
+                       case P2PMessage.IS_CHK_RESULT:
+                               if (chkPeers >= peerCount)
+                                       return true;
+                               return false;
+                       case P2PMessage.IS_3HASH_RESULT:
+                               if (hashPeers >= peerCount)
+                                       return true;
+                               return false;
+                       default:
+                               return false;
+                       }
+       }
+
+       /**
+        * Test if the required amount of traffic is available.
+        *
+        * @param port what type of traffic are we interested in?
+        * @param size how much traffic do we intend to produce?
+        * @param byteRatio how much cover traffic do we need (byteRatio*size)
+        * @param strictMatch does only traffic of exactly the same
+        *        type (port) count?
+        * @return true if enough cover traffic was be found, false if not.
+        */
+
+       protected boolean checkRatioPolicy( int port, int size, int byteRatio, 
boolean strictMatch )
+       {
+               int     cost;
+
+               cost = byteRatio * size;
+               if (strictMatch) {
+                       switch (port) {
+                       case P2PMessage.IS_QUERY:
+                               if (totalQueryBytes < cost)
+                                       return false;
+                               totalQueryBytes -= cost;
+                               return true;
+                       case P2PMessage.IS_CHK_RESULT:
+                               if (totalCHKBytes < cost)
+                                       return false;
+                               totalCHKBytes -= cost;
+                               return true;
+                       case P2PMessage.IS_3HASH_RESULT:
+                               if (total3HASHBytes < cost)
+                                       return false;
+                               total3HASHBytes -= cost;
+                               return true;
+                       default:
+                               return false;
+                       }
+                       }
+               if (totalReceiveBytes < cost)
+                       return false;
+               totalReceiveBytes -= cost;
+               return true;
+       }
+
+       /**
+        * Test if the policy requirements are fullfilled.
+        *
+        * @param policyValue the anonymity degree required by the user
+        * @param type the message type
+        * @param size the size of the message
+        * @return true if this isok for the policy, false if not.
+        */
+
+       protected boolean checkPolicy( int policyValue, int type, int size )
+       {
+               int     peerCount,byteRatio;
+
+               if (policyValue <= 0)
+                       return true; /* no policy */
+               if (policyValue >= 1000) {
+                       byteRatio = policyValue / 1000;
+                       peerCount = policyValue % 1000;
+               } else {
+                       byteRatio = policyValue;
+                       peerCount = 0;
+               }
+               if (peerCount > 0)
+                       if (false == checkPeerPolicy(type,
+                                       peerCount))
+                               return false;
+               if (byteRatio > 0)
+                       if (false == checkRatioPolicy(type,
+                                       size,
+                                       byteRatio,
+                                       policyValue >= 1000))
+                               return false;
+               return true;
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/esed2/Priority.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/esed2/Priority.java        
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/esed2/Priority.java        
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,99 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.esed2;
+
+import org.gnu.freeway.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.net.*;
+
+/**
+ * Keep track of the maximum priority that we are currently using.
+ */
+
+public class Priority extends LoggedObject implements AFSConstants
+{
+       private int                             maxPriority_;
+       private Scheduler               scheduler;
+       private AbstractClient  client;
+       private ScheduledTask           trackJob;
+
+
+       public Priority( Application app )
+       {
+               super(true);
+               maxPriority_=0;
+               client=(AbstractClient) app;
+               scheduler=app.getScheduler();
+               trackJob=new ScheduledTask("TRACK-PRIORITY",new 
AbstractAction() {
+                       public void perform()
+                       {
+                               trackPriority();
+                       }
+                       },TTL_DECREMENT);
+       }
+
+       /**
+        * This method must be called to start the priority
+        * tracker.
+        */
+
+       public void startAFSPriorityTracker()
+       {
+               trackPriority();
+               scheduler.addJob(trackJob,TTL_DECREMENT);
+       }
+
+       /**
+        * This method must be called to stop the priority
+        * tracker.  Call after cron has been stopped.
+        */
+
+       public void stopAFSPriorityTracker()
+       {
+               scheduler.deleteJob(trackJob);
+       }
+
+       /**
+        * What is the highest priority that AFS clients should use for 
requests at this point in time ?
+        * @return
+        */
+
+       public int getMaxPriority()
+       {
+               return maxPriority_;
+       }
+
+       protected void trackPriority()
+       {
+               CSSession               sock;
+               CSGetAvgPriority        req;
+               CSResult        rv;
+               int                             res;
+
+               sock=client.connect();
+               if (sock == null) {
+                       maxPriority_ = 0;
+                       return;
+                       }
+
+               req=new CSGetAvgPriority();
+               if (sock.send(req)) {
+                       rv=(CSResult) sock.receive(CSResult.class);
+                       if (rv!=null) {
+                               res=rv.getResult();
+                               maxPriority_ = 2*res+1;
+
+                               debug("Current maximum priority : 
"+maxPriority_);
+                               }
+                       else {
+                               maxPriority_ = 0;
+                               }
+                       }
+               else {
+                       maxPriority_ = 0;
+                       }
+               sock.disconnect();
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/esed2/ProgressModel.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/esed2/ProgressModel.java   
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/esed2/ProgressModel.java   
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,33 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.esed2;
+
+/**
+ * callback for updates on the progress of an operation
+ */
+
+public interface ProgressModel
+{
+       public static final ProgressModel       NO_MODEL        =       new 
ProgressModel() {
+               public void progress( ProgressStats stats, Object data )
+               {
+               }
+               };
+
+       /**
+        * Called whenever we make progress. Callback methods
+        * of this type are used during insertion and download to notify the
+        * user interface of the progress we're making. If the model is called
+        * with position == total, the download is complete. If the model
+        * is called with position == total == 0, then there was a fatal error
+        * and the download was aborted.
+        *
+        * @param stats progress statistics
+        * @param data a context passed around for use by the PM
+        *        implementation
+        */
+
+       public void progress( ProgressStats stats, Object data );
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/esed2/ProgressStats.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/esed2/ProgressStats.java   
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/esed2/ProgressStats.java   
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,54 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.esed2;
+
+/**
+ * statistics about the progress
+ *
+ * Progress of the current operation. Used for passing
+ * data to callbacks. Some of these make sense only for
+ * downloading.
+ */
+
+public class ProgressStats extends Object
+{
+       public int      progress;                       /* bytes processed */
+       public int      filesize;                       /* total file size */
+       public int      requestsSent;
+       public int      requestsPending;
+       public int      currentRetries;
+       public int      totalRetries;
+       public int      currentTTL;
+       public int      duplicationEstimate;
+
+
+       public ProgressStats()
+       {
+               super();
+       }
+
+       public ProgressStats( ProgressStats p )
+       {
+               super();
+               progress=p.progress;
+               filesize=p.filesize;
+               requestsSent=p.requestsSent;
+               requestsPending=p.requestsPending;
+               currentRetries=p.currentRetries;
+               totalRetries=p.totalRetries;
+               currentTTL=p.currentTTL;
+               duplicationEstimate=p.duplicationEstimate;
+       }
+
+       public String toString()
+       {
+               return "xxx";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/esed2/Pseudonym.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/esed2/Pseudonym.java       
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/esed2/Pseudonym.java       
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,282 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.esed2;
+
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.io.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+import java.util.*;
+import java.util.logging.*;
+
+/**
+ * functions for handling pseudonyms
+ */
+
+public class Pseudonym extends LoggedObject
+{
+       private static final String     PSEUDODIR       =       
"data/pseudonyms";
+       private static final String     NS_HANDLE       =       
"known_namespaces";
+
+       private Prefs           prefs;
+       private DirLocation     base;
+
+
+       public Pseudonym( Prefs p )
+       {
+               super(true);
+               prefs=p;
+
+               base=prefs.getDirLocation("","GNUNET_HOME");
+               if (base==null) {
+                       trace("Configuration file must specify a directory for 
GNUnet to store per-peer data under GNUNET_HOME !");
+                       throw new RuntimeException();
+                       }
+               base=base.getDirectory(PSEUDODIR);
+               base.create();
+       }
+
+       public String toString()
+       {
+               return "xxx";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       /**
+        * Create a new pseudonym.
+        *
+        * @param name the name of the pseudonym
+        * @param password passphrase to encrypt the pseudonym on disk (may be 
null)
+        * @return null on error (e.g. pseudonym exists), otherwise the secret 
key
+        */
+
+       public PrivateKey createPseudonym( String name, String password )
+       {
+               FileLocation            fileName;
+               PrivateKey              hk;
+               byte[]                  dst;
+               SessionKey              key;
+               HashCode160             hc;
+               byte[]                  iv;
+               MappedFile              f;
+
+               fileName=base.getFile(name);
+               if (fileName.exists()) {
+                       log(Level.WARNING,"Can't create pseudonym "+name+", 
file \""+fileName.getLabel()+"\" exists.");
+                       return null;
+                       }
+
+               hk=PrivateKey.create();
+               dst=PersistentHelper.toBytes(hk);
+
+               if (password!=null) {
+                       iv=new byte[SessionKey.BLOWFISH_BLOCK_LENGTH];
+                       
System.arraycopy(SessionKey.INITVALUE,0,iv,0,SessionKey.BLOWFISH_BLOCK_LENGTH);
+
+                       hc=HashCode160.create(password);
+                       key=hc.makeKey();
+
+                       dst=key.encrypt(dst,iv);
+                       if (dst==null) {
+                               return null;
+                               }
+                       }
+
+               f=fileName.openNew();
+               if (!f.writeBytes(dst)) {
+                       log(Level.WARNING,"Failed to write "+dst.length+" 
bytes.");
+                       f.close();
+
+                       fileName.delete();
+                       return null;
+                       }
+               f.close();
+               return hk;
+       }
+
+       /**
+        * Delete a pseudonym.
+        *
+        * @param name the name of the pseudonym
+        * @return OK on success, false on error
+        */
+
+       public boolean deletePseudonym( String name )
+       {
+               FileLocation            f;
+
+               f=base.getFile(name);
+               if (!f.exists()) {
+                       log(Level.WARNING,"File does not exist : "+f+".");
+                       return false;
+                       }
+               if (!f.delete()) {
+                       log(Level.WARNING,"Could not unlink "+f+".");
+                       return false;
+                       }
+               return true;
+       }
+
+       /**
+        * Read pseudonym.
+        *
+        * @param name the name of the pseudonym
+        * @param password passphrase to encrypt the pseudonym on disk (may be 
null)
+        * @return null on error (e.g. password invalid, pseudonym does not 
exist), otherwise the secret key
+        */
+
+       public PrivateKey readPseudonym( String name, String password )
+       {
+               PrivateKey              hk;
+               ByteBuffer              buf;
+               SessionKey              key;
+               HashCode160             hc;
+               byte[]                  iv,tmp;
+               MappedFile              f;
+               FileLocation            loc;
+
+               loc=base.getFile(name);
+               if (!loc.exists()) {
+                       log(Level.WARNING,"File \""+loc.getLabel()+"\" does not 
exist.");
+                       return null;
+                       }
+
+               f=loc.open();
+               buf=f.readBuffer();
+               if (buf==null) {
+                       log(Level.WARNING,"Failed to read "+loc.getLabel()+".");
+                       f.close();
+                       return null;
+                       }
+
+               if (password != null) {
+                       iv=new byte[SessionKey.BLOWFISH_BLOCK_LENGTH];
+                       
System.arraycopy(SessionKey.INITVALUE,0,iv,0,SessionKey.BLOWFISH_BLOCK_LENGTH);
+
+                       hc=HashCode160.create(password);
+                       key=hc.makeKey();
+
+                       tmp=new byte[buf.capacity()];
+                       buf.get(tmp);
+                       tmp=key.decrypt(tmp,iv);
+                       if (tmp==null) {
+                               log(Level.WARNING,"Decrypting pseudonym 
failed.");
+                               return null;
+                               }
+                       buf=ByteBuffer.wrap(tmp);
+                       }
+
+               hk=(PrivateKey) PersistentHelper.readFully(PrivateKey.class, 
buf);
+               if (hk==null) {
+                       // wrong PW happens A LOT, thus don't always print this 
warning !
+                       log(Level.FINEST,"Pseudonym format for "+name+" 
invalid. Wrong password ?");
+                       f.close();
+                       return null;
+                       }
+               f.close();
+               return hk;
+       }
+
+       /**
+        * Test if we have any pseudonyms.
+        *
+        * @return YES if we do have pseudonyms, otherwise NO.
+        */
+
+       public boolean havePseudonyms()
+       {
+               return base.count()>0;
+       }
+
+       /**
+        * Build a list of all available pseudonyms.
+        *
+        * @return null on error, otherwise the list of pseudonyms
+        */
+
+       public String[] listPseudonyms()
+       {
+               TraverserContext        ctx;
+               final List      _myList=new ArrayList();
+
+               ctx=new TraverserContext();
+               ctx.setFilesOnly(true);
+
+               base.traverse(new Traverser() {
+                       public boolean examine( Location node, int depth )
+                       {
+                               _myList.add(node.getName());
+                               return true;
+                       }
+                       },ctx);
+               return (String[]) _myList.toArray(new String[_myList.size()]);
+       }
+
+       /**
+        * Build a list of all known namespaces.
+        *
+        * @param list where to store the names of the namespaces
+        * @return false on error, otherwise the number of known namespaces
+        */
+
+       public int listNamespaces( HashCode160[][] list )
+       {
+               list[0]=listNamespaces();
+               return (list[0]!=null ? list[0].length : -1);
+       }
+
+       public HashCode160[] listNamespaces()
+       {
+               HashCode160[]   list;
+               ByteBuffer              buf;
+               int                             i;
+
+               buf=prefs.getContent(NS_HANDLE);
+               if (buf==null) {
+                       return null;
+                       }
+               if ((buf.limit() % HashCode160.SIZE)!=0) {
+                       log(Level.WARNING,"State database "+NS_HANDLE+" 
corrupted, deleting contents.");
+                       prefs.unlink(NS_HANDLE);
+                       return null;
+                       }
+
+               list=new HashCode160[buf.limit()/HashCode160.SIZE];
+               for (i=0; i<list.length; i++) {
+                       list[i]=(HashCode160) 
PersistentHelper.read(HashCode160.class,buf);
+                       if (list[i]==null) {
+                               return null;
+                               }
+                       }
+               return list;
+       }
+
+       /**
+        * Add a namespace to the set of known namespaces.
+        *
+        * @param ns the namespace identifier
+        */
+
+       public void addNamespace( HashCode160 ns )
+       {
+               HashCode160[]   list;
+               int                             i;
+
+               list=listNamespaces();
+               if (list!=null) {
+                       for (i=0; i<list.length; i++) {
+                               if (ns.equals(list[i])) {
+                                       return; // seen before
+                                       }
+                               }
+                       }
+
+               prefs.appendContent(NS_HANDLE,PersistentHelper.toBytes(ns));
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/esed2/RequestContinuation.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/esed2/RequestContinuation.java     
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/esed2/RequestContinuation.java     
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,37 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.esed2;
+
+/**
+ */
+
+public class RequestContinuation extends Object
+{
+       public NodeContext                      nc;
+       public RequestEntry                     entry;
+       /* in HOST byte order! */
+       public int                                      ttl;
+       /* in HOST byte order! */
+       public int                                      prevttl;
+       public int                                      prevpri;
+       public long                                     prevlt;
+       public RequestContinuation      next;
+
+
+       public RequestContinuation()
+       {
+               super();
+       }
+
+       public String toString()
+       {
+               return "xxx";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/esed2/RequestEntry.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/esed2/RequestEntry.java    
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/esed2/RequestEntry.java    
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,50 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.esed2;
+
+/**
+ * Format of a request as tracked by the RequestManager.
+ */
+
+public class RequestEntry extends Object
+{
+       /** The message that is send to gnunetd. */
+       public CSQuery  message;
+
+       /** Last time the query was send. */
+       public long                     lasttime;
+
+       /** Whom to call once we get a reply? */
+       public Listener         receiver;
+
+       /** The node to pass to the receiver method. */
+       public Block            receiverNode;
+
+       /** Opaque data handle to pass to the Listener. */
+       public NodeContext      data;
+
+       /** How long have we been actively trying this one? */
+       public int                      tries;
+
+       /** How many replies have we received for this entry? (for 
super-queries, thus always in [0,25]).
+        [reset for each retransmission; used to NOT increment the TTL if we 
got a reply] */
+       public int                      successful_replies;
+
+
+       public RequestEntry()
+       {
+               super();
+       }
+
+       public String toString()
+       {
+               return "xxx";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/esed2/RequestManager.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/esed2/RequestManager.java  
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/esed2/RequestManager.java  
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,855 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.esed2;
+
+import org.gnu.freeway.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.util.logging.*;
+
+/**
+ * The RequestManager keeps track of and re-issues queries
+ * The RequestManager keeps track of queries and re-issues
+ *        requests if no reply is received.
+ * structure that keeps track of currently pending requests for a download
+ *
+ * Handle to the state of a request manager.  Here we keep track of
+ * which queries went out with which priorities and which nodes in
+ * the merkle-tree are waiting for the replies.
+ */
+
+public class RequestManager extends LoggedObject implements AFSConstants
+{
+       private static int      lpri;
+
+       static {
+               lpri=0;
+               }
+
+       /** Mutex for synchronizing access to this struct */
+       private Object                          lock;
+
+       /** Current list of all pending requests */
+       private RequestEntry[]          requestList;
+
+       /** Number of pending requests (highest used index) */
+       private int                                     requestListIndex;
+
+       /** Number of entries allocated for requestList */
+       private int                                     requestListSize;
+
+       /** Current "good" TTL (initial) [64s].  In HOST byte order. */
+       private int                                     initialTTL;
+
+       /** Congestion window.  How many messages should be pending 
concurrently? */
+       private int                                     congestionWindow;
+
+       /** Slow-start threshold (see RFC 2001) */
+       private int                                     ssthresh;
+
+       /** Current estimate of "duplication" rate (amount of duplicate replies 
we get). */
+       private int                                     duplicationEstimate;
+
+       /** Socket used to talk to gnunetd. */
+       private CSSession                       sock;
+
+       /** The thread that receives results from gnunetd. */
+       private Task                                    receiveThread_;
+
+       /** */
+       private int                                     lastDET;
+
+       /** */
+       private RequestContinuation     start;
+
+       /** CRC of the top-IBlock, see downloadutil.c and 
block.c::childDownloadCompleted. */
+       public int                                      topCrc32;       
//fixme: private
+
+       /** The top block. */
+       public Block                                    top;            //todo: 
remettre en private
+
+       private Scheduler                       scheduler;
+       private Policy                          policy;
+
+       private ScheduledTask           rrjob = new 
ScheduledTask("REQUEST-JOB",new AbstractAction() {
+               public void perform()
+               {
+                       requestJob();
+               }
+               });
+       private Priority                priority;
+
+
+       private RequestManager()
+       {
+               super(true);
+       }
+
+       /**
+        * Create a request manager. Will create the request manager
+        * datastructures and also connect to gnunetd. Creates thread that
+        * listens to gnunetd replies and another thread that periodically
+        * re-issues the queries. Use destroyRequestManager to abort and/or to
+        * free resources after the download is complete. The callback method
+        * in nc will be invoked to notify the caller of the download progress
+        * such that it is possible to tell when we are done.
+        * @param app
+        * @param p
+        * @param pppp
+        */
+
+       public RequestManager( AbstractClient app, Policy p, Priority pppp )
+       {
+               this();
+               scheduler=app.getScheduler();
+               policy=p;
+               priority=pppp;
+
+               start=null;
+               lastDET=0;
+               lock=new Object();
+               requestListIndex=0;
+               requestListSize=256;
+               requestList=new RequestEntry[256];
+               initialTTL= (int) Scheduler.SECS_5;
+               /* RFC 2001 suggests to use 1 segment size initially;
+                Given 1500 octets per message in GNUnet, we would
+                have 2-3 queries of maximum size (552); but since
+                we are multi-casting to many peers at the same
+                time AND since queries can be much smaller,
+                we do WHAT??? */
+               congestionWindow=1; /* RSS is 1 */
+               ssthresh=65535;
+               duplicationEstimate=0;
+               sock=app.connect();
+               if (sock==null) {
+                       log(Level.WARNING,"Could not create socket to connect 
to gnunetd.");
+                       requestList=null;
+                       requestListSize=0;
+                       return;
+                       }
+
+               receiveThread_=new Task("REQUEST-MANAGER-RECEIVE",new 
AbstractAction() {
+                       public void perform()
+                       {
+                               receiveThread();
+                       }
+                       });
+               receiveThread_.launch();
+       }
+
+       public String toString()
+       {
+               return "xxx";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       /**
+        * Destroy the resources associated with a request manager.
+        * Invoke this method to abort the download or to clean up
+        * after the download is complete.
+        *
+        */
+
+       public void destroy()
+       {
+               CSSession       sockx;
+               int                     i;
+
+               scheduler.suspend();
+
+               synchronized(lock) {
+                       sockx=sock;
+                       sock = null;
+                       scheduler.deleteJob(rrjob);
+                       }
+
+               if (sockx != null) {
+                       sockx.disconnect(); /* unblock RM thread */
+                       }
+
+               receiveThread_.join();
+
+               synchronized(lock) {
+                       for (i=0; i<requestListIndex; i++) {
+                               freeInContinuations(requestList[i]);
+                               requestList[i]=null;
+                               }
+                       requestListIndex = 0;
+                       requestList=null;
+                       requestListSize=0;
+                       }
+
+               if (top != null) {
+                       top.destroy(this);
+                       }
+               scheduler.resume();
+       }
+
+       /**
+        * Print the contents of the request manager. For debugging.
+        * For debugging.
+        */
+
+       public void print()
+       {
+               int     i;
+
+               synchronized(lock) {
+                       log(Level.FINEST,"RM TTL "+initialTTL+" 
duplicates"+duplicationEstimate);
+
+                       for (i=0; i<requestListIndex; i++) {
+                               log(Level.FINEST,i+": 
"+requestList[i].message.getQuery(0).toHex()+" for node 
"+requestList[i].receiverNode+" ("+requestList[i].tries+" tries)");
+                               }
+                       }
+       }
+
+       /**
+        * Assert that there are no pending requests for this node.
+        * @param node
+        */
+
+       public void assertDead( Block node )
+       {
+               int i;
+
+               synchronized(lock) {
+                       for (i=0; i<requestListIndex; i++) {
+                               if (requestList[i].receiverNode == node) {
+                                       assert(false) : "Node "+node+" is being 
destroyed while request is pending.";
+                                       }
+                               }
+                       }
+       }
+
+       /**
+        * We are approaching the end of the download. Cut all TTLs in half.
+        */
+
+       public void endGame()
+       {
+               int i;
+
+               synchronized(lock) {
+                       for (i=0;i<requestListIndex;i++) {
+                               RequestEntry  entry = requestList[i];
+                               int ttl = entry.message.getTTL();
+                               entry.message.setTTL(ttl / 2);
+                               }
+                       }
+       }
+
+       /**
+        * Queue a request for execution.
+        *
+        * @param node the node to call once a reply is received
+        * @param callback the method to invoke
+        * @param data the data argument to the Listener
+        * @param message the query to send to gnunetd, freed by callee!
+        */
+
+       public void request( Block node, Listener callback, NodeContext data, 
CSQuery message )
+       {
+               RequestEntry[]  old;
+               RequestEntry    entry;
+
+               debug("Request request for "+node+" with callback "+callback);
+
+               entry = new RequestEntry();
+               entry.message= message;
+               entry.successful_replies= 0;
+               entry.lasttime = 0; /* never sent */
+               entry.receiver = callback;
+               entry.receiverNode= node;
+               entry.data = data;
+               entry.tries= 0; /* not tried so far */
+
+               synchronized(lock) {
+                       /* can we add to current list & issue instantly? */
+                       if (requestListSize == requestListIndex)  {
+                               old=requestList;
+
+                               requestListSize*=2;
+                               requestList=new RequestEntry[requestListSize];
+                               
System.arraycopy(old,0,requestList,0,old.length);
+                               }
+                       requestList[requestListIndex++] = entry;
+
+                       debug("Scheduling next run for now !");
+                       scheduler.advanceJob(rrjob);
+                       }
+               return;
+       }
+
+       /**
+        * Update a request. This method is used to selectively
+        * change a query or drop it entirely.
+        *
+        * @param node the block for which the request is updated
+        * @param msg the new query message for that node, null for
+        *        none (then the request is dropped)
+        */
+
+       public void update( Block node, CSQuery msg )
+       {
+               int i;
+
+               debug("DEBUG: updating request for "+node+" to "+msg);
+
+               synchronized(lock) {
+                       for (i=0;i<requestListIndex;i++) {
+                               if (requestList[i].receiverNode == node) {
+                                       if (msg != null) {
+                                               // update
+                                               // keep priority & ttl
+                                               
msg.setPriorityAndTTL(requestList[i].message.getPriority(),requestList[i].message.getTTL());
+                                               
requestList[i].successful_replies++;
+                                               requestList[i].message = msg;
+
+                                               // also wait a bit longer 
before re-issueing the request, after all, we got at least one of the replies !
+                                               // add 2*TTL grace time if we 
got a reply to a multi-query; this dramatically reduces the amount of "useless" 
(duplicate) replies we get !
+                                               
requestList[i].lasttime=Scheduler.now()+2*TTL_DECREMENT;
+                                               }
+                                       else {
+                                               // delete
+
+                                               // update stats
+                                               if(requestList[i].tries > 1)
+                                                       
requestList[i].data.stats.currentRetries-= (requestList[i].tries - 1);
+
+                                               requestList[i].message=null;
+                                               
freeInContinuations(requestList[i]);
+                                               requestList[i]=null;
+                                               requestList[i]= 
requestList[--requestListIndex];
+                                               
requestList[requestListIndex]=null;
+                                               }
+                                       return; // found !
+                                       }
+                               }
+                       }
+       }
+
+       /**
+        * Test that the given entry does not occur
+        * in the continuations list (and if it does,
+        * null it out).
+        * @param entry
+        */
+
+       protected void freeInContinuations( RequestEntry entry )
+       {
+               RequestContinuation     cur;
+
+               cur = start;
+               while (cur != null) {
+                       if (cur.entry == entry)
+                               cur.entry = null;
+                       cur = cur.next;
+                       }
+       }
+
+       /**
+        * We have determined success or failure for
+        * sending the query.  Now update the state of
+        * the RM adequately.  start contains the
+        * continuation that holds the state required
+        * to do this update.
+        * @param ok
+        */
+
+       protected void runContinuation( boolean ok )
+       {
+               RequestContinuation     cur;
+
+               cur = start;
+               if (cur.entry != null) {
+                       if (!ok) {
+                               /* we did not send this entry, revert! */
+                               log(Level.FINEST,"Sending canceled (would 
block).");
+                               cur.entry.message.setPriorityAndTTL( 
cur.prevpri, cur.prevttl);
+                               cur.entry.lasttime = cur.prevlt;
+                               cur.entry.tries--;
+                               }
+                       else {
+                               if (cur.entry.tries > 1) {
+                                       int     nowTT;
+
+                                       nowTT=(int) 
Scheduler.toSeconds(Scheduler.now());
+                                       if ( (nowTT - initialTTL) > lastDET) {
+                                               /* only consider congestion 
control every
+                                                "average" TTL seconds, 
otherwise the system
+                                                reacts to events that are far 
too old! */
+                                               /* we performed retransmission, 
treat as congestion (RFC 2001) */
+
+                                               debug("Received duplicate data, 
changing CW ("+congestionWindow+
+                                                       " to 
"+((congestionWindow/2)+1)+
+                                                       ") and SST ("+ssthresh+
+                                                       
"."+(congestionWindow/2)+")");
+
+                                               ssthresh= congestionWindow / 2;
+                                               if (ssthresh < 2)
+                                                       ssthresh = 2;
+                                               congestionWindow= ssthresh + 1;
+                                               lastDET = nowTT;
+                                               }
+                                       cur.nc.stats.totalRetries++;
+                                       cur.nc.stats.currentRetries++;
+                                       }
+                               }
+                       }
+               start = cur.next;
+       }
+
+       /**
+        * Send the request from the requestList[requestIndex] out onto
+        * the network.
+        *
+        * @param requestIndex the index of the Request to issue
+        */
+
+       protected void issueRequest( int requestIndex )
+       {
+               RequestContinuation     con;
+               RequestContinuation     pos;
+               RequestEntry                    entry;
+               NodeContext                     nc;
+               long                                    now;
+               CSMessage                       msg;
+               CSSession                       sockx;
+               boolean                         ok;
+
+               now=Scheduler.now();
+
+               con = new RequestContinuation();
+               con.next = null;
+               con.entry= entry= requestList[requestIndex];
+
+               if ((entry.lasttime+entry.message.getTTL()) > now - 
TTL_DECREMENT) {
+                       log(Level.WARNING,"Assertion failed: "+entry.lasttime+" 
+ "+entry.message.getTTL()+" > "+now+" + "+TTL_DECREMENT);
+                       assert(false);
+                       }
+
+               if (entry.lasttime == 0) {
+                       entry.message.setPriorityAndTTL( 
entry.message.getPriority(),  0); /* to avoid assert failure */
+                       con.ttl = initialTTL;
+                       con.prevttl = con.ttl;
+                       }
+               else {
+                       con.ttl = entry.message.getTTL();
+                       con.prevttl = con.ttl;
+                       if (con.ttl > MAX_TTL) {
+                               con.ttl = (int) (MAX_TTL + Crypto.nextInt((int) 
(2*TTL_DECREMENT)));
+                               entry.message.setPriorityAndTTL( 
entry.message.getPriority(),  (int) MAX_TTL); /* to avoid assert failure */
+                               }
+                       else if (con.ttl > initialTTL) {
+                               /* switch to slow back-off */
+                               int rd;
+                               if (initialTTL == 0)
+                                       rd = con.ttl;
+                               else
+                                       rd = con.ttl / initialTTL;
+                               if (rd == 0)
+                                       rd = 1; /* how? */
+                               rd = (int) (TTL_DECREMENT / rd);
+                               if (rd == 0)
+                                       rd = 1;
+                               con.ttl += Crypto.nextInt((int) 
(Scheduler.MILLIS_50+rd));
+                               /* rd == TTL_DECREMENT / (con.ttl / initialTTL) 
+ saveguards
+                                50ms: minimum increment */
+                               }
+                       else {
+                               con.ttl += Crypto.nextInt((int) (con.ttl + 2 * 
TTL_DECREMENT)); /* exponential backoff with random factor */
+                               }
+                       }
+
+               con.prevlt = entry.lasttime;
+               entry.lasttime = now + 2 * TTL_DECREMENT;
+               if (Crypto.nextInt(1+entry.tries) > 1) {
+                       /* do linear (in tries) extra back-off (in addition to 
ttl)
+                        to avoid repeatedly tie-ing with other peers; this is 
somewhat
+                        equivalent to what ethernet is doing, only that 
'tries' is our
+                        (rough) indicator for collisions.  For ethernet 
back-off, see:
+                        
http://www.industrialethernetuniversity.com/courses/101_4.htm
+                        */
+                       entry.lasttime += Crypto.nextInt((int) (TTL_DECREMENT * 
(1+entry.tries)));
+                       }
+
+               if 
(!policy.checkAnonymityPolicy(CSMessage.IS_QUERY,entry.message.getByteSize()+HostIdentity.SIZE))
 {
+                       debug("DEBUG: not sending query due to anonymity policy 
!");
+                       return;
+                       }
+
+               if (con.ttl < entry.message.getTTL()) {
+                       log(Level.WARNING,"WARNING: assertion failed; 
decrementing TTL from "+entry.message.getTTL()+" to "+con.ttl+" !");
+                       assert(false);
+                       }
+
+               con.prevpri = entry.message.getPriority();
+               if (con.prevpri > 0x0FFFFFF)
+                       con.prevpri = Crypto.nextInt(0xFFFFFF); /* bound! */
+               entry.tries++;
+               if (entry.successful_replies > 0) {
+                       /* do NOT change priority / ttl for n iterations
+                        where n is the number of successful replies!*/
+                       con.ttl = entry.message.getTTL();
+                       entry.successful_replies /= 2; /* better than --? 
better than = 0? */
+                       }
+               else {
+                       int tpriority;
+                       int mpriority;
+                       int count;
+
+                       if (con.ttl > (con.prevpri+8)* TTL_DECREMENT)  {
+                               con.ttl =(int) ((con.prevpri+8) * 
TTL_DECREMENT);
+                               }
+
+                       entry.message.setTTL(con.ttl);
+                       tpriority = con.prevpri + Crypto.nextInt(entry.tries);
+                       mpriority = priority.getMaxPriority();
+                       /* adjust mpriority according to the number of queries 
*/
+
+                       count=entry.message.getQueriesCount();
+                       if (count >= 2)
+                               count--; /* discount super-query */
+                       mpriority *= count;
+
+                       if (tpriority > mpriority) {
+                               /* mpriority is (2 * (current average priority 
+ 2)) and
+                                is used as the maximum priority that we use; 
if the
+                                calculated tpriority is above it, we reduce 
tpriority
+                                to random value between the average 
(mpriority/2) but
+                                bounded by mpriority */
+                               tpriority = mpriority / 2 + 
(Crypto.nextInt(1+mpriority/2));
+                               }
+                       entry.message.setPriority(tpriority);
+                       }
+
+               if (isDebug()) {
+                       int i;
+                       int count;
+                       String  hex;
+
+                       count=entry.message.getQueriesCount();
+                       for (i=0; i<count; i++) {
+                               hex=entry.message.getQuery(i).toHex();
+
+                               if (con.prevlt == 0) {
+                                       log(Level.FINEST,i+"sending 
"+entry.tries+"st time "+
+                                               "(last: NEVER; ttl 
"+con.prevttl+")"+
+                                               " "+hex+"; ttl "+con.ttl+", 
priority "+entry.message.getPriority()+" ("+initialTTL+")");
+                                       }
+                               else {
+                                       log(Level.FINEST,i+" sending 
"+entry.tries+"-th time "+
+                                               "(last: "+(now - con.prevlt)+" 
ms ago; ttl "+con.prevttl+")"+
+                                               " "+hex+"; ttl "+con.ttl+", 
priority "+entry.message.getPriority()+" ("+initialTTL+")");
+                                       }
+                               }
+                       }
+
+               con.nc= nc= entry.data;
+               nc.stats.requestsPending= requestListIndex;
+               nc.stats.requestsSent = requestListIndex;
+               nc.stats.currentTTL = con.ttl;
+               nc.stats.duplicationEstimate= duplicationEstimate;
+               nc.pmodel.progress(nc.stats, nc.data);
+
+               if (0 == (entry.tries % (MAX_TRIES * 50))) {
+                       log(Level.WARNING,"WARNING: 
"+entry.message.getQuery(0).toHex()+" seems to be not available on the 
network");
+                       entry.receiverNode.print(0);
+                       }
+
+               msg = (CSMessage) PersistentHelper.copy(entry.message);
+               synchronized(lock) {
+                       sockx = sock;
+                       }
+
+               ok = false;
+               if (sockx != null) {
+                       /* add con to the end of the (very short)
+                        linked list */
+                       pos = start;
+                       if (pos == null) {
+                               start = con;
+                               }
+                       else {
+                               while (pos.next != null)
+                                       pos = pos.next;
+                               pos.next = con;
+                               }
+
+                       /* destroyRM may set sock to null at ANY point! */
+                       sockx.setBlocking(false);
+                       ok = sockx.flushAndSend(msg);
+                       sockx.setBlocking(true);
+                       if (!ok) {
+                               log(Level.WARNING,"WARNING: could not send 
request to gnunetd, is it running ?");
+                               runContinuation(false);
+                               }
+                       else {
+                               /* receiverThread will call runContinuation */
+                               }
+                       }
+       }
+
+       /**
+        * Cron job that re-issues requests. Should compute how long to sleep
+        * (min ttl until next job is ready) and re-schedule itself
+        * accordingly!
+        */
+
+       protected void requestJob()
+       {
+               long minSleep;
+               long now;
+               long delta;
+               int i;
+               int pending;
+               int[] perm;
+
+               debug("CRON: requestJob running");
+
+               synchronized(lock) {
+                       if (requestListIndex == 0) {
+                               return;
+                               }
+                       now=Scheduler.now();
+                       pending = 0;
+
+                       for (i=0;i<requestListIndex;i++) {
+                               if (requestList[i].lasttime 
+requestList[i].message.getTTL() >= now) {
+                                       pending++;
+                                       }
+                               }
+
+                       minSleep = Scheduler.SECS_5; /* max-sleep! */
+                       perm = Crypto.permute(requestListIndex);
+                       for (i=0;i<requestListIndex;i++) {
+                               int j = perm[i];
+                               if ( (requestList[j].lasttime +
+                                               
requestList[j].message.getTTL()) <= now - TTL_DECREMENT) {
+                                       int pOCWCubed;
+                                       int pendingOverCWin = pending - 
congestionWindow;
+                                       if (pendingOverCWin <= 0)
+                                               pendingOverCWin = -1; /* avoid 
0! */
+                                       pOCWCubed = pendingOverCWin *
+                                       pendingOverCWin *
+                                       pendingOverCWin;
+
+                                       if ( (pOCWCubed <= 0) ||
+                                                       (pOCWCubed * 
requestListIndex <= 0) /* see #642 */ ||
+                                                       /* avoid no-start: 
override congestionWindow occasionally... */
+                                                       (0 == 
Crypto.nextInt(requestListIndex *pOCWCubed)) ) {
+                                               delta = 
requestList[j].message.getTTL() + Scheduler.MILLIS_10;
+                                               issueRequest(j);
+                                               pending++;
+                                               }
+                                       else {
+                                               if (isDebug()) {
+                                                       lpri++;
+                                                       /* do not print ALL the 
time, just once per iteration */
+                                                       if ( (lpri % 
(requestListIndex+1)) == 0)
+                                                               
log(Level.FINEST,"Congestion control: "+pending+" pending, "+congestionWindow+" 
window; "+initialTTL+" initial TTL.");
+                                                       }
+                                               delta = 0;
+                                               }
+                                       }
+                               else {
+                                       delta = (requestList[j].lasttime + 
TTL_DECREMENT +requestList[j].message.getTTL()) - now;
+
+                                       debug("Request "+i+":"+
+                                               
Utils.toHex(requestList[j].message.getQuery(0).getA())+" "+
+                                               "(TTL: 
"+requestList[j].message.getTTL()+") "+
+                                               "is still pending for 
"+Scheduler.toSeconds(delta)+"s.");
+                                       }
+
+                               if ( delta < minSleep ) {
+                                       minSleep = delta;
+                                       }
+                               }
+
+                       if (minSleep < Scheduler.MILLIS_100) {
+                               minSleep = Scheduler.MILLIS_100; /* maximum 
resolution: 100ms */
+                               }
+
+                       if (requestListIndex > 0) {
+                               debug("Scheduling next run for in 
"+Scheduler.toMillis(minSleep)+"ms.");
+
+                               scheduler.addJob(rrjob,minSleep);
+                               }
+                       else {
+                               debug("No more jobs pending, cron not renewed 
!");
+                               }
+                       }
+       }
+
+       /**
+        * This method receives data corresponding to the indicated filename
+        * (hashcode). Finds the Listener that scheduled this request and drop
+        * it from the list of pending requests.<p>
+        *
+        * @param msg the message received from gnunetd
+        */
+
+       protected void receive( CSResultChk msg )
+       {
+               int                             pos,i,j;
+               HashCode160             query;
+               RequestEntry    entry;
+
+               /* check type of reply msg, fill in query */
+               query=HashCode160.create(PersistentHelper.toBytes(msg.result));
+               pos = -1;
+               /* find which query matches the reply, call the callback
+                and recycle the slot */
+               for (i=0;i<requestListIndex;i++) {
+                       CSQuery acq;
+
+                       acq = requestList[i].message;
+                       j = (acq.getByteSize()-CSQuery.SIZE)/HashCode160.SIZE;
+                       while ( j > 0 ) {
+                               j--;
+                               if (query.equals(acq.getQuery(j)))
+                                       pos = i;
+                                       }
+                               }
+               if (pos == -1) {
+                       int     nowTT;
+
+                       nowTT=(int) Scheduler.toSeconds(Scheduler.now());
+                       duplicationEstimate++;
+                       if ( (nowTT - initialTTL) > lastDET) {
+                               /* only consider congestion control every
+                                "average" TTL seconds, otherwise the system
+                                reacts to events that are far too old! */
+
+                               /* duplicate reply, treat as congestion (RFC 
2001) */
+                               debug("Received duplicate data, changing CW 
("+congestionWindow+
+                                       " to "+((congestionWindow/2)+1)+
+                                       ") and SST ("+ssthresh+
+                                       "."+(congestionWindow/2)+")");
+
+                               ssthresh = congestionWindow / 2;
+                               if (ssthresh < 2)
+                                       ssthresh = 2;
+                               congestionWindow
+                               = ssthresh + 1;
+                               lastDET = nowTT;
+                       }
+
+                       debug("Received useless data matching query 
"+query.toHex()+" ("+duplicationEstimate+", 
"+Scheduler.toSeconds(initialTTL)+"s) !");
+                       return;
+                       }
+
+               debug("Received reply for request "+pos+" 
("+requestList[pos].message.getQuery(0).toHex()+")");
+
+               entry = requestList[pos];
+
+               if ( (entry.lasttime < Scheduler.now()) &&
+                               (entry.lasttime != 0) ) {
+                       int weight = 15;
+                       int ettl = entry.message.getTTL();
+                       if (ettl > TTL_DECREMENT)
+                               ettl -= TTL_DECREMENT;
+                       else
+                               ettl = 0;
+                       if ( (ettl > 4 * initialTTL) &&
+                                       ( (Scheduler.now() - entry.lasttime) < 
initialTTL) ) {
+                               weight = 127; /* eTTL is MUCH bigger than what 
we currently expect AND the time
+                               between the last query and the reply was in the 
range of the
+                               expected TTL => don't take ettl too much into 
account! */
+                       }
+                       initialTTL = ((initialTTL) * weight + ettl) / 
(weight+1);
+
+                       /* RFC 2001: increase cwnd; note that we can't really 
discriminate between
+                        slow-start and cong. control mode since our RSS is too 
small... */
+                       if (congestionWindow < ssthresh)
+                               congestionWindow += 2; /* slow start */
+                       else
+                               congestionWindow += 1; /* slower start :-) */
+               }
+               /* and finally use the entry to notify the node
+                that we got a reply! */
+
+               debug("DEBUG: request manager receives data for 
"+entry.receiverNode);
+
+               if (!entry.receiver.listen(query,
+                               msg,
+                               this,
+                               entry.data)) {
+
+                       /* ABORT download, receiver method has
+                        already notified the controller via
+                        the pmodel callback, we need to stop
+                        requesting... */
+
+                       debug("DEBUG: entry.receiver aborted download !");
+
+                       for (i=0;i<requestListIndex;i++) {
+                               freeInContinuations(requestList[i]);
+                               requestList[i]=null;
+                               }
+                       requestListIndex = 0;
+                       }
+       }
+
+       /**
+        * Listen on socket and receive messages. Call requestManagerReceive
+        * on every reply. Never returns, if the RM dies, it will cancel us.
+        */
+
+       protected void receiveThread()
+       {
+               CSMessage                       buffer;
+               CSSession       sockx;
+               PersistentDecoder       decoder;
+
+               while (sock != null) {
+                       synchronized(lock) {
+                               sockx = sock;
+                               }
+
+                       if (sockx == null)
+                               break;
+
+                       //todo: a ne faire qu'une fois
+                       decoder=new PersistentDecoder();
+                       decoder.add(CSMessage.IS_RESULT,CSResult.class);
+                       decoder.add(CSMessage.IS_RESULT_CHK,CSResultChk.class);
+
+                       buffer = (CSMessage) sockx.receive(decoder);
+                       if (buffer==null) {
+                               if (sock == null)
+                                       break;
+                               log(Level.WARNING,"Receive thread could not 
read data from gnunetd, is the server running ?");
+                               Scheduler.sleep(Scheduler.SECS_15);
+                               continue;
+                               }
+
+                       if (buffer instanceof CSResult) {
+                               int value;
+
+                               value = ((CSResult) buffer).getResult();
+
+                               synchronized(lock) {
+                                       if (start == null) {
+                                               log(Level.SEVERE,"Received 
return value from gnunetd but I have no continuation! (bug!)");
+                                               }
+                                       else {
+                                               
runContinuation(value!=0);//todo: value!=0 est le bon test ???
+                                               }
+                                       }
+                               }
+                       else if (buffer instanceof CSResultChk) {
+                               synchronized(lock) {
+                                       receive((CSResultChk) buffer);
+                                       }
+                               }
+                       }
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/esed2/RootNode.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/esed2/RootNode.java        
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/esed2/RootNode.java        
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,120 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.esed2;
+
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * full CONTENT_SIZE'ed root node
+ * header of the RootNode (search result with meta-data)
+ *
+ * The structure of the root node - contains pertinent information for
+ * the file (file length, checksum, hashcode of main indirection node,
+ * description length, and description.
+ *
+ * The structure of the root node, including padding to make it to 1k.
+ */
+
+public class RootNode extends Node
+{
+       public static final int SIZE                            =       1024;
+       public static final int NOT_PADDED_SIZE =       564;
+
+
+       public RootNode()
+       {
+               super(ROOT_MAJOR_VERSION,ROOT_MINOR_VERSION);
+       }
+
+       public RootNode( FileIdentifier fid )
+       {
+               this();
+               fileIdentifier=(FileIdentifier) PersistentHelper.copy(fid);     
        //todo: copie utile ?
+       }
+
+       public String toString()
+       {
+               return "Root node [description="+description+", 
filename="+fileName+", mimetype="+mimeType+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       /**
+        * Convert this node to a human readable string.
+        * @return
+        */
+
+       public String toLabel()
+       {
+               StringBuffer    buf;
+
+               buf=new StringBuffer();
+               if (mimeType.equals(GNUNET_DIRECTORY_MIME)) {
+                       buf.append(GNDirectory.expandDirectoryName(fileName));
+                       }
+               else {
+                       buf.append(fileName);
+                       }
+               buf.append(": ");
+               buf.append(description);
+               buf.append(" of type '");
+               buf.append(mimeType);
+               buf.append("' (size ");
+               buf.append(fileIdentifier.getFileLength());
+               buf.append(")\n");
+               buf.append(fileIdentifier.toURI());
+               return buf.toString();
+       }
+
+       public int getByteSize()
+       {
+               return SIZE;
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               int     i;
+
+               buf.putShort((short) ROOT_MAJOR_VERSION);
+               buf.putShort((short) ROOT_MINOR_VERSION);
+
+               fileIdentifier.writeBytes(buf);
+
+               AFSUtils.put(description,buf,256);
+               AFSUtils.put(fileName,buf,128);
+               AFSUtils.put(mimeType,buf,128);
+
+               for (i=NOT_PADDED_SIZE; i<SIZE; i++) {
+                       buf.put((byte) 0);
+                       }
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               int             major,minor,i;
+               byte            b;
+
+               major=buf.getShort() & 0x0000ffff;
+               minor=buf.getShort() & 0x0000ffff;
+
+               err.reportIf(major!=ROOT_MAJOR_VERSION,"bad major version");
+               err.reportIf(minor!=ROOT_MINOR_VERSION,"bad minor version");
+
+               fileIdentifier=new FileIdentifier();
+               fileIdentifier.readBytes(buf,err);
+
+               description=AFSUtils.get(buf,256);
+               fileName=AFSUtils.get(buf,128);
+               mimeType=AFSUtils.get(buf,128);
+
+               for (i=NOT_PADDED_SIZE; i<SIZE; i++) {
+                       b=buf.get();
+                       err.reportIf(b!=0,"bad zero-padding");
+                       }
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/esed2/RootNodeCallback.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/esed2/RootNodeCallback.java        
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/esed2/RootNodeCallback.java        
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,19 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.esed2;
+
+/**
+ */
+
+public interface RootNodeCallback
+{
+       /**
+        * Callback function.
+        * @param root a root-node
+        * @param closure a closure
+        */
+
+       public void rootNode( RootNode root, Object closure );
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/esed2/SBlock.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/esed2/SBlock.java  2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/esed2/SBlock.java  2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,438 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.esed2;
+
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.io.*;
+import java.nio.*;
+import java.util.logging.*;
+
+/**
+ * data structure SBlock
+ * data structure SBlock
+ * total: 1024 bytes
+ */
+
+public class SBlock extends Node
+{
+       public static final int ENCRYPTED_SIZE  =       484;
+       public static final int SIGNED_SIZE             =       504;
+       public static final int SIZE                            =       1024;
+
+       /** */
+       private int                     creationTime;
+
+       /** */
+       private int                     updateInterval;
+
+       /** N */
+       private HashCode160     nextIdentifier;
+
+       /** I */
+       private HashCode160     identifierIncrement;
+
+       /** R = H(N-I)^S where S= H(subspace) */
+       private HashCode160     identifier;
+
+       /** */
+       private Signature       signature;
+
+       /** */
+       private PublicKey       subspace;
+
+
+       public SBlock()
+       {
+               super(SBLOCK_MAJOR_VERSION,SBLOCK_MINOR_VERSION);
+               creationTime=0;
+               updateInterval=SBLOCK_UPDATE_NONE;
+               nextIdentifier=null;
+               identifierIncrement=null;
+               identifier=null;
+               signature=null;
+               subspace=null;
+       }
+
+       public SBlock( FileIdentifier fid )
+       {
+               this();
+               fileIdentifier=(FileIdentifier) PersistentHelper.copy(fid);     
        //todo: copie utile ?
+       }
+
+       public String toString()
+       {
+               return "S(igned)Block [description="+description+", 
filename="+fileName+", mimetype="+mimeType+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       /**
+        * Convert a root-node to a string (to display it to the user).
+        * @return
+        */
+
+       public String toLabel()
+       {
+               HashCode160     ns;
+               String          str;
+
+               //todo: add creation time & update frequency
+
+               if (mimeType.equals(GNUNET_DIRECTORY_MIME)) {
+                       str=GNDirectory.expandDirectoryName(fileName);
+                       }
+               else {
+                       str=fileName;
+                       }
+               
ns=HashCode160.create(PersistentHelper.toBytes(subspace),0,PublicKey.SIZE);
+               return str+": "+description+" of type '"+mimeType+"' (size 
"+fileIdentifier.getFileLength()+", namespace 
"+ns.toHex()+")\n"+fileIdentifier.toURI();
+       }
+
+       public int getCreationTime()
+       {
+               return creationTime;
+       }
+
+       public void setCreationTime( int ctime )
+       {
+               creationTime=ctime;
+       }
+
+       public int getUpdateInterval()
+       {
+               return updateInterval;
+       }
+
+       /**
+        * @param interval the update frequency (0: never, -1: sporadic)
+        */
+
+       public void setUpdateInterval( int interval )
+       {
+               updateInterval=interval;
+       }
+
+       /**
+        * Returns K=N-I
+        * @return
+        */
+
+       public HashCode160 getKey()
+       {
+               HashCode160     h;
+
+               h=new HashCode160(nextIdentifier);
+               h.sub(identifierIncrement);
+               return h;
+       }
+
+       public HashCode160 getIdentifierIncrement()
+       {
+               return identifierIncrement;
+       }
+
+       public HashCode160 getIdentifier()
+       {
+               return identifier;
+       }
+
+       /**
+        * Compute the "current" ID of an updateable SBlock. Will set the ID of 
the sblock itself for non-updatable content,
+        * the ID of the next identifier for sporadically updated SBlocks and 
the ID computed from the timing function
+        * for periodically updated SBlocks.
+        *
+        * @param now the time for which the ID should be computed
+        * @return the resulting current ID (set)
+        */
+
+       public HashCode160 computeIdAtTime( int now )
+       {
+               int                     pos;
+               HashCode160     tmp,c;
+
+               if (updateInterval == SBLOCK_UPDATE_SPORADIC) {
+                       return (HashCode160) 
PersistentHelper.copy(nextIdentifier);
+                       }
+
+               if (updateInterval == SBLOCK_UPDATE_NONE) {
+                       /* H(N-I)^S is the current routing key, so N-I = k */
+                       return getKey();
+                       }
+
+               pos = creationTime;
+
+               c=getKey();
+
+               while (pos + updateInterval < now) {
+                       pos += updateInterval;
+
+                       tmp=new HashCode160(c);
+                       tmp.add(identifierIncrement);
+
+                       c=(HashCode160) PersistentHelper.copy(tmp);
+
+                       debug("Update at 
"+Utils.formatMoment(Scheduler.seconds(pos))+" should have key "+c.toHex());
+                       }
+               return c;
+       }
+
+       public PublicKey getPublisherKey()
+       {
+               return subspace;
+       }
+
+       public HashCode160 getPublisherNameSpace()
+       {
+               return HashCode160.create(PersistentHelper.toBytes(subspace));
+       }
+
+       /**
+        * Build an (encrypted) SBlock.
+        *
+        * @param priv
+        * @param key           the key for this SBlock
+        * @param nextKey       the key for the next SBlock (if updateable)
+        * @return
+        */
+
+       public EncryptedSBlock encryptAndSign( PrivateKey priv, HashCode160 
key, HashCode160 nextKey )
+       {
+               byte[]          tmp;
+               HashCode160     hk;
+               byte[]          iv,raw;
+               SessionKey      skey;
+
+               // next identifier N
+               nextIdentifier=(HashCode160) PersistentHelper.copy(nextKey);
+
+               // identifier increment I=N-K
+               identifierIncrement=new HashCode160(nextKey);
+               identifierIncrement.sub(key);
+
+               // routing identifier R=H(K)^S   =H(N-I)^S
+               hk=HashCode160.create(PersistentHelper.toBytes(key));
+               
identifier=HashCode160.create(PersistentHelper.toBytes(priv.toPublicKey()));
+               identifier.xor(hk);
+
+               raw=new byte[SIZE];
+               writeBytes(ByteBuffer.wrap(raw));
+
+               iv=new byte[SessionKey.BLOWFISH_BLOCK_LENGTH];
+               skey=key.extractKey(iv);
+
+               tmp=skey.encrypt(raw,0,ENCRYPTED_SIZE,iv);
+               if (tmp==null || tmp.length!=ENCRYPTED_SIZE) {
+                       log(Level.SEVERE,"Failed to encrypt SBlock !");
+                       return null;
+                       }
+               System.arraycopy(tmp,0,raw,0,tmp.length);
+
+               signature=priv.sign(raw,0,SIGNED_SIZE);
+               if (signature==null) {
+                       log(Level.SEVERE,"Failed to sign SBlock !");
+                       return null;
+                       }
+
+               subspace=priv.toPublicKey();
+               return new EncryptedSBlock(tmp,identifier,signature,subspace);
+       }
+
+       /**
+        * Verify that a given SBlock is well-formed.
+        * @return
+        */
+
+       public boolean verify()
+       {
+               byte[]          iv,raw,tmp;
+               SessionKey      skey;
+
+               raw=new byte[SIZE];
+               writeBytes(ByteBuffer.wrap(raw));
+
+               iv=new byte[SessionKey.BLOWFISH_BLOCK_LENGTH];
+               skey=getKey().extractKey(iv);
+
+               tmp=skey.encrypt(raw,0,ENCRYPTED_SIZE,iv);
+               if (tmp==null || tmp.length!=ENCRYPTED_SIZE) {
+                       log(Level.SEVERE,"Failed to decrypt SBlock !");
+                       return false;
+                       }
+               System.arraycopy(tmp,0,raw,0,tmp.length);
+
+               return subspace.verify(signature,raw,0,SIGNED_SIZE);
+       }
+
+       /**
+        * Print the information contained in an SBlock.
+        *
+        * @param stream where to print the information to
+        */
+
+       public void print( PrintWriter stream )
+       {
+               HashCode160     hc;
+               int                     now,pos;
+               String          fstring;
+               String          fn;
+
+               /* if it is a GNUnet directory, replace suffix '/' with ".gnd" 
*/
+               if (mimeType.equals(GNUNET_DIRECTORY_MIME)) {
+                       fn = GNDirectory.expandDirectoryName(fileName);
+                       }
+               else {
+                       fn = fileName;
+                       }
+
+               hc=HashCode160.create(PersistentHelper.toBytes(subspace));
+               stream.println(description+" ("+mimeType+") published by 
"+hc.toHex());
+
+               fstring = fileIdentifier.toURI();
+               stream.println("gnunet-download -o \""+fn+"\" "+fstring);
+
+               switch (updateInterval) {
+                       case SBLOCK_UPDATE_SPORADIC:
+                               stream.println("Next update will be 
"+nextIdentifier.toHex()+".");
+                               break;
+
+                       case SBLOCK_UPDATE_NONE:
+                               stream.println("SBlock indicates no updates.");
+                               break;
+
+                       default:
+                               pos = creationTime;
+
+                               hc=new HashCode160(nextIdentifier);
+                               hc.sub(identifierIncrement);
+
+                               now=(int) Scheduler.toSeconds(Scheduler.now());
+                               while (pos + updateInterval < now) {
+                                       HashCode160 tmp;
+
+                                       pos += updateInterval;
+
+                                       tmp=new HashCode160(hc);
+                                       tmp.add(identifierIncrement);
+
+                                       stream.println("Update due at 
"+Utils.formatMoment(Scheduler.seconds(pos))+" has key "+tmp.toHex());
+                                       }
+                               break;
+                       }
+       }
+
+       public int getByteSize()
+       {
+               return SIZE;
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               int     i;
+
+               buf.putShort((short) SBLOCK_MAJOR_VERSION);
+               buf.putShort((short) SBLOCK_MINOR_VERSION);
+
+               if (fileIdentifier!=null) {
+                       fileIdentifier.writeBytes(buf);
+                       }
+               else {
+                       for (i=0; i<FileIdentifier.SIZE; i++) {
+                               buf.put((byte) 0);
+                               }
+                       }
+
+               AFSUtils.put(description,buf,256);
+               AFSUtils.put(fileName,buf,64);
+               AFSUtils.put(mimeType,buf,64);
+
+               buf.putInt(creationTime);
+               buf.putInt(updateInterval);
+
+               if (nextIdentifier!=null) {
+                       nextIdentifier.writeBytes(buf);
+                       }
+               else {
+                       for (i=0; i<HashCode160.SIZE; i++) {
+                               buf.put((byte) 0);
+                               }
+                       }
+
+               if (identifierIncrement!=null) {
+                       identifierIncrement.writeBytes(buf);
+                       }
+               else {
+                       for (i=0; i<HashCode160.SIZE; i++) {
+                               buf.put((byte) 0);
+                               }
+                       }
+
+               if (identifier!=null) {
+                       identifier.writeBytes(buf);
+                       }
+               else {
+                       for (i=0; i<HashCode160.SIZE; i++) {
+                               buf.put((byte) 0);
+                               }
+                       }
+
+               if (signature!=null) {
+                       signature.writeBytes(buf);
+                       }
+               else {
+                       for (i=0; i<Signature.SIZE; i++) {
+                               buf.put((byte) 0);
+                               }
+                       }
+
+               if (subspace!=null) {
+                       subspace.writeBytes(buf);
+                       }
+               else {
+                       for (i=0; i<PublicKey.SIZE; i++) {
+                               buf.put((byte) 0);
+                               }
+                       }
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               int     major,minor;
+
+               major=buf.getShort() & 0x0000ffff;
+               minor=buf.getShort() & 0x0000ffff;
+
+               err.reportIf(major!=SBLOCK_MAJOR_VERSION,"bad major version");
+               err.reportIf(minor!=SBLOCK_MINOR_VERSION,"bad minor version");
+
+               fileIdentifier=new FileIdentifier();
+               fileIdentifier.readBytes(buf,err);
+
+               description=AFSUtils.get(buf,256);
+               fileName=AFSUtils.get(buf,64);
+               mimeType=AFSUtils.get(buf,64);
+
+               creationTime=buf.getInt();
+               updateInterval=buf.getInt();
+
+               nextIdentifier=new HashCode160();
+               nextIdentifier.readBytes(buf,err);
+
+               identifierIncrement=new HashCode160();
+               identifierIncrement.readBytes(buf,err);
+
+               identifier=new HashCode160();
+               identifier.readBytes(buf,err);
+
+               signature=new Signature();
+               signature.readBytes(buf,err);
+
+               subspace=new PublicKey();
+               subspace.readBytes(buf,err);
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/esed2/SearchResultCallback.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/esed2/SearchResultCallback.java    
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/esed2/SearchResultCallback.java    
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,29 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.esed2;
+
+/**
+ *
+ */
+
+public interface SearchResultCallback
+{
+       /**
+        * Type of a callback method for results that have
+        * been received.
+        *
+        * @param root the RootNode of the result that has been received
+        */
+
+       public void searchResult( RootNode root );
+
+       /**
+        * @param root
+        * @param str
+        *
+        */
+
+       public void newNameFor( RootNode root, String str );
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/esed2/SearchURI.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/esed2/SearchURI.java       
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/esed2/SearchURI.java       
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,34 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.esed2;
+
+import org.gnu.freeway.util.crypto.*;
+
+/**
+ */
+
+public class SearchURI extends GeneralURI
+{
+//     public int                      action;
+       public HashCode160      namespace;
+       public HashCode160      keyhash;
+       public String[]         keywords;
+       public int                      keycount;
+
+
+       public SearchURI()
+       {
+               super();
+       }
+
+       public String toString()
+       {
+               return "xxx";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/esed2/SendNSQueryContext.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/esed2/SendNSQueryContext.java      
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/esed2/SendNSQueryContext.java      
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,249 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.esed2;
+
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.util.logging.*;
+
+/**
+ */
+
+public class SendNSQueryContext extends LoggedObject implements AFSConstants
+{
+       /** Time when the cron-job was first started. */
+       public long                                     start;
+
+       /** How many cron-units may we run (total) ? */
+       public long                                     timeout;
+
+       /** */
+       public CSSession        sock;
+
+       /** */
+       public CSNSQuery                query;
+
+
+       public SendNSQueryContext()
+       {
+               super(true);
+       }
+
+       public String toString()
+       {
+               return "xxx";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       /**
+        * Retrieve an SBlock.
+        *
+        * @param policy
+        * @param scheduler
+        * @param prefs
+        * @param sockx socket to use to contact gnunetd
+        * @param s namespace which namespace to search
+        * @param k key to decrypt the SBlock in the namespace (query
+        *        to identify the block is derived from k)
+        * @param testTerminate function to poll for abort
+        * @param resultCallback function to call for results
+        * @param closure argument to pass to resultCallback
+        * @return OK on success, false on error (= no result found)
+        */
+
+       public boolean searchSBlock( Policy policy, Scheduler scheduler, Prefs 
prefs, CSSession sockx, HashCode160 s, HashCode160 k, TestTerminateThread 
testTerminate, NSSearchResultCallback resultCallback, Object closure )
+       {
+               CSResultSBlock          reply;
+               boolean                         ret;
+               HashCode160                     hc,hk;
+               HashCode160                     r; /* r = H(k) ^ s */
+               SendNSQueryContext      sqc;
+               SBlock                          result;
+               ScheduledTask           sendTask;
+
+               hk=HashCode160.create(PersistentHelper.toBytes(k));
+               r=new HashCode160(s);
+               r.xor(hk);  /* compute routing key R */
+
+               sqc=new SendNSQueryContext();
+               sqc.sock = sockx;
+               sqc.query = new CSNSQuery();
+               sqc.query.priority = 1;
+               sqc.query.ttl = (int) (1+Crypto.nextLong(TTL_DECREMENT));
+               sqc.query.namespace=(HashCode160) PersistentHelper.copy(s);
+               sqc.query.identifier=(HashCode160) PersistentHelper.copy(r);
+
+               // add cron job to send search query
+               final SendNSQueryContext        _sqc = sqc;
+               final Policy                            _policy = policy;
+               final Scheduler                 _scheduler = scheduler;
+
+               sendTask=new ScheduledTask("SEND-NSQUERY2",new AbstractAction() 
{
+                       public void perform()
+                       {
+                               sendNSQuery(_sqc,_policy,_scheduler);
+                       }
+                       });
+               scheduler.addJob(sendTask,0);
+
+               ret = false;
+               while (!testTerminate.test()) {
+                       reply=(CSResultSBlock) 
sock.receive(CSResultSBlock.class);
+                       if (reply==null) {
+                               log(Level.WARNING,"Unable to read *correct* 
message.");
+                               if (testTerminate.test())
+                                       break;
+                               Scheduler.sleep(Scheduler.SECS_1);
+                               continue;
+                               }
+
+                       debug("Received message from gnunetd");
+
+                       if (!reply.getResult().verify()) {
+                               log(Level.WARNING,"WARNING: SBlock received 
from gnunetd failed verification.");
+                               break;
+                               }
+                       new 
Pseudonym(prefs).addNamespace(reply.getResult().getNameSpace());
+
+                       // internal identifier (for routing HT, etc.) is "xor" 
of the user-identifier with the namespace ID
+                       // to avoid keyword collisions with realnames in the 
global, 3HASH namespace
+
+                       hc=reply.getResult().getNameSpace();
+                       if (!hc.equals(s)) {
+                               log(Level.WARNING,"WARNING: SBlock received 
from gnunetd belongs to wrong namespace.");
+                               break;
+                               }
+                       if (!r.equals(reply.getResult().getIdentifier()) ) {
+                               log(Level.WARNING,"WARNING: SBlock received 
from gnunetd has wrong identifier.");
+                               break;
+                               }
+
+                       result=reply.getResult().decrypt(k);
+                       resultCallback.searchResult(result,closure);
+                       ret = true;
+                       }
+
+               // delete cron job
+               scheduler.deleteJob(sendTask);
+               return ret;
+       }
+
+       public void sendNSQuery( SendNSQueryContext sqc, Policy policy, 
Scheduler scheduler )
+       {
+               long    now,remTime,new_ttl,new_priority;
+
+               debug("enter sendnsquery");
+
+               now=Scheduler.now();
+
+               if (sqc.timeout != 0) {
+                       remTime = (int) (sqc.start - now + sqc.timeout);
+                       if (remTime <= 0) {
+                               log(Level.FINEST,"Exiting sendnsquery without 
making a query.");
+                               return;
+                               }
+                       }
+               else {
+                       remTime = 0x7FFFFFFF; /* max signed int */
+                       }
+
+               if 
(policy.checkAnonymityPolicy(CSMessage.IS_NSQUERY,P2PNSQuery.SIZEX)) {
+                       if (sqc.sock.send(sqc.query)) {
+                               /* successful transmission to GNUnet,
+                                increase ttl/priority for the next time */
+                               new_ttl = sqc.query.ttl;
+                               if (new_ttl > 0x00FFFFFF) {
+                                       new_ttl = Crypto.nextLong(0x00FFFFFF); 
/* if we get too large, reduce! */
+                                       }
+                               sqc.query.ttl= (int) 
Crypto.nextLong(1+4*new_ttl);
+                               new_priority = sqc.query.priority;
+                               if (new_priority > 0xFFFFFF) {
+                                       new_priority = 
Crypto.nextLong(0xFFFFFF); /* if we get too large, reduce! */
+                                       }
+                               sqc.query.priority= (int) 
Crypto.nextLong(1+4*new_priority);
+                               }
+                       else {
+                               new_ttl = Scheduler.SECS_5; /* wait at least 5s 
for gnunetd */
+                               }
+                       }
+               else {
+                       new_ttl = TTL_DECREMENT;
+                       }
+
+               /* Don't repeat a search faster than TTL_DEC seconds */
+               if (new_ttl < TTL_DECREMENT)
+                       new_ttl = TTL_DECREMENT;
+
+               debug("Will wait for min("+new_ttl+", "+remTime+") ms");
+
+               /* Do not sleep longer than the amount of time we have until
+                we shut down */
+               if (new_ttl >= remTime)
+                       new_ttl = remTime;
+
+               if (remTime > 0) {
+                       debug("Reinstating sendnsquery in "+new_ttl);
+
+                       final SendNSQueryContext        _sqc = sqc;
+                       final Policy                            _policy = 
policy;
+                       final Scheduler                 _scheduler = scheduler;
+
+                       scheduler.addJob(new ScheduledTask("SEND-NSQUERY",new 
AbstractAction() {
+                               public void perform()
+                               {
+                                       sendNSQuery(_sqc,_policy,_scheduler);
+                               }
+                               }),new_ttl);
+                       }
+       }
+
+       /**
+        * Perform a namespace search.
+        *
+        * @param prefs
+        * @param scheduler
+        * @param policy
+        * @param sock
+        * @param keyStrings
+        * @param keywordCount
+        * @param handler
+        * @param handlerArgs
+        * @param testTerminate
+        * @param ttContext
+        * @return
+        */
+
+       public static boolean searchRBlock( Prefs prefs, final Scheduler 
scheduler, final Policy policy, CSSession sock, String[] keyStrings, int 
keywordCount, SearchResultCallback handler, Object handlerArgs, 
TestTerminateThread testTerminate, Object ttContext )
+       {
+               SendQueriesContext      sqc;
+               HashCode160[]           keywords;
+               ScheduledTask           sendJob;
+
+               keywords=AFSUtils.parseKeywords(keyStrings);
+
+               sqc=new SendQueriesContext(keywords,sock);
+               
sqc.setTimeOut(Scheduler.seconds(prefs.getInt("AFS","SEARCHTIMEOUT",0)));
+
+               final SendQueriesContext        _sqc = sqc;
+
+               sendJob=new ScheduledTask("SEND-RBLOCK",new AbstractAction() {
+                       public void perform()
+                       {
+                               _sqc.repeatedlySend(scheduler,policy);
+                       }
+                       });
+               scheduler.addJob(sendJob,0);
+
+//             receiveResults(sock,keywordCount,keywords,      
messages,handler,handlerArgs,testTerminate,ttContext);
+               sqc.receive(prefs,handler,testTerminate);
+
+               scheduler.deleteJob(sendJob);
+               return true;
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/esed2/SendQueriesContext.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/esed2/SendQueriesContext.java      
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/esed2/SendQueriesContext.java      
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,356 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.esed2;
+
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+import java.util.*;
+import java.util.logging.*;
+
+/**
+ * Context of the send queries cron job.
+ */
+
+public class SendQueriesContext extends LoggedObject implements AFSConstants
+{
+       private static Comparator       fComparator;
+
+       static {
+               fComparator=createFilenameComparator();
+               }
+
+       /** Time when the cron job was first started. */
+       private long                    start;
+
+       /** How many cron units may we run (total) ? */
+       private long                    timeout;
+
+       /** Socket for communication with gnunetd. */
+       private CSSession       sock;
+
+       /** */
+       private HashCode160[]   keywords;
+
+       /** Query messages. */
+       private CSQuery[]               messages;
+
+       private PersistentDecoder               decoder;
+
+       /**
+        * Construct a new query context.
+        *
+        * @param kwords        the keywords (for decryption)
+        * @param s                     socket used to receive results from
+        */
+
+       public SendQueriesContext( HashCode160[] kwords, CSSession s )
+       {
+               super(true);
+               start=Scheduler.now();
+               timeout=0;
+
+               sock=s;
+
+               decoder=new PersistentDecoder();
+               decoder.add(CSMessage.IS_RESULT,CSResult.class);
+               decoder.add(CSMessage.IS_RESULT_3HASH,CSResult3Hash.class);
+
+               keywords=kwords;
+               messages=AFSUtils.buildMessages(keywords);
+       }
+
+       public String toString()
+       {
+               return "Send queries context";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public CSSession getSocket()
+       {
+               return sock;
+       }
+
+       public CSQuery[] getQueries()
+       {
+               return messages;
+       }
+
+       public void setTimeOut( long t )
+       {
+               timeout=t;
+       }
+
+       /**
+        * Repeatedly send out the queries to GNUnet.
+        * This cron-job self-terminates after sqc.timout cron-units have 
passed.
+        * @param scheduler
+        * @param policy
+        */
+
+       public void repeatedlySend( final Scheduler scheduler, final Policy 
policy )
+       {
+               long            ttl;
+
+               ttl=send(policy);
+               if (ttl>0) {
+                       scheduler.addJob(new ScheduledTask("REPEAT-SEND",new 
AbstractAction() {
+                               public void perform()
+                               {
+                                       repeatedlySend(scheduler,policy);
+                               }
+                               }),ttl);
+                       }
+       }
+
+       public int send( Policy policy )
+       {
+               long    now;
+               int             remTime,ttl,newTTL,newPriority,i;
+
+               now=Scheduler.now();
+
+               if (timeout != 0) {
+                       remTime = (int) (start - now + timeout);
+                       if (remTime <= 0)
+                               return 0;
+                       }
+               else {
+                       remTime = 0x7FFFFFFF;   // max signed int
+                       }
+
+               ttl=0;
+               for (i=0; i<messages.length; i++) {
+                       log(Level.FINEST,"Sending query with ttl 
"+messages[i].getTTL()+".");
+
+                       ttl=Crypto.nextInt((int) TTL_DECREMENT)+1;
+
+                       if 
(policy.checkAnonymityPolicy(CSMessage.IS_QUERY,messages[i].getByteSize()+HostIdentity.SIZE))
 {
+                               if (sock.send(messages[i])) {
+                                       // successful transmission, increase 
ttl/priority for the next time
+                                       newTTL=messages[i].getTTL();
+                                       ttl=Math.max(newTTL,ttl);
+
+                                       if (newTTL>0xFFFFFF) {
+                                               newTTL=Crypto.nextInt(0xFFFFFF);
+                                               }
+
+                                       newPriority=messages[i].getPriority();
+                                       if (newPriority>0xFFFFFF) {
+                                               
newPriority=Crypto.nextInt(0xFFFFFF);
+                                               }
+
+                                       messages[i].setPriorityAndTTL( 
Crypto.nextInt(1+4*newPriority),Crypto.nextInt(1+4*newTTL));
+                                       }
+                               }
+                       }
+
+               // sleep approximately the time the longest ttl will take
+               ttl=ttl+Crypto.nextInt(ttl+1);
+
+               // don't repeat a search faster than TTL_DECREMENT seconds
+               if (ttl<TTL_DECREMENT) {
+                       ttl=(int) TTL_DECREMENT;
+                       }
+
+               log(Level.FINEST,"Will wait for min("+ttl+","+remTime+") ms.");
+
+               // do not sleep longer than the amount of time we have until we 
shut down
+               if (ttl>=remTime) {
+                       ttl=remTime;
+                       }
+               return ttl;
+       }
+
+       /**
+        * Start retrieving results from GNUnet. This method terminates only
+        * if the testTerminate-method returns YES after a result was received
+        * or there is an error with reading from the socket.
+        *
+        * @param prefs
+        * @param handler the method to call on each result matching all 
keywords
+        * @param testTerminate method used to check if we should termiante
+        */
+
+       public void receive( Prefs prefs, SearchResultCallback handler, 
TestTerminateThread testTerminate )
+       {
+               ResultContext   rc;
+               Persistent              buffer;
+               CSResult3Hash   reply;
+               byte[]                  rootNodeBytes;
+               int                             i;
+               HashCode160             tripleHash;
+
+               rc=new ResultContext(keywords.length);
+
+               while (!testTerminate.test()) {
+                       buffer=sock.receive(decoder);
+                       if (buffer==null) {
+                               if (testTerminate.test())
+                                       break;
+                               Scheduler.sleep(Scheduler.SECS_1);
+                               continue;
+                               }
+
+                       if (buffer instanceof CSResult) {
+                               log(Level.FINEST,"Daemon has acknowledeged.");
+                               // ignore: confirmation of gnunetd that it 
received a search request from the other thread
+                               }
+                       else {
+                               reply=(CSResult3Hash) buffer;
+
+                               // now decrypt the reply & call a method to use 
it
+                               tripleHash=reply.getTripleHash();
+                               for (i=0; i<keywords.length; i++) {
+                                       if 
(tripleHash.equals(messages[i].getQuery(0))) {
+                                               
rootNodeBytes=reply.decrypt(keywords[i]);
+                                               if (rootNodeBytes==null) {
+                                                       
log(Level.SEVERE,"Failed to decrypt content !?");
+                                                       continue;
+                                                       }
+                                               if 
(Node.extractMajorVersion(rootNodeBytes)!=ROOT_MAJOR_VERSION || 
Node.extractMinorVersion(rootNodeBytes)!=ROOT_MINOR_VERSION) {
+                                                       log(Level.INFO,"Content 
has unsupported version: 
"+Node.extractMajorVersion(rootNodeBytes)+"."+Node.extractMinorVersion(rootNodeBytes));
+                                                       continue; /* bah! 
broken! */
+                                                       }
+
+                                               filterResult(prefs,
+                                                       (RootNode) 
PersistentHelper.readFully(RootNode.class,ByteBuffer.wrap(rootNodeBytes)),
+                                                       i,rc,handler);
+                                               }
+                                       else {
+                                               log(Level.FINEST,"Reply 
"+reply.getDoubleHash().toHex()+" does not match expected hash 
"+messages[i].getQuery(0).toHex());
+                                               }
+                                       }
+                               }
+                       }
+       }
+
+       /**
+        * Filter results that do not match ALL keywords.
+        * Display the result, but make sure that
+        * every file is only displayed once.
+        *
+        * @param prefs
+        * @param rootNode      the new reply
+        * @param keyIndex      for which key this result matches
+        * @param rc            the context to keep track of which replies we 
got so far
+        * @param handler
+        */
+
+       protected void filterResult( Prefs prefs, RootNode rootNode, int 
keyIndex, ResultContext rc, SearchResultCallback handler )
+       {
+               Map             tmp;
+               List    tmp2;
+               String  name;
+               int             i,j;
+
+               tmp=rc.key2hash[keyIndex];
+               if 
(tmp.containsKey(rootNode.getFileIdentifier().getFileQuery())) {
+                       name=(String) 
tmp.get(rootNode.getFileIdentifier().getFileQuery());
+                       if (fComparator.compare(rootNode.getFileName(),name)>0) 
{
+                               log(Level.FINEST,"Daemon has returned *a 
better* filename for already seen result.");
+                               
tmp.put(PersistentHelper.copy(rootNode.getFileIdentifier().getFileQuery()),rootNode.getFileName());
+
+                               
handler.newNameFor(rootNode,rootNode.getFileName());
+                               return;
+                               }
+
+                       log(Level.FINEST,"Daemon has returned *already seen* 
results to filter.");
+                       return;
+                       }
+
+               log(Level.FINEST,"Daemon has returned new results to filter.");
+               // add to the matching files for this key
+               
tmp.put(PersistentHelper.copy(rootNode.getFileIdentifier().getFileQuery()),rootNode.getFileName());
+
+               // check if the file now matches all keys
+               for (i=0; i<keywords.length; i++) {
+                       tmp2=new ArrayList(rc.key2hash[i].keySet());
+
+                       for (j=0; j<tmp2.size(); j++) {
+                               if 
(tmp2.get(j).equals(rootNode.getFileIdentifier().getFileQuery())) {
+                                       break;
+                                       }
+                               }
+
+                       if (j == tmp2.size()) {
+                               log(Level.FINEST,"Not enough results for the 
AND query.");
+                               return; // not found, exit !
+                               }
+                       }
+
+               // ok, rootNode matches all the AND criteria, display
+
+               // this check should be redundant...
+               if 
(rc.results.contains(rootNode.getFileIdentifier().getFileQuery())) {
+                       log(Level.FINEST,"We have seen this result before : 
"+rootNode.getFileIdentifier().getFileQuery().toHex());
+                       return;
+                       }
+
+               // directory support...
+               new 
GNDirectoryDatabase(prefs).makeRootNodeAvailable(rootNode,DIR_CONTEXT_SEARCH);
+
+               
rc.results.add(PersistentHelper.copy(rootNode.getFileIdentifier().getFileQuery()));
+               handler.searchResult(rootNode);
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       protected static Comparator createFilenameComparator()
+       {
+               return new Comparator() {
+                       public int compare( Object o1, Object o2 )
+                       {
+                               if (o1==null || o2==null) {
+                                       return (o1!=null ? 1 : (o2!=null ? -1 : 
0));
+                                       }
+                               return 
evalCorrectness(o1.toString())-evalCorrectness(o2.toString());
+                       }
+                       };
+       }
+
+       protected static int evalCorrectness( String str )
+       {
+               int             count,i;
+               char    c;
+
+               for (i=count=0; i<str.length(); i++) {
+                       c=str.charAt(i);
+                       if (c<' ' || c>127) {
+                               count+=2;
+                               }
+                       else if (c==' ') {
+                               count++;
+                               }
+               }
+               return -count;
+       }
+}
+
+class ResultContext extends Object
+{
+       /** the results we've got so far (hash of root-node) */
+       public List             results;
+
+       /** unmatched ("AND") results so far, list of root-node hashes that 
were received for each keyword */
+       public Map[]    key2hash;
+
+
+       public ResultContext( int count )
+       {
+               super();
+               results=new ArrayList();
+
+               key2hash=new Map[count];
+               while (count>0) {
+                       key2hash[--count]=new HashMap();
+                       }
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/esed2/TestTerminateThread.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/esed2/TestTerminateThread.java     
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/esed2/TestTerminateThread.java     
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,19 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.esed2;
+
+/**
+ *
+ */
+
+public interface TestTerminateThread
+{
+       /**
+        * Method to test if the receive-thread should terminate.
+        * @return
+        */
+
+       public boolean test();
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/esed2/URI.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/esed2/URI.java     2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/esed2/URI.java     2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,480 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.esed2;
+
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+
+import java.util.*;
+import java.util.logging.*;
+
+/**
+ * URI handling
+ * GNUnet AFS uri format
+ *
+ * gnunet://afs/action/tag1=value1?tag2=value2...
+ *
+ * where action is one of {download,search,insert,delete}
+ * and supported tags are
+ *
+ * tag         description
+ * -------------------------------------------------------
+ * ns          Namespace identifier
+ * kh          Keyhash
+ * qh          Queryhash
+ * crc         File CRC32
+ * fn          File name (suggested)
+ * size        File length
+ * keyword     Search keyword (can be specified multiple times)
+ * pseudonym   Pseudonym for namespace insertion
+ * password    Password for a pseudonym
+ *
+ * Not all tags make sense in all contexts.
+ *
+ * Parses and produces uri strings.
+ *
+ * What spaghetti ...
+ *
+ * How it works: first parse all tag/value pairs into a table. Take
+ * note of the "action" type. Then call a specific parser to create
+ * the actual data structure.
+ *
+ * Bugs: leaks mem if some tags except keyword or numeric tags
+ * are specified more than once.
+ */
+
+public class URI extends LoggedObject
+{
+       public static final String      AFS_URI_PREFIX  =       "gnunet://afs/";
+
+       public static final int URI_ACTION_DOWNLOAD     =       1;
+       public static final int URI_ACTION_SEARCH       =       2;
+       public static final int URI_ACTION_INSERT       =       3;
+       public static final int URI_ACTION_DELETE       =       4;
+
+       public static final int GOT_FILENAME            =       (1<<0);
+       public static final int GOT_NS                  =       (1<<1);
+       public static final int GOT_QH                  =       (1<<2);
+       public static final int GOT_KH                  =       (1<<3);
+       public static final int GOT_KEYWORD             =       (1<<4);
+       public static final int GOT_SIZE                        =       (1<<5);
+       public static final int GOT_CRC                 =       (1<<6);
+       public static final int GOT_PSEUDONYM   =       (1<<7);
+       public static final int GOT_PASSWORD            =       (1<<8);
+
+
+       public URI()
+       {
+               super(true);
+       }
+
+       public String toString()
+       {
+               return "xxx";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       /**
+        * Parses an AFS URI string to an internal representation
+        *
+        * Usage:
+        *   GeneralURI * block;
+        *   parseURI(string, &block);
+        *   if(block.action == URI_ACTION_DOWNLOAD) {
+        *     downloadURI * bl;
+        *     bl = (downloadURI *)block;
+        *   ...
+        *
+        * @param uri an uri string
+        * @param block output, the parsed values
+        * @return false on failure
+        */
+
+       public boolean parseURI( String uri, GeneralURI[] block )
+       {
+               String  scratch;
+               String  uptr;
+               String  sptr;
+               String  nptr;
+               int action;
+               TagTable        tags = new TagTable();
+               int tagcount = 0;
+               boolean ret = false;
+               int     index;
+
+               if (uri==null || !uri.startsWith(AFS_URI_PREFIX)) {
+                       return false;
+                       }
+
+               /* parse action */
+               uptr = uri.substring(AFS_URI_PREFIX.length());
+
+               index=uptr.indexOf('/');
+               scratch=(index>=0 ? uptr.substring(0,index) : "");
+               uptr=uptr.substring(index+1);
+
+               if (uptr.length()==0) {
+                       log(Level.SEVERE,"Premature end of URI");
+                       return false;
+                       }
+
+               if (scratch.equals("download")) {
+                       action = URI_ACTION_DOWNLOAD;
+                       }
+               else if (scratch.equals("search")) {
+                       action = URI_ACTION_SEARCH;
+                       }
+               else if (scratch.equals("insert")) {
+                       action = URI_ACTION_INSERT;
+                       }
+               else if (scratch.equals("delete")) {
+                       action = URI_ACTION_DELETE;
+                       }
+               else {
+                       log(Level.SEVERE,"Unknown action in "+scratch);
+                       return false;
+                       }
+
+               /* parse all tags to a tagTable */
+               while (uptr.length()>0) {
+                       index=uptr.indexOf('=');
+                       nptr = (index>=0 ? uptr.substring(0,index) : "");
+                       uptr=uptr.substring(index+1);
+
+                       if (uptr.length()==0) {
+                               log(Level.SEVERE,"Premature end of tag/name 
pair (1)");
+                               return false;
+                               }
+
+                       index=uptr.indexOf('?');
+                       sptr = (index>=0 ? uptr.substring(0,index) : "");
+                       uptr=uptr.substring(index+1);
+
+                       /* get the value */
+
+                       if (sptr.length()==0) {
+                               log(Level.SEVERE,"Missing value for tag "+nptr);
+                               return false;
+                               }
+
+                       tags.add(nptr,sptr);
+                       }
+
+               switch (action) {
+                       case URI_ACTION_DOWNLOAD:
+                               ret = parseDownloadURI(tags,tagcount,block);
+                               break;
+
+                       case URI_ACTION_SEARCH:
+                               ret = parseSearchURI(tags,tagcount,block);
+                               break;
+
+                       case URI_ACTION_INSERT:
+                               ret = parseInsertURI(tags,tagcount,block);
+                               break;
+
+                       case URI_ACTION_DELETE:
+                               ret = parseDeleteURI(tags,tagcount,block);
+                               break;
+
+                       default:
+                               break;
+                       }
+               return ret;
+       }
+
+       protected boolean parseDownloadURI( TagTable tags, int tagcount, 
GeneralURI[] block )
+       {
+               int                     i;
+               int                     gotmask=0;
+               String          tag,value;
+               DownloadURI     ret;
+
+               long            fileLength=0;
+               int             fileCRC=0;
+               ChkHashes       fileHashes=new ChkHashes();
+
+               ret = new DownloadURI();
+               ret.action = URI_ACTION_DOWNLOAD;
+
+               for(i=0; i<tagcount; i++) {
+                       tag=tags.getTag(i);
+                       value=tags.getValue(i);
+
+                       if (tag.equals("filename")) {
+                               ret.filename = value;
+                               gotmask |= GOT_FILENAME;
+                               }
+                       else if (tag.equals("kh")) {
+                               fileHashes.key=HashCode160.parse(value);
+                               gotmask |= GOT_KH;
+                               }
+                       else if (tag.equals("qh")) {
+                               fileHashes.query=HashCode160.parse(value);
+                               gotmask |= GOT_QH;
+                               }
+                       else if (tag.equals("size")) {
+                               fileLength=Long.parseLong(value);
+                               gotmask |= GOT_SIZE;
+                               }
+                       else if (tag.equals("crc")) {
+                               fileCRC=Integer.parseInt(value,16);
+                               gotmask |= GOT_CRC;
+                               }
+                       else {
+                               log(Level.WARNING,"Unknown tag "+tag+" in 
download context");
+                               }
+                       }
+
+               ret.fid=new FileIdentifier(fileLength,fileCRC,fileHashes);
+
+               if((gotmask & GOT_CRC)==0 || (gotmask & GOT_KH)==0 || (gotmask 
& GOT_QH)==0 || (gotmask & GOT_SIZE)==0) {
+                       log(Level.SEVERE,"Insufficient tags for download");
+                       return false;
+                       }
+
+               block[0]=ret;
+               return true;
+       }
+
+       protected boolean parseSearchURI( TagTable tags, int tagcount, 
GeneralURI[] block )
+       {
+               int i;
+               int gotmask=0;
+               String  tag,value;
+               SearchURI       ret;
+
+               ret = new SearchURI();
+               ret.action = URI_ACTION_SEARCH;
+
+               for(i=0;i<tagcount;i++) {
+                       tag = tags.getTag(i);
+                       value = tags.getValue(i);
+
+                       if (tag.equals("namespace")) {
+                               ret.namespace = HashCode160.parse(value);
+                               if (ret.namespace==null) {
+                                       log(Level.SEVERE,"Namespace is not in 
HEX format");
+                                       return false;
+                                       }
+                               gotmask |= GOT_NS;
+                               }
+                       /* namespace keyhash identifier */
+                       /* FIXME: either keywords or kh is redundant */
+                       else if (tag.equals("kh")) {
+                               ret.keyhash = HashCode160.parse(value);
+                               if (ret.keyhash==null) {
+                                       log(Level.FINEST,"Namespace ID is not 
in HEX format, using hash of ASCII text ("+value+").");
+                                       ret.keyhash=HashCode160.create(value);
+                                       }
+                               gotmask |= GOT_KH;
+                               }
+                       else if (tag.equals("keyword")) {
+                               String[]        old;
+
+                               old=ret.keywords;
+                               ret.keycount++;
+                               ret.keywords=new String[ret.keycount];
+                               
System.arraycopy(old,0,ret.keywords,0,old.length);
+
+                               ret.keywords[ret.keycount-1] = value;
+                               gotmask |= GOT_KEYWORD;
+                               }
+                       else {
+                               log(Level.WARNING,"Unknown tag name "+tag+" in 
search context");
+                               }
+                       }
+
+               if ((gotmask & GOT_KEYWORD)==0) {
+                       log(Level.SEVERE,"Insufficient tags for search");
+                       return false;
+                       }
+
+               block[0]=ret;
+               return true;
+       }
+
+       protected boolean parseInsertURI( TagTable tags, int tagcount, 
GeneralURI[] block )
+       {
+               int i;
+               int gotmask=0;
+               String  tag,value;
+               InsertURI       ret;
+
+               ret = new InsertURI();
+               ret.action = URI_ACTION_INSERT;
+
+               for(i=0;i<tags.getSize();i++) {
+                       tag = tags.getTag(i);
+                       value = tags.getValue(i);
+
+                       if(tag.equals("filename")) {
+                               ret.filename = value;
+                               gotmask |= GOT_FILENAME;
+                               }
+                       else if(tag.equals("pseudonym")) {
+                               ret.pseudonym = value;
+                               gotmask |= GOT_PSEUDONYM;
+                               }
+                       else if(tag.equals("password")) {
+                               ret.password = value;
+                               gotmask |= GOT_PASSWORD;
+                               }
+                       else {
+                               log(Level.WARNING,"Unknown tag name "+tag+" in 
search context");
+                               }
+               }
+
+               if ((gotmask & GOT_FILENAME)==0) {
+                       log(Level.SEVERE,"Insufficient tags for insert");
+                       return false;
+                       }
+
+               block[0]=ret;
+               return true;
+       }
+
+       protected boolean parseDeleteURI( TagTable tags, int tagcount, 
GeneralURI[] block )
+       {
+               int i;
+               int gotmask=0;
+               DeleteURI       ret;
+               String  tag,value;
+
+               ret = new DeleteURI();
+               ret.action = URI_ACTION_DELETE;
+
+               for(i=0; i<tags.getSize(); i++) {
+                       tag = tags.getTag(i);
+                       value = tags.getValue(i);
+
+                       if (tag.equals("filename")) {
+                               ret.filename=value;
+                               gotmask |= GOT_FILENAME;
+                               }
+                       else {
+                               log(Level.WARNING,"Unknown tag name "+tag+" in 
search context");
+                               }
+                       }
+
+               if ((gotmask & GOT_FILENAME)==0) {
+                       log(Level.SEVERE,"Insufficient tags for delete");
+                       return false;
+                       }
+               block[0]=ret;
+               return true;
+       }
+
+       /**
+        * Turns an internal representation into a AFS uri string
+        *
+        * @param block the values to print
+        * @param url   output
+        * @return false on failure
+        */
+
+       public boolean produceURI( GeneralURI block, String[] url )
+       {
+               int     i;
+
+               if (block==null) {
+                       log(Level.SEVERE,"null block passed to produceURI()");
+                       return false;
+                       }
+
+               url[0]=AFS_URI_PREFIX;
+
+               switch(block.action) {
+                       case URI_ACTION_DOWNLOAD:
+                               DownloadURI     du;
+
+                               du = (DownloadURI) block;
+                               url[0]+="download/";
+                               url[0]+="kh="+du.fid.getFileKey().toHex()+"?";
+                               url[0]+="qh="+du.fid.getFileQuery().toHex()+"?";
+                               url[0]+="size="+du.fid.getFileLength()+"?";
+                               
url[0]+="crc="+Utils.toHex(du.fid.getFileCRC())+"?";
+                               if (du.filename != null) {
+                                       url[0]+=du.filename+"?";
+                                       }
+                               break;
+
+                       case URI_ACTION_SEARCH:
+                               SearchURI       su;
+
+                               su=(SearchURI) block;
+                               url[0]+="search/";
+                               if (su.namespace!=null) {
+                                       url[0]+="ns="+su.namespace.toHex()+"?";
+                                       }
+                               if (su.keyhash!=null) {
+                                       url[0]+="kh="+su.keyhash.toHex()+"?";
+                                       }
+                               for (i=0; i<su.keycount; i++) {
+                                       url[0]+="keyword="+su.keywords[i]+"?";
+                                       }
+                               break;
+
+                       case URI_ACTION_INSERT:
+                               InsertURI       iu;
+
+                               iu=(InsertURI) block;
+                               url[0]+="insert/";
+                               if (iu.filename!=null) {
+                                       url[0]+=iu.filename+"?";
+                                       }
+                               break;
+
+                       case URI_ACTION_DELETE:
+                               DeleteURI       dd;
+
+                               dd=(DeleteURI) block;
+                               url[0]+="delete/";
+                               if (dd.filename!=null) {
+                                       url[0]+=dd.filename+"?";
+                                       }
+                               break;
+
+                       default:
+                               url[0]=null;
+                               log(Level.SEVERE,"Unknown action 
"+block.action);
+                               return false;
+                       }
+
+               if (url[0].endsWith("?")) {
+                       url[0]=url[0].substring(0,url[0].length()-1);
+                       }
+               return true;
+       }
+}
+
+/* internal struct for tag/value pairs */
+
+class TagTable extends Object
+{
+       private List    all=new ArrayList();
+
+       public void add( String t, String v )
+       {
+               all.add(t);
+               all.add(v);
+       }
+       public int getSize()
+       {
+               return all.size()/2;
+       }
+
+       public String getTag( int i )
+       {
+               return (String) all.get(i*2);
+       }
+
+       public String getValue( int i )
+       {
+               return (String) all.get(i*2+1);
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/swing/CreatePseudonymDialog.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/swing/CreatePseudonymDialog.java   
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/swing/CreatePseudonymDialog.java   
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,161 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.swing;
+
+import org.gnu.freeway.protocol.afs.esed2.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.ui.*;
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+
+/**
+ *
+ */
+
+public class CreatePseudonymDialog extends GDialog implements ActionListener
+{
+       private SController             controller;
+
+       private JTextField              pseudoField;
+       private JPasswordField  passwordField;
+       private JButton                 cancelButton;
+       private JButton                 createButton;
+
+
+       public CreatePseudonymDialog( SController ctr, GFrame f )
+       {
+               super(f,"pseudo-create");
+               setTitle("Create Pseudonym");
+               controller=ctr;
+               addWindowListener(new WindowAdapter() {
+                       public void windowClosing(WindowEvent we)
+                       {
+                               onClose();
+                       }
+                       });
+       }
+
+       public String toString()
+       {
+               return "Create pseudonym dialog";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       /**
+        * Setup a window to allow the user to create a pseudonym
+        * @return
+        */
+
+       public JComponent createContent()
+       {
+               JPanel          panel,buttonPane;
+               GForm   form;
+               JLabel          iconLabel;
+
+               pseudoField=new JTextField(10);
+               passwordField=new JPasswordField(10);
+
+               form=new GForm();
+               form.addWidget("Pseudonym :",pseudoField);
+               form.addWidget("Password :",passwordField);
+
+               cancelButton=new JButton("Cancel");
+               cancelButton.addActionListener(this);
+
+               createButton=new JButton("Create");
+               createButton.addActionListener(this);
+
+               buttonPane=new JPanel();
+               buttonPane.setLayout(new BoxLayout(buttonPane, 
BoxLayout.LINE_AXIS));
+               buttonPane.add(Box.createHorizontalGlue());
+               buttonPane.add(cancelButton);
+               buttonPane.add(Box.createRigidArea(new Dimension(3,0)));
+               buttonPane.add(createButton);
+
+               iconLabel=new JLabel(new 
ImageIcon(controller.getResources().getImageURL("app.minilogo")));
+               iconLabel.setVerticalAlignment(SwingConstants.TOP);
+
+               panel=new JPanel(new BorderLayout(3,1));
+               panel.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
+               panel.add(iconLabel,BorderLayout.WEST);
+               panel.add(form,BorderLayout.CENTER);
+               panel.add(buttonPane,BorderLayout.SOUTH);
+
+               getRootPane().setDefaultButton(createButton);
+
+               return panel;
+       }
+
+       public void updateContent()
+       {
+               pseudoField.setText("");
+               passwordField.setText("");
+       }
+
+       public void leaveContent()
+       {
+               pseudoField.setText("");
+               passwordField.setText("");
+       }
+
+       public void actionPerformed( ActionEvent evt )
+       {
+               if (evt.getSource()==createButton) {
+                       onCreate();
+                       }
+               else if (evt.getSource()==cancelButton) {
+                       onClose();
+                       }
+       }
+
+       protected void onClose()
+       {
+               close();
+       }
+
+       protected void onCreate()
+       {
+               Task    task;
+
+               final String _name = pseudoField.getText();
+               if (_name.length()==0) {
+                       controller.guiMessage("Cowardly refusing to create 
pseudonym without name.");
+                       return;
+                       }
+
+               final String _pass = new String(passwordField.getPassword());
+
+               pseudoField.setEnabled(false);
+               passwordField.setEnabled(false);
+               createButton.setEnabled(false);
+               cancelButton.setEnabled(false);
+
+               task=new Task("NEW-PSEUDO",new 
org.gnu.freeway.util.AbstractAction() {
+                       public void perform()
+                       {
+                               PrivateKey      priv;
+                               // we may want to do this in another thread to 
keep the event manager running (and potentially even
+                               // give feedback in the form of a popup 
window). After all, this can take a while...
+                               priv=new 
Pseudonym(controller.getPreferences()).createPseudonym(_name,_pass);
+                               if (priv==null) {
+                                       controller.guiMessage("Failed to create 
pseudonym (see logs).");
+                                       }
+                               controller.refreshMenus();
+
+                               pseudoField.setEnabled(true);
+                               passwordField.setEnabled(true);
+                               createButton.setEnabled(true);
+                               cancelButton.setEnabled(true);
+                               close();
+                       }
+                       });
+               task.launch();
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/swing/DaemonWindow.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/swing/DaemonWindow.java    
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/swing/DaemonWindow.java    
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,207 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.swing;
+
+import org.gnu.freeway.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.io.*;
+import org.gnu.freeway.util.ui.*;
+
+import java.awt.event.*;
+import javax.swing.*;
+
+/**
+ *
+ */
+
+public class DaemonWindow extends GFrame
+{
+       private SController             controller;
+       private UIResources             resources;
+       private DaemonLauncher  launcher;
+
+       private GStatus                 status;
+       private GConsole                text;
+       private GBar            bar;
+       private JTextField              argsField;
+
+
+       public DaemonWindow( UIResources res, SController ctr )
+       {
+               super(ctr,"daemon-window");
+               setTitle("GNUnet Swing : Daemon");
+               controller=ctr;
+               resources=new UIResources(res,"daemon-window.xml");
+               resources.setGlobalTarget(this);
+               launcher=new DaemonLauncher(ctr.getApplication());
+
+               addWindowListener(new WindowAdapter() {
+                       public void windowClosing( WindowEvent evt )
+                       {
+                               onClose();
+                       }
+                       });
+       }
+
+       public String toString()
+       {
+               return "Daemon window";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public JComponent createContent()
+       {
+               JPanel  panel;
+               Box             box;
+
+               status=new GStatus();
+
+               text=new GConsole();
+
+               argsField=new JTextField(10);
+               argsField.setText("-L INFO");
+
+               bar=new GBar();
+               bar.add("Arguments :",argsField);
+               bar.add(resources.getAction("start.action"));
+               bar.add(resources.getAction("stop.action"));
+               bar.addSeparator();
+               bar.add(resources.getAction("close.action"));
+
+               resources.getAction("close.action").setEnabled(true);
+
+               box=new Box(BoxLayout.X_AXIS);
+               box.add(status);
+               box.add(Box.createHorizontalGlue());
+               box.add(text.createControls());
+
+               panel=new JPanel(new TileLayout());
+               panel.add(box/*status*/,TileLayout.NORTH);
+               panel.add(bar,TileLayout.SOUTH);
+               panel.add(text);
+
+               setJMenuBar(resources.getMenuBar("main.menu"));
+               return panel;
+       }
+
+       public void updateContent()
+       {
+       }
+
+       /**
+        * Launch gnunetd w/ checks
+        */
+
+       public void onLaunchDaemon()
+       {
+               GProgress       dialog;
+               final boolean[] _beurk = new boolean[1];
+
+               if (launcher.checkDaemonRunning() ) {
+                       showMessage("Daemon is already running !");
+                       return;
+                       }
+
+               _beurk[0]=false;
+               dialog=new GProgress(this,"launch-progress") {
+                       public void perform()
+                       {
+                               String  argLine;
+
+                               argLine=(argsField!=null ? argsField.getText() 
: null);
+                               
_beurk[0]=launcher.launchWithExec(this,argLine,new ConnectorEndPoint() {
+                                       public void gotMessage( int type, 
String str )
+                                       {
+                                               text.println(str);
+                                       }
+                                       });
+                               Scheduler.sleep(Scheduler.MILLIS_200);
+                       }
+                       };
+               dialog.setTitle("Launch daemon");
+               dialog.launch("Please wait while daemon is being 
launched...\n");
+
+               if (!_beurk[0]) {
+                       return;
+                       }
+
+               resources.getAction("start.action").setEnabled(false);
+               resources.getAction("stop.action").setEnabled(false);
+               status.setText("Checking...");
+               argsField.setEnabled(false);
+       }
+
+       /**
+        * Kill gnunetd
+        */
+
+       public void onKillDaemon()
+       {
+               resources.getAction("start.action").setEnabled(false);
+               resources.getAction("stop.action").setEnabled(false);
+               status.setText("Waiting for daemon's response...");
+               argsField.setEnabled(false);
+
+               if (!launcher.checkDaemonRunning() ) {
+                       showMessage("Daemon is not running...");
+                       return;
+                       }
+
+               if (launcher.killDaemon()) {
+                       showMessage("Daemon agreed to shut down.");
+                       }
+               else {
+                       showMessage("Daemon refuses to shut down.");
+                       }
+       }
+
+       public void cronCheckDaemon()
+       {
+               UIAction        killEntry;
+               UIAction        launchEntry;
+               UIAction        statsEntry;
+               String          host;
+               boolean         isLocal;
+
+               killEntry=resources.getAction("advanced.kill.gnunetd");
+               launchEntry=resources.getAction("advanced.launch.gnunetd");
+               statsEntry=resources.getAction("file.show.stats");
+
+               host = 
controller.getPreferences().getString("NETWORK","HOST",null);
+               isLocal=(host==null || host.equalsIgnoreCase("localhost"));
+
+               if (!launcher.checkDaemonRunning()) {
+                       statsEntry.setEnabled(false);
+                       killEntry.setEnabled(false);
+                       launchEntry.setEnabled(isLocal);
+
+                       resources.getAction("start.action").setEnabled(true);
+                       resources.getAction("stop.action").setEnabled(false);
+                       if (status!=null) {
+                               status.setText("No daemon found.");
+                               argsField.setEnabled(true);
+                               }
+                       }
+               else {
+                       statsEntry.setEnabled(true);
+                       killEntry.setEnabled(true);
+                       launchEntry.setEnabled(false);
+
+                       resources.getAction("start.action").setEnabled(false);
+                       resources.getAction("stop.action").setEnabled(true);
+                       if (status!=null) {
+                               status.setText("Daemon is now up and running.");
+                               argsField.setEnabled(false);
+                               }
+                       }
+       }
+
+       public void onClose()
+       {
+               close();
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/swing/DeletePseudonymDialog.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/swing/DeletePseudonymDialog.java   
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/swing/DeletePseudonymDialog.java   
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,151 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.swing;
+
+import org.gnu.freeway.protocol.afs.esed2.*;
+import org.gnu.freeway.util.ui.*;
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import javax.swing.event.*;
+
+/**
+ *
+ */
+
+public class DeletePseudonymDialog extends GDialog implements ActionListener, 
ListSelectionListener
+{
+       private SController             controller;
+       private Pseudonym               pseudonym;
+
+       private JList                   pseudoList;
+       private JButton                 cancelButton;
+       private JButton                 deleteButton;
+
+
+       public DeletePseudonymDialog( SController ctr, GFrame f )
+       {
+               super(f,"pseudo-delete");
+               setTitle("Delete Pseudonym");
+               controller=ctr;
+               pseudonym=new Pseudonym(controller.getPreferences());
+               addWindowListener(new WindowAdapter() {
+                       public void windowClosing(WindowEvent we)
+                       {
+                               onClose();
+                       }
+                       });
+       }
+
+       public String toString()
+       {
+               return "Delete pseudonym dialog";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       /**
+        * Open a window to allow the user to delete a pseudonym
+        * @return
+        */
+
+       public JComponent createContent()
+       {
+               JScrollPane     scrollPane;
+               JPanel          panel,buttonPane,listPane;
+               JLabel          iconLabel,titleLabel;
+
+               pseudoList=new JList(pseudonym.listPseudonyms());
+               
pseudoList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+               pseudoList.setLayoutOrientation(JList.VERTICAL);
+               pseudoList.setVisibleRowCount(5);
+               pseudoList.addListSelectionListener(this);
+
+               scrollPane=new JScrollPane(pseudoList);
+               scrollPane.setAlignmentX(LEFT_ALIGNMENT);
+
+               titleLabel=new JLabel("Select a pseudonym to delete :");
+
+               listPane = new JPanel();
+               listPane.setLayout(new BoxLayout(listPane, 
BoxLayout.PAGE_AXIS));
+               listPane.add(titleLabel);
+               listPane.add(Box.createRigidArea(new Dimension(0,1)));
+               listPane.add(scrollPane);
+
+               cancelButton=new JButton("Cancel");
+               cancelButton.setEnabled(true);
+               cancelButton.addActionListener(this);
+
+               deleteButton=new JButton("Delete");
+               deleteButton.setEnabled(false);
+               deleteButton.addActionListener(this);
+
+               buttonPane=new JPanel();
+               buttonPane.setLayout(new BoxLayout(buttonPane, 
BoxLayout.LINE_AXIS));
+               buttonPane.add(Box.createHorizontalGlue());
+               buttonPane.add(cancelButton);
+               buttonPane.add(Box.createRigidArea(new Dimension(3,0)));
+               buttonPane.add(deleteButton);
+
+               iconLabel=new JLabel(new 
ImageIcon(controller.getResources().getImageURL("app.minilogo")));
+               iconLabel.setVerticalAlignment(SwingConstants.TOP);
+
+               panel=new JPanel(new BorderLayout(3,1));
+               panel.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
+               panel.add(iconLabel,BorderLayout.WEST);
+               panel.add(listPane,BorderLayout.CENTER);
+               panel.add(buttonPane,BorderLayout.SOUTH);
+
+               getRootPane().setDefaultButton(deleteButton);
+               return panel;
+       }
+
+       public void actionPerformed( ActionEvent evt )
+       {
+               if (evt.getSource()==deleteButton) {
+                       onDelete();
+                       }
+               else if (evt.getSource()==cancelButton) {
+                       onClose();
+                       }
+       }
+
+       public void valueChanged( ListSelectionEvent evt )
+       {
+               if (!evt.getValueIsAdjusting()) {
+                       
deleteButton.setEnabled(pseudoList.getSelectedIndex()>=0);
+                       }
+       }
+
+       protected void onClose()
+       {
+               close();
+       }
+
+       protected void onDelete()
+       {
+//             int     index;
+
+//             index=pseudoList.getSelectedIndex();
+/*
+               listModel.remove(index);
+
+               int size = listModel.getSize();
+
+               if (size == 0) { //Nobody's left, disable firing.
+                       fireButton.setEnabled(false);
+                       }
+               else { //Select an index.
+                       if (index == listModel.getSize()) {
+                               //removed item in last position
+                               index--;
+                               }
+                       list.setSelectedIndex(index);
+                       list.ensureIndexIsVisible(index);
+                       }*/
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/swing/DownloadAdapter.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/swing/DownloadAdapter.java 
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/swing/DownloadAdapter.java 
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,64 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.swing;
+
+import org.gnu.freeway.protocol.afs.esed2.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.ui.*;
+
+import java.io.*;
+
+/**
+ *
+ */
+
+public class DownloadAdapter extends AbstractAdapter implements AFSConstants
+{
+       public DownloadAdapter()
+       {
+               super(8);
+       }
+
+       protected void fillFacets( Object obj, Object[] facets )
+       {
+               DownloadModel   dlm;
+               double                  currentRetryAvg, averageBps, percentage;
+               long                    now;
+
+               dlm=(DownloadModel) obj;
+
+               currentRetryAvg=0;
+               averageBps=0;
+               percentage=0;
+
+               now=Scheduler.toSeconds(Scheduler.now());
+               if (dlm.pstats!=null) {
+                       if (dlm.pstats.requestsSent>0) {
+                               currentRetryAvg=(double) 
dlm.pstats.currentRetries/(double) dlm.pstats.requestsSent;
+                               }
+                       if (now-dlm.downloadStartTime>0) {
+                               averageBps=(double) 
dlm.pstats.progress/(double) (now-dlm.downloadStartTime);
+                               }
+                       if (dlm.pstats.filesize>0) {
+                               percentage=100.0*((double) 
dlm.pstats.progress/(double) dlm.pstats.filesize);
+                               }
+                       }
+
+               
facets[0]=dlm.fileName.substring(dlm.fileName.lastIndexOf(File.separator)+1);
+               facets[1]=(percentage!=0 ? (Object) new Double(percentage) : 
(Object) "0%");
+               facets[2]=(dlm.pstats!=null ? (Object) new 
Integer(dlm.pstats.progress) : (Object) "-");
+               
facets[3]=String.valueOf(dlm.root.getFileIdentifier().getFileLength());
+               facets[4]=(dlm.pstats!=null ? (Object) new 
Integer(dlm.pstats.requestsSent) : (Object) "-");
+               facets[5]=(dlm.pstats!=null ? (Object) new 
Double(currentRetryAvg) : (Object) "-");
+               facets[6]=(dlm.pstats!=null ? (Object) new 
Integer(dlm.pstats.totalRetries) : (Object) "-");
+               facets[7]=(dlm.pstats!=null ? (Object) new Double(averageBps) : 
(Object) "-");
+
+               if (dlm.pstats!=null && dlm.pstats.filesize == 
dlm.pstats.progress) {
+                       // reset the request counters (just cosmetic)
+                       facets[4]="0";
+                       facets[5]="0.0";
+                       }
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/swing/DownloadModel.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/swing/DownloadModel.java   
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/swing/DownloadModel.java   
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,209 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.swing;
+
+import org.gnu.freeway.*;
+import org.gnu.freeway.protocol.afs.esed2.*;
+import org.gnu.freeway.util.*;
+
+import EDU.oswego.cs.dl.util.concurrent.*;
+
+import java.util.logging.*;
+import javax.swing.*;
+
+/**
+* State associated with a download window.
+ */
+
+public class DownloadModel extends Object implements AFSConstants
+{
+       /* Suitable values of DownloadModel.downloadStatus */
+       public static final int DOWNLOAD_COMPLETE       =       0;
+       public static final int DOWNLOAD_FAILED         =       1;
+       public static final int DOWNLOAD_ABORTED        =       2;
+       public static final int DOWNLOAD_PENDING        =       3;
+
+       public Node                     root;
+       public String                   fileName;
+       public Task                             downloadThread;
+       public RequestManager   rm;
+       public Semaphore                doneSem;
+       public long                             downloadStartTime;
+       public boolean                  successfulStart;
+       public int                              downloadStatus;
+       public long                             lastDisplayTime;
+       public JTable                   dlList;
+
+       public ProgressStats    pstats;
+
+       private SController             controller;
+
+
+       public String[] cols;
+
+
+       public DownloadModel( SController ctr )
+       {
+               super();
+               root=null;
+               fileName="";
+               downloadThread=null;
+               controller=ctr;
+               successfulStart=false;
+               pstats=null;
+       }
+
+       /**
+        * Main function of the download thread. This function terminates
+        * automagically once the download is complete or if the download
+        * entry is removed while dl.  It is responsible for stopping the
+        * requestmanager.
+        * @throws InterruptedException
+        */
+
+       public void downloadFile_() throws InterruptedException
+       {
+               String  mime;
+
+               controller.log(Level.FINEST,"Entering downloadFile_ for 
"+fileName);
+
+               /* initiate download */
+               downloadStartTime=Scheduler.toSeconds(Scheduler.now());
+
+               /* this starts the "real" download thread in the background,
+                with "modelCallback" called back to tell us about the progress 
*/
+               rm = new DownloadUtil().downloadFile((AbstractClient) 
controller.getApplication(),
+                       controller.getPolicy(),
+                       null,   //todo: va planter
+                       root.getFileIdentifier(),fileName,new ProgressModel() {
+                       public void progress( ProgressStats stats, Object data )
+                       {
+                               modelCallback(stats);
+                       }
+                       },this);
+               if (rm == null) {
+                       controller.guiMessage("Could not download 
"+fileName+".\nConsult logs.\n");
+                       doneSem=null;
+                       return;
+                       }
+
+               /* Wait here until download is complete or the window is closed 
or gnunet-gtk is terminated */
+               controller.log(Level.FINEST,"Waiting for DL completion.");
+
+               doneSem.acquire();
+
+               controller.log(Level.FINEST,"Download complete 
("+downloadStatus+") enter destroyRequestManager.");
+
+               /* stop the RequestManager */
+               if (rm != null) {
+                       rm.destroy();
+                       }
+               else {
+                       /* this can happen if the requestmanager initialization
+                        * failed for reason or another (e.g. write permission 
denied) */
+                       controller.log(Level.WARNING,"rm was null !");
+                       }
+
+               /*
+                ok, now why are we here? 4 possibilities:
+                a) download aborted (user closed window)
+                b) gnunet-gtk terminated (same as download aborted)
+                c) download failed (gnunetd exit, out-of-space)
+                d) download completed
+
+                In case "d" we show the "YAY" window
+                and wait for another signal.
+                In case "c" we show the "BAH" window
+                and wait for another signal.
+                */
+
+               switch (downloadStatus) {
+                       case DOWNLOAD_COMPLETE:
+//                             gdk_threads_enter();
+                               /* color successful dl green */
+//                             gtk_clist_freeze(GTK_CLIST(dlList));
+//                             row = 
gtk_clist_find_row_from_data(GTK_CLIST(dlList),dlm);
+//                             gtk_clist_set_foreground(GTK_CLIST(dlList), 
row,textColors[3]);
+//                             
gtk_clist_set_text(GTK_CLIST(dlList),row,1,"DONE");
+//                             gtk_clist_thaw(GTK_CLIST(dlList));
+
+                               mime=root.getMimeType();
+                               if (mime.equals(GNUNET_DIRECTORY_MIME)) {
+                                       
controller.displayDirectory(fileName,root);
+                                       }
+
+//                             gdk_threads_leave();
+                               doneSem.acquire(); /* wait for window closing */
+                               break;
+
+                       case DOWNLOAD_FAILED:
+//                             gdk_threads_enter();
+                               /* color failed dl red */
+//                             gtk_clist_freeze(GTK_CLIST(dlList));
+//                             row = 
gtk_clist_find_row_from_data(GTK_CLIST(dlList),dlm);
+//                             
gtk_clist_set_foreground(GTK_CLIST(dlList),row,textColors[4]);
+//                             
gtk_clist_set_text(GTK_CLIST(dlList),row,1,"FAIL");
+//                             gtk_clist_thaw(GTK_CLIST(dlList));
+//                             gdk_threads_leave();
+                               doneSem.acquire(); /* wait for window closing */
+                               break;
+
+                       default:
+                               /* do nothing */
+                               break;
+                       }
+
+               /* finally, disentangle from the clist and free dlm resources */
+//             gdk_threads_enter();
+//             gtk_clist_freeze(GTK_CLIST(dlList));
+//             row = gtk_clist_find_row_from_data(GTK_CLIST(dlList),dlm);
+//             gtk_clist_set_row_data(GTK_CLIST(dlList),row,null);
+//             gtk_clist_thaw(GTK_CLIST(dlList));
+//             gdk_threads_leave();
+               doneSem=null;
+               fileName=null;
+       }
+
+       /**
+        * This method is called by the download code to notify the user
+        * interface of the download progress.
+        *
+        * @param stats the new statistical values
+        */
+
+       protected void modelCallback( ProgressStats stats )
+       {
+               long    now;
+
+               if (downloadStatus != DOWNLOAD_PENDING)
+                       return;
+
+               /* don't display more often than once/sec */
+               now=Scheduler.toSeconds(Scheduler.now());
+               if ((now-lastDisplayTime) < 1 && (stats.filesize != 
stats.progress))
+                       return;
+
+               lastDisplayTime = now;
+
+               pstats=new ProgressStats(stats);
+
+
+               if (!successfulStart && stats.progress > 0) {
+//                     
gtk_clist_set_foreground(GTK_CLIST(dlList),row,textColors[1]);
+                       successfulStart=true;
+                       }
+
+               if (stats.filesize == stats.progress) {
+                       /* reset the request counters (just cosmetic) */
+                       controller.refreshMenus();
+
+                       if (stats.filesize == 0)
+                               downloadStatus = DOWNLOAD_FAILED;
+                       else
+                               downloadStatus = DOWNLOAD_COMPLETE;
+                       doneSem.release(); /* signal: we're done with download 
*/
+                       }
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/swing/DownloadWindow.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/swing/DownloadWindow.java  
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/swing/DownloadWindow.java  
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,125 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.swing;
+
+import org.gnu.freeway.protocol.afs.esed2.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.net.*;
+import org.gnu.freeway.util.ui.*;
+
+import EDU.oswego.cs.dl.util.concurrent.*;
+
+import java.awt.event.*;
+import javax.swing.*;
+
+/**
+ *
+ */
+
+public class DownloadWindow extends GFrame implements AFSConstants, 
SwingConstants
+{
+       private SController                     controller;
+       private UIResources                     resources;
+
+       private DefaultListModel        dModel;
+       private ListSelectionModel      dSelection;
+       private GTable                          dTable;
+
+
+       public DownloadWindow( SController ctr )
+       {
+               super(ctr,"download-window");
+               setTitle("GNUnet Swing : Downloads");
+               addWindowListener(new WindowAdapter() {
+                       public void windowClosing(WindowEvent we)
+                       {
+                               onClose();
+                       }
+                       });
+
+               controller=ctr;
+
+               resources=new UIResources("download.xml",controller.getCache());
+               resources.setGlobalTarget(this);
+       }
+
+       public String toString()
+       {
+               return "Download window";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public JComponent createContent()
+       {
+               JPanel  panel;
+               GStatus status;
+               GBar    bar;
+
+               resources.getAction("abort.id").setEnabled(false);
+               resources.getAction("close.id").setEnabled(true);
+
+               dModel=new DefaultListModel();
+               dSelection=new DefaultListSelectionModel();
+
+               dTable=new GTable(new DownloadAdapter(),dModel,dSelection);
+               dTable.setColumns(new GColumn[] {
+                       new GColumn("Filename", 290, LEFT,   true),
+                       new GColumn("%",        50,  CENTER, true),     /* 
Completion percentage */
+                       new GColumn("Position", 70,  RIGHT,  true),     /* 
Bytes dl'ed so far */
+                       new GColumn("Size",     70,  RIGHT,  true),
+                       new GColumn("AReq",     40,  LEFT,   true),     /* 
Active block requests */
+                       new GColumn("CR/A",     30,  LEFT,   true),     /* 
Current Retries per Active requests */
+                       new GColumn("totR",     50,  LEFT,   true),     /* 
Total Retries */
+                       new GColumn("BPS",      50,  LEFT,   true),     /* 
Current bytes per second -estimate */
+                       });
+
+               status=new GStatus();
+
+               bar=new GBar();
+               bar.add(resources.getAction("abort.id"));
+               bar.add(resources.getAction("close.id"));
+
+               panel=new JPanel(new TileLayout());
+               panel.add(status,TileLayout.NORTH);
+               panel.add(bar,TileLayout.SOUTH);
+               panel.add(new JScrollPane(dTable));
+               return panel;
+       }
+
+       public void addDownload( String str, Node node )
+       {
+               DownloadModel   dlm;
+
+               dlm=new DownloadModel(controller);
+               dlm.fileName=str;
+               dlm.root=(Node) PersistentHelper.copy(node);
+               dlm.lastDisplayTime=Scheduler.toSeconds(Scheduler.now());
+               dlm.dlList = dTable;
+               dlm.downloadStatus=DownloadModel.DOWNLOAD_PENDING;
+               dlm.doneSem=new Semaphore(0);
+
+//             gtk_clist_freeze(GTK_CLIST(dTable));
+
+               dModel.addElement(dlm);
+
+//             gtk_clist_set_foreground(GTK_CLIST(dTable),row,textColors[11]);
+//             gtk_clist_thaw(GTK_CLIST(dTable));
+
+               /* create thread that runs the download */
+               dlm.downloadThread=new Task("DOWNLOAD-"+str,new 
EvalAction(dlm,"downloadFile_"));
+               dlm.downloadThread.launch();
+       }
+
+       public void onClose()
+       {
+               close();
+       }
+
+       public void onAbort()
+       {
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/swing/InsertDialog.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/swing/InsertDialog.java    
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/swing/InsertDialog.java    
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,515 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.swing;
+
+import org.gnu.freeway.protocol.afs.esed2.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.io.*;
+import org.gnu.freeway.util.net.*;
+import org.gnu.freeway.util.ui.*;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.io.*;
+import java.util.logging.*;
+import javax.swing.*;
+import javax.swing.event.*;
+import javax.swing.text.*;
+
+/**
+ *
+ */
+
+public class InsertDialog extends GWizard
+{
+       private SController     controller;
+       private UIResources     resources;
+
+       private DirLocation     chosenBase;
+       private FileLocation    chosenFile;
+       private String          chosenFilename;
+       private String          chosenMime;
+       private String          chosenDescription;
+       private boolean         choosedToIndex;
+       private String[]                chosenKeywords;
+
+
+       public InsertDialog( GFrame frame )
+       {
+               super(frame,"insert");
+               setTitle("GNUNet Swing : Insert");
+
+               controller=(SController) frame.getController();
+               resources=new 
UIResources("insert.xml",frame.getController().getApplication().getPreferences().getSystemCache());
+               resources.setGlobalTarget(this);
+
+               init();
+       }
+
+       public String toString()
+       {
+               return "Insert dialog";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public UIResources getResources()
+       {
+               return resources;
+       }
+
+       public GWizardPage[] getSteps()
+       {
+               return new GWizardPage[] {
+                       new ChooseFileStep(),
+                       new EditAttributesStep(),
+                       new ReviewStep()
+                       };
+       }
+
+       protected void init()
+       {
+               Prefs   prefs;
+
+               prefs=controller.getApplication().getPreferences();
+
+               chosenBase=new 
DirLocation(prefs.getGlobalString("insert","directory",""));
+               chosenFile=null;
+               chosenFilename="";
+               chosenMime="";
+               chosenDescription="";
+               choosedToIndex=true;
+               chosenKeywords=new String[] {};
+       }
+
+       public void close()
+       {
+               Prefs   prefs;
+
+               prefs=controller.getApplication().getPreferences();
+               prefs.setGlobalString("insert","directory",(chosenBase!=null ? 
chosenBase.getLabel() : ""));
+
+               super.close();
+       }
+
+       public void doTask()
+       {
+               GProgress       progress;
+
+               final boolean[] _beurk = new boolean[1];
+
+               _beurk[0]=false;
+
+               progress=new GProgress(this,"insert-progress") {
+                       public void perform()
+                       {
+                               doInsert(this,false);
+                               _beurk[0]=true;
+                       }
+                       };
+               progress.setTitle(chosenFile.getLabel());
+               progress.launch("Please wait while file is being 
inserted...\n");
+               if (_beurk[0]) {
+                       close();
+                       }
+       }
+
+       protected void doInsert( final GProgress _progress, boolean 
deleteAfterInsert )
+       {
+               Prefs   prefs;
+               RootNode                                res;
+               CSSession                       sock;
+               Block                                   top;
+               InsertUtil                              insertUtil;
+               FileIdentifier                  fid;
+               String                                  fstring;
+
+               prefs=controller.getPreferences();
+               prefs.setString("GNUNET-INSERT","INDEX-CONTENT",(choosedToIndex 
? "YES" : "NO"));
+
+               insertUtil=new InsertUtil(prefs);
+
+               controller.enterCritical();
+               try {
+                       sock=controller.connect();
+
+                       top=insertUtil.insertFile(sock,chosenFile.getPath(),new 
ProgressModel() {
+                               public void progress( ProgressStats stats, 
Object data )
+                               {
+                                       insertModelCallback(stats,_progress);
+                               }
+                               },null);
+                       if (top!=null) {
+                               //todo: erreur ? getName() ou getPath() ???
+                               
res=insertUtil.insertRoot(sock,top,chosenDescription,chosenFile.getName(),chosenMime,chosenKeywords);
+                               }
+                       else {
+                               res=null;
+                               }
+
+                       controller.refreshMenus();
+
+                       if (res!=null) {
+                               fid=new FileIdentifier(top);
+
+                               fstring = fid.toURI();
+
+                               controller.infoMessage(false,"Successfully 
processed "+chosenFile.getLabel()+"\n  => "+fstring+"\n");
+                               controller.log(Level.FINEST,"Successfully 
processed "+chosenFile.getLabel()+"\n  => "+fstring);
+                               }
+                       else {
+                               controller.guiMessage("Insertion of 
"+chosenFile.getLabel()+" FAILED !");
+                               }
+
+                       if (top!=null) {
+                               top.destroy(null);
+                               }
+                       sock.disconnect();
+                       }
+               finally {
+                       /* insert complete */
+                       controller.leaveCritical();
+                       }
+
+               if (deleteAfterInsert) {
+                       chosenFile.delete();
+                       }
+       }
+
+       public void onQuit()
+       {
+               if (JOptionPane.showConfirmDialog(
+                               this,
+                               "Do you really want to close insertion dialog 
?",
+                               "Closing...",
+                               JOptionPane.YES_NO_OPTION,
+                               JOptionPane.QUESTION_MESSAGE,
+                               new 
ImageIcon(resources.getImageURL("application.medium.icon"))
+                               )==JOptionPane.YES_OPTION) {
+
+                       close();
+                       }
+       }
+
+       protected void insertModelCallback( ProgressStats stats, GProgress 
progress )
+       {
+               progress.progress(
+                       stats.progress,
+                       stats.filesize,
+                       stats.progress+" bytes of "+stats.filesize+" 
"+(choosedToIndex ? "indexed" : "inserted")
+                       );
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public class ChooseFileStep extends GWizardPage
+       {
+               private GStack  stack;
+               private GText   text;
+               private GFile   file;
+
+
+               public ChooseFileStep()
+               {
+                       super("Choose file");
+               }
+
+               public JComponent createView()
+               {
+                       text=new GText();
+                       text.setText("Choose file to be inserted.");
+
+                       file=new GFile();
+
+                       stack=new GStack();
+                       stack.addWidget(text);
+                       stack.addWidget(file);
+                       return stack;
+               }
+
+               public void updateView()
+               {
+                       file.selectFile("File to insert",chosenBase);
+                       file.setSelectedLocation(chosenFile);
+               }
+
+               public String canLeaveView()
+               {
+                       FileLocation            f;
+
+                       f=(FileLocation) file.getSelectedLocation();
+                       if (f==null) {
+                               return "You must choose a file to insert !";
+                               }
+                       if (!f.exists()) {
+                               return "Please select a valid file !";
+                               }
+                       return null;
+               }
+
+               public void leaveView()
+               {
+                       chosenFile=(FileLocation) file.getSelectedLocation();
+                       chosenBase=file.getSelectedBase();
+               }
+       }
+
+       public class EditAttributesStep extends GWizardPage
+       {
+               private GStyledText                     text;
+               private JTextField                      filenameField;
+               private JTextField                      mimeField;
+               private JTextField                      descriptionField;
+               private JComboBox                       methodCombo;
+               private DefaultListModel        keywordModel;
+               private JList                           keywordList;
+               private JTextField                      keywordField;
+               private JButton                         addButton;
+               private JButton                         deleteButton;
+
+
+               public EditAttributesStep()
+               {
+                       super("Edit attributes");
+               }
+
+               public JComponent createView()
+               {
+                       GStack  stack;
+                       GForm   form;
+                       GBar    bar;
+
+                       filenameField=new JTextField(10);
+                       mimeField=new JTextField(10);
+                       descriptionField=new JTextField(10);
+                       methodCombo=new JComboBox(new String[] { "Index only", 
"Full insertion" });
+
+                       keywordModel=new DefaultListModel();
+
+                       keywordList=new JList(keywordModel);
+                       
keywordList.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
+                       keywordList.setLayoutOrientation(JList.VERTICAL);
+                       keywordList.setVisibleRowCount(5);
+                       keywordList.addListSelectionListener(new 
ListSelectionListener() {
+                               public void valueChanged( ListSelectionEvent 
evt )
+                               {
+                                       
deleteButton.setEnabled(!keywordList.isSelectionEmpty());
+                               }
+                               });
+
+                       keywordField=new JTextField(10);
+                       keywordField.addActionListener(new ActionListener() {
+                               public void actionPerformed( ActionEvent evt )
+                               {
+                                       addKeyword();
+                               }
+                               });
+
+                       addButton=new JButton("Add keyword");
+                       addButton.addActionListener(new ActionListener() {
+                               public void actionPerformed( ActionEvent evt )
+                               {
+                                       addKeyword();
+                               }
+                               });
+
+                       deleteButton=new JButton("Delete keyword");
+                       deleteButton.addActionListener(new ActionListener() {
+                               public void actionPerformed( ActionEvent evt )
+                               {
+                                       deleteKeyword();
+                               }
+                               });
+                       deleteButton.setEnabled(false);
+
+                       bar=new GBar();
+                       bar.add(addButton);
+                       bar.add(deleteButton);
+
+                       text=new GStyledText();
+                       
StyleConstants.setFontFamily(text.style("fi"),"SansSerif");
+                       StyleConstants.setBold(text.style("fi"),true);
+                       StyleConstants.setItalic(text.style("fi"),true);
+                       StyleConstants.setForeground(text.style("fi"),new 
Color(33,87,136));
+
+                       form=new GForm();
+                       form.addWidget("Published filename :",filenameField);
+                       form.addWidget("MIME type :",mimeField);
+                       form.addWidget("Description :",descriptionField);
+                       form.addWidget("Insertion method :",methodCombo);
+                       form.addWidget("Keyword(s) used :",new 
JScrollPane(keywordList),0);
+                       form.addWidget("",keywordField);
+                       form.addWidget("",bar);
+
+                       stack=new GStack();
+                       stack.addWidget(text);
+                       stack.addWidget(form);
+                       return stack;
+               }
+
+               public void updateView()
+               {
+                       String[]                                keywords;
+                       Prefs   prefs;
+                       StringBuffer                    description,mimetype;
+                       int                                             i;
+
+                       text.clear();
+                       text.print("Now, edit attributes file 
<fi>"+chosenFile.getLabel()+"</fi> will be inserted with.");
+
+                       // try to extract keywords
+                       description=new StringBuffer();
+                       mimetype=new StringBuffer();
+                       keywords=new KeyWords().extractKeywords(new 
File(chosenFile.getPath()),description,mimetype);
+                       if (description.length()==0) {
+                               description.append("No description supplied");
+                               }
+                       if (mimetype.length()==0) {
+                               mimetype.append("unknown");
+                               }
+
+                       filenameField.setText(chosenFile.getName());
+                       mimeField.setText(mimetype.toString());
+                       descriptionField.setText(description.toString());
+
+                       prefs=controller.getPreferences();
+                       if 
(prefs.testString("GNUNET-INSERT","INDEX-CONTENT","YES")) {
+                               methodCombo.setSelectedIndex(0);
+                               }
+                       else {
+                               methodCombo.setSelectedIndex(1);
+                               }
+
+                       keywordModel.clear();
+                       for (i=0; i<keywords.length; i++) {
+                               keywordModel.addElement(keywords[i]);
+                               }
+                       keywordField.setText("");
+               }
+
+               /**
+                * The keyword add button was clicked. Add whatever is in the 
keyword box to the list of keywords.
+                */
+
+               protected void addKeyword()
+               {
+                       String  key;
+
+                       key=keywordField.getText();
+                       if (key==null || key.trim().length()==0) {
+                               return;
+                               }
+                       key=key.trim();
+
+                       keywordModel.addElement(key);
+
+                       keywordField.setText("");
+               }
+
+               /**
+                * The keyword delete button was clicked. Delete the currently 
selected keyword.
+                */
+
+               protected void deleteKeyword()
+               {
+                       int[]   indices;
+                       int             i;
+
+                       indices=keywordList.getSelectedIndices();
+                       for (i=indices.length-1; i>=0; i--) {
+                               keywordModel.remove(indices[i]);
+                               }
+               }
+
+               public void leaveView()
+               {
+                       int     i;
+
+                       chosenFilename=filenameField.getText().trim();
+                       if (chosenFilename.length()==0) {
+                               chosenFilename="none specified";
+                               }
+
+                       chosenMime=mimeField.getText().trim();
+                       if (chosenMime.length()==0) {
+                               chosenMime="unknown";
+                               }
+
+                       chosenDescription=descriptionField.getText().trim();
+                       if (chosenDescription.length()==0) {
+                               chosenDescription="no description specified";
+                               }
+
+                       choosedToIndex=methodCombo.getSelectedIndex()==0;
+
+                       chosenKeywords=new String[keywordModel.getSize()];
+                       for (i=0; i<chosenKeywords.length; i++) {
+                               chosenKeywords[i]=(String) keywordModel.get(i);
+                               }
+               }
+       }
+
+       public class ReviewStep extends GWizardPage
+       {
+               private GStyledText     text;
+               private Icon                    dot;
+
+
+               public ReviewStep()
+               {
+                       super("Review");
+               }
+
+               public JComponent createView()
+               {
+                       dot=new ImageIcon(resources.getImageURL("dot.icon"));
+
+                       text=new GStyledText();
+                       StyleConstants.setLeftIndent(text.style("ind"),10);
+                       StyleConstants.setSpaceAbove(text.style("ind"),3);
+                       StyleConstants.setSpaceBelow(text.style("ind"),1);
+
+                       StyleConstants.setForeground(text.style("v"),new 
Color(33,87,136));
+                       
StyleConstants.setFontFamily(text.style("v"),"Monospaced");
+
+                       
StyleConstants.setFontFamily(text.style("fi"),"SansSerif");
+                       StyleConstants.setBold(text.style("fi"),true);
+                       StyleConstants.setItalic(text.style("fi"),true);
+                       StyleConstants.setForeground(text.style("fi"),new 
Color(33,87,136));
+                       return text;
+               }
+
+               public void updateView()
+               {
+                       StringBuffer    buf;
+                       int                             i;
+
+                       buf=new StringBuffer();
+                       if (chosenKeywords.length>0) {
+                               for (i=0; i<chosenKeywords.length; i++) {
+                                       buf.append(chosenKeywords[i]);
+                                       if (i<chosenKeywords.length-1) {
+                                               buf.append(", ");
+                                               }
+                                       }
+                               }
+                       else {
+                               buf.append("(none)");
+                               }
+
+                       text.clear();
+                       text.println("You'd like to insert file 
<fi>"+chosenFile.getLabel()+"</fi>.");
+                       text.print("Please check information you've entered are 
correct. ");
+                       text.println("Then click on Go button to perform 
insertion.");
+                       text.print("<ind>").print(dot).print(" Filename : 
<v>"+chosenFilename).println("</v></ind>");
+                       text.print("<ind>").print(dot).print(" Mime type : 
<v>"+chosenMime).println("</v></ind>");
+                       text.print("<ind>").print(dot).print(" Description : 
<v>"+chosenDescription).println("</v></ind>");
+                       text.print("<ind>").print(dot).print(" Insertion method 
: <v>"+(choosedToIndex ? "Index only" : "Full 
insertion")).println("</v></ind>");
+                       text.print("<ind>").print(dot).print(" Keywords : 
<v>"+buf).println("</v></ind>");
+               }
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/swing/SController.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/swing/SController.java     
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/swing/SController.java     
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,53 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.swing;
+
+import org.gnu.freeway.protocol.afs.esed2.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.net.*;
+import org.gnu.freeway.util.ui.*;
+
+import java.beans.*;
+import java.io.*;
+import java.util.logging.*;
+
+/**
+ *
+ */
+
+public interface SController extends Controller
+{
+       public static final String      QUIT_EVENT      =       "*quit*";
+
+       public Service service( Class c );
+       public void log( Level level, String msg );
+       public void err( String msg, Throwable x );
+
+       public void refreshMenus();
+
+       public void download( Node node );
+
+       public Policy getPolicy();
+
+       public void guiMessage( String str );
+       public void infoMessage( boolean doPopup, String str );
+
+       public void displayDirectory( String fileName, Node root );
+       public void showStats();
+       public CSSession connect();
+
+       public void addPropertyChangeListener( PropertyChangeListener listener 
);
+       public void addPropertyChangeListener( String name, 
PropertyChangeListener listener );
+       public void removePropertyChangeListener( PropertyChangeListener 
listener );
+       public void removePropertyChangeListener( String name, 
PropertyChangeListener listener );
+
+       public UIResources getResources();
+
+       public void enterCritical();
+       public void leaveCritical();
+
+       public File getCache();
+       public Prefs getPreferences();
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/swing/SearchAdapter.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/swing/SearchAdapter.java   
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/swing/SearchAdapter.java   
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,44 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.swing;
+
+import org.gnu.freeway.protocol.afs.esed2.*;
+import org.gnu.freeway.util.ui.*;
+
+import java.io.*;
+
+/**
+ *
+ */
+
+public class SearchAdapter extends AbstractAdapter implements AFSConstants
+{
+       public SearchAdapter()
+       {
+               super(7);
+       }
+
+       protected void fillFacets( Object obj, Object[] facets )
+       {
+               Node    node;
+               String  str;
+
+               node=(Node) obj;
+
+               facets[0]=node.getDescription();
+               facets[1]=new Long(node.getFileIdentifier().getFileLength());
+
+               str=node.getFileName();
+               if (node.getMimeType().equals(GNUNET_DIRECTORY_MIME) && 
!str.endsWith(File.separator) ) {
+                       str+="/";
+                       }
+
+               facets[2]=str;
+               facets[3]=new Long(node.getFileIdentifier().getFileCRC());
+               facets[4]=node.getFileIdentifier().getFileQuery().toHex();
+               facets[5]=node.getFileIdentifier().getFileKey().toHex();
+               facets[6]=node.getMimeType();
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/swing/SearchOverviewPanel.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/swing/SearchOverviewPanel.java     
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/swing/SearchOverviewPanel.java     
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,122 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.swing;
+
+import org.gnu.freeway.util.ui.*;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.beans.*;
+import javax.swing.*;
+
+/**
+ * Box displaying search results for the swing client.
+ */
+
+public class SearchOverviewPanel extends JPanel implements ActionListener, 
PropertyChangeListener
+{
+       private SController     controller;
+
+       private JTabbedPane     resultTab;
+       private JTextField      inputField;
+       private JButton         searchButton;
+
+
+       public SearchOverviewPanel( SController ctr )
+       {
+               super(new BorderLayout());
+               controller=ctr;
+
+               setup();
+       }
+
+       public String toString()
+       {
+               return "Search overview panel";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       protected void setup()
+       {
+               GBar    bar;
+
+               resultTab=new JTabbedPane();
+               resultTab.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
+
+               inputField=new JTextField(15);
+               inputField.addActionListener(this);
+
+               searchButton=new JButton("Search");
+               searchButton.addActionListener(this);
+
+               bar=new GBar();
+               bar.add("Keyword(s) :",inputField);
+               bar.add(searchButton);
+
+               add(bar,BorderLayout.SOUTH);
+               add(resultTab,BorderLayout.CENTER);
+       }
+
+       /**
+        * This method is called whenever the user clicks the search button of 
the main window.
+        * @param evt
+        */
+
+       public void actionPerformed( ActionEvent evt )
+       {
+               SearchPanel     panel;
+               String          str;
+               int                     index;
+
+               str=inputField.getText();
+               if (str.length()==0) {
+                       JOptionPane.showMessageDialog(this,"searchString == 
null");
+                       return;
+                       }
+
+               // remove heading spaces (parsing bugs if not)
+               str=str.trim();
+               if (str.length()==0) {
+                       JOptionPane.showMessageDialog(this,"No search key 
given!");
+                       return;
+                       }
+
+               // add a new page in the notebook with the search results. 
getSearchWindow returns the page
+               panel=new SearchPanel(controller,str);
+               if (panel.launch()) {
+                       index=resultTab.getTabCount();
+
+                       
panel.addPropertyChangeListener(SearchPanel.RESULTS_COUNT,this);
+                       
panel.addPropertyChangeListener(SearchPanel.CLOSE_ME,this);
+                       resultTab.addTab(panel.getKeyword(),panel);
+                       resultTab.setSelectedIndex(index);
+                       }
+
+               // reset search line to empty
+               inputField.setText("");
+       }
+
+       public void propertyChange( PropertyChangeEvent evt )
+       {
+               if (evt.getPropertyName().equals(SearchPanel.RESULTS_COUNT)) {
+                       updateTitleFor((SearchPanel) evt.getSource());
+                       }
+               else if (evt.getPropertyName().equals(SearchPanel.CLOSE_ME)) {
+                       close((SearchPanel) evt.getSource());
+                       }
+       }
+
+       protected void updateTitleFor( SearchPanel p )
+       {
+               
resultTab.setTitleAt(resultTab.indexOfComponent(p),p.getKeyword()+" 
("+p.getResultCount()+")");
+       }
+
+       protected void close( SearchPanel p )
+       {
+               resultTab.remove(p);
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/swing/SearchPanel.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/swing/SearchPanel.java     
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/swing/SearchPanel.java     
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,472 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.swing;
+
+import org.gnu.freeway.*;
+import org.gnu.freeway.protocol.afs.esed2.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+import org.gnu.freeway.util.ui.*;
+
+import EDU.oswego.cs.dl.util.concurrent.*;
+
+import java.awt.event.*;
+import java.util.*;
+import java.util.logging.*;
+import javax.swing.*;
+import javax.swing.event.*;
+
+/**
+ * box displaying search results for the gtk+ client.
+ */
+
+public class SearchPanel extends JPanel implements AFSConstants, SwingConstants
+{
+       public static final String      RESULTS_COUNT   =       
"#results#count#";
+       public static final String      CLOSE_ME                =       
"#close#me#";
+
+       public static final int LM_TYPE_SEARCH          =       1;
+       public static final int LM_TYPE_DIRECTORY       =       2;
+       public static final int LM_TYPE_NSSEARCH        =       3;
+
+       private SController                                     controller;
+       private String                                          keyword;
+       private ListModel2                                      model;
+       private ScheduledTask           sendJob;
+
+       private GTable                          searchTable;
+       private DefaultListModel        searchModel;
+       private ListSelectionModel      searchSelection;
+
+       private UIResources                     resources;
+       private SelectDialog            selectDialog;
+
+
+       public SearchPanel( SController ctr, String str )
+       {
+               super(new TileLayout());
+               keyword=str;
+               controller=ctr;
+               resources=new 
UIResources("search-panel.xml",controller.getCache());
+               resources.setGlobalTarget(this);
+               setup();
+       }
+
+       public String toString()
+       {
+               return "Search panel";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       protected void setup()
+       {
+               JPopupMenu      popup;
+               GBar            bar;
+
+               resources.getAction("download.id").setEnabled(false);
+
+               searchModel=new DefaultListModel();
+
+               searchSelection=new DefaultListSelectionModel();
+               searchSelection.addListSelectionListener(new 
ListSelectionListener() {
+                       public void valueChanged( ListSelectionEvent evt )
+                       {
+                               
resources.getAction("download.id").setEnabled(!searchSelection.isSelectionEmpty());
+                       }
+                       });
+
+               bar=new GBar();
+               bar.add(resources.getAction("download.id"));
+               bar.add(resources.getAction("popup.searchClose"));
+               add(bar,TileLayout.SOUTH);
+
+               searchTable=new GTable(new 
SearchAdapter(),searchModel,searchSelection);
+               searchTable.setColumns(new GColumn[] {
+                       new GColumn("Description", 470, LEFT,   true),
+                       new GColumn("Size",        70,  RIGHT,  true),
+                       new GColumn("Filename",    200, LEFT,   true),
+                       new GColumn("CRC",         40,  LEFT,   false),
+                       new GColumn("HASH1",       40,  LEFT,   false),
+                       new GColumn("HASH2",       40,  LEFT,   false),
+                       new GColumn("Mimetype",    200, CENTER, true)
+                       });
+               add(new JScrollPane(searchTable));
+
+               // when we are destroyed (e.g. search aborted), stop the search 
thread
+               addHierarchyListener(new HierarchyListener() {
+                       public void hierarchyChanged( HierarchyEvent e )
+                       {
+                               if (getParent()==null && (e.getChangeFlags() & 
HierarchyEvent.PARENT_CHANGED)!=0) {
+                                       stopSearch();
+                                       }
+                       }
+                       });
+
+               // add a right button popup menu
+               resources.getAction("popup.selectall").setEnabled(true);
+               resources.getAction("popup.unselectall").setEnabled(true);
+               
resources.getAction("popup.searchSelectByName").setEnabled(true);
+               
resources.getAction("popup.searchSelectByDesc").setEnabled(true);
+               
resources.getAction("popup.searchSelectByMime").setEnabled(true);
+               
resources.getAction("popup.searchDownloadSelected").setEnabled(true);
+               resources.getAction("popup.searchClose").setEnabled(true);
+
+               popup=resources.getPopupMenu("popup.menu");
+
+               UIHelper.addPopupToComponent(popup,this);
+               UIHelper.addPopupToComponent(popup,bar);
+               UIHelper.addPopupToComponent(popup,searchTable);
+       }
+
+       public String getKeyword()
+       {
+               return keyword;
+       }
+
+       public int getResultCount()
+       {
+               return searchTable.getRowCount();
+       }
+
+       protected void addRow( Node node )
+       {
+               int     size;
+
+               size=searchTable.getRowCount();
+
+               searchModel.addElement(node);
+
+               firePropertyChange(RESULTS_COUNT,size,size+1);
+       }
+
+       /**
+        * Returns a box containing the search results list.
+        * @return
+        */
+
+       public boolean launch()
+       {
+               boolean ok;
+
+               model=new ListModel2();
+               model.type = LM_TYPE_SEARCH;
+               model.doTerminate = false;
+               model.skipMenuRefresh = false;
+               model.sem = new Semaphore(0);
+               model.searchTable= searchTable;
+               model.SEARCH_socket_= ((AbstractClient) 
controller.getApplication()).connect();
+               if (model.SEARCH_socket_ == null) {
+                       return false;
+                       }
+
+               /* start searching */
+               ok=startSearch();
+               if (!ok) {
+                       model.SEARCH_socket_.disconnect();
+                       return false;
+                       }
+               return ok;
+       }
+
+       /**
+        * The main method of the search-thread.
+        *
+        * @return OK on success, false on error
+        */
+
+       protected boolean startSearch()
+       {
+               HashCode160[]           keys;
+               final SendQueriesContext        sqc;
+               Task                            receiveThread;
+               String[]                        keywords;
+               ArrayList                       list;
+               String[]                        p;
+               int                                     i;
+               final Scheduler 
scheduler=controller.getApplication().getScheduler();
+
+               list=new ArrayList();
+
+               p=keyword.split("\\s+");
+               for (i=0; i<p.length; i++) {
+                       if (p[i].length()>0) {
+                               list.add(p[i]);
+                               }
+                       }
+
+               keywords=(String[]) list.toArray(new String[list.size()]);
+
+               keys=AFSUtils.parseKeywords(keywords);
+               if (keys.length==0) {
+                       return false;
+                       }
+
+               sqc=new SendQueriesContext(keys,model.SEARCH_socket_);
+               sqc.setTimeOut(0x00FFFFFF);     // "forever" is 6 months...
+
+               model.sqc = sqc;
+
+               sendJob=new ScheduledTask("SEND-JOB",new 
org.gnu.freeway.util.AbstractAction() {
+                       public void perform()
+                       {
+                               
sqc.repeatedlySend(scheduler,controller.getPolicy());
+                       }
+                       });
+               scheduler.addJob(sendJob,0);
+
+               receiveThread=new Task("RECEIVE-RESULTS",new 
org.gnu.freeway.util.AbstractAction() {
+                       public void perform()
+                       {
+                               sqc.receive(
+                                       controller.getPreferences(),
+                                       new SearchResultCallback() {
+                                               public void searchResult( 
RootNode root )
+                                               {
+                                                       displayResultGTK(root);
+                                               }
+
+                                               public void newNameFor( 
RootNode root, String str )
+                                               {
+                                                       //fixme: 'd better 
replace row in data model !
+                                                       searchResult(root);
+                                               }
+                                               },
+                                       new TestTerminateThread() {
+                                               public boolean test()
+                                               {
+                                                       return 
model.doTerminate;
+                                               }
+                                               }
+                                       );
+
+                               model.sem.release();    // signal: thread 
terminated
+                       }
+                       });
+               receiveThread.launch();
+               return true;
+       }
+
+       /**
+        * Stop the search thread and free the model.  This method MUST always
+        * be called when the search ends, either because the search was
+        * aborted or because gnunet-gtk exists as a whole.
+        */
+
+       protected void stopSearch()
+       {
+               Scheduler       scheduler;
+
+               //todo: verifier que c'est appelé
+
+               controller.log(Level.FINEST,"stopSearch called");
+               /* this must be done as a cron-job, since otherwise
+                it may deadlock (this is called from the
+                gtk event thread, and cron may be waiting for
+                the gtk event lock, so we can't delete a cron
+                job in this thread */
+               model.doTerminate = true;
+
+               scheduler=controller.getApplication().getScheduler();
+               scheduler.addJob(new ScheduledTask("STOP-SEARCH",new 
EvalAction(this,"stopSearch_")),0);
+       }
+
+       /**
+        * Cron job that stops the search.
+        * @throws InterruptedException
+        */
+
+       public void stopSearch_() throws InterruptedException
+       {
+               switch (model.type) {
+                       case LM_TYPE_DIRECTORY:
+                               break;
+
+                       case LM_TYPE_SEARCH:
+                               /* the terminated search thread ups this 
semaphore
+                                once it is done and we can free data 
structures */
+                               model.doTerminate = true;
+
+                               /* this signals the download thread to 
terminate */
+                               model.SEARCH_socket_.disconnect();
+
+                               /* stop the cron-job that does the requests */
+                               
controller.getApplication().getScheduler().deleteJob(sendJob);
+
+                               /* wait for download thread signal */
+                               model.sem.acquire();
+
+                               /* Now we can finally free the shared data 
structures.
+                                Note that the terminated search thread freed
+                                some of the memory that was allocated in
+                                x (see receive_Results_)
+                                we free the rest. */
+                               model.sem=null;
+                               break;
+
+                       case LM_TYPE_NSSEARCH:
+                               model.doTerminate = true;
+                               /* this signals the download thread to 
terminate */
+                               model.SEARCH_socket_.disconnect();
+                               /* wait for download thread signal */
+                               model.sem.acquire();
+
+                               /* Now we can finally free the shared data 
structures.
+                                Note that the terminated search thread freed
+                                some of the memory that was allocated in
+                                x (see receive_Results_)
+                                we free the rest. */
+                               model.sem = null;
+                               break;
+
+                       default:
+                               controller.log(Level.SEVERE,"Unknown model.type 
: "+model.type);
+                               break;
+                       }
+       }
+
+       /**
+        * Display results.  This is a callback from receive_Results that is
+        * called on every new result.
+        *
+        * @param node  Data about a file
+        */
+
+       public void displayResultGTK( Node node )
+       {
+               final   Node    _copy = (Node) PersistentHelper.copy(node);
+
+               if (model.doTerminate)
+                       return;
+
+               SwingUtilities.invokeLater(new Runnable() {
+                       public void run()
+                       {
+                               addRow(_copy);
+
+                               if (!model.skipMenuRefresh) {
+                                       controller.refreshMenus();
+                                       }
+                       }
+                       });
+       }
+
+       /**
+        * Selects all search results from the current search page.
+        */
+
+       public void onSelectAll()
+       {
+               searchTable.selectAll();
+       }
+
+       /**
+        * Unselects all search results from the current search page.
+        */
+
+       public void onClearSelection()
+       {
+               searchSelection.clearSelection();
+       }
+
+       public void onSelectByName()
+       {
+               if (selectDialog==null) {
+                       selectDialog=new SelectDialog(controller,(GFrame) 
SwingUtilities.windowForComponent(this));
+                       }
+               selectDialog.setTitle("Select By Filename");
+               selectDialog.display();
+               if (selectDialog.getTypedText()!=null) {
+                       if 
(UIHelper.selectMatching(searchTable,2,selectDialog.getTypedText())==0) {
+                               controller.guiMessage("No matches...");
+                               }
+                       }
+       }
+
+       public void onSelectByDescription()
+       {
+               if (selectDialog==null) {
+                       selectDialog=new SelectDialog(controller,(GFrame) 
SwingUtilities.windowForComponent(this));
+                       }
+               selectDialog.setTitle("Select By Description");
+               selectDialog.display();
+               if (selectDialog.getTypedText()!=null) {
+                       if 
(UIHelper.selectMatching(searchTable,0,selectDialog.getTypedText())==0) {
+                               controller.guiMessage("No matches...");
+                               }
+                       }
+       }
+
+       public void onSelectByMime()
+       {
+               if (selectDialog==null) {
+                       selectDialog=new SelectDialog(controller,(GFrame) 
SwingUtilities.windowForComponent(this));
+                       }
+               selectDialog.setTitle("Select By Mimetype");
+               selectDialog.display();
+               if (selectDialog.getTypedText()!=null) {
+                       if 
(UIHelper.selectMatching(searchTable,6,selectDialog.getTypedText())==0) {
+                               controller.guiMessage("No matches...");
+                               }
+                       }
+       }
+
+       /**
+        * This method is called whenever the user clicks the download button. 
It opens the "save-as" dialog.
+        */
+
+       public void onDownload()
+       {
+               Node    node;
+               int             i;
+
+               // download all selected entries
+               for (i=searchSelection.getMaxSelectionIndex(); 
i>=searchSelection.getMinSelectionIndex(); i--) {
+                       if (searchSelection.isSelectedIndex(i)) {
+                               node=(Node) searchModel.get(i);
+                               controller.download(node);
+
+                               // Remove entry from search results.
+                               // Yes, if the user cancel's the download, the 
entry does not re-appear.
+                               // That's intended, after all, if you cancel, 
it's probably because it took too long to download anyway...
+                               // If you really need it back, just search 
again!
+
+                               // already on swing thread
+                               searchModel.remove(i);
+                               
firePropertyChange(RESULTS_COUNT,searchModel.getSize()+1,searchModel.getSize());
+                               }
+                       }
+       }
+
+       /**
+        * Remove the active page from the search results notebook.
+        * The respective search will be stopped as well
+        * (by a callback assigned to the page earlier on).
+        */
+
+       public void onAbortSearch()
+       {
+               firePropertyChange(CLOSE_ME,null,new Object());
+       }
+}
+
+/**
+ * Data for a search process
+ */
+
+class ListModel2 extends Object
+{
+       public int                      type;
+       public JTable           searchTable;
+       public boolean          doTerminate;
+       public Semaphore        sem;
+       public CSSession        SEARCH_socket_;
+       /** contents determined by type! */
+       public Object           sqc;
+       /** don't refresh gtk menus (its slow)? (YES/NO) */
+       public boolean          skipMenuRefresh;
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/swing/SearchWindow.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/swing/SearchWindow.java    
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/swing/SearchWindow.java    
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,280 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.swing;
+
+import org.gnu.freeway.*;
+import org.gnu.freeway.protocol.afs.esed2.*;
+import org.gnu.freeway.server.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.net.*;
+import org.gnu.freeway.util.ui.*;
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+
+/**
+ *
+ */
+
+public class SearchWindow extends GFrame implements AFSConstants
+{
+       private UIResources             resources;
+
+       private SController                     controller;
+       private JMenuBar                        menuBar;
+       private SearchOverviewPanel     searchPanel;
+       private GStatus                 statusBar;
+       private DaemonWindow    daemonWin;
+
+
+       public SearchWindow( SController ctr )
+       {
+               super(ctr,"search-window");
+               setTitle("GNUnet Swing : Search");
+               controller=ctr;
+
+
+               resources=new 
UIResources("search-window.xml",controller.getCache());
+               resources.setGlobalTarget(this);
+
+               daemonWin=new DaemonWindow(resources,controller);
+
+               init();
+       }
+
+       public String toString()
+       {
+               return "Main window";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       protected void init()
+       {
+               JPanel          panel;
+               UIAction        entry;
+
+//resources.getImage("app.logo");
+
+               resources.getAction("download.show.id").setEnabled(true);
+               
resources.getAction("download.show.id").setTarget(controller.getApplication());
+
+               getAction("file.insert").setEnabled(true);
+               getAction("file.download.uri").setEnabled(true);
+               getAction("file.import.directory").setEnabled(true);
+               getAction("file.unindex.file").setEnabled(true);
+               getAction("file.show.downloads").setEnabled(true);
+               getAction("file.show.messages").setEnabled(true);
+               getAction("file.show.stats").setEnabled(false);
+               getAction("file.quit").setEnabled(true);
+               getAction("advanced.manage.pseudonyms.create").setEnabled(true);
+               getAction("advanced.launch.gnunetd").setEnabled(false);
+               getAction("advanced.kill.gnunetd").setEnabled(false);
+               getAction("advanced.daemon.window").setEnabled(true);
+               getAction("help.about").setEnabled(true);
+
+               menuBar=resources.getMenuBar("window-menu");
+               
menuBar.add(UIHelper.createLAFMenu(controller.getApplication()));
+
+               searchPanel=new SearchOverviewPanel(controller);
+
+               statusBar=new GStatus();
+
+               panel=new JPanel(new BorderLayout());
+               panel.add(searchPanel,BorderLayout.CENTER);
+               panel.add(statusBar,BorderLayout.NORTH);
+
+               addWindowListener(new WindowAdapter() {
+                       public void windowClosing( WindowEvent evt )
+                       {
+                               close();
+                       }
+                       });
+
+               setContent(panel);
+               setJMenuBar(menuBar);
+
+//             getRootPane().setWindowDecorationStyle(JRootPane.FRAME);
+//             setIconImage(new 
ImageIcon(getClass().getClassLoader().getResource("images/gnunetd.png")).getImage());
+
+               entry=getAction("file.show.downloads");
+               entry.setEnabled(false);
+
+               refreshMenus();
+
+               Scheduler       scheduler;
+
+               scheduler=controller.getApplication().getScheduler();
+               scheduler.addJob(new 
ScheduledTask("REFRESH-CONNECTED-PEERS",new 
EvalAction(this,"onRefreshConnectedPeers"),Scheduler.SECS_5),Scheduler.SECS_5);
+       }
+
+       protected UIAction getAction( String str )
+       {
+               return resources.getAction(str);
+       }
+
+       public void refreshMenus()
+       {
+               GNDirectoryDatabase     db;
+               UIAction        entry;
+               boolean         
havePseudo,haveSearch,haveInsert,haveDirect,haveNamesp,haveAny;
+
+               db=new GNDirectoryDatabase(controller.getPreferences());
+
+               havePseudo = new 
Pseudonym(controller.getPreferences()).havePseudonyms();
+               haveSearch = 
db.iterateDirectoryDatabase(DIR_CONTEXT_SEARCH,null,null)>0;
+               haveInsert = 
db.iterateDirectoryDatabase(DIR_CONTEXT_INSERT,null,null)>0;
+               haveDirect = 
db.iterateDirectoryDatabase(DIR_CONTEXT_DIRECTORY,null,null)>0;
+               haveNamesp = 
db.iterateDirectoryDatabase(DIR_CONTEXT_INSERT_SB,null,null)>0;
+               haveAny = 
db.iterateDirectoryDatabase(DIR_CONTEXT_ALL,null,null)>0;
+
+               entry=getAction("advanced.assemble.directory.from.sr");
+               entry.setEnabled(haveSearch);
+
+               entry=getAction("advanced.assemble.directory.from.if");
+               entry.setEnabled(haveInsert);
+
+               entry=getAction("advanced.assemble.directory.from.lns");
+               entry.setEnabled(haveNamesp);
+
+               entry=getAction("advanced.assemble.directory.from.fid");
+               entry.setEnabled(haveDirect);
+
+               entry=getAction("advanced.assemble.directory.from.all");
+               entry.setEnabled(haveAny);
+
+               entry=getAction("advanced.manage.pseudonyms.delete");
+               entry.setEnabled(havePseudo);
+
+               entry=getAction("advanced.insert_into_namespace.select_sr");
+               entry.setEnabled(havePseudo && haveSearch);
+
+               entry=getAction("advanced.insert_into_namespace.select_if");
+               entry.setEnabled(havePseudo && haveInsert);
+
+               entry=getAction("advanced.insert_into_namespace.select_dd");
+               entry.setEnabled(havePseudo && haveDirect);
+
+               entry=getAction("advanced.insert_into_namespace.select_lns");
+               entry.setEnabled(havePseudo && haveNamesp);
+
+               entry=getAction("advanced.insert_into_namespace.select_all");
+               entry.setEnabled(havePseudo && haveAny);
+
+               entry=getAction("advanced.search.namespace");
+               entry.setEnabled(haveNamesp);
+
+               entry=getAction("advanced.reset_fid.list_sr");
+               entry.setEnabled(haveSearch);
+
+               entry=getAction("advanced.reset_fid.list_if");
+               entry.setEnabled(haveInsert);
+
+               entry=getAction("advanced.reset_fid.list_lns");
+               entry.setEnabled(haveNamesp);
+
+               entry=getAction("advanced.reset_fid.list_dd");
+               entry.setEnabled(haveDirect);
+
+               entry=getAction("advanced.reset_fid.all");
+               entry.setEnabled(haveAny);
+       }
+
+       public void cronCheckDaemon()
+       {
+               daemonWin.cronCheckDaemon();
+       }
+
+       /**
+        * This displays an about dialog
+        */
+
+       public void about()
+       {
+               String  about;
+
+               about="\nGNUnet "+GNUNetDaemon.VERSION+
+                       ", gnunet-swing 
"+controller.getApplication().getVersion()+
+                       "\n\n\n"+
+                       "GNUnet is free software, released under GNU General 
Public License version 2."+
+                       "\n\n\n"+
+                       "For more information, visit the GNUnet homepage at 
\n\n"+
+                       "http://www.ovmj.org/GNUnet/\n";;
+
+               JOptionPane.showOptionDialog(this,about,"About 
gnunet-swing",JOptionPane.YES_NO_OPTION,JOptionPane.INFORMATION_MESSAGE,null,new
 Object[] { "Right" },null);
+       }
+
+       public void showStats()
+       {
+               controller.showStats();
+       }
+
+       public void destroy_stub()
+       {
+       }
+
+       public void onCreatePseudonym()
+       {
+               CreatePseudonymDialog   dialog;
+
+               dialog=new CreatePseudonymDialog(controller,this);
+               dialog.display();
+       }
+
+       public void onDeletePseudonym()
+       {
+               DeletePseudonymDialog   dialog;
+
+               dialog=new DeletePseudonymDialog(controller,this);
+               dialog.display();
+       }
+
+       public void openSelectFile()
+       {
+               new InsertDialog(this).display();
+       }
+
+       public void onDaemonWindow()
+       {
+               daemonWin.display();
+       }
+
+       public void onLaunchDaemon()
+       {
+               daemonWin.display();
+               daemonWin.onLaunchDaemon();
+       }
+
+       public void onKillDaemon()
+       {
+               daemonWin.display();
+               daemonWin.onKillDaemon();
+       }
+
+       public void onRefreshConnectedPeers()
+       {
+               CSSession       sock;
+               CSResult        rv;
+
+               sock=controller.connect();
+               try {
+                       if (!sock.send(new CSGetClientCount())) {
+                               return;
+                               }
+
+                       rv=(CSResult) sock.receive(CSResult.class);
+                       if (rv==null) {
+                               return;
+                               }
+
+                       statusBar.setText("Connected hosts : "+rv.getResult());
+                       }
+               finally {
+                       sock.disconnect();
+                       }
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/swing/SelectDialog.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/swing/SelectDialog.java    
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/swing/SelectDialog.java    
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,165 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.swing;
+
+import org.gnu.freeway.util.ui.*;
+
+import java.awt.event.*;
+import java.beans.*;
+import javax.swing.*;
+
+/**
+ *
+ */
+
+public class SelectDialog extends GDialog implements PropertyChangeListener
+{
+       private SController     controller;
+
+       private JTextField      textField;
+       private JOptionPane     optionPane;
+       private String          typedText;
+
+
+       /**
+        * Creates the reusable dialog.
+        * @param ctr
+        * @param parent
+        */
+
+       public SelectDialog( SController ctr, GFrame parent )
+       {
+               super(parent,"select-dialog");
+               controller=ctr;
+               typedText=null;
+
+               addWindowListener(new WindowAdapter() {
+                       public void windowClosing(WindowEvent we)
+                       {
+                               onClose();
+                       }
+                       });
+
+               // ensure the text field always gets the first focus
+               addComponentListener(new ComponentAdapter() {
+                       public void componentShown( ComponentEvent evt )
+                       {
+                               textField.requestFocusInWindow();
+                       }
+                       });
+       }
+
+       public String toString()
+       {
+               return "Select dialog";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       /**
+        * Returns null if the typed string was invalid;
+        * otherwise, returns the string as the user entered it.
+        * @return
+        */
+
+       public String getTypedText()
+       {
+               return typedText;
+       }
+
+       public JComponent createContent()
+       {
+               Object[]        options;
+
+               textField=new JTextField(10);
+               textField.addActionListener(new ActionListener() {
+                       public void actionPerformed( ActionEvent evt )
+                       {
+                               onEnter();
+                       }
+                       });
+
+               options=new Object[] { "Enter", "Cancel" };
+
+               optionPane=new JOptionPane(
+                               new Object[] { "Pattern ?", textField },
+                               JOptionPane.QUESTION_MESSAGE,
+                               JOptionPane.YES_NO_OPTION,
+                               new 
ImageIcon(controller.getResources().getImageURL("app.minilogo")),
+                               options,
+                               options[0]
+                               );
+               optionPane.addPropertyChangeListener(this);
+               return optionPane;
+       }
+
+       public void updateContent()
+       {
+               textField.setText("");
+       }
+
+       public void leaveContent()
+       {
+               textField.setText("");
+       }
+
+       /**
+        * This method reacts to state changes in the option pane.
+        * @param e
+        */
+
+       public void propertyChange( PropertyChangeEvent e )
+       {
+               String  str;
+               Object  value;
+
+               if (!isVisible() || e.getSource()!=optionPane) {
+                       return;
+                       }
+
+               str=e.getPropertyName();
+               if (str.equals(JOptionPane.VALUE_PROPERTY) || 
str.equals(JOptionPane.INPUT_VALUE_PROPERTY)) {
+                       value=optionPane.getValue();
+
+                       if (value==JOptionPane.UNINITIALIZED_VALUE) {
+                               //ignore reset
+                               return;
+                               }
+
+                       // if you don't do this, then if the user presses the 
same button next time, no property change event will be fired.
+                       optionPane.setValue(JOptionPane.UNINITIALIZED_VALUE);
+
+                       if (value.equals(optionPane.getOptions()[0])) {
+                               onEnter();
+                               }
+                       else {
+                               onClose();
+                               }
+                       }
+       }
+
+       protected void onEnter()
+       {
+               typedText=textField.getText().trim();
+               if (typedText.length()==0) {
+                       textField.selectAll();
+                       
JOptionPane.showMessageDialog(SelectDialog.this,"Please, enter a non empty 
response.","Try again",JOptionPane.ERROR_MESSAGE);
+                       textField.requestFocusInWindow();
+
+                       typedText=null;
+                       }
+               else {
+                       // we're done; clear and dismiss the dialog
+                       close();
+                       }
+       }
+
+       protected void onClose()
+       {
+               typedText=null;
+               close();
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/swing/StatsWindow.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/swing/StatsWindow.java     
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/swing/StatsWindow.java     
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,156 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.afs.swing;
+
+import org.gnu.freeway.protocol.afs.esed2.*;
+import org.gnu.freeway.server.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.net.*;
+import org.gnu.freeway.util.ui.*;
+
+import java.util.logging.*;
+import javax.swing.*;
+
+/**
+ *
+ */
+
+public class StatsWindow extends GFrame implements AFSConstants, SwingConstants
+{
+       private SController     controller;
+       private UIResources     resources;
+
+       private GTable          statTable;
+       private GStatus         statusBar;
+
+
+       public StatsWindow( SController ctr )
+       {
+               super(ctr,"stats-window");
+               setTitle("GNUnet Swing : Statistics");
+               controller=ctr;
+
+               resources=new 
UIResources("stats-window.xml",controller.getCache());
+               resources.setGlobalTarget(this);
+       }
+
+       public String toString()
+       {
+               return "Statistics window";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public JComponent createContent()
+       {
+               JPanel  panel;
+               GBar    bar;
+
+               resources.getAction("main.refresh").setEnabled(true);
+               resources.getAction("main.close").setEnabled(true);
+
+               setJMenuBar(resources.getMenuBar("window-menu"));
+
+               bar=new GBar();
+               bar.add(resources.getAction("main.refresh"));
+               bar.add(resources.getAction("main.close"));
+
+               statTable=new GTable(new AbstractAdapter(2) {
+                       protected void fillFacets( Object obj, Object[] facets )
+                       {
+                               Stat    stat;
+
+                               stat=(Stat) obj;
+                               facets[0]=stat.getName();
+                               facets[1]=new Long(stat.get());
+                       }
+                       });
+               statTable.setColumns(new GColumn[] {
+                       new GColumn("Statistic", 400, RIGHT, true),
+                       new GColumn("Value",     270, LEFT,  true)
+                       });
+
+               statusBar=new GStatus();
+               statusBar.setText("");
+
+               panel=new JPanel(new TileLayout());
+               panel.add(bar,TileLayout.SOUTH);
+               panel.add(statusBar,TileLayout.NORTH);
+               panel.add(new JScrollPane(statTable));
+               return panel;
+       }
+
+       public void onRefresh()
+       {
+               if (!refresh()) {
+                       statusBar.setText("Failed to connect to daemon.");
+                       }
+       }
+
+       public void onClose()
+       {
+               close();
+       }
+
+       protected boolean refresh()
+       {
+               Stat[]                  stats;
+               int[]                           sel;
+               CSSession               sock;
+               DefaultListModel        sModel;
+               CSStatistics            statMsg;
+               long                            t;
+               int                                     totalCounters,count,i;
+
+               sel=statTable.getSelectedRows();
+
+               sModel=(DefaultListModel) statTable.getUnsortedModel();
+               sModel.clear();
+
+               sock=controller.connect();
+               try {
+                       if (!sock.send(new CSStatisticsRequest())) {
+                               controller.log(Level.WARNING,"Error sending 
request for statistics to gnunetd.");
+                               return false;
+                               }
+
+                       count=0;
+                       totalCounters=1;
+
+                       while (count<totalCounters) {
+                               statMsg=(CSStatistics) 
sock.receive(CSStatistics.class);
+                               if (statMsg==null) {
+                                       controller.log(Level.WARNING,"Error 
receiving reply for statistics from gnunetd.");
+                                       return false;
+                                       }
+
+                               if (count==0) {
+                                       t=Scheduler.toSeconds(Scheduler.now());
+                                       t-=statMsg.getStartTime();
+
+                                       statusBar.setText("Uptime : 
"+Utils.formatDuration(Scheduler.seconds(t))+" (refreshed at 
"+Utils.formatMoment(Scheduler.now())+")");
+                                       
totalCounters=statMsg.getTotalCounters();
+                                       }
+
+                               if (statMsg.getTotalCounters()!=totalCounters) {
+                                       controller.log(Level.WARNING,"Corrupted 
data ?");
+                                       return false;
+                                       }
+
+                               stats=statMsg.getStatistics();
+                               for (i=0; i<stats.length; i++) {
+                                       sModel.addElement(stats[i]);
+                                       }
+                               count+=stats.length;
+                               }
+                       }
+               finally {
+                       sock.disconnect();
+                       }
+               statTable.setSelectedRows(sel);
+               return true;
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/afs/swing/about.c
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/swing/about.c      2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/swing/about.c      2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,128 @@
+/*
+     This file is part of GNUnet
+
+     GNUnet is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with GNUnet; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file src/applications/afs/gtkui/about.c
+ * @author Christian Grothoff
+ * @author Igor Wronsky
+ *
+ * This file contains the about dialog.
+ **/
+
+#include "gnunet_afs_esed2.h"
+
+#include "helper.h"
+#include "about.h"
+
+#define ABOUT_STRING "\nGNUnet "\
+  VERSION\
+  ", gnunet-gtk "\
+  AFS_VERSION\
+  "\n\n\n"\
+  "GNUnet is free software, released under GNU General Public License version 
2."\
+  "\n\n\n"\
+  "For more information, visit the GNUnet homepage at \n\n"\
+  "http://www.ovmj.org/GNUnet/\n";
+
+
+/**
+ * This displays an about window
+ **/
+void about(GtkWidget *dummy,
+          gpointer data) {
+  GtkWidget * window;
+  GtkWidget * box1;
+  GtkWidget * table;
+  GtkWidget * text;
+  GtkWidget * button;
+
+  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+  gtk_signal_connect(GTK_OBJECT(window), 
+                     "delete_event",
+                     GTK_SIGNAL_FUNC(deleteEvent), 
+                    NULL);
+ 
+  gtk_window_set_title(GTK_WINDOW(window), 
+                      "About gnunet-gtk");
+  gtk_widget_set_usize(GTK_WIDGET(window), 
+                      600, 
+                      300);
+
+  box1 = gtk_vbox_new(FALSE, 0);
+  gtk_container_add(GTK_CONTAINER (window), 
+                   box1);
+  gtk_widget_show(box1);
+
+  table = gtk_table_new(2, 2, FALSE);
+  gtk_table_set_row_spacing(GTK_TABLE (table), 
+                           0,
+                           2);
+  gtk_table_set_col_spacing(GTK_TABLE (table), 
+                           0, 
+                           2);
+  gtk_box_pack_start(GTK_BOX (box1), 
+                    table, 
+                    TRUE,
+                    TRUE,
+                    0);
+  gtk_widget_show(table);
+
+  /* create a text widget */
+  text = gtk_text_new(NULL, NULL);
+  gtk_text_set_editable(GTK_TEXT (text), 
+                       FALSE);
+  gtk_table_attach(GTK_TABLE (table), 
+                  text,
+                  0, 
+                  1, 
+                  0, 
+                  1,
+                  GTK_EXPAND | GTK_SHRINK | GTK_FILL,
+                  GTK_EXPAND | GTK_SHRINK | GTK_FILL,
+                  0, 0);
+  gtk_widget_show(text);
+  gtk_widget_realize(text);
+
+  /* write some about text */
+  gtk_text_freeze(GTK_TEXT (text));
+
+  gtk_text_insert(GTK_TEXT(text), 
+                 NULL, 
+                 &text->style->black, 
+                 NULL,
+                 ABOUT_STRING, -1); 
+  
+  gtk_text_thaw(GTK_TEXT(text));
+
+  /* finish with a close button */
+  button = gtk_button_new_with_label("Right");
+  gtk_box_pack_start(GTK_BOX (box1), 
+                    button, 
+                    FALSE, 
+                    FALSE, 
+                    0);
+  gtk_signal_connect(GTK_OBJECT(button), 
+                    "clicked",
+                    GTK_SIGNAL_FUNC(destroyWidget), 
+                    window);
+  gtk_widget_show(button);
+  gtk_widget_show(window);
+}
+
+/* end of about.c */

Added: freeway/src/org/gnu/freeway/protocol/afs/swing/about.h
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/swing/about.h      2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/swing/about.h      2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,35 @@
+/*
+     This file is part of GNUnet
+
+     GNUnet is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with GNUnet; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file applications/afs/gtkui/about.h
+ * @author Christian Grothoff
+ **/
+
+#ifndef GTKUI_ABOUT_H
+#define GTKUI_ABOUT_H
+
+/**
+ * This displays an about window
+ **/
+void about(GtkWidget *dummy,
+          gpointer data);
+
+ 
+#endif

Added: freeway/src/org/gnu/freeway/protocol/afs/swing/delete.c
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/swing/delete.c     2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/swing/delete.c     2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,175 @@
+/*
+     This file is part of GNUnet
+
+     GNUnet is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with GNUnet; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file src/applications/afs/gtkui/delete.c
+ * @brief handles file deletions
+ * @author Igor Wronsky
+ **/
+
+#include "gnunet_afs_esed2.h"
+#include "helper.h"
+#include "main.h"
+#include "insertprogress.h"
+#include "delete.h"
+
+static gint setProgressValue(SaveCall *call) {
+  gtk_progress_set_value(GTK_PROGRESS(((SetProgress *)call->args)->bar),
+                        ((SetProgress *)call->args)->val);
+  gtkSaveCallDone(call->sem);
+  
+  return FALSE;
+}
+
+static void deleteModelCallback(ProgressStats * stats,
+                               InsertModel * ilm) {
+  SetProgress progress;
+       
+  progress.val = stats->progress;
+  progress.bar = ilm->progressBar;     
+  gtkSaveCall((GtkFunction) setProgressValue, &progress);
+}
+
+static gint destroyProgressBar(SaveCall *call) {
+  gtk_widget_destroy((GtkWidget *) call->args);
+  gtkSaveCallDone(call->sem);
+  
+  return FALSE;
+}
+
+static void deleteFileGtkThread(InsertModel * ilm) {
+  int res;
+  GNUNET_TCP_SOCKET * sock;
+  
+  SEMAPHORE_DOWN(refuseToDie);
+  sock = getClientSocket();
+  if (sock == NULL) {
+    guiMessage("Failed to connect to gnunetd.  Consult logs.");
+    SEMAPHORE_UP(refuseToDie);
+    return;
+  }
+  
+  LOG(LOG_DEBUG, 
+      "DEBUG: attempt to delete %s\n",
+      ilm->fileName);  
+
+  res = deleteFile(sock,
+                  ilm->fileName,
+                  (ProgressModel)&deleteModelCallback,
+                  ilm);
+
+  gtkSaveCall((GtkFunction) destroyProgressBar, ilm->progressBarWindow);
+  refreshMenuSensitivity();
+  
+  if(res != OK) 
+    guiMessage("Failed to delete\n\n%s", 
+              ilm->fileName);
+  else
+    guiMessage("File deleted.");
+
+  releaseClientSocket(sock);
+
+  SEMAPHORE_UP(refuseToDie);
+  
+  FREE(ilm->fileName);
+  FREE(ilm);
+}
+
+/**
+ * Callback for the file selection window. Launches the
+ * thread to delete the selected file.
+ *
+ * @param okButton not used
+ * @param window the file selection window
+ */
+static gint file_selected(GtkWidget * okButton, 
+                         GtkWidget * window) {
+  gchar * filename;
+  InsertModel * ilm;
+  PTHREAD_T deleteThread;
+
+  filename 
+    = gtk_file_selection_get_filename(GTK_FILE_SELECTION(window));
+  if ( (filename == NULL) ||
+       (0 == assertIsFile(filename)) ) {
+    guiMessage("Please select a file!\n");
+    gtk_widget_destroy(window);
+    return FALSE;
+  }
+
+  if (filename[0] != '/')
+    errexit("FATAL: ASSERTION failed: path name does not start with a '/'");
+
+  ilm = MALLOC(sizeof(InsertModel));
+  ilm->fileName = expandFileName(filename);
+
+  strcpy(ilm->opDescription, "deleted");
+  createInsertProgressBar(ilm);
+  /* start the delete thread */
+  if (0 != PTHREAD_CREATE(&deleteThread,
+                         (PThreadMain) deleteFileGtkThread,
+                         ilm,
+                         16 * 1024))
+    errexit("FATAL: could not create delete thread (%s)!\n",
+           STRERROR(errno));
+  PTHREAD_DETACH(&deleteThread);
+  
+  /* destroy the file selector */
+  gtk_widget_destroy(window);
+ 
+  return FALSE;
+}
+
+
+/**
+ * Close the open-file window.
+ **/
+static gint destroyOpenFile(GtkWidget * widget,
+                           GtkWidget * window) {
+  LOG(LOG_DEBUG, 
+      "DEBUG: destroying open-file window (%x)\n", 
+      window);
+  return TRUE;
+}
+
+/**
+ * Pops up a file selector for the user. Callback starts
+ * the file deletion thread.
+ *
+ **/
+void openDeleteFile(void) {
+  GtkWidget * window;
+
+  window = gtk_file_selection_new("Choose file to be unindexed");
+  gtk_signal_connect(GTK_OBJECT(window), 
+                    "destroy",
+                    GTK_SIGNAL_FUNC(destroyOpenFile),
+                    window);
+  gtk_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(window)->ok_button),
+                    "clicked", 
+                    GTK_SIGNAL_FUNC(file_selected),
+                    window);
+  gtk_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(window)->cancel_button),
+                    "clicked", 
+                    GTK_SIGNAL_FUNC(destroyWidget),
+                    window);
+  gtk_widget_show(window);
+}
+
+/* end of delete.c */

Added: freeway/src/org/gnu/freeway/protocol/afs/swing/delete.h
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/swing/delete.h     2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/swing/delete.h     2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,35 @@
+/*
+     This file is part of GNUnet
+
+     GNUnet is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with GNUnet; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file applications/afs/gtkui/delete.h
+ * @author Igor Wronsky 
+ **/
+
+#ifndef GTKUI_DELETE_H
+#define GTKUI_DELETE_H
+
+typedef struct {
+  size_t val;
+  GtkWidget *bar;
+} SetProgress;
+
+void openDeleteFile(void);
+
+#endif

Added: freeway/src/org/gnu/freeway/protocol/afs/swing/directory.c
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/swing/directory.c  2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/swing/directory.c  2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,731 @@
+/*
+     This file is part of GNUnet
+
+     GNUnet is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with GNUnet; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file src/applications/afs/gtkui/directory.c
+ * @brief Directory dialog for the AFS interface
+ * @author Christian Grothoff
+ * @author Igor Wronsky
+ **/
+#include "gnunet_afs_esed2.h"
+#include "helper.h"
+#include "insertprogress.h"
+#include "directory.h"
+#include "directorydisplay.h"
+
+
+/**
+ * @brief state of the create Directory window
+ **/
+typedef struct {
+  char * fileName;
+  GtkWidget * editAttributesWindow;
+  GtkWidget * fileNameLine;
+  GtkWidget * descriptionLine;
+  GtkWidget * keywordLine;
+  GtkWidget * keywordList;
+  GtkWidget * availableList;
+  GtkWidget * selectedList;
+  RootNode ** availableEntries;
+  RootNode ** selectedEntries;
+  int availableCount;
+  int selectedCount;
+} AssembleWindowModel;
+
+/**
+ * Collects the results of the assembly dialog, creates an insertion 
+ * progressbar and launches the insertion thread.
+ *
+ * @param dummy not used
+ * @param ewm the state of the edit window
+ **/
+static void startAssemble(GtkWidget * dummy, 
+                         AssembleWindowModel * ewm) {
+  int i;
+  RootNode * coll;
+  GNUnetDirectory * dir;
+  char * name;
+  InsertModel * ilm;
+  char * fileName;
+  gchar * txt;
+  PTHREAD_T insertThread;
+  
+  if (ewm->selectedCount == 0) {
+    guiMessage("WARNING: cowardly refusing to build empty directory.\n");
+    LOG(LOG_WARNING,
+       "WARNING: cowardly refusing to build empty directory.\n");
+    return;
+  }
+  ilm = MALLOC(sizeof(InsertModel));
+
+  name = gtk_entry_get_text(GTK_ENTRY(ewm->descriptionLine));
+  if (name == NULL)
+    name = "no description specified";
+
+  coll = MALLOC(ewm->selectedCount * sizeof(RootNode));
+  for (i=0;i<ewm->selectedCount;i++)
+    memcpy(&coll[i],
+          ewm->selectedEntries[i],
+          sizeof(RootNode));
+  dir = buildDirectory(ewm->selectedCount,
+                      name,
+                      coll);
+  FREE(coll);
+
+  /* get the published filename */
+  txt = gtk_entry_get_text(GTK_ENTRY(ewm->fileNameLine));
+  if (txt == NULL)
+    ilm->fileNameRoot = STRDUP("directory");
+  else
+    ilm->fileNameRoot = STRDUP(txt);
+ 
+  fileName = MALLOC(strlen("/tmp/gnunetdir_") + strlen(ilm->fileNameRoot) + 
strlen(".XXXXXX") + 1);
+  strcpy(fileName, "/tmp/gnunetdir_");
+  strcat(fileName, ilm->fileNameRoot);
+  strcat(fileName, ".XXXXXX");
+  mkstemp(fileName);
+  
+  if (SYSERR == writeGNUnetDirectory(dir, fileName)) {
+    LOG(LOG_WARNING,
+       "WARNING: could not write directory to temporary file.\n");
+    FREE(fileName);
+    FREE(dir);
+    FREE(ilm);
+    return;
+  }
+  ilm->fileName = fileName;
+  ilm->indexContent = NO;
+  ilm->deleteAfterInsert = YES;
+  /* get the new description, if any */
+  txt = gtk_entry_get_text(GTK_ENTRY(ewm->descriptionLine));
+  if (txt == NULL)
+    ilm->description = STRDUP("no description specified");
+  else
+    ilm->description = STRDUP(txt);
+  ilm->mimetype = STRDUP(GNUNET_DIRECTORY_MIME);
+
+  /* get list of keywords */
+  ilm->num_keywords = GTK_CLIST(ewm->keywordList)->rows;
+  if (ilm->num_keywords > 0) {
+    ilm->keywords = (char**) MALLOC(ilm->num_keywords * sizeof(char*));
+    for(i=0;i<ilm->num_keywords;i++) {     
+      gtk_clist_get_text(GTK_CLIST(ewm->keywordList),
+                        i,
+                        0,
+                        &txt);
+      ilm->keywords[i] = STRDUP(txt);
+    } 
+  } else
+    ilm->keywords = NULL;
+ 
+  strcpy(ilm->opDescription, "processed");
+  createInsertProgressBar(ilm);
+  /* start the insert thread */
+  if (0 != PTHREAD_CREATE(&insertThread,
+                         (PThreadMain) insertFileGtkThread,
+                         ilm,
+                         16 * 1024))
+    errexit("FATAL: could not create insert thread (%s)!\n",
+           STRERROR(errno));
+  PTHREAD_DETACH(&insertThread);
+
+  /* destroy the "assemble directory" window */
+  gtk_widget_destroy(ewm->editAttributesWindow);
+}
+
+/**
+ * Exit the application (called when the main window
+ * is closed or the user selects File-Quit).
+ **/
+static void destroyAssembleWindow(GtkWidget * widget,
+                                 AssembleWindowModel * ewm) {
+  int i;
+
+  for (i=0;i<ewm->availableCount;i++)
+    FREE(ewm->availableEntries[i]);
+  for (i=0;i<ewm->selectedCount;i++)
+    FREE(ewm->selectedEntries[i]);
+  GROW(ewm->availableEntries,
+       ewm->availableCount,
+       0);
+  GROW(ewm->selectedEntries,
+       ewm->selectedCount,
+       0);
+  FREE(ewm);
+}
+
+/**
+ * The keyword add button was clicked. Add whatever 
+ * is in the keyword box to the list of keywords.
+ *
+ * @param w not used
+ * @param ewm the state of the edit window
+ **/
+static void button_add_clicked(GtkWidget * w, 
+                              AssembleWindowModel * ewm) {
+  gchar * key;
+  gchar * newKeyword;
+  int i;
+
+  key = gtk_entry_get_text(GTK_ENTRY(ewm->keywordLine));
+  if (key == NULL) {
+    /* message to enter a string? */
+    return;
+  }    
+
+  newKeyword = STRDUP(key);
+  key = newKeyword;
+
+  /* remove trailing & heading spaces */
+  i = strlen(key)-1;
+  while ( (newKeyword[i] == ' ') && 
+         (i >= 0) ) {
+    newKeyword[i--] = '\0';
+  }
+  while (*newKeyword == ' ')
+    newKeyword++;
+
+  if ( *newKeyword == '\0' ) {
+    /* message to enter more than spaces? */    
+  } else {
+    gtk_clist_append(GTK_CLIST(ewm->keywordList),
+                    &newKeyword);
+  } 
+  FREE(key);
+  gtk_entry_set_text(GTK_ENTRY(ewm->keywordLine),
+                    "");
+}
+
+
+/**
+ * The keyword delete button was clicked. Delete the 
+ * currently selected keyword.
+ *
+ * @param w not used
+ * @param ewm state of the edit window
+ **/
+static void button_del_clicked(GtkWidget * w, 
+                              AssembleWindowModel * ewm) {
+  GList * tmp;
+
+  tmp = GTK_CLIST(ewm->keywordList)->selection;
+  if (NULL == tmp) {
+    /* message that keyword must be selected to delete one? */
+    return;
+  }  
+  gtk_clist_remove(GTK_CLIST(ewm->keywordList),
+                  (int)tmp->data);
+}
+
+/**
+ * The keyword add button was clicked. Add whatever 
+ * is in the keyword box to the list of keywords.
+ *
+ * @param w not used
+ * @param ewm the state of the edit window
+ **/
+static void button_select_clicked(GtkWidget * w, 
+                                 AssembleWindowModel * ewm) {
+  gchar * key[1];
+  GList * tmp;
+  int row;
+  int i;
+
+  tmp = GTK_CLIST(ewm->availableList)->selection;
+  if (NULL == tmp) 
+    return;  
+  row = (int) tmp->data;
+  if ( (row < 0) ||
+       (row >= ewm->availableCount) )
+    return; /* should never happen... */
+  key[0] = NULL;
+  gtk_clist_get_text(GTK_CLIST(ewm->availableList),
+                    row,
+                    0,
+                    &key[0]);
+  gtk_clist_append(GTK_CLIST(ewm->selectedList),
+                  &key[0]); 
+  gtk_clist_remove(GTK_CLIST(ewm->availableList),
+                  row);
+  if (row > 0)
+    gtk_clist_select_row(GTK_CLIST(ewm->availableList),
+                        row-1,
+                        0);
+  else
+    gtk_clist_select_row(GTK_CLIST(ewm->availableList),
+                        0,
+                        0);
+  GROW(ewm->selectedEntries,
+       ewm->selectedCount,
+       ewm->selectedCount+1);
+  ewm->selectedEntries[ewm->selectedCount-1] 
+    = ewm->availableEntries[row];
+  for (i=row;i<ewm->availableCount-1;i++)
+    ewm->availableEntries[i] 
+    = ewm->availableEntries[i+1];
+  GROW(ewm->availableEntries,
+       ewm->availableCount,
+       ewm->availableCount-1);
+}
+
+/**
+ * The keyword add button was clicked. Add whatever 
+ * is in the keyword box to the list of keywords.
+ *
+ * @param w not used
+ * @param ewm the state of the edit window
+ **/
+static void button_deselect_clicked(GtkWidget * w, 
+                                   AssembleWindowModel * ewm) {
+  gchar * key[1];
+  GList * tmp;
+  int row;
+  int i;
+
+  tmp = GTK_CLIST(ewm->selectedList)->selection;
+  if (NULL == tmp) 
+    return;  
+  row = (int) tmp->data;
+  if ( (row < 0) ||
+       (row >= ewm->selectedCount) )
+    return; /* should never happen... */
+  key[0] = NULL;
+  gtk_clist_get_text(GTK_CLIST(ewm->selectedList),
+                    row,
+                    0,
+                    &key[0]);
+  gtk_clist_append(GTK_CLIST(ewm->availableList),
+                  &key[0]); 
+  gtk_clist_remove(GTK_CLIST(ewm->selectedList),
+                  row);
+  if (row > 0)
+    gtk_clist_select_row(GTK_CLIST(ewm->selectedList),
+                        row-1,
+                        0);
+  else
+    gtk_clist_select_row(GTK_CLIST(ewm->selectedList),
+                        0,
+                        0);
+  GROW(ewm->availableEntries,
+       ewm->availableCount,
+       ewm->availableCount+1);
+  ewm->availableEntries[ewm->availableCount-1] 
+    = ewm->selectedEntries[row];
+  for (i=row;i<ewm->selectedCount-1;i++)
+    ewm->selectedEntries[i] 
+    = ewm->selectedEntries[i+1];
+  GROW(ewm->selectedEntries,
+       ewm->selectedCount,
+       ewm->selectedCount-1);
+}
+
+
+static void appendToCList(RootNode * root,
+                         AssembleWindowModel * ewm) {
+  gchar * entry[1];
+
+  entry[0] = STRDUP(root->header.description);
+  gtk_clist_append(GTK_CLIST(ewm->availableList), 
+                  entry);
+  FREE(entry[0]);
+  GROW(ewm->availableEntries,
+       ewm->availableCount,
+       ewm->availableCount+1);
+  ewm->availableEntries[ewm->availableCount-1]
+    = MALLOC(sizeof(RootNode));
+  memcpy(ewm->availableEntries[ewm->availableCount-1],
+        root,
+        sizeof(RootNode));
+}
+
+
+/**
+ * Open a window to allow the user to build a directory.
+ *
+ * @param unused GTK handle that is not used
+ * @param context selector for a subset of the known RootNodes
+ **/
+void openAssembleDirectoryDialog(GtkWidget * unused,
+                                unsigned int context) {
+  AssembleWindowModel * ewm;
+  GtkWidget * window;
+  GtkWidget * vbox, * hbox;
+  GtkWidget * clist;
+  GtkWidget * scrolled_window;
+  GtkWidget * label;
+  GtkWidget * separator; 
+  GtkWidget * button_add;
+  GtkWidget * button_delete;
+  GtkWidget * button_ok;
+  GtkWidget * button_cancel;
+  GtkWidget * keyword_line;
+  gchar * titles[1] = { "Keyword(s) used" };
+  gchar * titlesAvailable[1] = { "Files available" };
+  gchar * titlesSelected[1] = { "Files selected"};
+  gchar * directoryMimetype[1] = { GNUNET_DIRECTORY_MIME };
+
+  ewm = MALLOC(sizeof(AssembleWindowModel));
+  /* create new window for editing */
+  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+  ewm->editAttributesWindow = window;
+  gtk_widget_set_usize(GTK_WIDGET(window),
+                      620,
+                      480);
+  gtk_window_set_title(GTK_WINDOW(window), 
+                      "Assemble directory");
+
+  /* add container for window elements */
+  vbox = gtk_vbox_new(FALSE, 0);
+  gtk_container_add(GTK_CONTAINER(window),
+                   vbox);
+  gtk_widget_show(vbox);
+
+  /* when user clicks on close box, always "destroy" */
+  gtk_signal_connect(GTK_OBJECT(window),
+                    "delete_event",
+                    GTK_SIGNAL_FUNC(deleteEvent),
+                    ewm);
+  /* whenever edit window gets destroyed, 
+     free *ALL* ewm data */
+  gtk_signal_connect(GTK_OBJECT(window),
+                    "destroy",
+                    GTK_SIGNAL_FUNC(destroyAssembleWindow),
+                    ewm);
+
+  gtk_container_set_border_width(GTK_CONTAINER(window), 
+                                10);
+
+  /* Create a line to change the published filename */
+  hbox = gtk_hbox_new(FALSE, 0);
+  gtk_box_pack_start(GTK_BOX(vbox),
+                    hbox,
+                    FALSE,
+                    TRUE,
+                    0);
+  gtk_widget_show(hbox);
+  label = gtk_label_new("Published directory name:");
+  gtk_box_pack_start(GTK_BOX(hbox),
+                    label, 
+                    FALSE, 
+                    FALSE, 
+                    0);
+  gtk_widget_show(label); 
+  ewm->fileNameLine = gtk_entry_new();
+  gtk_box_pack_start(GTK_BOX(hbox),
+                    ewm->fileNameLine,
+                    TRUE,
+                    TRUE,
+                    0);
+  gtk_entry_set_text(GTK_ENTRY(ewm->fileNameLine), 
+                    "");
+  gtk_widget_show(ewm->fileNameLine);
+  
+  /* Create a line to change the description */
+  hbox = gtk_hbox_new(FALSE, 0);
+  gtk_box_pack_start(GTK_BOX(vbox),
+                    hbox,
+                    FALSE,
+                    TRUE,
+                    0);
+  gtk_widget_show(hbox);
+  label = gtk_label_new("Description:");
+  gtk_box_pack_start(GTK_BOX(hbox),
+                    label, 
+                    FALSE, 
+                    FALSE, 
+                    0);
+  gtk_widget_show(label);  
+  ewm->descriptionLine = gtk_entry_new();
+  gtk_box_pack_start(GTK_BOX(hbox),
+                    ewm->descriptionLine, 
+                    TRUE, 
+                    TRUE,
+                    0);
+  gtk_entry_set_text(GTK_ENTRY(ewm->descriptionLine), 
+                    "A GNUnet directory");
+  gtk_widget_show(ewm->descriptionLine);
+  
+  separator = gtk_hseparator_new();
+  gtk_box_pack_start(GTK_BOX(vbox),
+                    separator,
+                    TRUE, 
+                    TRUE,
+                    0);
+  gtk_widget_show(separator);
+
+  /* add a list of keywords */
+  scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
+                                GTK_POLICY_AUTOMATIC, 
+                                GTK_POLICY_ALWAYS);
+  gtk_box_pack_start(GTK_BOX(vbox), 
+                    scrolled_window, 
+                    TRUE, 
+                    TRUE, 
+                    0);
+  gtk_widget_show(scrolled_window);  
+  clist = gtk_clist_new_with_titles(1, titles); 
+  /* add mimetype as a keyword */
+  gtk_clist_append(GTK_CLIST(clist), directoryMimetype);
+  ewm->keywordList = clist;
+  gtk_container_add(GTK_CONTAINER(scrolled_window), 
+                   clist);
+  gtk_widget_show(clist);
+
+  /* add a line to input new keywords */
+  keyword_line = gtk_entry_new();
+  ewm->keywordLine = keyword_line;
+  gtk_box_pack_start(GTK_BOX(vbox),
+                    keyword_line, 
+                    FALSE,
+                    FALSE,
+                    0);
+  gtk_entry_set_text(GTK_ENTRY(keyword_line), 
+                    "");
+  gtk_signal_connect(GTK_OBJECT(keyword_line),
+                    "activate",
+                     GTK_SIGNAL_FUNC(button_add_clicked),
+                     ewm);
+  gtk_widget_show(keyword_line);
+
+  /* add the buttons to add and delete keywords */
+  hbox = gtk_hbox_new(FALSE, 0);
+  gtk_box_pack_start(GTK_BOX(vbox),
+                    hbox,
+                    FALSE,
+                    TRUE,
+                    0);
+  gtk_widget_show(hbox);
+  button_add = gtk_button_new_with_label("Add keyword");
+  button_delete = gtk_button_new_with_label("Delete keyword");
+  gtk_box_pack_start(GTK_BOX(hbox), 
+                    button_add, 
+                    TRUE, 
+                    TRUE, 
+                    0);
+  gtk_box_pack_start(GTK_BOX(hbox), 
+                    button_delete, 
+                    TRUE, 
+                    TRUE, 
+                    0);
+  gtk_signal_connect(GTK_OBJECT(button_add),
+                    "clicked",
+                    GTK_SIGNAL_FUNC(button_add_clicked),
+                    ewm);
+  gtk_signal_connect(GTK_OBJECT(button_delete), 
+                    "clicked",
+                    GTK_SIGNAL_FUNC(button_del_clicked),
+                    ewm);
+  gtk_widget_show(button_add);
+  gtk_widget_show(button_delete);
+
+  separator = gtk_hseparator_new();
+  gtk_box_pack_start(GTK_BOX(vbox),
+                    separator,
+                    TRUE, 
+                    TRUE,
+                    0);
+  gtk_widget_show(separator);
+
+  /* add the box for the two lists */
+  hbox = gtk_hbox_new(FALSE, 0);
+  gtk_box_pack_start(GTK_BOX(vbox),
+                    hbox,
+                    TRUE,
+                    TRUE,
+                    0);
+  gtk_widget_show(hbox);
+
+  /* add a list of available entries */
+  scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
+                                GTK_POLICY_AUTOMATIC, 
+                                GTK_POLICY_ALWAYS);
+  gtk_box_pack_start(GTK_BOX(hbox), 
+                    scrolled_window, 
+                    TRUE, 
+                    TRUE, 
+                    0);
+  gtk_widget_show(scrolled_window);  
+  clist = gtk_clist_new_with_titles(1, titlesAvailable); 
+  ewm->availableList = clist;
+  gtk_container_add(GTK_CONTAINER(scrolled_window), 
+                   clist);
+  /* add the known RootNodes to the list */
+  gtk_clist_freeze(GTK_CLIST(clist));
+  iterateDirectoryDatabase(context,
+                          (RootNodeCallback)&appendToCList,
+                          ewm);
+  gtk_clist_thaw(GTK_CLIST(clist));
+  gtk_widget_show(clist);
+
+
+  /* add a list of selected entries */
+  scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
+                                GTK_POLICY_AUTOMATIC, 
+                                GTK_POLICY_ALWAYS);
+  gtk_box_pack_start(GTK_BOX(hbox), 
+                    scrolled_window, 
+                    TRUE, 
+                    TRUE, 
+                    0);
+  gtk_widget_show(scrolled_window);  
+  clist = gtk_clist_new_with_titles(1, titlesSelected); 
+  ewm->selectedList = clist;
+  gtk_container_add(GTK_CONTAINER(scrolled_window), 
+                   clist);
+  gtk_widget_show(clist);
+
+
+  /* add the box for the buttons to move between the
+     two lists */
+  hbox = gtk_hbox_new(FALSE, 0);
+  gtk_box_pack_start(GTK_BOX(vbox),
+                    hbox,
+                    FALSE,
+                    TRUE,
+                    0);
+  gtk_widget_show(hbox);
+
+  button_add = gtk_button_new_with_label("=>");
+  button_delete = gtk_button_new_with_label("<=");
+  gtk_box_pack_start(GTK_BOX(hbox), 
+                    button_add, 
+                    TRUE, 
+                    TRUE, 
+                    0);
+  gtk_box_pack_start(GTK_BOX(hbox), 
+                    button_delete, 
+                    TRUE, 
+                    TRUE, 
+                    0);
+  gtk_signal_connect(GTK_OBJECT(button_add),
+                    "clicked",
+                    GTK_SIGNAL_FUNC(button_select_clicked),
+                    ewm);
+  gtk_signal_connect(GTK_OBJECT(button_delete), 
+                    "clicked",
+                    GTK_SIGNAL_FUNC(button_deselect_clicked),
+                    ewm);
+  gtk_widget_show(button_add);
+  gtk_widget_show(button_delete);
+
+
+
+  /* add the insertion ok/cancel buttons */
+  separator = gtk_hseparator_new();
+  gtk_box_pack_start(GTK_BOX(vbox),
+                    separator,
+                    TRUE, 
+                    TRUE,
+                    0);
+  gtk_widget_show(separator);
+
+  hbox = gtk_hbox_new(FALSE, 0);
+  gtk_box_pack_start(GTK_BOX(vbox),
+                    hbox, 
+                    FALSE, 
+                    TRUE, 
+                    0);
+  gtk_widget_show(hbox);
+  button_ok = gtk_button_new_with_label("Ok");
+  button_cancel = gtk_button_new_with_label("Cancel");
+  gtk_box_pack_start(GTK_BOX(hbox),
+                    button_ok,
+                    TRUE,
+                    TRUE,
+                    0);
+  gtk_box_pack_start(GTK_BOX(hbox), 
+                    button_cancel, 
+                    TRUE,
+                    TRUE, 
+                    0);
+  gtk_signal_connect(GTK_OBJECT(button_ok), 
+                    "clicked",
+                    GTK_SIGNAL_FUNC(startAssemble),
+                    ewm);
+  gtk_signal_connect(GTK_OBJECT(button_cancel),
+                    "clicked",
+                    GTK_SIGNAL_FUNC(destroyWidget),
+                    window);
+  gtk_widget_show(button_ok);
+  gtk_widget_show(button_cancel);
+
+  /* all clear, show the window */
+  gtk_widget_show(window);
+}
+
+/**
+ * Callback for displaying user-selected directory
+ **/ 
+static gint importDirectoryCallback(GtkWidget * okButton,
+                                   GtkWidget * window) 
+{
+  gchar * filename;
+
+  filename 
+    = gtk_file_selection_get_filename(GTK_FILE_SELECTION(window));
+  if ( (filename == NULL) ||
+       (0 == assertIsFile(filename)) ) {
+    guiMessage("Please select a file!\n");
+    gtk_widget_destroy(window);
+    return FALSE;
+  }
+ 
+  displayDirectory(filename,
+                  NULL);
+
+  gtk_widget_destroy(window); 
+
+  return FALSE;
+}
+
+/**
+ * Asks user to select a .gnd directory (from disk) to be displayed
+ **/
+void importDirectory(void) 
+{
+  GtkWidget * window;
+  char pattern[16];
+  
+  window = gtk_file_selection_new("Choose directory to be imported");
+
+  sprintf(pattern, "*%s", GNUNET_DIRECTORY_EXT);
+  gtk_file_selection_complete(GTK_FILE_SELECTION(window),
+                             pattern);
+  
+  gtk_signal_connect(GTK_OBJECT(window),
+                     "destroy",
+                     GTK_SIGNAL_FUNC(destroyWidget),
+                     window);
+  gtk_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(window)->ok_button),
+                     "clicked",
+                     GTK_SIGNAL_FUNC(importDirectoryCallback),
+                     window);
+  gtk_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(window)->cancel_button),
+                     "clicked",
+                     GTK_SIGNAL_FUNC(destroyWidget),
+                     window);
+  gtk_widget_show(window);
+
+}
+
+
+/* end of directory.c */

Added: freeway/src/org/gnu/freeway/protocol/afs/swing/directory.h
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/swing/directory.h  2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/swing/directory.h  2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,44 @@
+/*
+     This file is part of GNUnet
+
+     GNUnet is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with GNUnet; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file applications/afs/gtkui/directory.h
+ * @author Christian Grothoff
+ **/
+
+#ifndef GTKUI_DIRECTORY_H
+#define GTKUI_DIRECTORY_H
+
+
+
+/**
+ * Open a window to allow the user to build a directory.
+ *
+ * @param context selector for a subset of the known RootNodes
+ **/
+void openAssembleDirectoryDialog(GtkWidget * unused,
+                                unsigned int context);
+
+/**
+ * Asks user to select a .gnd directory (from disk) to be displayed
+ **/
+void importDirectory(void);
+
+
+#endif

Added: freeway/src/org/gnu/freeway/protocol/afs/swing/directorydisplay.c
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/swing/directorydisplay.c   
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/swing/directorydisplay.c   
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,91 @@
+/*
+     This file is part of GNUnet
+
+     GNUnet is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with GNUnet; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file src/applications/afs/gtkui/directorydisplay.c
+ * @brief code that displays the contents of a directory
+ * @author Christian Grothoff
+ **/
+
+
+#include "gnunet_afs_esed2.h"
+#include "helper.h"
+#include "directory.h"
+#include "directorydisplay.h"
+#include "search.h"
+
+void displayDirectory(char * filename,
+                     RootNode * rn) {
+  GtkWidget * box;
+  ListModel * model;  
+  GNUnetDirectory * dir;
+  int i;
+
+  dir = readGNUnetDirectory(filename);
+  if (dir == NULL) {
+    LOG(LOG_WARNING,
+        "WARNING: directory downloaded (%s) has invalid format.\n",
+        filename);
+    guiMessage("WARNING: directory downloaded (%s) has invalid format.\n",
+               filename);
+    return;
+  }
+  model = (ListModel*) MALLOC(sizeof(ListModel));
+  model->type = LM_TYPE_DIRECTORY;
+  box = initializeSearchResultList(model);
+  
+  /* do a nested freeze on the clist, for efficiency */
+  gtk_clist_freeze(GTK_CLIST(model->search_result_list));
+    
+  for (i=0;i<ntohl(dir->number_of_files);i++) {
+    /* sneaky side-effect: add to state DB!
+     (note that if you download a directory
+     with gnunet-download, this does not happen
+     since we don't know the mime-type in
+     gnunet-download.) */
+    makeRootNodeAvailable(&((GNUnetDirectory_GENERIC*)dir)->contents[i], 
+                         DIR_CONTEXT_DIRECTORY);
+    model->skipMenuRefresh = (i==ntohl(dir->number_of_files)-1 ? NO : YES );
+    displayResultGTK(&((GNUnetDirectory_GENERIC*)dir)->contents[i],
+                    model);
+  }
+  FREE(dir);
+ 
+  gtk_clist_thaw(GTK_CLIST(model->search_result_list));
+ 
+  if (rn != NULL ) {
+    rn->header.description[MAX_DESC_LEN-1] = '\0';
+    addToNotebook(rn->header.description,
+                 box);
+  } else {
+    char * fileNameRoot = filename;
+    int i;
+
+    for (i=strlen(filename)-1;i>=0;i--) {
+      if (filename[i] == DIR_SEPARATOR) {
+        fileNameRoot = &filename[i+1];
+        break;
+      }
+    }
+    addToNotebook(fileNameRoot,
+                 box);
+  }
+}
+
+/* end of directorydisplay.c */

Added: freeway/src/org/gnu/freeway/protocol/afs/swing/directorydisplay.h
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/swing/directorydisplay.h   
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/swing/directorydisplay.h   
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,36 @@
+/*
+     This file is part of GNUnet
+
+     GNUnet is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with GNUnet; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file applications/afs/gtkui/directorydisplay.h
+ * @author Christian Grothoff
+ **/
+
+#ifndef GTKUI_DIRECTORY_DISPLAY_H
+#define GTKUI_DIRECTORY_DISPLAY_H
+
+#include "gnunet_afs_esed2.h"
+#include <gtk/gtk.h>
+#include "directorydisplay.h"
+#include "download.h"
+
+void displayDirectory(char * filename,
+                     RootNode * rn);
+ 
+#endif

Added: freeway/src/org/gnu/freeway/protocol/afs/swing/download.c
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/swing/download.c   2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/swing/download.c   2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,887 @@
+/*
+     This file is part of GNUnet
+
+     GNUnet is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with GNUnet; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file src/applications/afs/gtkui/download.c
+ * @brief code that handles the download window
+ * @author Christian Grothoff
+ * @author Igor Wronsky
+ *
+ * FIXME:
+ * - shutdown of gnunet-gtk does NOT terminate
+ *   each of the pending downloads.  There
+ *   should be a handler function that is invoked
+ *   whenever gnunet-gtk shuts down and that
+ *   stops all pending downloads.  This used to
+ *   be implemented but got lost when Igor added
+ *   the download window.
+ **/
+
+#include "gnunet_afs_esed2.h"
+#include "helper.h"
+#include "directorydisplay.h"
+#include "download.h"
+#include "main.h"
+
+GtkWidget * dlWindow = NULL;
+
+#define DEBUG_WRITE_CPSDATA NO
+
+/* Suitable values of DownloadModel.downloadStatus */
+#define DOWNLOAD_COMPLETE 0
+#define DOWNLOAD_FAILED   1
+#define DOWNLOAD_ABORTED  2
+#define DOWNLOAD_PENDING  3
+
+/* colors taken from x-chat source, regards */
+GdkColor textColors[] = {
+        {0, 0xcf3c, 0xcf3c, 0xcf3c}, /* 0  white */
+        {0, 0x0000, 0x0000, 0x0000}, /* 1  black */
+        {0, 0x0000, 0x0000, 0xcccc}, /* 2  blue */
+        {0, 0x0000, 0xcccc, 0x0000}, /* 3  green */
+        {0, 0xdddd, 0x0000, 0x0000}, /* 4  red */
+        {0, 0xaaaa, 0x0000, 0x0000}, /* 5  light red */
+        {0, 0xbbbb, 0x0000, 0xbbbb}, /* 6  purple */
+        {0, 0xffff, 0xaaaa, 0x0000}, /* 7  orange */
+        {0, 0xeeee, 0xdddd, 0x2222}, /* 8  yellow */
+        {0, 0x3333, 0xdede, 0x5555}, /* 9  green */
+        {0, 0x0000, 0xcccc, 0xcccc}, /* 10 aqua */
+        {0, 0x3333, 0xeeee, 0xffff}, /* 11 light aqua */
+        {0, 0x0000, 0x0000, 0xffff}, /* 12 blue */
+        {0, 0xeeee, 0x2222, 0xeeee}, /* 13 light purple */
+        {0, 0x7777, 0x7777, 0x7777}, /* 14 grey */
+        {0, 0x9999, 0x9999, 0x9999}, /* 15 light grey */
+        {0, 0xa4a4, 0xdfdf, 0xffff}, /* 16 marktext Back (blue) */
+        {0, 0x0000, 0x0000, 0x0000}, /* 17 marktext Fore (black) */
+        {0, 0xdf3c, 0xdf3c, 0xdf3c}, /* 18 foreground (white) */
+        {0, 0x0000, 0x0000, 0x0000}, /* 19 background (black) */
+        {0, 0x8c8c, 0x1010, 0x1010}, /* 20 tab New Data (dark red) */
+        {0, 0x0000, 0x0000, 0xffff}, /* 21 tab Nick Mentioned (blue) */
+        {0, 0xf5f5, 0x0000, 0x0000}, /* 22 tab New Message (red) */
+};
+
+static void selectAll(void);
+static void unSelectAll(void);
+static void removeFinished(void);
+static void abortHelper(void);
+static void hideHelper(void);
+static gint abortSelectedDownloads(GtkWidget * widget,
+                           GtkWidget * clist);
+
+static GtkItemFactoryEntry dlWindowMenu[] = {
+ { "/Select all",       NULL,    selectAll,           0, "<Item>" },
+ { "/Unselect all",     NULL,    unSelectAll,         0, "<Item>" },
+ { "/sep1",             NULL,    NULL,                0, "<Separator>" },
+ { "/Remove selected",  NULL,    abortHelper,         0, "<Item>" },
+ { "/Remove finished",  NULL,    removeFinished,      0, "<Item>" },
+ { "/sep2",             NULL,    NULL,                0, "<Separator>" },
+ { "/Hide dl window",   NULL,    hideHelper,          0, "<Item>" }
+};
+
+static gint dlWindowMenuItems 
+  = sizeof (dlWindowMenu) / sizeof (dlWindowMenu[0]);
+
+static void selectAll(void) 
+{
+    GtkWidget * clist;
+    
+    clist = gtk_object_get_data(GTK_OBJECT(dlWindow),
+                                "LIST");
+    gtk_clist_select_all(GTK_CLIST(clist));                                
+}
+
+static void unSelectAll(void) 
+{
+    GtkWidget * clist;
+    
+    clist = gtk_object_get_data(GTK_OBJECT(dlWindow),
+                                "LIST");
+    gtk_clist_unselect_all(GTK_CLIST(clist));                              
+}
+
+static void removeFinished(void)
+{
+    GtkCList * clist;
+    int i;
+    gchar * string;
+
+    unSelectAll();
+
+    clist = GTK_CLIST(gtk_object_get_data(GTK_OBJECT(dlWindow),
+                                           "LIST"));
+    gtk_clist_freeze(clist);
+
+    for(i=0;i<clist->rows;i++) {
+      gtk_clist_get_text(clist, 
+                         i,
+                        1,
+                        &string);
+      if(strcmp(string, "DONE")==0)
+        gtk_clist_select_row(clist,
+                            i,
+                            1);
+      else
+       gtk_clist_unselect_row(clist,
+                            i,
+                            1);
+    }
+      
+    gtk_clist_thaw(clist);
+    
+    abortSelectedDownloads(NULL, GTK_WIDGET(clist));
+}
+
+static void hideHelper(void)
+{
+  if(dlWindow)
+    gtk_widget_hide(dlWindow);
+}
+  
+static void abortHelper(void)
+{
+    GtkWidget * clist;
+    
+    clist = gtk_object_get_data(GTK_OBJECT(dlWindow),
+                                "LIST");
+    abortSelectedDownloads(NULL, clist);
+}
+
+/**
+ * Changes the current sort column and sorts the list.
+ **/
+static void sort_column_callback(GtkCList * clist,
+                                 gint column,
+                                 gpointer data) {
+  static int sortOrder[8]={0,0,0,0,0,0,0,0};
+
+  sortOrder[column]^=1;
+
+  if(sortOrder[column]==1)
+    gtk_clist_set_sort_type(clist,
+                           GTK_SORT_ASCENDING);
+  else
+    gtk_clist_set_sort_type(clist,
+                           GTK_SORT_DESCENDING);
+  
+  /* Sort column 0 as string, 1 as percent and rest as numbers */
+  switch(column) {
+    case 0: 
+      gtk_clist_set_compare_func(clist,
+                                 (GtkCListCompareFunc)alphaComp);
+      break; 
+    case 1:
+      gtk_clist_set_compare_func(clist,
+                                 (GtkCListCompareFunc)percentComp);
+      break;
+    default:
+      gtk_clist_set_compare_func(clist,
+                                 (GtkCListCompareFunc)numericComp);
+      break;
+  }
+  gtk_clist_set_sort_column(clist, column);
+  gtk_clist_freeze(clist);
+  gtk_clist_sort(clist);
+  gtk_clist_thaw(clist);
+}
+
+/**
+ * "delete_event" handler for a download. Sets the 
+ * download state as aborted, and releases the wait semaphore for 
+ * the thread awaiting download completion. Returns TRUE so that 
+ * gtk doesn't destroy the widget (its never destroyed).
+ *
+ * @param widget not used
+ * @param clist the list of downloads
+ **/
+static gint abortSelectedDownloads(GtkWidget * widget,
+                                  GtkWidget * clist) {
+  DownloadModel * dlm;
+  int row;
+  GList * tmp;
+
+  LOG(LOG_DEBUG,
+      "DEBUG: abortSelectedDownloads %x (%x)\n",
+      clist, widget);
+  if(!GTK_CLIST(clist))
+    return TRUE;
+  
+  gtk_clist_freeze(GTK_CLIST(clist));
+
+  tmp = GTK_CLIST(clist)->selection;
+  while (tmp) {
+    row = (int)tmp->data;
+    tmp = tmp->next;
+
+    /* if dlm is not NULL, abort the download */
+    dlm = gtk_clist_get_row_data(GTK_CLIST(clist),
+                                row);
+    if(dlm) {
+      dlm->downloadStatus = DOWNLOAD_ABORTED;
+      SEMAPHORE_UP(dlm->doneSem);
+    } 
+    
+    /* remove list entry */
+    gtk_clist_remove(GTK_CLIST(clist),
+                    row);
+  }
+
+  gtk_clist_thaw(GTK_CLIST(clist));
+  
+  return TRUE;
+}
+
+static gint displayStats(SaveCall *call) {
+  DLStats *dlStats = (DLStats *) call->args;
+  DownloadModel *dlm = dlStats->dlm;
+  gint row;
+
+  gtk_clist_freeze(GTK_CLIST(dlm->dlList));
+  row = gtk_clist_find_row_from_data(GTK_CLIST(dlm->dlList),
+                                    dlStats->dlm);
+  gtk_clist_set_text(GTK_CLIST(dlm->dlList),
+                    row,
+                    1,
+                    dlStats->perc);
+  gtk_clist_set_text(GTK_CLIST(dlm->dlList),
+                    row,
+                    2,
+                    dlStats->pos);
+  gtk_clist_set_text(GTK_CLIST(dlm->dlList),
+                    row,
+                    4,
+                    dlStats->areq);
+  gtk_clist_set_text(GTK_CLIST(dlm->dlList),
+                    row,
+                    5,
+                    dlStats->cra);
+  gtk_clist_set_text(GTK_CLIST(dlm->dlList),
+                    row,
+                    6,
+                    dlStats->tr);
+  gtk_clist_set_text(GTK_CLIST(dlm->dlList),
+                    row,
+                    7,
+                    dlStats->kbs);
+  if(dlm->successfulStart == NO && dlStats->stats->progress > 0) {
+      gtk_clist_set_foreground(GTK_CLIST(dlm->dlList), 
+                               row,
+                               &textColors[1]);
+      dlm->successfulStart = YES;
+  } 
+  gtk_clist_thaw(GTK_CLIST(dlm->dlList));
+
+  if (dlStats->stats->filesize == dlStats->stats->progress) {
+    /* reset the request counters (just cosmetic) */
+    snprintf(dlStats->areq, sizeof(dlStats->areq), "0");
+    snprintf(dlStats->cra, sizeof(dlStats->cra), "0.0");
+    
+    gtk_clist_freeze(GTK_CLIST(dlm->dlList));
+    gtk_clist_set_text(GTK_CLIST(dlm->dlList),
+                      row,
+                        4,
+                        dlStats->areq);
+    gtk_clist_set_text(GTK_CLIST(dlm->dlList),
+                      row,
+                          5,
+                          dlStats->cra);
+    gtk_clist_thaw(GTK_CLIST(dlm->dlList));
+    refreshMenuSensitivity();
+
+    if (dlStats->stats->filesize == 0)       
+      dlm->downloadStatus = DOWNLOAD_FAILED;
+    else
+      dlm->downloadStatus = DOWNLOAD_COMPLETE;
+    SEMAPHORE_UP(dlm->doneSem); /* signal: we're done with download */
+    
+    gtkSaveCallDone(call->sem);
+    
+    return FALSE;
+  }
+  gtkSaveCallDone(call->sem);
+    
+  return FALSE;  
+}
+
+/**
+ * This method is called by the download code to notify the user
+ * interface of the download progress.
+ *
+ * @param stats the new statistical values
+ * @param dlm the accessor to the GTK window
+ **/
+static void modelCallback(ProgressStats * stats,
+                         DownloadModel * dlm) {
+  double currentRetryAvg, averageBps, percentage;
+  DLStats dlStats;
+  TIME_T now;
+#if DEBUG_WRITE_CPSDATA
+  char scratch[128];
+  FILE * fp;
+#endif
+  
+  if (dlm->downloadStatus != DOWNLOAD_PENDING) 
+    return;
+
+  /* don't display more often than once/sec */
+  TIME(&now);
+  if ((now-(dlm->lastDisplayTime)) < 1 &&
+      (stats->filesize != stats->progress))
+    return;
+  else
+    dlm->lastDisplayTime = now;
+ 
+  if(stats->requestsSent > 0)
+    currentRetryAvg = 
+      (double)stats->currentRetries / 
+      (double)stats->requestsSent;
+  else
+    currentRetryAvg = 0;
+  
+  if (now - dlm->downloadStartTime > 0)
+    averageBps =
+      (double)(stats->progress) / 
+      ((double)(now - dlm->downloadStartTime));
+  else
+    averageBps = 0;
+
+#if DEBUG_WRITE_CPSDATA
+  sprintf(scratch, "/tmp/cps-%x.txt", dlm->root.header.fileIdentifier.crc);
+  fp = FOPEN(scratch, "a+");
+  fprintf(fp, "%d %d %d %f\n", (int)(now-(dlm->downloadStartTime)),
+         stats->progress,
+         stats->totalRetries,
+          averageBps);
+  fclose(fp);
+#endif
+  
+  if(stats->filesize>0)
+    percentage = 100.0*((double)stats->progress/(double)stats->filesize);
+  else
+    percentage = 0;
+
+  snprintf(dlStats.pos, sizeof(dlStats.pos), "%d", stats->progress);
+  snprintf(dlStats.kbs, sizeof(dlStats.kbs), "%.1f", averageBps);
+  snprintf(dlStats.perc, sizeof(dlStats.perc), "%3.1f%%", percentage);
+  snprintf(dlStats.areq, sizeof(dlStats.areq), "%d", stats->requestsSent);
+  snprintf(dlStats.cra, sizeof(dlStats.cra), "%3.1f", currentRetryAvg);
+  snprintf(dlStats.tr, sizeof(dlStats.tr), "%d", stats->totalRetries);
+  dlStats.stats = stats; 
+  dlStats.dlm = dlm;
+  
+  gtkSaveCall((GtkFunction) displayStats, &dlStats);
+}
+
+gint setDownloadEntry(SaveCall *call) {
+  gint row;
+  DownloadModel *dlm = ((SetDownloadEntry *) call->args)->dlm;
+
+  gtk_clist_freeze(GTK_CLIST(dlm->dlList));
+  row = gtk_clist_find_row_from_data(GTK_CLIST(dlm->dlList),
+                      dlm);
+  gtk_clist_set_foreground(GTK_CLIST(dlm->dlList), 
+                           row,
+                           ((SetDownloadEntry *) call->args)->color);
+  gtk_clist_set_text(GTK_CLIST(dlm->dlList),
+                      row,
+             1,
+             ((SetDownloadEntry *) call->args)->text);
+  gtk_clist_thaw(GTK_CLIST(dlm->dlList));
+
+  gtkSaveCallDone(call->sem);
+  
+  return FALSE;
+}
+
+gint disentangleFromCLIST(SaveCall *call) {
+  gint row;
+  DownloadModel *dlm = (DownloadModel *) call->args;
+
+  gtk_clist_freeze(GTK_CLIST(dlm->dlList));
+  row = gtk_clist_find_row_from_data(GTK_CLIST(dlm->dlList),
+                                    dlm);
+  gtk_clist_set_row_data(GTK_CLIST(dlm->dlList),
+                         row,
+                                          NULL);
+  gtk_clist_thaw(GTK_CLIST(dlm->dlList));
+
+  gtkSaveCallDone(call->sem);
+
+  return FALSE;
+}
+
+/**
+ * Main function of the download thread. This function terminates
+ * automagically once the download is complete or if the download
+ * entry is removed while dl.  It is responsible for stopping the
+ * requestmanager.
+ * 
+ * @param dlm the download model
+ **/
+static void downloadFile_(DownloadModel * dlm) {
+  char * mime;
+  SetDownloadEntry entry;
+
+  LOG(LOG_DEBUG,
+      "DEBUG: Entering downloadFile_ for %s (%x)\n", 
+      dlm->fileName,
+      dlm);
+
+  /* initiate download */
+  TIME(&dlm->downloadStartTime);
+  
+  /* this starts the "real" download thread in the background,
+     with "modelCallback" called back to tell us about the progress */
+  dlm->rm = downloadFile(&dlm->root.header.fileIdentifier,
+                        dlm->fileName,
+                        (ProgressModel)&modelCallback,
+                        dlm);
+  if (dlm->rm == NULL) {
+    guiMessage("ERROR: could not download %s.\nConsult logs.\n",
+              dlm->fileName);
+    SEMAPHORE_FREE(dlm->doneSem);
+    FREE(dlm->fileName);
+    FREE(dlm);
+    return;
+  }
+  /* Wait here until download is complete or
+     the window is closed or gnunet-gtk is terminated */
+  LOG(LOG_DEBUG,
+      "DEBUG: waiting for DL completion (%x)\n",
+      dlm);
+  SEMAPHORE_DOWN(dlm->doneSem);
+  LOG(LOG_DEBUG,
+      "DEBUG: download complete (%d) enter destroyRequestManager (%x)\n",
+      dlm->downloadStatus,
+      dlm);
+
+  /* stop the RequestManager */
+  if (dlm->rm != NULL) {
+    destroyRequestManager(dlm->rm); 
+  } else {
+    /* this can happen if the requestmanager initialization
+     * failed for reason or another (e.g. write permission denied) */
+    LOG(LOG_WARNING, 
+        "WARNING: dlm->rm was NULL\n");
+  }
+
+  /*
+    ok, now why are we here? 4 possibilities:
+     a) download aborted (user closed window)
+     b) gnunet-gtk terminated (same as download aborted)
+     c) download failed (gnunetd exit, out-of-space)
+     d) download completed 
+
+     In case "d" we show the "YAY" window
+     and wait for another signal.
+     In case "c" we show the "BAH" window
+     and wait for another signal.
+  */
+
+  switch (dlm->downloadStatus) {
+  case DOWNLOAD_COMPLETE:
+    /* color successful dl green */
+    entry.dlm = dlm;
+    entry.color = &textColors[3];
+    entry.text = "DONE";
+    gtkSaveCall((GtkFunction) setDownloadEntry, &entry);
+    
+    switch (ntohs(dlm->root.header.major_formatVersion)) {
+    case ROOT_MAJOR_VERSION:
+      mime = dlm->root.header.mimetype;
+      break;
+    case SBLOCK_MAJOR_VERSION:
+      mime = ((SBlock*)&dlm->root)->mimetype;
+      break;
+    default:
+      mime = "unknown";
+      break;
+    }
+    if (0 == strcmp(mime,
+                   GNUNET_DIRECTORY_MIME)) {
+      displayDirectory(dlm->fileName,
+                      &dlm->root);
+    }
+    SEMAPHORE_DOWN(dlm->doneSem); /* wait for window closing */
+    break;
+  case DOWNLOAD_FAILED:
+    /* color failed dl red */ 
+    entry.dlm = dlm;
+    entry.color = &textColors[4];
+    entry.text = "FAIL";
+    gtkSaveCall((GtkFunction) setDownloadEntry, &entry);
+  
+    SEMAPHORE_DOWN(dlm->doneSem); /* wait for window closing */    
+    break;
+  default:
+    /* do nothing */
+    break;
+  }
+
+  /* finally, disentangle from the clist and free dlm resources */
+  gtkSaveCall((GtkFunction) disentangleFromCLIST, dlm);
+  SEMAPHORE_FREE(dlm->doneSem);
+  FREE(dlm->fileName);
+  FREE(dlm);
+}
+
+/**
+ * Open the download window and start the download of a file in the
+ * background. The method executes during a signal handler, so a GTK
+ * lock is not required to to GUI operations.
+ *
+ * @param filename the name of the file to download
+ *        (must copy, will be freed by caller)
+ * @param root information about what to download 
+ *        (will be freed by caller, must copy!)
+ **/
+void startDownload(gchar * filename,
+                  RootNode * root) {
+  GtkWidget * clist;
+  char * fileNameRoot;
+  int i;
+  gint row;
+  DownloadModel * dlm;
+  gchar * fileInfo[8];
+  char * fstring;
+
+  dlm = MALLOC(sizeof(DownloadModel));
+  memset(dlm, 
+        0, 
+        sizeof(DownloadModel));
+  dlm->fileName = STRDUP(filename);
+  memcpy(&dlm->root,
+        root,
+        sizeof(RootNode));
+
+  fileNameRoot = dlm->fileName;
+  for (i=strlen(dlm->fileName)-1;i>=0;i--) {
+    if (dlm->fileName[i] == DIR_SEPARATOR) {
+      fileNameRoot = &dlm->fileName[i+1];
+      break;
+    }
+  }
+  
+  /* create new download window? */
+  if (!dlWindow) {
+    GtkWidget * scrolled_window;
+    GtkWidget * button;
+    GtkWidget * box;
+    GtkWidget * entry;
+    GtkWidget * menu;
+    GtkItemFactory *popupFactory;
+    static gchar * descriptions[] = {
+      "Filename",
+      "%",              /* Completion percentage */
+      "Position",      /* Bytes dl'ed so far */
+      "Size",
+      "AReq",          /* Active block requests */
+      "CR/A",           /* Current Retries per Active requests */
+      "totR",           /* Total Retries */
+      "BPS",            /* Current bytes per second -estimate */
+    };
+    static int widths[] = {
+      290, 50, 70, 70, 40, 30, 50, 50
+    };
+
+    dlWindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+    gtk_window_set_title(GTK_WINDOW(dlWindow), 
+                        "gnunet-gtk: Downloads");
+    gtk_widget_set_usize(GTK_WIDGET(dlWindow),
+                         780, /* x-size */
+                         300); /* y-size */
+    gtk_signal_connect_object(GTK_OBJECT(dlWindow), "delete_event",
+                              GTK_SIGNAL_FUNC(hideWindow),
+                              GTK_OBJECT(dlWindow));
+    gtk_signal_connect_object(GTK_OBJECT(dlWindow), "destroy",
+                              GTK_SIGNAL_FUNC(hideWindow),
+                              GTK_OBJECT(dlWindow));
+
+
+    box = gtk_vbox_new(FALSE, 0);
+    gtk_container_add(GTK_CONTAINER(dlWindow), 
+                     box);
+    gtk_container_set_border_width(GTK_CONTAINER(dlWindow), 
+                                  8);
+   
+    /* scrolled window for the dl list */
+    scrolled_window = gtk_scrolled_window_new(NULL, NULL);
+    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
+                                   GTK_POLICY_AUTOMATIC,
+                                   GTK_POLICY_ALWAYS);
+    gtk_box_pack_start(GTK_BOX(box),
+                       scrolled_window,
+                       TRUE,
+                       TRUE,
+                       0);
+    gtk_widget_show(scrolled_window);
+    /* create a list to hold the downloads in */
+    clist = gtk_clist_new_with_titles(8, descriptions);
+    gtk_clist_set_selection_mode
+              (GTK_CLIST(clist),
+              GTK_SELECTION_EXTENDED);
+    for (i=0;i<8;i++) {
+      gtk_clist_set_column_width(GTK_CLIST(clist),
+                                 i,
+                                 widths[i]);
+      gtk_clist_column_title_active(GTK_CLIST(clist),
+                                 i);
+    }
+    gtk_signal_connect(GTK_OBJECT(clist),
+                       "click-column",
+                       GTK_SIGNAL_FUNC(sort_column_callback),
+                       NULL);
+    gtk_container_add(GTK_CONTAINER(scrolled_window),
+                     clist);
+    gtk_object_set_data(GTK_OBJECT(dlWindow),
+                       "LIST",
+                       clist);
+    gtk_widget_show(box);
+    gtk_widget_show(scrolled_window);
+    /* cancel/remove button */
+    button = gtk_button_new_with_label("Remove selected entries");
+    gtk_signal_connect (GTK_OBJECT(button),
+                      "clicked",
+                      GTK_SIGNAL_FUNC(abortSelectedDownloads),
+                      clist);
+    gtk_box_pack_start(GTK_BOX(box),
+                       button,
+                       FALSE,
+                       FALSE,
+                       0);
+    gtk_widget_show(button);
+
+    /* activate the pulldown menu option now */
+    entry = gtk_item_factory_get_widget(itemFactory,
+                                        "/File/Show downloads");
+    gtk_widget_set_sensitive(entry, TRUE);
+
+    /* add popup menu */
+    popupFactory = gtk_item_factory_new (GTK_TYPE_MENU, "<main>",
+                                         NULL);
+    gtk_item_factory_create_items(popupFactory, 
+                                  dlWindowMenuItems,
+                                 dlWindowMenu,
+                                 NULL);
+    menu = gtk_item_factory_get_widget (popupFactory, "<main>");
+    gtk_signal_connect(GTK_OBJECT(dlWindow),
+                     "event",
+                     GTK_SIGNAL_FUNC(popupCallback),
+                     menu);
+  } else {
+    /* use existing dlWindow and list */
+    clist = gtk_object_get_data(GTK_OBJECT(dlWindow),
+                               "LIST");
+  }
+
+  /* set default disp. values for new download */
+  fileInfo[0]=STRDUP(fileNameRoot);
+  fileInfo[1]="0%";
+  fileInfo[2]="-";
+  fileInfo[3]=MALLOC(32);
+  fileInfo[4]="-";
+  fileInfo[5]="-";
+  fileInfo[6]="-";
+  fileInfo[7]="-";
+  snprintf(fileInfo[3], 
+          32, 
+          "%u", 
+          (unsigned int) ntohl(dlm->root.header.fileIdentifier.file_length));
+
+  TIME(&dlm->lastDisplayTime);
+
+  gtk_clist_freeze(GTK_CLIST(clist));  
+  row = gtk_clist_append(GTK_CLIST(clist),
+                        fileInfo);
+  FREE(fileInfo[0]);
+  FREE(fileInfo[3]);
+  gtk_clist_set_foreground(GTK_CLIST(clist), 
+                          row,
+                          &textColors[11]);
+  gtk_clist_set_row_data(GTK_CLIST(clist),
+                        row,
+                        dlm);
+  gtk_clist_thaw(GTK_CLIST(clist));
+                        
+  /* remember clist for updates in the DLM struct */
+  dlm->dlList = clist;
+  dlm->downloadStatus = DOWNLOAD_PENDING;
+  dlm->doneSem = SEMAPHORE_NEW(0);
+  
+  /* display download window */
+  gtk_widget_show(clist);
+  gtk_widget_show(dlWindow);
+
+  /* append an info note */
+  fstring = fileIdentifierToString(&dlm->root.header.fileIdentifier);
+  infoMessage(NO, "gnunet-download -o \"%s\" %s\n",
+             dlm->fileName,
+             fstring);
+  FREE(fstring);
+
+  /* create thread that runs the download */
+  if (0 != PTHREAD_CREATE(&dlm->downloadThread,
+                         (PThreadMain) &downloadFile_,
+                         dlm,
+                         64 * 1024))
+    errexit("FATAL: could not create download thread (%s)!\n",
+           STRERROR(errno));
+  PTHREAD_DETACH(&dlm->downloadThread);  
+}
+
+/**
+ * Starts a file download when user has filled in the fields
+ **/ 
+void fetchURICallback(GtkWidget * widget,
+                        gpointer data) {
+  gchar * uri;
+  FileIdentifier * fid;
+  GtkWidget * entry;
+  RootNode root;
+
+  entry = gtk_object_get_data(GTK_OBJECT(data),
+                             "entry");
+
+  uri = gtk_entry_get_text(GTK_ENTRY(entry));
+  if(!uri)
+    return;
+
+  fid = stringToFileIdentifier(uri);
+  if(!fid) {
+    guiMessage("Invalid gnunet AFS URI");
+    gtk_widget_destroy(data);
+    return;
+  }
+
+  root.header.major_formatVersion = ROOT_MAJOR_VERSION;
+  root.header.minor_formatVersion = ROOT_MINOR_VERSION;
+  memcpy(&root.header.fileIdentifier,
+        fid,
+         sizeof(FileIdentifier));
+  strcpy(root.header.mimetype,
+        "unknown");
+  
+  /* FIXME: Future URLs should contain suggested filename 
+   * (or the code should ask one from user) */
+  sprintf(root.header.filename, "unknown.%ld", (unsigned long)time(NULL));
+
+  startDownload(root.header.filename,
+                &root);
+
+  gtk_widget_destroy(data);
+}
+
+void fetchURI(GtkWidget * widget,
+             gpointer data) {
+  GtkWidget * window;
+  GtkWidget * vbox;
+  GtkWidget * label;
+  GtkWidget * entry;
+  GtkWidget * hbox;
+  GtkWidget * button_ok;
+  GtkWidget * button_cancel;
+
+  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+  gtk_widget_set_usize(GTK_WIDGET(window),
+                      780,
+                      100);
+  gtk_window_set_title(GTK_WINDOW(window),
+                      "Download URI");
+  /* add container for window elements */
+  vbox = gtk_vbox_new(FALSE, 15);
+  gtk_container_add(GTK_CONTAINER(window),
+                    vbox);
+  gtk_widget_show(vbox);
+
+  /* when user clicks on close box, always "destroy" */
+  gtk_signal_connect(GTK_OBJECT(window),
+                     "destroy",
+                     GTK_SIGNAL_FUNC(destroyWidget),
+                     window);
+
+  gtk_container_set_border_width(GTK_CONTAINER(window),
+                                 10);
+  
+  hbox = gtk_hbox_new(FALSE, 0);
+  gtk_box_pack_start(GTK_BOX(vbox),
+                    hbox,
+                    TRUE,
+                    TRUE,
+                    0);
+  gtk_widget_show(hbox);
+ 
+  label = gtk_label_new("GNUnet AFS URI: ");
+  gtk_box_pack_start(GTK_BOX(hbox),
+                    label,
+                    FALSE,
+                    FALSE,
+                    0);
+  gtk_widget_show(label);
+  entry = gtk_entry_new();
+  gtk_box_pack_start(GTK_BOX(hbox),
+                    entry,
+                     TRUE,
+                    TRUE,      
+                    0);
+  gtk_signal_connect(GTK_OBJECT(entry),
+                     "activate",
+                     GTK_SIGNAL_FUNC(fetchURICallback),
+                     window);
+  gtk_object_set_data(GTK_OBJECT(window),
+                     "entry",
+                     entry);
+  gtk_widget_show(entry);
+
+  button_ok = gtk_button_new_with_label("Ok");
+  button_cancel = gtk_button_new_with_label("Cancel");
+
+  hbox = gtk_hbox_new(FALSE, 0);
+  gtk_box_pack_start(GTK_BOX(vbox),
+                     hbox,
+                     FALSE,
+                     FALSE,
+                     0);
+  gtk_box_pack_start(GTK_BOX(hbox),
+                    button_ok,
+                    TRUE,
+                    TRUE,
+                    0);
+  gtk_box_pack_start(GTK_BOX(hbox),
+                    button_cancel,
+                    TRUE,
+                    TRUE,
+                    0);
+  gtk_signal_connect(GTK_OBJECT(button_cancel),
+                    "clicked",
+                    GTK_SIGNAL_FUNC(destroyWidget),
+                    window);
+  gtk_signal_connect(GTK_OBJECT(button_ok),
+                    "clicked",
+                    GTK_SIGNAL_FUNC(fetchURICallback),
+                    window);
+  gtk_widget_show(hbox);
+  gtk_widget_show(button_ok);
+  gtk_widget_show(button_cancel);
+  gtk_widget_show(window);
+ 
+
+
+}
+                
+
+/* end of download.c */

Added: freeway/src/org/gnu/freeway/protocol/afs/swing/download.h
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/swing/download.h   2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/swing/download.h   2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,74 @@
+/*
+     This file is part of GNUnet
+
+     GNUnet is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with GNUnet; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file applications/afs/gtkui/download.h
+ * @author Christian Grothoff
+ **/
+
+#ifndef GTKUI_DOWNLOAD_H
+#define GTKUI_DOWNLOAD_H
+
+/**
+ * @brief state associated with a GTK download window
+ **/
+typedef struct {
+  RootNode root;
+  char * fileName;
+  PTHREAD_T downloadThread;
+  RequestManager * rm;
+  Semaphore * doneSem;
+  TIME_T downloadStartTime;
+  int successfulStart;
+  int downloadStatus;
+  TIME_T lastDisplayTime;
+  GtkWidget * dlList;
+} DownloadModel;
+
+typedef struct {
+  char pos[16], kbs[14], perc[14], areq[14], cra[14], tr[14];
+  DownloadModel *dlm;
+  ProgressStats *stats;
+} DLStats;
+
+typedef struct {
+  DownloadModel *dlm;
+  GdkColor *color;
+  gchar *text;
+} SetDownloadEntry;
+
+/**
+ * Open the download window and start the download of a file in the
+ * background. The method executes during a signal handler, so a GTK
+ * lock is not required to to GUI operations.
+ *
+ * @param filename the name of the file to download
+ * (must copy, will be freed by caller)
+ * @param dlm some information about what to download 
+ * (will be freed by caller, must copy!)
+ **/
+void startDownload(gchar * filename,
+                  RootNode * root);
+
+void fetchURI(GtkWidget * widget,
+             gpointer data);
+
+extern GtkWidget * dlWindow;
+               
+#endif

Added: freeway/src/org/gnu/freeway/protocol/afs/swing/helper.c
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/swing/helper.c     2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/swing/helper.c     2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,1009 @@
+/*
+     This file is part of GNUnet
+
+     GNUnet is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with GNUnet; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+/**
+ * @file src/applications/afs/gtkui/helper.c
+ * @brief This file contains some GUI helper functions
+ * @author Igor Wronsky
+ **/
+
+#include "gnunet_afs_esed2.h"
+#include "helper.h"
+#include <stdlib.h>
+#ifndef MINGW
+ #include <sys/wait.h>
+#endif
+#include "main.h"
+
+#define HELPER_DEBUG NO
+
+GtkWidget * infoWindow = NULL;
+static GtkWidget * infoText = NULL;
+
+/* are we waiting for gnunetd to start? */
+static int pollForLaunch = FALSE;
+
+/* PID of the main thread */
+static pthread_t mainThread;
+
+
+static SaveCall ** psc;
+static unsigned int pscCount;
+static Mutex sclock;
+
+/**
+ * Call a callback function from the mainloop/main thread ("SaveCall").
+ * Since GTK doesn't work with multi-threaded applications under Windows,
+ * all GTK operations have to be done in the main thread
+ **/
+void gtkSaveCall(GtkFunction func, void *args) {
+  SaveCall call;
+  int i;
+
+  call.args = args;
+  call.func = func;
+  MUTEX_LOCK(&sclock);
+  if ( (mainThread != 0) &&
+       (mainThread != pthread_self()) ) {
+    call.sem = SEMAPHORE_NEW(0);
+    GROW(psc,
+        pscCount,
+        pscCount+1);
+    psc[pscCount-1] = &call;
+    MUTEX_UNLOCK(&sclock);
+    gtk_idle_add(func, &call);
+    SEMAPHORE_DOWN(call.sem);
+    /* remove from psc list */
+    MUTEX_LOCK(&sclock);
+    for (i=0;i<pscCount;i++)
+      if (psc[i] == &call) {
+       psc[i] = psc[pscCount-1];
+       break;
+      }
+    if (i == pscCount)
+      errexit("ASSERTION failed at %s:%d\n",
+             __FILE__, __LINE__);
+    GROW(psc,
+        pscCount,
+        pscCount-1);
+    MUTEX_UNLOCK(&sclock);
+    SEMAPHORE_FREE(call.sem);
+  } else {
+    MUTEX_UNLOCK(&sclock);
+    call.sem = NULL;
+    func(&call);
+  }
+}
+
+/**
+ * Initialize "SaveCalls"
+ **/
+void gtkInitSaveCalls() {
+  MUTEX_CREATE_RECURSIVE(&sclock);
+  mainThread = pthread_self();
+}
+
+void gtkRunSomeSaveCalls() {
+  int i;
+
+  if (mainThread != pthread_self())
+    return;
+  MUTEX_LOCK(&sclock);
+  if (pscCount == 0) {
+    MUTEX_UNLOCK(&sclock);
+    return;
+  }
+  i = randomi(pscCount);
+  if (TRUE == g_idle_remove_by_data(psc[i]))
+    psc[i]->func(psc[i]);
+  MUTEX_UNLOCK(&sclock);
+  gnunet_util_sleep(50 * cronMILLIS);
+  /* sleep here is somewhat important, first of
+     all, after completion we need to give the
+     semaphore-mechanism time to remove the save-call
+     from the list to avoid running it twice; 
+     also, this function might be called in a tight
+     loop (see search.c), so we should give the
+     other threads some time to run.  */
+     
+}
+
+void gtkDoneSaveCalls() {
+  int i;
+  mainThread = 0;
+  MUTEX_LOCK(&sclock);
+  for (i=0;i<pscCount;i++) 
+    psc[i]->func(psc[i]);
+  i = pscCount;
+  MUTEX_UNLOCK(&sclock);  
+  /* wait until all PSC-jobs have left
+     the gtkSaveCall method before destroying
+     the mutex! */
+  while (i != 0) {
+    gnunet_util_sleep(50 * cronMILLIS);    
+    MUTEX_LOCK(&sclock);
+    i = pscCount;
+    MUTEX_UNLOCK(&sclock);
+  }
+  MUTEX_DESTROY(&sclock);
+}
+
+
+/**
+ * Called from a "SaveCall"-function to indicate that it is done
+ **/
+void gtkSaveCallDone(Semaphore *sem) {
+  if (sem)
+    SEMAPHORE_UP(sem);
+}
+
+/**
+ * Destroy a widget. Called from threads other than the main thread
+ **/
+gint doDestroyWidget(SaveCall *call) {
+  gtk_widget_destroy((GtkWidget *) call->args);
+
+  gtkSaveCallDone(call->sem);
+
+  return FALSE;
+}
+
+/**
+ * Callback for handling "delete_event": close the window 
+ **/
+gint deleteEvent(GtkWidget * widget,
+                GdkEvent * event,
+                gpointer data) {
+#if DEBUG_HELPER
+  LOG(LOG_DEBUG, 
+      "DEBUG: deleteEvent\n");
+#endif
+  return FALSE;
+}
+
+/**
+ * A callback to destroy any widget given as second argument
+ **/
+void destroyWidget(GtkWidget * dummy, 
+                  GtkWidget * widget) {
+#if DEBUG_HELPER
+  LOG(LOG_DEBUG, 
+      "DEBUG: destroyWidget %d\n", 
+      widget);
+#endif
+  gtk_widget_destroy(widget);
+}
+
+/**
+ * Callback function for guiMessage()
+ **/
+gint doGuiMessage(SaveCall *call) {
+  GtkWidget * window;
+  GtkWidget * label;
+  GtkWidget * box;
+  GtkWidget * button;
+
+  window = gtk_window_new(GTK_WINDOW_DIALOG);
+  gtk_container_set_border_width(GTK_CONTAINER(window), 10);
+  gtk_window_set_title(GTK_WINDOW(window), 
+                      "Note");
+  gtk_signal_connect(GTK_OBJECT(window), 
+                    "delete_event",
+                    GTK_SIGNAL_FUNC(deleteEvent), 
+                    NULL);
+
+  box = gtk_vbox_new(FALSE, 0);
+  gtk_container_add(GTK_CONTAINER(window), 
+                   box);
+
+  label = gtk_label_new((gchar *) call->args);
+  free((gchar *) call->args); /* allocated in g_strdup_vprintf */
+  gtk_box_pack_start(GTK_BOX(box),
+                    label,
+                    FALSE,
+                    FALSE,
+                    0);
+  
+  button = gtk_button_new_with_label("Ok");
+  gtk_signal_connect(GTK_OBJECT (button),
+                     "clicked",
+                     GTK_SIGNAL_FUNC(destroyWidget),
+                    window);
+  gtk_box_pack_start(GTK_BOX(box),button,FALSE,FALSE,0);
+  
+  gtk_window_set_position(GTK_WINDOW(window),
+                         GTK_WIN_POS_MOUSE);
+  gtk_widget_show_all(window);
+  gtk_widget_grab_focus(button);
+  
+  gtkSaveCallDone(call->sem);
+
+  return FALSE;
+}
+
+/** 
+ * Displays an informative message to the user in a fresh window 
+ **/
+void guiMessage(const char * format, ...) {
+  va_list args;
+  gchar *note;
+
+  va_start(args, format);
+  note = g_strdup_vprintf(format, args);
+  va_end(args);
+  
+  gtkSaveCall((GtkFunction) doGuiMessage, note);
+}
+
+/**
+ * Callback for infoMessage()
+ **/
+gint doInfoMessage(SaveCall *call) {
+  if(!infoWindow) {
+    GtkWidget * box1;
+    GtkWidget * button;
+    GtkWidget * scrolled_window;
+
+    infoWindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+    gtk_signal_connect(GTK_OBJECT(infoWindow),
+                       "delete_event",
+                       GTK_SIGNAL_FUNC(deleteEvent),
+                       NULL);
+
+    gtk_window_set_title(GTK_WINDOW(infoWindow),
+                         "Information");
+    gtk_widget_set_usize(GTK_WIDGET(infoWindow),
+                         780,
+                         300);
+
+    box1 = gtk_vbox_new(FALSE, 0);
+    gtk_container_add(GTK_CONTAINER (infoWindow),
+                      box1);
+    gtk_widget_show(box1);
+    
+    /* create a scrollable window */
+    scrolled_window = gtk_scrolled_window_new(NULL, NULL);
+    gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
+                                  GTK_POLICY_AUTOMATIC,
+                                  GTK_POLICY_ALWAYS);
+    gtk_box_pack_start(GTK_BOX(box1),
+                      scrolled_window,
+                      TRUE,
+                      TRUE,
+                      0);
+    gtk_widget_show(scrolled_window);
+
+    /* create a text widget */
+    infoText = gtk_text_new(NULL, NULL);
+    gtk_text_set_editable(GTK_TEXT (infoText),
+                          FALSE);
+    gtk_container_add(GTK_CONTAINER(scrolled_window),
+                     infoText);
+    gtk_widget_show(infoText);
+    gtk_widget_realize(infoText);
+  
+    /* finish with a close button */
+    button = gtk_button_new_with_label("Close");
+    gtk_box_pack_start(GTK_BOX (box1),
+                       button,
+                       FALSE,
+                       FALSE,
+                       0);
+    gtk_signal_connect_object(GTK_OBJECT(button),
+                              "clicked",
+                              GTK_SIGNAL_FUNC(hideWindow),
+                              GTK_OBJECT(infoWindow));
+    gtk_signal_connect_object(GTK_OBJECT(infoWindow), "delete_event",
+                              GTK_SIGNAL_FUNC(hideWindow),
+                              GTK_OBJECT(infoWindow));
+    gtk_signal_connect_object(GTK_OBJECT(infoWindow), "destroy",
+                              GTK_SIGNAL_FUNC(hideWindow),
+                              GTK_OBJECT(infoWindow));
+    gtk_widget_show(button);
+  }
+  if(((InfoMessage *) call->args)->doPopup==YES)
+    gtk_widget_show(infoWindow);
+
+  /* append the text */
+  gtk_text_freeze(GTK_TEXT(infoText));
+  gtk_text_insert(GTK_TEXT(infoText),
+                  NULL,
+                  &infoText->style->black,
+                  NULL,
+                  ((InfoMessage *) call->args)->note, -1);
+  gtk_text_thaw(GTK_TEXT(infoText));
+
+  gtkSaveCallDone(call->sem);
+
+  return FALSE;
+}
+
+/** 
+ * Appends a message to the info window
+ *
+ * @param doPopup do we open the window, YES or NO
+ *
+ **/
+void infoMessage(int doPopup, const char * format, ...) {
+  va_list args;
+  InfoMessage info;
+
+  va_start(args, format);
+  info.note = g_strdup_vprintf(format, args);
+  va_end(args);
+  info.doPopup = doPopup;
+  gtkSaveCall((GtkFunction) doInfoMessage, &info);
+  g_free(info.note);
+}
+
+/** 
+ * Appends a log entry to the info window
+ *
+ * @param txt the log entry
+ *
+ **/
+void addLogEntry(const char *txt) {
+  infoMessage(NO, txt);
+}
+
+GtkNotebook * notebook = NULL;
+
+gint doAddToNotebook(SaveCall *call) {
+  GtkWidget * label = gtk_label_new(((AddNotebook *) call->args)->labelName);
+  gtk_notebook_append_page(notebook, ((AddNotebook *) call->args)->frame, 
label);
+  gtk_widget_show(((AddNotebook *) call->args)->frame);
+
+  gtkSaveCallDone(call->sem);
+  
+  return FALSE;
+}
+
+void addToNotebook(char * labelName,
+                  GtkWidget * frame) {
+  AddNotebook note;
+  
+  note.labelName = labelName;
+  note.frame = frame;
+  /* add a new notebook for the search results */
+  gtkSaveCall((GtkFunction) doAddToNotebook, &note);
+}
+
+void hideWindow(GtkWidget * widget,
+               gpointer data) {
+  if(widget)
+    gtk_widget_hide(widget);
+}
+
+/**
+ * A dirty way to pump some stats from gnunetd
+ **/
+void showStats(GtkWidget * widget,
+              gpointer data) {
+  FILE *fp;
+  char * ptr;
+  char buffer[512];
+  char fn[512];
+  char path[260];
+  char * cfgFile;
+  GtkWidget * window;
+  GtkWidget * scrolled_window;
+  GtkWidget * clist;
+  GtkWidget * vbox;
+  GtkWidget * button;
+  gchar * results[2];
+  static gchar * descriptions[] = {
+    "Statistic",
+    "Value"
+  };
+  static int widths[] = {
+    600, 70
+  };
+  int i;
+
+  /* create window etc */
+  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+  gtk_window_set_title(GTK_WINDOW(window),
+                      "GNUnet: gnunetd statistics");
+  gtk_widget_set_usize(GTK_WIDGET(window),
+                      780,
+                      300);
+  vbox = gtk_vbox_new(FALSE, 1);
+  gtk_container_add(GTK_CONTAINER(window), vbox);
+
+  scrolled_window = gtk_scrolled_window_new(NULL,NULL);
+  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
+                                GTK_POLICY_AUTOMATIC,
+                                GTK_POLICY_ALWAYS);
+  gtk_box_pack_start(GTK_BOX(vbox),
+                    scrolled_window,
+                    TRUE,
+                    TRUE,
+                    0);
+
+  clist = gtk_clist_new_with_titles(2, descriptions);
+  for(i=0;i<2;i++)
+    gtk_clist_set_column_width(GTK_CLIST(clist),
+                              i,
+                              widths[i]);
+  gtk_clist_set_column_justification(GTK_CLIST(clist),
+                                    1,
+                                    GTK_JUSTIFY_RIGHT);
+  gtk_container_add(GTK_CONTAINER(scrolled_window),
+                   clist);
+
+  button = gtk_button_new_with_label("Close");
+  gtk_signal_connect(GTK_OBJECT(button),
+                    "clicked",
+                    GTK_SIGNAL_FUNC(destroyWidget),
+                    window);
+  gtk_box_pack_start(GTK_BOX(vbox),
+                    button,
+                    FALSE,
+                    FALSE,
+                    0);
+
+  gtk_clist_freeze(GTK_CLIST(clist));
+
+  /* Poll for statistics */
+  /* FIXME: done with a kludge until someone bothers to
+   * use gnunet-stats code */
+
+  sprintf(fn, "gnunet-gtk_stats.XXXXXX");
+  mkstemp(fn);
+
+  cfgFile = getConfigurationString("FILES",
+                                  "gnunet.conf");
+  if(cfgFile == NULL) {
+    LOG(LOG_WARNING,
+       "WARNING: cfgFile was NULL (shouldn't happen ?!)\n");
+    cfgFile = STRDUP(DEFAULT_CLIENT_CONFIG_FILE);
+  }
+
+#ifdef MINGW
+  conv_to_win_path("/bin", path);
+  strcat(path, "\\");
+#else
+  strcpy(path, "");
+#endif  
+  
+  sprintf(buffer, 
+          "%sgnunet-stats -c \"%s\" >%s",
+    path, 
+         cfgFile,
+         fn);
+  system(buffer);
+  FREE(cfgFile);
+  fp=FOPEN(fn, "r");
+  if(!fp) {
+    gtk_widget_destroy(window);
+    guiMessage("Error reading gnunet-stats output from %s\n",
+              fn);
+    LOG(LOG_ERROR, "Unable to open %s for reading\n", 
+       fn);
+    return;
+  }
+  while(!feof(fp)) {
+    fgets(buffer, 512, fp);
+    if(buffer[0]==0 || buffer[0]=='\n' || feof(fp))
+      continue;
+    ptr = buffer;
+    while(*ptr!=':') ptr++;
+    *ptr=0;
+    ptr++;
+    while(*ptr==' ') ptr++;
+    results[0]=buffer;
+    results[1]=ptr;
+    
+    /* remove CR/LF */
+    while(*ptr != '\n') ptr++;
+    *ptr = 0;
+    if(*(ptr-1) == '\r') *(ptr-1) = 0;
+    
+    gtk_clist_append(GTK_CLIST(clist),
+                    results);
+  }
+  fclose(fp);
+  UNLINK(fn);
+  
+  gtk_clist_thaw(GTK_CLIST(clist));
+  
+  gtk_widget_show_all(window);
+}
+
+/** 
+ * Checks if gnunetd is running
+ * 
+ * NOTE: Uses CS_PROTO_CLIENT_COUNT query to determine if 
+ * gnunetd is running
+ **/
+static int checkDaemonRunning(void) {
+  GNUNET_TCP_SOCKET * sock;
+  CS_HEADER csHdr;
+  int ret;
+
+  sock = getClientSocket();
+  if(sock == NULL) {
+    LOG(LOG_WARNING,
+       "WARNING: socket create failed, shouldn't happen.\n");
+    return SYSERR;  
+  }    
+
+  csHdr.size
+    = htons(sizeof(CS_HEADER));
+  csHdr.tcpType
+    = htons(CS_PROTO_CLIENT_COUNT);
+  if (SYSERR == writeToSocket(sock,
+                              &csHdr)) {
+    LOG(LOG_DEBUG, "DEBUG: gnunetd is NOT running\n");
+    releaseClientSocket(sock);
+    return SYSERR;
+  } 
+  if (SYSERR == readTCPResult(sock, 
+                             &ret)) {
+    LOG(LOG_DEBUG, "DEBUG: failed to read reply from gnunetd\n");
+    releaseClientSocket(sock);
+    return SYSERR;
+  }
+  releaseClientSocket(sock);
+  
+  return OK;
+}
+
+#if LINUX || OSX || SOLARIS || SOMEBSD
+static int launchWithExec() {
+  pid_t pid;
+
+  pid = fork();
+  if (pid == 0) {
+    char * args[4];
+    char * path;
+    char * cp;
+
+    cp = getConfigurationString("MAIN", "ARGV[0]");
+    if (cp != NULL) {
+      int i = strlen(cp);
+      while ( (i >= 0) && 
+             (cp[i] != DIR_SEPARATOR) )
+       i--;
+      cp[i+1] = '\0';
+      path = MALLOC(i+1+strlen("gnunetd"));
+      strcpy(path, cp);
+      strcat(path, "gnunetd");      
+      args[0] = path;
+      FREE(cp);
+    } else {
+      path = NULL;
+      args[0] = "gnunetd";
+    }    
+    args[1] = "-c";
+    args[2] = getConfigurationString("FILES",
+                                     "gnunet.conf");
+    args[3] = NULL;
+    errno = 0;
+    nice(10); /* return value is not well-defined */
+    if (errno != 0) {
+      LOG(LOG_WARNING,
+         "WARNING: could not nice gnunetd (%s)\n",
+         STRERROR(errno));
+    }
+    if (path != NULL)
+      execv(path,
+           args);
+    else
+      execvp("gnunetd",
+            args);
+    LOG(LOG_FAILURE,
+       "FAILURE: could not exec gnunetd: %s\n",
+       STRERROR(errno));
+    if (path != NULL)
+      LOG(LOG_FAILURE,
+         "FAILURE: determined path to be %s\n",
+         path);
+    FREENONNULL(path); /* yeah, right, like we're likely to get
+                         here... */
+    exit(-1);
+  } else {
+    pid_t ret;
+    int status;
+
+    ret = waitpid(pid, &status, 0);
+    if (ret == -1) {
+      LOG(LOG_FAILURE,
+         "FAILURE: waitpid failed: %s\n",
+         STRERROR(errno));
+      return SYSERR;
+    }
+    if ( (WIFEXITED(status) &&
+         (0 != WEXITSTATUS(status)) ) ) {
+      guiMessage("Starting gnunetd failed, error code: %d",
+                WEXITSTATUS(status));
+      return SYSERR;
+    }
+#ifdef WCOREDUMP
+    if (WCOREDUMP(status)) {
+      guiMessage("Starting gnunetd failed (core dumped)");
+      return SYSERR;
+    }
+#endif
+    if (WIFSIGNALED(status) ||
+       WTERMSIG(status) ) {
+      guiMessage("Starting gnunetd failed (aborted by signal)");
+      return SYSERR;
+    }
+    return OK;
+  }
+}
+#endif
+
+static int doLaunch() {
+  
+#if LINUX || OSX || SOLARIS || SOMEBSD
+  return launchWithExec();
+#elif MINGW
+  char szCall[_MAX_PATH + 1], szWd[_MAX_PATH + 1], szCWd[_MAX_PATH + 1];
+  char *args[1];
+
+  conv_to_win_path("/bin/gnunetd.exe", szCall);
+  conv_to_win_path("/bin", szWd);
+  _getcwd(szCWd, _MAX_PATH);
+
+  chdir(szWd);
+  args[0] = NULL;
+  spawnvp(_P_NOWAIT, szCall, (const char *const *) args);
+  chdir(szCWd);
+  
+  return OK;
+#else
+  /* any system out there that does not support THIS!? */
+  system("gnunetd"); /* we may not have nice,
+                       so let's be minimalistic here. */
+  return OK;
+#endif  
+}
+
+/** 
+ * Launch gnunetd, don't check if its running
+ **/
+static void launchDaemonNoCheck(GtkWidget * widget,
+                               gpointer data) {
+  /* sanity checks, not critical for ports */
+  char * host = getConfigurationString("NETWORK",
+                                      "HOST");
+  if (host != NULL) {
+    if (0 != strcmp(host,
+                   "localhost")) {
+      char * hostname;
+      hostname = MALLOC(1024);
+      if (0 != gethostname(hostname, 1024)) {
+       LOG(LOG_ERROR,
+           "ERROR: failed to get hostname (%s)\n",
+           STRERROR(errno));
+      } else {
+       /* we could go crazy here and try to open a socket locally
+          and then attempt to connect to it using the NS lookup result
+          for "host" -- and do it for IPv4 and IPv6 and possibly still
+          be wrong due some crazy firewall configuration.  Or we can
+          just do the simpelst thing (strcmp) and expect the user to
+          fix it if he cares to have the warning go away... */
+       if (0 != strcmp(host,
+                       hostname)) {
+         guiMessage("WARNING: gnunetd is configured to run on host %s and\n"
+                    "gnunet-gtk is running on host %s, which seems to be a 
different machine.\n"
+                    "gnunet-gtk can only start gnunetd on host %s.\n"
+                    "This may not be what you want (it may not work).\n"
+                    "I will proceed anyway, good luck.",
+                    host,
+                    hostname,
+                    hostname);
+       }         
+      }
+      FREE(hostname);
+    }
+    FREE(host);
+  }
+  /* end of sanity checks */
+  doLaunch();
+  pollForLaunch = TRUE;
+  gtk_widget_destroy(GTK_WIDGET(data));
+}
+
+/** 
+ * Launch gnunetd w/ checks
+ **/
+void launchDaemon(GtkWidget * widget,
+                 gpointer data) {
+  if (OK == checkDaemonRunning() ) {
+    guiMessage("gnunetd is already running");
+    return;
+  } else {                                     
+    doLaunch();
+    pollForLaunch = TRUE;
+  }
+}
+
+/** 
+ * Kill gnunetd
+ **/
+void killDaemon(GtkWidget * widget,
+               gpointer data) {
+  if (OK == checkDaemonRunning() ) {
+    GNUNET_TCP_SOCKET * sock;
+    CS_HEADER csHdr;
+    int ret;
+
+    sock = getClientSocket();
+    if (sock == NULL) {
+      /* well, probably already dead */
+      return;
+    }
+    csHdr.size 
+      = htons(sizeof(CS_HEADER));
+    csHdr.tcpType
+      = htons(CS_PROTO_SHUTDOWN_REQUEST);
+    if (SYSERR == writeToSocket(sock,
+                               &csHdr)) {
+      guiMessage("Error sending shutdown request to gnunetd");
+      releaseClientSocket(sock);
+      return;
+    }
+    if (SYSERR == readTCPResult(sock,
+                               &ret)) {
+      guiMessage("Error reading shutdown reply from gnunetd");
+      releaseClientSocket(sock);
+      return;
+    }
+    if (ret == OK)
+      guiMessage("gnunetd agreed to shut down.");
+    else
+      guiMessage("gnunetd refuses to shut down (reply=%d).", 
+                 ret);
+    releaseClientSocket(sock);
+  } else {
+    guiMessage("gnunetd is not running...");
+  }
+}
+
+/**
+ * Ask if the user wishes to start gnunetd
+ **/
+static void initDaemonStartDialog(void) {
+   GtkWidget *dialog;
+   GtkWidget *label;
+   GtkWidget *okay_button;
+   GtkWidget *no_button;
+
+   dialog = gtk_dialog_new();
+   label = gtk_label_new("gnunetd (daemon) doesn't seem to be running.\nWould 
you like to start it?\n");
+   gtk_container_add (GTK_CONTAINER(GTK_DIALOG(dialog)->vbox),
+                      label);
+
+   okay_button = gtk_button_new_with_label("Yes!");
+   no_button = gtk_button_new_with_label("Naah");
+   
+   gtk_signal_connect(GTK_OBJECT(okay_button), 
+                      "clicked",
+                      GTK_SIGNAL_FUNC(launchDaemonNoCheck), 
+                     dialog);
+   gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->action_area),
+                      okay_button);
+   gtk_signal_connect(GTK_OBJECT(no_button), 
+                     "clicked",
+                      GTK_SIGNAL_FUNC(destroyWidget), 
+                      dialog);
+   gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->action_area),
+                      no_button);
+
+   gtk_widget_show_all(dialog);
+}
+
+/**
+ * Checks if gnunetd is running and if not, prompts user 
+ * to run gnunetd. Always returns OK.
+ **/
+int checkForDaemon(void) {  
+  if (SYSERR == checkDaemonRunning()) {
+    char * host;
+
+    host = getConfigurationString("NETWORK",
+                                 "HOST");
+    if (host != NULL && strcmp(host,"localhost")==0 )
+      initDaemonStartDialog();
+    else
+      guiMessage("gnunetd doesn't seem to be running.\n"
+                 "Unfortunately, gnunet-gtk can't identify config entry"
+                "\n\nNETWORK/HOST '%s'\n\n"
+                "as a local machine, so gnunetd can not be\n"
+                "launched by gnunet-gtk.",
+                (host == NULL ? "" : host) );
+  }
+
+  return OK;
+}
+
+static gint doUpdateMenus(SaveCall * call) {
+  static GtkWidget * killEntry = NULL;
+  static GtkWidget * launchEntry = NULL;
+  static GtkWidget * statsEntry = NULL;
+  static int once = 1;
+  static int isLocal;
+  char * host;
+  int ret;
+
+  ret = * (int*) call->args;
+  if (once) {
+    once = 0;
+    killEntry = gtk_item_factory_get_widget(itemFactory,
+                                           "/Advanced/"
+                                           "Kill gnunetd");
+    launchEntry = gtk_item_factory_get_widget(itemFactory,
+                                             "/Advanced/"
+                                             "Launch gnunetd");
+    statsEntry = gtk_item_factory_get_widget(itemFactory,
+                                            "/File/"
+                                            "Show gnunetd stats");    
+    host = getConfigurationString("NETWORK",
+                                 "HOST");
+    if ( (host == NULL) ||
+        (strcmp(host, "localhost")==0) )
+      isLocal = TRUE;
+    else
+      isLocal = FALSE;
+    FREENONNULL(host);
+  }
+  if (ret == SYSERR) {
+    gtk_widget_set_sensitive(statsEntry, FALSE);
+    gtk_widget_set_sensitive(killEntry, FALSE);
+    gtk_widget_set_sensitive(launchEntry, (TRUE & isLocal) );
+  } else {
+    gtk_widget_set_sensitive(statsEntry, TRUE);
+    gtk_widget_set_sensitive(killEntry, TRUE);
+    gtk_widget_set_sensitive(launchEntry, FALSE);
+    
+    if (pollForLaunch == TRUE) {
+      pollForLaunch = FALSE;
+      guiMessage("gnunetd is now up and running");
+    }
+  }    
+  gtkSaveCallDone(call->sem);
+  return FALSE;
+}
+
+void cronCheckDaemon(void * dummy) {
+  static int last = 42;
+  int ret;
+  
+  ret = checkDaemonRunning();
+  if (ret != last) {
+    last = ret;    
+    gtkSaveCall((GtkFunction) doUpdateMenus, &ret);
+  }      
+}
+
+
+/**
+ * A function for numeric comparisons of strings
+ **/
+gint numericComp(GtkCList *clist,
+                 gconstpointer ptr1,
+                 gconstpointer ptr2) {
+  double value1;
+  double value2;
+  GtkCListRow * row1 = (GtkCListRow *) ptr1;
+  GtkCListRow * row2 = (GtkCListRow *) ptr2;
+
+  value1 = atof(GTK_CELL_TEXT(row1->cell[clist->sort_column])->text);
+  value2 = atof(GTK_CELL_TEXT(row2->cell[clist->sort_column])->text);
+
+  if(value1>value2)
+    return(-1);
+  else if(value1==value2)
+    return(0);
+  else
+    return(1);
+}
+
+/**
+ * A function for case-insensitive text comparisons
+ **/
+gint alphaComp(GtkCList *clist,
+               gconstpointer ptr1,
+               gconstpointer ptr2) {
+  char * text1;
+  char * text2;
+  GtkCListRow * row1 = (GtkCListRow *) ptr1;
+  GtkCListRow * row2 = (GtkCListRow *) ptr2;
+
+  text1 = GTK_CELL_TEXT(row1->cell[clist->sort_column])->text;
+  text2 = GTK_CELL_TEXT(row2->cell[clist->sort_column])->text;
+
+  return (strcasecmp(text1,text2));
+}
+
+/**
+ * A function for percentage comparisons 
+ **/
+gint percentComp(GtkCList *clist,
+                 gconstpointer ptr1,
+                 gconstpointer ptr2) {
+  char * tmp1;
+  char * tmp2;
+  double value1;
+  double value2;
+  GtkCListRow * row1 = (GtkCListRow *) ptr1;
+  GtkCListRow * row2 = (GtkCListRow *) ptr2;
+
+  tmp1 = GTK_CELL_TEXT(row1->cell[clist->sort_column])->text;
+  tmp2 = GTK_CELL_TEXT(row2->cell[clist->sort_column])->text;
+
+  /* Hack for DONE strings :) */
+  if(strstr(tmp1,"%") == 0) {
+    if(strstr(tmp2,"%") == 0)
+      return 0;        /* Both "DONE" */
+    else
+      return -1; /* A done, B not */
+  }
+  if(strstr(tmp2,"%")==0) 
+    return 1; /* B done, A not */
+
+  /* Both have %, must remove */
+  tmp1 = STRDUP(GTK_CELL_TEXT(row1->cell[clist->sort_column])->text);
+  tmp2 = STRDUP(GTK_CELL_TEXT(row2->cell[clist->sort_column])->text);
+ 
+  tmp1[strlen(tmp1)-1]=0;
+  tmp2[strlen(tmp2)-1]=0;
+  
+  value1 = atof(tmp1);
+  value2 = atof(tmp2);
+
+  FREE(tmp1);
+  FREE(tmp2);
+
+  if(value1>value2)
+    return(-1);
+  else if(value1==value2)
+    return(0);
+  else
+    return(1);
+}
+
+/**
+ * A general right-button popup menu callback
+ **/
+gboolean popupCallback(GtkWidget *widget,
+                       GdkEvent *event,
+                       GtkWidget *menu )
+{
+   GdkEventButton *bevent = (GdkEventButton *)event;
+
+   /* Only take button presses */
+   if (event->type != GDK_BUTTON_PRESS)
+     return FALSE;
+
+   if (bevent->button != 3)
+     return FALSE;
+
+   /* Show the menu */
+   gtk_widget_show(menu);
+   gtk_menu_popup (GTK_MENU(menu), NULL, NULL,
+                   NULL, NULL, bevent->button, bevent->time);
+
+   return TRUE;
+}
+
+/* end of helper.c */
+

Added: freeway/src/org/gnu/freeway/protocol/afs/swing/helper.h
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/swing/helper.h     2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/swing/helper.h     2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,157 @@
+/*
+     This file is part of GNUnet
+
+     GNUnet is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with GNUnet; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file applications/afs/gtkui/helper.h
+ * @author Igor Wronsky 
+ **/
+
+#ifndef GTKUI_HELPER_H
+#define GTKUI_HELPER_H
+
+
+#include "platform.h"
+
+/* for GTK 2 */
+#define GTK_ENABLE_BROKEN
+
+#include <gtk/gtk.h>
+#include <gtk/gtktext.h>
+
+typedef struct {
+  Semaphore *sem;
+  void *args;
+  GtkFunction func;
+} SaveCall;
+
+typedef struct {
+  int doPopup;
+  gchar *note;
+} InfoMessage;      
+
+typedef struct {
+  char *labelName;
+  GtkWidget *frame;
+} AddNotebook;
+
+
+/* callback: window close: close the window */
+gint deleteEvent(GtkWidget * widget,
+                GdkEvent * event,
+                gpointer data);
+
+/**
+ * A callback to destroy any widget given as second argument
+ *
+ */
+void destroyWidget(GtkWidget * dummy, GtkWidget * widget);
+
+/**
+ * Displays an informative message to the user
+ */
+void guiMessage(const char * format, ...);
+
+/**
+ * Appends a message to the info window 
+ */
+void infoMessage(int doPopup, const char * format, ...);
+
+/** 
+ * Appends a log entry to the info window
+ *
+ * @param txt the log entry
+ *
+ **/
+void addLogEntry(const char *txt);
+
+void addToNotebook(char * labelName,
+                  GtkWidget * frame);
+
+void hideWindow(GtkWidget * widget,
+               gpointer data);
+
+void showStats(GtkWidget * widget,
+              gpointer data);
+
+int checkForDaemon(void);
+
+void cronCheckDaemon(void * dummy);
+void launchDaemon(GtkWidget * widget,
+                 gpointer data);
+void killDaemon(GtkWidget * widget,
+               gpointer data);
+
+/**
+ * A function for numeric comparisons of strings
+ **/
+gint numericComp(GtkCList *clist,
+                 gconstpointer ptr1,
+                 gconstpointer ptr2);
+
+/**
+ * A function for case-insensitive text comparisons
+ **/
+gint alphaComp(GtkCList *clist,
+               gconstpointer ptr1,
+               gconstpointer ptr2);
+
+/**
+ * A function for comparisons of percentages
+ **/
+gint percentComp(GtkCList *clist,
+                 gconstpointer ptr1,
+                 gconstpointer ptr2);
+
+/**
+ * A general right-button popup menu callback
+ **/
+gboolean popupCallback(GtkWidget *widget,
+                       GdkEvent *event,
+                                  GtkWidget *menu );
+                      
+/**
+ * Call a callback function from the mainloop/main thread ("SaveCall").
+ * Since GTK doesn't work with multi-threaded applications under Windows,
+ * all GTK operations have to be done in the main thread
+ **/
+void gtkSaveCall(GtkFunction func, void *args);
+
+/**
+ * Initialize "SaveCalls"
+ **/
+void gtkInitSaveCalls();
+
+void gtkDoneSaveCalls();
+
+void gtkRunSomeSaveCalls();
+ 
+/**
+ * Called from a "SaveCall"-function to indicate that it is done
+ **/
+void gtkSaveCallDone(Semaphore *sem);
+
+/**
+ * Destroy a widget. Called from threads other than the main thread
+ **/
+gint doDestroyWidget(SaveCall *call);
+
+extern GtkNotebook * notebook;
+extern GtkWidget * infoWindow;
+
+#endif

Added: freeway/src/org/gnu/freeway/protocol/afs/swing/insert.c
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/swing/insert.c     2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/swing/insert.c     2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,1297 @@
+/*
+     This file is part of GNUnet
+
+     GNUnet is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with GNUnet; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file src/applications/afs/gtkui/insert.c
+ * @brief handles file insertions in the GTK GUI
+ * @author Igor Wronsky
+ * @author Christian Grothoff (refactoring, added bugs)
+ **/
+
+#include "gnunet_afs_esed2.h"
+#include "helper.h"
+#include "insertprogress.h"
+#include "insert.h"
+#include "main.h"
+
+/**
+ * @brief state of the edit RootNode window
+ **/
+typedef struct {
+  char * fileName;
+  GtkWidget * editAttributesWindow;
+  GtkWidget * fileNameLine;
+  GtkWidget * descriptionLine;
+  GtkWidget * mimeLine;
+  GtkWidget * indexButton;
+  GtkWidget * checkCopy;
+  GtkWidget * keywordLine;
+  GtkWidget * keywordList;
+} EditWindowModel;
+
+/**
+ * @brief state of the edit RootNode window
+ **/
+typedef struct {
+  char * fileName;
+  GtkWidget * editAttributesWindow;
+  GtkWidget * fileNameLine;
+  GtkWidget * descriptionLine;
+  GtkWidget * indexButton;
+  GtkWidget * checkCopy;
+  GtkWidget * keywordLine;
+  GtkWidget * keywordList;
+  GtkWidget * gkeywordLine;
+  GtkWidget * gkeywordList;
+} EditDirectoryWindowModel;
+
+
+/**
+ * Collects the results of editAttributes, creates an insertion 
+ * progressbar and launches the insertion thread.
+ *
+ * @param dummy not used
+ * @param ewm the state of the edit window
+ **/
+static void startInsert(GtkWidget * dummy, 
+                       EditWindowModel * ewm) {
+  InsertModel * ilm;
+  gchar * txt;
+  int i;
+  PTHREAD_T insertThread;
+
+  ilm = MALLOC(sizeof(InsertModel));
+  ilm->fileName = STRDUP(ewm->fileName);
+
+  /* get content indexing style */
+  if (gtk_toggle_button_get_active((GtkToggleButton *)ewm->indexButton) == 
TRUE)
+    ilm->indexContent = YES;
+  else
+    ilm->indexContent = NO;
+  ilm->copyFile = gtk_toggle_button_get_active(
+                    (GtkToggleButton *)ewm->checkCopy);
+
+  /* get the published filename */
+  txt = gtk_entry_get_text(GTK_ENTRY(ewm->fileNameLine));
+  if (txt == NULL)
+    ilm->fileNameRoot = STRDUP("none specified");
+  else
+    ilm->fileNameRoot = STRDUP(txt);
+  
+  /* get the new description, if any */
+  txt = gtk_entry_get_text(GTK_ENTRY(ewm->descriptionLine));
+  if (txt == NULL)
+    ilm->description = STRDUP("no description specified");
+  else
+    ilm->description = STRDUP(txt);
+
+  txt = gtk_entry_get_text(GTK_ENTRY(ewm->mimeLine));
+  if (txt == NULL)
+    ilm->mimetype = STRDUP("unknown");
+  else
+    ilm->mimetype = STRDUP(txt);
+
+  /* get new list of keywords */
+  ilm->num_keywords = GTK_CLIST(ewm->keywordList)->rows;
+
+  if (ilm->num_keywords > 0) {
+    ilm->keywords = (char**) MALLOC(ilm->num_keywords * sizeof(char*));
+    for(i=0;i<ilm->num_keywords;i++) {     
+      gtk_clist_get_text(GTK_CLIST(ewm->keywordList),
+                        i,
+                        0,
+                        &txt);
+      ilm->keywords[i] = STRDUP(txt);
+    } 
+  } else
+    ilm->keywords = NULL;
+
+  if(ilm->indexContent == YES)
+    strcpy(ilm->opDescription, "indexed");
+  else
+    strcpy(ilm->opDescription, "inserted");
+  createInsertProgressBar(ilm);
+  /* start the insert thread */
+  if (0 != PTHREAD_CREATE(&insertThread,
+                         (PThreadMain) insertFileGtkThread,
+                         ilm,
+                         16 * 1024))
+    errexit("FATAL: could not create insert thread (%s)!\n",
+           STRERROR(errno));
+  PTHREAD_DETACH(&insertThread);
+
+  /* destroy the "editAttributes" window */
+  gtk_widget_destroy(ewm->editAttributesWindow);
+}
+
+/**
+ * Collects the results of editAttributes, creates an insertion 
+ * progressbar and launches the insertion thread.
+ *
+ * @param dummy not used
+ * @param ewm the state of the edit window
+ **/
+static void startInsertDirectory(GtkWidget * dummy, 
+                                EditDirectoryWindowModel * ewm) {
+  InsertDirectoryModel * ilm;
+  gchar * txt;
+  int i;
+  PTHREAD_T insertThread;
+
+  ilm = MALLOC(sizeof(InsertDirectoryModel));
+  ilm->fileName = STRDUP(ewm->fileName);
+
+  /* get content indexing style */
+  if (gtk_toggle_button_get_active((GtkToggleButton *)ewm->indexButton) == 
TRUE)
+    ilm->indexContent = YES;
+  else
+    ilm->indexContent = NO;
+  ilm->copyFile = gtk_toggle_button_get_active(
+                    (GtkToggleButton *)ewm->checkCopy);
+  /* get the published filename */
+  txt = gtk_entry_get_text(GTK_ENTRY(ewm->fileNameLine));
+  if (txt == NULL)
+    ilm->fileNameRoot = STRDUP("none specified");
+  else
+    ilm->fileNameRoot = STRDUP(txt);
+  
+  /* get the new description, if any */
+  txt = gtk_entry_get_text(GTK_ENTRY(ewm->descriptionLine));
+  if (txt == NULL)
+    ilm->description = STRDUP("no description specified");
+  else
+    ilm->description = STRDUP(txt);
+
+  ilm->mimetype = STRDUP(GNUNET_DIRECTORY_MIME);
+
+  /* get new list of keywords */
+  ilm->num_keywords = GTK_CLIST(ewm->keywordList)->rows;
+
+  if (ilm->num_keywords > 0) {
+    ilm->keywords = (char**) MALLOC(ilm->num_keywords * sizeof(char*));
+    for(i=0;i<ilm->num_keywords;i++) {     
+      gtk_clist_get_text(GTK_CLIST(ewm->keywordList),
+                        i,
+                        0,
+                        &txt);
+      ilm->keywords[i] = STRDUP(txt);
+    } 
+  } else
+    ilm->keywords = NULL;
+
+  /* get new list of keywords */
+  ilm->num_gkeywords = GTK_CLIST(ewm->gkeywordList)->rows;
+
+  if (ilm->num_gkeywords > 0) {
+    ilm->gkeywords = (char**) MALLOC(ilm->num_gkeywords * sizeof(char*));
+    for(i=0;i<ilm->num_gkeywords;i++) {     
+      gtk_clist_get_text(GTK_CLIST(ewm->gkeywordList),
+                        i,
+                        0,
+                        &txt);
+      ilm->gkeywords[i] = STRDUP(txt);
+    } 
+  } else
+    ilm->gkeywords = NULL;
+
+  if(ilm->indexContent == YES)
+    strcpy(ilm->opDescription, "indexed");
+  else
+    strcpy(ilm->opDescription, "inserted");
+  /*
+    FIXME: allow setting this as an option in the dialog!
+  FREENONNULL(setConfigurationString("GNUNET-INSERT",
+                                    "EXTRACT-KEYWORDS",
+                                    "NO"));
+  */
+  createInsertDirectoryProgressBar(ilm);
+  /* start the insert thread */
+  if (0 != PTHREAD_CREATE(&insertThread,
+                         (PThreadMain) insertDirectoryGtkThread,
+                         ilm,
+                         16 * 1024))
+    errexit("FATAL: could not create insert thread (%s)!\n",
+           STRERROR(errno));
+  PTHREAD_DETACH(&insertThread);
+  /* destroy the "editAttributes" window */
+  gtk_widget_destroy(ewm->editAttributesWindow);
+}
+
+/**
+ * The keyword add button was clicked. Add whatever 
+ * is in the keyword box to the list of keywords.
+ *
+ * @param w not used
+ * @param ewm the state of the edit window
+ **/
+static void button_add_clicked(GtkWidget * w, 
+                              EditWindowModel * ewm) {
+  gchar * key;
+  gchar * newKeyword;
+  int i;
+
+  key = gtk_entry_get_text(GTK_ENTRY(ewm->keywordLine));
+  if (key == NULL) {
+    /* message to enter a string? */
+    return;
+  }    
+
+  newKeyword = STRDUP(key);
+  key = newKeyword;
+
+  /* remove trailing & heading spaces */
+  i = strlen(key)-1;
+  while ( (newKeyword[i] == ' ') && 
+         (i >= 0) ) {
+    newKeyword[i--] = '\0';
+  }
+  while (*newKeyword == ' ')
+    newKeyword++;
+
+  if ( *newKeyword == '\0' ) {
+    /* message to enter more than spaces? */    
+  } else {
+    gtk_clist_append(GTK_CLIST(ewm->keywordList),
+                    &newKeyword);
+  } 
+  FREE(key);
+  gtk_entry_set_text(GTK_ENTRY(ewm->keywordLine),
+                    "");
+}
+
+/**
+ * The keyword delete button was clicked. Delete the 
+ * currently selected keyword.
+ *
+ * @param w not used
+ * @param ewm state of the edit window
+ **/
+static void button_del_clicked(GtkWidget * w, 
+                              EditWindowModel * ewm) {
+  GList * tmp;
+
+  tmp = GTK_CLIST(ewm->keywordList)->selection;
+  if (NULL == tmp) {
+    /* message that keyword must be selected to delete one? */
+    return;
+  }  
+  gtk_clist_remove(GTK_CLIST(ewm->keywordList),
+                  (int)tmp->data);
+}
+
+/**
+ * The keyword add button was clicked. Add whatever 
+ * is in the keyword box to the list of keywords.
+ *
+ * @param w not used
+ * @param ewm the state of the edit window
+ **/
+static void button_dir_add_clicked1(GtkWidget * w, 
+                                   EditDirectoryWindowModel * ewm) {
+  gchar * key;
+  gchar * newKeyword;
+  int i;
+
+  key = gtk_entry_get_text(GTK_ENTRY(ewm->keywordLine));
+  if (key == NULL) {
+    /* message to enter a string? */
+    return;
+  }    
+
+  newKeyword = STRDUP(key);
+  key = newKeyword;
+
+  /* remove trailing & heading spaces */
+  i = strlen(key)-1;
+  while ( (newKeyword[i] == ' ') && 
+         (i >= 0) ) {
+    newKeyword[i--] = '\0';
+  }
+  while (*newKeyword == ' ')
+    newKeyword++;
+
+  if ( *newKeyword == '\0' ) {
+    /* message to enter more than spaces? */    
+  } else {
+    gtk_clist_append(GTK_CLIST(ewm->keywordList),
+                    &newKeyword);
+  } 
+  FREE(key);
+  gtk_entry_set_text(GTK_ENTRY(ewm->keywordLine),
+                    "");
+}
+
+/**
+ * The keyword delete button was clicked. Delete the 
+ * currently selected keyword.
+ *
+ * @param w not used
+ * @param ewm state of the edit window
+ **/
+static void button_dir_del_clicked1(GtkWidget * w, 
+                                   EditDirectoryWindowModel * ewm) {
+  GList * tmp;
+
+  tmp = GTK_CLIST(ewm->keywordList)->selection;
+  if (NULL == tmp) {
+    /* message that keyword must be selected to delete one? */
+    return;
+  }  
+  gtk_clist_remove(GTK_CLIST(ewm->keywordList),
+                  (int)tmp->data);
+}
+
+/**
+ * The keyword add button was clicked. Add whatever 
+ * is in the keyword box to the list of keywords.
+ *
+ * @param w not used
+ * @param ewm the state of the edit window
+ **/
+static void button_dir_add_clicked2(GtkWidget * w, 
+                                   EditDirectoryWindowModel * ewm) {
+  gchar * key;
+  gchar * newKeyword;
+  int i;
+
+  key = gtk_entry_get_text(GTK_ENTRY(ewm->gkeywordLine));
+  if (key == NULL) {
+    /* message to enter a string? */
+    return;
+  }    
+
+  newKeyword = STRDUP(key);
+  key = newKeyword;
+
+  /* remove trailing & heading spaces */
+  i = strlen(key)-1;
+  while ( (newKeyword[i] == ' ') && 
+         (i >= 0) ) {
+    newKeyword[i--] = '\0';
+  }
+  while (*newKeyword == ' ')
+    newKeyword++;
+
+  if ( *newKeyword == '\0' ) {
+    /* message to enter more than spaces? */    
+  } else {
+    gtk_clist_append(GTK_CLIST(ewm->gkeywordList),
+                    &newKeyword);
+  } 
+  FREE(key);
+  gtk_entry_set_text(GTK_ENTRY(ewm->gkeywordLine),
+                    "");
+}
+
+/**
+ * The keyword delete button was clicked. Delete the 
+ * currently selected keyword.
+ *
+ * @param w not used
+ * @param ewm state of the edit window
+ **/
+static void button_dir_del_clicked2(GtkWidget * w, 
+                                   EditDirectoryWindowModel * ewm) {
+  GList * tmp;
+
+  tmp = GTK_CLIST(ewm->gkeywordList)->selection;
+  if (NULL == tmp) {
+    /* message that keyword must be selected to delete one? */
+    return;
+  }  
+  gtk_clist_remove(GTK_CLIST(ewm->gkeywordList),
+                  (int)tmp->data);
+}
+
+/**
+ * The index/insert file button was clicked.
+ *
+ * @param w the button
+ * @param ewm state of the edit window
+ **/
+static void button_index_clicked(GtkWidget * w,
+                                 EditWindowModel * ewm) {
+  gtk_widget_set_sensitive(ewm->checkCopy, w == ewm->indexButton);
+}
+
+/**
+ * Exit the application (called when the main window
+ * is closed or the user selects File-Quit).
+ **/
+static void destroyEditWindow(GtkWidget * widget,
+                             EditWindowModel * ewm) {
+  FREE(ewm->fileName);
+  FREE(ewm);
+}
+
+/**
+ * Exit the application (called when the main window
+ * is closed or the user selects File-Quit).
+ **/
+static void destroyEditDirectoryWindow(GtkWidget * widget,
+                                      EditDirectoryWindowModel * ewm) {
+  FREE(ewm->fileName);
+  FREE(ewm);
+}
+
+/**
+ * Show user a window to edit information related to this file.
+ * After user is done, call startInsert.
+ *
+ * @param filename the name of the file that is inserted
+ * @param fileNameRoot the short name of the file
+ * @param description the description of the file
+ * @param mimetype the mimetype of the ile
+ * @param keywords the extracted keywords of the file
+ * @param num_keywords the number of keywords extracted
+ **/
+static void editAttributes(char * filename,
+                          char * fileNameRoot,
+                          char * description,
+                          char * mimetype,
+                          char ** keywords,
+                          int num_keywords) {
+  EditWindowModel * ewm;
+  GtkWidget * window;
+  GtkWidget * vbox, * hbox;
+  GtkWidget * clist;
+  GtkWidget * scrolled_window;
+  GtkWidget * label;
+  GtkWidget * separator; 
+  GtkWidget * button_add;
+  GtkWidget * button_delete;
+  GtkWidget * button_ok;
+  GtkWidget * button_cancel;
+  GtkWidget * keyword_line;
+  GSList * group;
+  GtkWidget * indexbutton1;
+  GtkWidget * indexbutton2;
+  GtkWidget * check_copy;
+  int doIndex;
+  gchar * titles[1] = { "Keyword(s) used" };
+  int i;
+
+  ewm = MALLOC(sizeof(EditWindowModel));
+  ewm->fileName = STRDUP(filename);
+  /* create new window for editing */
+  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+  ewm->editAttributesWindow = window;
+  gtk_widget_set_usize(GTK_WIDGET(window),
+                      400,
+                      480);
+  gtk_window_set_title(GTK_WINDOW(window), 
+                      "Edit attributes");
+
+  /* add container for window elements */
+  vbox = gtk_vbox_new(FALSE, 0);
+  gtk_container_add(GTK_CONTAINER(window),
+                   vbox);
+  gtk_widget_show(vbox);
+
+  /* when user clicks on close box, always "destroy" */
+  gtk_signal_connect(GTK_OBJECT(window),
+                    "delete_event",
+                    GTK_SIGNAL_FUNC(deleteEvent),
+                    ewm);
+  /* whenever edit window gets destroyed, 
+     free *ALL* ewm data */
+  gtk_signal_connect(GTK_OBJECT(window),
+                    "destroy",
+                    GTK_SIGNAL_FUNC(destroyEditWindow),
+                    ewm);
+
+  gtk_container_set_border_width(GTK_CONTAINER(window), 
+                                10);
+
+  /* Create a line to change the published filename */
+  label = gtk_label_new("Published filename:");
+  gtk_box_pack_start(GTK_BOX(vbox),
+                    label, 
+                    FALSE, 
+                    FALSE, 
+                    0);
+  gtk_widget_show(label); 
+  ewm->fileNameLine = gtk_entry_new();
+  gtk_box_pack_start(GTK_BOX(vbox),
+                    ewm->fileNameLine,
+                    TRUE,
+                    TRUE,
+                    0);
+  gtk_entry_set_text(GTK_ENTRY(ewm->fileNameLine), 
+                    fileNameRoot);
+  gtk_widget_show(ewm->fileNameLine);
+  
+  /* Create a line to change the mime type */
+  label = gtk_label_new("Mimetype:");
+  gtk_box_pack_start(GTK_BOX(vbox),
+                    label, 
+                    FALSE, 
+                    FALSE, 
+                    0);
+  gtk_widget_show(label);  
+  ewm->mimeLine = gtk_entry_new();
+  gtk_box_pack_start(GTK_BOX(vbox),
+                    ewm->mimeLine, 
+                    TRUE, 
+                    TRUE,
+                    0);
+  gtk_entry_set_text(GTK_ENTRY(ewm->mimeLine), 
+                    mimetype);
+  gtk_widget_show(ewm->mimeLine);
+
+  /* Create a line to change the description */
+  label = gtk_label_new("Description:");
+  gtk_box_pack_start(GTK_BOX(vbox),
+                    label, 
+                    FALSE, 
+                    FALSE, 
+                    0);
+  gtk_widget_show(label);  
+  ewm->descriptionLine = gtk_entry_new();
+  gtk_box_pack_start(GTK_BOX(vbox),
+                    ewm->descriptionLine, 
+                    TRUE, 
+                    TRUE,
+                    0);
+  gtk_entry_set_text(GTK_ENTRY(ewm->descriptionLine), 
+                    description);
+  gtk_widget_show(ewm->descriptionLine);
+  
+  /* add buttons to select the insertion method */
+  label = gtk_label_new("Insertion method:");
+  gtk_box_pack_start(GTK_BOX(vbox),
+                    label,
+                    FALSE, 
+                    FALSE,
+                    0);
+  gtk_widget_show(label);  
+  hbox = gtk_hbox_new(FALSE,0);
+  gtk_box_pack_start(GTK_BOX(vbox), 
+                    hbox, 
+                    TRUE,
+                    TRUE,
+                    0);
+  gtk_widget_show(hbox);
+  indexbutton1 = gtk_radio_button_new_with_label(NULL,
+                                                "Index only");
+  ewm->indexButton = indexbutton1;
+  gtk_signal_connect(GTK_OBJECT(indexbutton1),
+                          "toggled",
+                     GTK_SIGNAL_FUNC(button_index_clicked),
+                     ewm);
+  gtk_box_pack_start(GTK_BOX (hbox),
+                    indexbutton1,
+                    TRUE,
+                    TRUE,
+                    0);
+  gtk_widget_show(indexbutton1);  
+  group = gtk_radio_button_group(GTK_RADIO_BUTTON(indexbutton1));
+  indexbutton2 = gtk_radio_button_new_with_label(group, 
+                                                "Full insertion");
+  gtk_signal_connect(GTK_OBJECT(indexbutton2),
+                          "toggled",
+                     GTK_SIGNAL_FUNC(button_index_clicked),
+                     ewm);
+  gtk_box_pack_start(GTK_BOX(hbox), 
+                    indexbutton2, 
+                    TRUE,
+                    TRUE,
+                    0);
+  gtk_widget_show(indexbutton2);
+  if (testConfigurationString("GNUNET-INSERT",
+                             "INDEX-CONTENT",
+                               "YES") == YES) {
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(indexbutton1), 
+                                TRUE);
+         doIndex = 1;
+       }
+  else {
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(indexbutton2), 
+                                TRUE);
+    doIndex = 0;
+       }
+       
+  check_copy = gtk_check_button_new_with_label("Copy file to shared dir");
+  ewm->checkCopy = check_copy;
+  gtk_box_pack_start(GTK_BOX(hbox),
+                     check_copy,
+                     TRUE,
+                     TRUE,
+                     0);
+  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_copy), 
+                              ! testConfigurationString("GNUNET-INSERT",
+                                                      "LINK",
+                                                        "YES"));
+  gtk_widget_set_sensitive(check_copy, doIndex);
+  gtk_widget_show(check_copy);
+
+  /* add a list of keywords */
+  scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
+                                GTK_POLICY_AUTOMATIC, 
+                                GTK_POLICY_ALWAYS);
+  gtk_box_pack_start(GTK_BOX(vbox), 
+                    scrolled_window, 
+                    TRUE, 
+                    TRUE, 
+                    0);
+  gtk_widget_show(scrolled_window);  
+  clist = gtk_clist_new_with_titles(1, titles); 
+  ewm->keywordList = clist;
+  gtk_container_add(GTK_CONTAINER(scrolled_window), 
+                   clist);
+
+  /* add the pre-extracted keywords to the list */
+  gtk_clist_freeze(GTK_CLIST(clist));
+  for(i=0;i<num_keywords;i++) {
+    gtk_clist_append(GTK_CLIST(clist), 
+                    &keywords[i]);
+  }
+  gtk_clist_thaw(GTK_CLIST(clist));
+  gtk_widget_show(clist);
+
+  /* add a line to input new keywords */
+  keyword_line = gtk_entry_new();
+  ewm->keywordLine = keyword_line;
+  gtk_box_pack_start(GTK_BOX(vbox),
+                    keyword_line, 
+                    FALSE,
+                    FALSE,
+                    0);
+  gtk_entry_set_text(GTK_ENTRY(keyword_line), 
+                    "");
+  gtk_signal_connect(GTK_OBJECT(keyword_line),
+                    "activate",
+                     GTK_SIGNAL_FUNC(button_add_clicked),
+                     ewm);
+  gtk_widget_show(keyword_line);
+
+  /* add the buttons to add and delete keywords */
+  hbox = gtk_hbox_new(FALSE, 0);
+  gtk_box_pack_start(GTK_BOX(vbox),
+                    hbox,
+                    FALSE,
+                    TRUE,
+                    0);
+  gtk_widget_show(hbox);
+  button_add = gtk_button_new_with_label("Add keyword");
+  button_delete = gtk_button_new_with_label("Delete keyword");
+  gtk_box_pack_start(GTK_BOX(hbox), 
+                    button_add, 
+                    TRUE, 
+                    TRUE, 
+                    0);
+  gtk_box_pack_start(GTK_BOX(hbox), 
+                    button_delete, 
+                    TRUE, 
+                    TRUE, 
+                    0);
+  gtk_signal_connect(GTK_OBJECT(button_add),
+                    "clicked",
+                    GTK_SIGNAL_FUNC(button_add_clicked),
+                    ewm);
+  gtk_signal_connect(GTK_OBJECT(button_delete), 
+                    "clicked",
+                    GTK_SIGNAL_FUNC(button_del_clicked),
+                    ewm);
+  gtk_widget_show(button_add);
+  gtk_widget_show(button_delete);
+
+  /* add the insertion ok/cancel buttons */
+  separator = gtk_hseparator_new();
+  gtk_box_pack_start(GTK_BOX(vbox),
+                    separator,
+                    TRUE, 
+                    TRUE,
+                    0);
+  gtk_widget_show(separator);
+
+  hbox = gtk_hbox_new(FALSE, 0);
+  gtk_box_pack_start(GTK_BOX(vbox),
+                    hbox, 
+                    FALSE, 
+                    TRUE, 
+                    0);
+  gtk_widget_show(hbox);
+  button_ok = gtk_button_new_with_label("Ok");
+  button_cancel = gtk_button_new_with_label("Cancel");
+  gtk_box_pack_start(GTK_BOX(hbox),
+                    button_ok,
+                    TRUE,
+                    TRUE,
+                    0);
+  gtk_box_pack_start(GTK_BOX(hbox), 
+                    button_cancel, 
+                    TRUE,
+                    TRUE, 
+                    0);
+  gtk_signal_connect(GTK_OBJECT(button_ok), 
+                    "clicked",
+                    GTK_SIGNAL_FUNC(startInsert),
+                    ewm);
+  gtk_signal_connect(GTK_OBJECT(button_cancel),
+                    "clicked",
+                    GTK_SIGNAL_FUNC(destroyWidget),
+                    window);
+  gtk_widget_show(button_ok);
+  gtk_widget_show(button_cancel);
+
+  /* all clear, show the window */
+  gtk_widget_show(window);
+}
+
+/**
+ * Launches the attribute edit routine for the selected file,
+ * after keywords have been extracted.
+ *
+ * @param filename the selected filename
+ */
+static void file_selected(char *filename) {
+  int i;
+  char * fileNameRoot;
+  char * description;
+  char * mimetype;
+  char ** keywords;
+  int num_keywords; 
+
+
+  /* if filename is '/home/user/foo', use 'foo' as the filenameRoot */
+  fileNameRoot = NULL;
+  for (i=strlen(filename)-1;i>=0;i--) {
+    if (filename[i] == DIR_SEPARATOR) {
+      fileNameRoot = STRDUP(&filename[i+1]);
+      break;
+    }
+  }  
+  if (i == -1)
+    errexit("FATAL: ASSERTION failed: filename does not start with a '/'");
+
+  /* try to extract keywords */ 
+  description = NULL;
+  mimetype = NULL; 
+  num_keywords = 0;
+  keywords = NULL;
+  extractKeywords(filename,
+                 &description,
+                 &mimetype,
+                 &keywords,
+                 &num_keywords);
+
+  if (description == NULL)
+    description = STRDUP("No description supplied");
+  if (mimetype == NULL)
+    mimetype = STRDUP("unknown");
+  
+  /* allow the user to edit the insertion related info */
+  editAttributes(filename,
+                fileNameRoot,
+                description,
+                mimetype,
+                keywords,
+                num_keywords);
+
+  for (i=0;i<num_keywords;i++)
+    FREE(keywords[i]);
+  FREENONNULL(keywords);
+  FREE(mimetype);
+  FREE(description);
+  FREE(filename);
+  FREE(fileNameRoot);
+}
+
+
+
+/**
+ * Show user a window to edit information related to this file.
+ * After user is done, call startInsert.
+ *
+ * @param filename the name of the file that is inserted
+ * @param fileNameRoot the short name of the file
+ **/
+static void editDirectoryAttributes(char * filename,
+                                   char * fileNameRoot) {
+  EditDirectoryWindowModel * ewm;
+  GtkWidget * window;
+  GtkWidget * vbox, * hbox;
+  GtkWidget * clist;
+  GtkWidget * scrolled_window;
+  GtkWidget * label;
+  GtkWidget * separator; 
+  GtkWidget * button_add;
+  GtkWidget * button_delete;
+  GtkWidget * button_ok;
+  GtkWidget * button_cancel;
+  GtkWidget * keyword_line;
+  GSList * group;
+  GtkWidget * indexbutton1;
+  GtkWidget * indexbutton2;
+  GtkWidget * check_copy;
+  int doIndex;
+  gchar * titles[1] = { "Keyword(s) used for directory" };
+  gchar * gtitles[1] = { "Keyword(s) used for all files in directory" };
+
+  ewm = MALLOC(sizeof(EditDirectoryWindowModel));
+  ewm->fileName = STRDUP(filename);
+  /* create new window for editing */
+  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+  ewm->editAttributesWindow = window;
+  gtk_widget_set_usize(GTK_WIDGET(window),
+                      400,
+                      480);
+  gtk_window_set_title(GTK_WINDOW(window), 
+                      "Edit attributes");
+
+  /* add container for window elements */
+  vbox = gtk_vbox_new(FALSE, 0);
+  gtk_container_add(GTK_CONTAINER(window),
+                   vbox);
+  gtk_widget_show(vbox);
+
+  /* when user clicks on close box, always "destroy" */
+  gtk_signal_connect(GTK_OBJECT(window),
+                    "delete_event",
+                    GTK_SIGNAL_FUNC(deleteEvent),
+                    ewm);
+  /* whenever edit window gets destroyed, 
+     free *ALL* ewm data */
+  gtk_signal_connect(GTK_OBJECT(window),
+                    "destroy",
+                    GTK_SIGNAL_FUNC(destroyEditDirectoryWindow),
+                    ewm);
+
+  gtk_container_set_border_width(GTK_CONTAINER(window), 
+                                10);
+
+  /* Create a line to change the published filename */
+  label = gtk_label_new("Published directoryname:");
+  gtk_box_pack_start(GTK_BOX(vbox),
+                    label, 
+                    FALSE, 
+                    FALSE, 
+                    0);
+  gtk_widget_show(label); 
+  ewm->fileNameLine = gtk_entry_new();
+  gtk_box_pack_start(GTK_BOX(vbox),
+                    ewm->fileNameLine,
+                    TRUE,
+                    TRUE,
+                    0);
+  gtk_entry_set_text(GTK_ENTRY(ewm->fileNameLine), 
+                    fileNameRoot);
+  gtk_widget_show(ewm->fileNameLine);
+  
+
+  /* Create a line to change the description */
+  label = gtk_label_new("Description:");
+  gtk_box_pack_start(GTK_BOX(vbox),
+                    label, 
+                    FALSE, 
+                    FALSE, 
+                    0);
+  gtk_widget_show(label);  
+  ewm->descriptionLine = gtk_entry_new();
+  gtk_box_pack_start(GTK_BOX(vbox),
+                    ewm->descriptionLine, 
+                    TRUE, 
+                    TRUE,
+                    0);
+  gtk_entry_set_text(GTK_ENTRY(ewm->descriptionLine), 
+                    "No description supplied");
+  gtk_widget_show(ewm->descriptionLine);
+  
+  /* add buttons to select the insertion method */
+  label = gtk_label_new("Insertion method (for files in directory):");
+  gtk_box_pack_start(GTK_BOX(vbox),
+                    label,
+                    FALSE, 
+                    FALSE,
+                    0);
+  gtk_widget_show(label);  
+  hbox = gtk_hbox_new(FALSE,0);
+  gtk_box_pack_start(GTK_BOX(vbox), 
+                    hbox, 
+                    TRUE,
+                    TRUE,
+                    0);
+  gtk_widget_show(hbox);
+  indexbutton1 = gtk_radio_button_new_with_label(NULL,
+                                                "Index only");
+       gtk_signal_connect(GTK_OBJECT(indexbutton1),
+                          "toggled",
+                     GTK_SIGNAL_FUNC(button_index_clicked),
+                     ewm);
+  ewm->indexButton = indexbutton1;
+  gtk_box_pack_start(GTK_BOX (hbox),
+                    indexbutton1,
+                    TRUE,
+                    TRUE,
+                    0);
+  gtk_widget_show(indexbutton1);  
+  group = gtk_radio_button_group(GTK_RADIO_BUTTON(indexbutton1));
+  indexbutton2 = gtk_radio_button_new_with_label(group, 
+                                                "Full insertion");
+  gtk_signal_connect(GTK_OBJECT(indexbutton2),
+                          "toggled",
+                     GTK_SIGNAL_FUNC(button_index_clicked),
+                     ewm);
+  gtk_box_pack_start(GTK_BOX(hbox), 
+                    indexbutton2, 
+                    TRUE,
+                    TRUE,
+                    0);
+  gtk_widget_show(indexbutton2);
+  if (testConfigurationString("GNUNET-INSERT",
+                             "INDEX-CONTENT",
+                               "YES") == YES) {
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(indexbutton1), 
+                                TRUE);
+               doIndex = 1;
+  }
+  else {
+    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(indexbutton2), 
+                                TRUE);
+               doIndex = 0;
+  }
+
+
+  check_copy = gtk_check_button_new_with_label("Copy file to shared dir");
+  ewm->checkCopy = check_copy;
+  gtk_box_pack_start(GTK_BOX(hbox),
+                     check_copy,
+                     TRUE,
+                     TRUE,
+                     0);
+  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_copy), 
+                              ! testConfigurationString("GNUNET-INSERT",
+                                                      "LINK",
+                                                        "YES"));
+  gtk_widget_set_sensitive(check_copy, doIndex);
+  gtk_widget_show(check_copy);
+
+
+
+
+  /* add list of local keywords */
+  scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
+                                GTK_POLICY_AUTOMATIC, 
+                                GTK_POLICY_ALWAYS);
+  gtk_box_pack_start(GTK_BOX(vbox), 
+                    scrolled_window, 
+                    TRUE, 
+                    TRUE, 
+                    0);
+  gtk_widget_show(scrolled_window);  
+  clist = gtk_clist_new_with_titles(1, titles); 
+  ewm->keywordList = clist;
+  gtk_container_add(GTK_CONTAINER(scrolled_window), 
+                   clist);
+  gtk_widget_show(clist);
+
+  /* add a line to input new keywords */
+  keyword_line = gtk_entry_new();
+  ewm->keywordLine = keyword_line;
+  gtk_box_pack_start(GTK_BOX(vbox),
+                    keyword_line, 
+                    FALSE,
+                    FALSE,
+                    0);
+  gtk_entry_set_text(GTK_ENTRY(keyword_line), 
+                    "");
+  gtk_signal_connect(GTK_OBJECT(keyword_line),
+                    "activate",
+                     GTK_SIGNAL_FUNC(button_dir_add_clicked1),
+                     ewm);
+  gtk_widget_show(keyword_line);
+
+  /* add the buttons to add and delete keywords */
+  hbox = gtk_hbox_new(FALSE, 0);
+  gtk_box_pack_start(GTK_BOX(vbox),
+                    hbox,
+                    FALSE,
+                    TRUE,
+                    0);
+  gtk_widget_show(hbox);
+  button_add = gtk_button_new_with_label("Add keyword");
+  button_delete = gtk_button_new_with_label("Delete keyword");
+  gtk_box_pack_start(GTK_BOX(hbox), 
+                    button_add, 
+                    TRUE, 
+                    TRUE, 
+                    0);
+  gtk_box_pack_start(GTK_BOX(hbox), 
+                    button_delete, 
+                    TRUE, 
+                    TRUE, 
+                    0);
+  gtk_signal_connect(GTK_OBJECT(button_add),
+                    "clicked",
+                    GTK_SIGNAL_FUNC(button_dir_add_clicked1),
+                    ewm);
+  gtk_signal_connect(GTK_OBJECT(button_delete), 
+                    "clicked",
+                    GTK_SIGNAL_FUNC(button_dir_del_clicked1),
+                    ewm);
+  gtk_widget_show(button_add);
+  gtk_widget_show(button_delete);
+
+
+
+  /* add list of global keywords */
+  scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
+                                GTK_POLICY_AUTOMATIC, 
+                                GTK_POLICY_ALWAYS);
+  gtk_box_pack_start(GTK_BOX(vbox), 
+                    scrolled_window, 
+                    TRUE, 
+                    TRUE, 
+                    0);
+  gtk_widget_show(scrolled_window);  
+  clist = gtk_clist_new_with_titles(1, gtitles); 
+  ewm->gkeywordList = clist;
+  gtk_container_add(GTK_CONTAINER(scrolled_window), 
+                   clist);
+  gtk_widget_show(clist);
+
+  /* add a line to input new keywords */
+  keyword_line = gtk_entry_new();
+  ewm->gkeywordLine = keyword_line;
+  gtk_box_pack_start(GTK_BOX(vbox),
+                    keyword_line, 
+                    FALSE,
+                    FALSE,
+                    0);
+  gtk_entry_set_text(GTK_ENTRY(keyword_line), 
+                    "");
+  gtk_signal_connect(GTK_OBJECT(keyword_line),
+                    "activate",
+                     GTK_SIGNAL_FUNC(button_dir_add_clicked2),
+                     ewm);
+  gtk_widget_show(keyword_line);
+
+  /* add the buttons to add and delete keywords */
+  hbox = gtk_hbox_new(FALSE, 0);
+  gtk_box_pack_start(GTK_BOX(vbox),
+                    hbox,
+                    FALSE,
+                    TRUE,
+                    0);
+  gtk_widget_show(hbox);
+  button_add = gtk_button_new_with_label("Add keyword");
+  button_delete = gtk_button_new_with_label("Delete keyword");
+  gtk_box_pack_start(GTK_BOX(hbox), 
+                    button_add, 
+                    TRUE, 
+                    TRUE, 
+                    0);
+  gtk_box_pack_start(GTK_BOX(hbox), 
+                    button_delete, 
+                    TRUE, 
+                    TRUE, 
+                    0);
+  gtk_signal_connect(GTK_OBJECT(button_add),
+                    "clicked",
+                    GTK_SIGNAL_FUNC(button_dir_add_clicked2),
+                    ewm);
+  gtk_signal_connect(GTK_OBJECT(button_delete), 
+                    "clicked",
+                    GTK_SIGNAL_FUNC(button_dir_del_clicked2),
+                    ewm);
+  gtk_widget_show(button_add);
+  gtk_widget_show(button_delete);
+
+
+
+  /* add the insertion ok/cancel buttons */
+  separator = gtk_hseparator_new();
+  gtk_box_pack_start(GTK_BOX(vbox),
+                    separator,
+                    TRUE, 
+                    TRUE,
+                    0);
+  gtk_widget_show(separator);
+
+  hbox = gtk_hbox_new(FALSE, 0);
+  gtk_box_pack_start(GTK_BOX(vbox),
+                    hbox, 
+                    FALSE, 
+                    TRUE, 
+                    0);
+  gtk_widget_show(hbox);
+  button_ok = gtk_button_new_with_label("Ok");
+  button_cancel = gtk_button_new_with_label("Cancel");
+  gtk_box_pack_start(GTK_BOX(hbox),
+                    button_ok,
+                    TRUE,
+                    TRUE,
+                    0);
+  gtk_box_pack_start(GTK_BOX(hbox), 
+                    button_cancel, 
+                    TRUE,
+                    TRUE, 
+                    0);
+  gtk_signal_connect(GTK_OBJECT(button_ok), 
+                    "clicked",
+                    GTK_SIGNAL_FUNC(startInsertDirectory),
+                    ewm);
+  gtk_signal_connect(GTK_OBJECT(button_cancel),
+                    "clicked",
+                    GTK_SIGNAL_FUNC(destroyWidget),
+                    window);
+  gtk_widget_show(button_ok);
+  gtk_widget_show(button_cancel);
+
+  /* all clear, show the window */
+  gtk_widget_show(window);
+}
+
+
+/**
+ * Insert a directory.
+ **/
+static void directory_selected(char * filename) {
+  int i;
+  char * fileNameRoot;
+
+  /* if filename is '/home/user/foo/', use 'foo.gnd' as the filenameRoot */
+  fileNameRoot = NULL;
+  if (filename[strlen(filename)-1] == DIR_SEPARATOR)
+    filename[strlen(filename)-1] = '\0'; 
+  for (i=strlen(filename)-1;i>=0;i--) {
+    if (filename[i] == DIR_SEPARATOR) {
+      fileNameRoot = 
MALLOC(strlen(&filename[i+1])+1+strlen(GNUNET_DIRECTORY_EXT));
+      strcpy(fileNameRoot, &filename[i+1]);
+      strcat(fileNameRoot, GNUNET_DIRECTORY_EXT);
+      break;
+    }
+  }  
+  if (strlen(filename) == 0) {
+    fileNameRoot = STRDUP("");
+  } else {
+    if (i == -1)
+      errexit("FATAL: ASSERTION failed: filename does not start with a %c",
+             DIR_SEPARATOR);
+  }
+
+  /* allow the user to edit the insertion related info */
+  editDirectoryAttributes(filename,
+                         fileNameRoot);
+  FREE(filename);
+  FREE(fileNameRoot);
+}
+
+/**
+ * Callback for the file selection window.
+ *
+ * @param okButton not used
+ * @param window the file selection window
+ */
+static gint gtk_file_selected(GtkWidget * okButton, 
+                         GtkWidget * window) {
+  gchar * filename;
+  char * fn;  
+
+  filename 
+    = gtk_file_selection_get_filename(GTK_FILE_SELECTION(window));
+  if (filename == NULL) {
+    gtk_widget_destroy(window);
+    return FALSE;
+  }
+  if ( (NO == isDirectory(filename)) &&
+       (0 == assertIsFile(filename)) ) {
+    guiMessage("%s is not a file!\n",
+              filename);
+    gtk_widget_destroy(window);
+    return FALSE;
+  }
+
+  fn = STRDUP((char *) filename);
+
+  /* destroy the open-file window */
+  gtk_widget_destroy(window);
+  
+  if (isDirectory(fn))
+    directory_selected(fn);
+  else
+    file_selected(fn);
+
+  return FALSE;
+}
+
+/**
+ * Close the open-file window.
+ **/
+static gint destroyOpenFile(GtkWidget * widget,
+                           GtkWidget * window) {
+  LOG(LOG_DEBUG, 
+      "DEBUG: destroying open-file window (%x)\n", 
+      window);
+  return TRUE;
+}
+
+#ifdef MINGW
+/* Remember the previously selected path */
+static char szFilename[_MAX_PATH + 1] = "\0";
+#endif
+
+/**
+ * Pops up a file selector for the user.
+ *
+ * Explanation: In insertion, functions will be called in the
+ * following order,
+ *
+ * openSelectFile[OK click->]gtk_file_selected->file_selected->
+ * editAttributes[OK click]->startInsert->newthread.insertFile_
+ **/
+void openSelectFile(void) {
+#ifndef MINGW
+  GtkWidget * window;
+
+  window = gtk_file_selection_new("Choose file to be inserted");
+  gtk_signal_connect(GTK_OBJECT(window), 
+                    "destroy",
+                    GTK_SIGNAL_FUNC(destroyOpenFile),
+                    window);
+  gtk_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(window)->ok_button),
+                    "clicked", 
+                    GTK_SIGNAL_FUNC(gtk_file_selected),
+                    window);
+  gtk_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(window)->cancel_button),
+                    "clicked", 
+                    GTK_SIGNAL_FUNC(destroyWidget),
+                    window);
+  gtk_widget_show(window);
+#else
+  OPENFILENAME theDlg;
+  
+  memset(&theDlg, 0, sizeof(OPENFILENAME));
+  szFilename[0] = '\0';
+  
+  theDlg.lStructSize = sizeof(OPENFILENAME);
+  theDlg.hwndOwner = GetActiveWindow();
+  theDlg.lpstrFile = szFilename;
+  theDlg.nMaxFile = _MAX_PATH;
+  theDlg.Flags = OFN_FILEMUSTEXIST | OFN_SHAREAWARE;
+  if (GetOpenFileName(&theDlg))
+    file_selected(STRDUP(theDlg.lpstrFile));
+#endif
+}
+
+/* end of insert.c */

Added: freeway/src/org/gnu/freeway/protocol/afs/swing/insert.h
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/swing/insert.h     2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/swing/insert.h     2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,30 @@
+/*
+     This file is part of GNUnet
+
+     GNUnet is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with GNUnet; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file applications/afs/gtkui/insert.h
+ * @author Igor Wronsky 
+ **/
+
+#ifndef GTKUI_INSERT_H
+#define GTKUI_INSERT_H
+
+void openSelectFile(void);
+
+#endif

Added: freeway/src/org/gnu/freeway/protocol/afs/swing/insertprogress.c
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/swing/insertprogress.c     
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/swing/insertprogress.c     
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,547 @@
+/*
+     This file is part of GNUnet
+
+     GNUnet is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with GNUnet; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file src/applications/afs/gtkui/insertprogress.c
+ * @brief handles file insertions in the GTK GUI
+ * @author Igor Wronsky
+ * @author Christian Grothoff (refactoring, added bugs)
+ **/
+
+#include "gnunet_afs_esed2.h"
+#include "platform.h"
+#if USE_LIBEXTRACTOR
+#include <extractor.h>
+#endif
+#include "helper.h"
+#include "insertprogress.h"
+#include "insert.h"
+#include "main.h"
+
+typedef struct {
+  GtkWidget *bar;
+  size_t progress;
+} SetStat;
+
+
+static gint setInsertProgressVal(SaveCall *call) {
+  gtk_progress_set_value(GTK_PROGRESS(((SetStat *)call->args)->bar),
+                        ((SetStat *)call->args)->progress);
+
+  gtkSaveCallDone(call->sem);
+  
+  return FALSE;
+}
+
+typedef struct {
+  GtkWidget * bar;
+  size_t value;
+} SetAdj;
+
+
+static gint updateAdjustment(SaveCall * call) {
+  GtkObject * adj;
+
+  adj = gtk_adjustment_new(0, 0,
+                          ((SetAdj *)call->args)->value,
+                          1, 0, 0);
+  gtk_progress_set_adjustment(GTK_PROGRESS(((SetAdj *)call->args)->bar),
+                             GTK_ADJUSTMENT(adj));
+  gtkSaveCallDone(call->sem);
+  
+  return FALSE;
+}
+
+
+
+/**
+ * Callback function to update the insert progressbar.
+ *
+ * @param stats Statistics related to insert progression
+ * @param ilm Data related to the insertion
+ **/
+static void insertModelCallback(ProgressStats * stats,
+                               InsertModel * ilm) {
+  SetStat stat;
+  
+  stat.bar = ilm->progressBar;
+  stat.progress = stats->progress;
+  gtkSaveCall((GtkFunction) setInsertProgressVal, &stat);
+}
+
+
+/**
+ * Callback function to update the insert progressbar.
+ *
+ * @param stats Statistics related to insert progression
+ * @param ilm Data related to the insertion
+ **/
+static void insertDirectoryModelCallback(ProgressStats * stats,
+                                        InsertDirectoryModel * ilm) {
+  SetStat stat;
+  
+  stat.bar = ilm->progressBar;
+  stat.progress = stats->progress;
+  gtkSaveCall((GtkFunction) setInsertProgressVal, &stat);
+}
+
+static gint destroyInsertProgressBar(SaveCall *call) {
+  gtk_widget_destroy((GtkWidget *) call->args);  
+
+  gtkSaveCallDone(call->sem);
+
+  return FALSE;
+}
+
+/**
+ * A function to be run by the insert thread. Does the
+ * actual insertion.
+ *
+ * @param ilm Collected data related to the insertion
+ */
+void insertFileGtkThread(InsertModel * ilm) {
+  int res;
+  GNUNET_TCP_SOCKET * sock;
+  Block * top;
+  int i;
+  
+  SEMAPHORE_DOWN(refuseToDie);
+  if (ilm->indexContent == YES) {
+    FREENONNULL(setConfigurationString("GNUNET-INSERT",
+                                      "INDEX-CONTENT",
+                                      "YES"));
+
+    FREENONNULL(setConfigurationString("GNUNET-INSERT",
+                                       "LINK",
+                                       ilm->copyFile == YES ? "NO" : "YES"));
+
+  } else {
+    FREENONNULL(setConfigurationString("GNUNET-INSERT",
+                                      "INDEX-CONTENT",
+                                      "NO"));
+  }
+  sock = getClientSocket();
+  if (sock == NULL) {
+    SEMAPHORE_UP(refuseToDie);
+    return; /* warning should have been printed */
+  }
+  top = insertFile(sock,
+                  ilm->fileName,
+                  (ProgressModel)&insertModelCallback,
+                  ilm);
+  if (top != NULL) {
+    res = insertRoot(sock,
+                    top, 
+                    ilm->description,
+                    ilm->fileNameRoot,
+                    ilm->mimetype,
+                    ilm->num_keywords,
+                    ilm->keywords,
+                    NULL);
+  } else {
+    res = SYSERR;
+  }
+  gtkSaveCall((GtkFunction) destroyInsertProgressBar, 
+             ilm->progressBarWindow);
+  refreshMenuSensitivity();
+  if (res == OK) {
+    FileIdentifier fid;
+    char * fstring;
+
+    memcpy(&fid.chk, &top->chk, sizeof(CHK_Hashes));
+    fid.crc = htonl(crc32N(top->data, top->len));
+    {
+      unsigned int fs = (unsigned int) top->filesize;
+      fid.file_length = htonl(fs);
+    }
+    
+    fstring = fileIdentifierToString(&fid);
+    
+    infoMessage(NO,
+               "Successfully processed %s\n  => %s\n", 
+               ilm->fileName,
+               fstring);
+    LOG(LOG_DEBUG,
+        "DEBUG: Successfully processed %s\n  => %s\n",
+        ilm->fileName,
+        fstring);
+    FREE(fstring);
+  } else {
+    guiMessage("Insertion of %s FAILED!\n", 
+                ilm->fileName);
+  }
+  if(top != NULL)
+    top->vtbl->done(top, NULL);
+  releaseClientSocket(sock);
+
+  /* insert complete */
+  SEMAPHORE_UP(refuseToDie);
+  for (i=0;i<ilm->num_keywords;i++)
+    FREE(ilm->keywords[i]);
+  FREENONNULL(ilm->keywords);
+  if(ilm->deleteAfterInsert == YES) 
+    UNLINK(ilm->fileName);
+  FREE(ilm->fileName);
+  FREE(ilm->mimetype);
+  FREE(ilm->description);
+  FREE(ilm->fileNameRoot);
+  FREE(ilm);
+}
+
+/**
+ * Callback for handling "delete_event": keep the window OPEN.
+ **/
+static gint refuseDeleteEvent(GtkWidget * widget,
+                             GdkEvent * event,
+                             gpointer data) {
+  LOG(LOG_DEBUG, 
+      "DEBUG: refuseDeleteEvent\n");
+  return TRUE;
+}
+
+
+void createInsertProgressBar(InsertModel * ilm) {
+  GtkWidget * window;
+  GtkWidget * box;
+  GtkObject * adjustment;
+  char format[128];
+  int fileLength;
+
+  /* create a new window for a progressbar */
+  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+  ilm->progressBarWindow = window;
+  gtk_window_set_title(GTK_WINDOW(window), 
+                      ilm->fileName);
+  box = gtk_hbox_new(FALSE, 0);
+  gtk_container_add(GTK_CONTAINER(window), 
+                   box);
+  gtk_signal_connect(GTK_OBJECT(window),
+                    "delete_event",
+                    GTK_SIGNAL_FUNC(refuseDeleteEvent),
+                    NULL);
+  gtk_container_set_border_width(GTK_CONTAINER(window), 
+                                10);
+
+  sprintf(format, 
+         "%%v bytes %s",
+         ilm->opDescription);
+ 
+  /* create the actual progressbar */
+  fileLength = getFileSize(ilm->fileName);
+  ilm->progressBar = gtk_progress_bar_new();
+  gtk_progress_set_show_text(GTK_PROGRESS(ilm->progressBar),
+                            1);
+  gtk_progress_set_format_string(GTK_PROGRESS(ilm->progressBar),
+                                (gchar*)format);
+  adjustment = gtk_adjustment_new(0,
+                                 0,
+                                 fileLength,
+                                 1,
+                                 0,
+                                 0);
+  gtk_progress_set_adjustment(GTK_PROGRESS(ilm->progressBar),
+                             GTK_ADJUSTMENT(adjustment));
+  gtk_box_pack_start(GTK_BOX(box),
+                    ilm->progressBar, 
+                    TRUE, 
+                    TRUE, 
+                    0);
+  gtk_progress_bar_set_orientation(GTK_PROGRESS_BAR(ilm->progressBar),
+                                  GTK_PROGRESS_LEFT_TO_RIGHT);
+  gtk_widget_show(ilm->progressBar);
+  gtk_widget_show(box);
+  gtk_widget_show(window);
+}
+
+
+void createInsertDirectoryProgressBar(InsertDirectoryModel * ilm) {
+  GtkWidget * window;
+  GtkWidget * box;
+  char format[128];
+  int fileLength;
+
+  /* create a new window for a progressbar */
+  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+  ilm->progressBarWindow = window;
+  gtk_window_set_title(GTK_WINDOW(window), 
+                      ilm->fileName);
+  box = gtk_vbox_new(FALSE, 0);
+  gtk_container_add(GTK_CONTAINER(window), 
+                   box);
+  gtk_signal_connect(GTK_OBJECT(window),
+                    "delete_event",
+                    GTK_SIGNAL_FUNC(refuseDeleteEvent),
+                    NULL);
+  gtk_container_set_border_width(GTK_CONTAINER(window), 
+                                10);
+
+  sprintf(format, 
+         "%%v bytes %s",
+         ilm->opDescription);
+ 
+  /* create the actual progressbar */
+  fileLength = getFileSize(ilm->fileName);
+  ilm->progressBar = gtk_progress_bar_new();
+  gtk_progress_set_show_text(GTK_PROGRESS(ilm->progressBar),
+                            1);
+  gtk_progress_set_format_string(GTK_PROGRESS(ilm->progressBar),
+                                (gchar*)format);
+  ilm->adjustment = gtk_adjustment_new(0,
+                                      0,
+                                      10000,
+                                      1,
+                                      0,
+                                      0);
+  gtk_progress_set_adjustment(GTK_PROGRESS(ilm->progressBar),
+                             GTK_ADJUSTMENT(ilm->adjustment));
+  gtk_box_pack_start(GTK_BOX(box),
+                    ilm->progressBar, 
+                    TRUE, 
+                    TRUE, 
+                    0);
+  gtk_progress_bar_set_orientation(GTK_PROGRESS_BAR(ilm->progressBar),
+                                  GTK_PROGRESS_LEFT_TO_RIGHT);
+  gtk_widget_show(ilm->progressBar);
+
+  ilm->progressBar2 = gtk_progress_bar_new();
+  gtk_progress_set_show_text(GTK_PROGRESS(ilm->progressBar2),
+                            1);
+  gtk_progress_set_format_string(GTK_PROGRESS(ilm->progressBar2),
+                                (gchar*)format);
+  ilm->adjustment2 = gtk_adjustment_new(0,
+                                       0,
+                                       fileLength,
+                                       1,
+                                       0,
+                                       0);
+  gtk_progress_set_adjustment(GTK_PROGRESS(ilm->progressBar2),
+                             GTK_ADJUSTMENT(ilm->adjustment2));
+  gtk_box_pack_start(GTK_BOX(box),
+                    ilm->progressBar2, 
+                    TRUE, 
+                    TRUE, 
+                    0);
+  gtk_progress_bar_set_orientation(GTK_PROGRESS_BAR(ilm->progressBar2),
+                                  GTK_PROGRESS_LEFT_TO_RIGHT);
+  gtk_widget_show(ilm->progressBar2);
+  gtk_widget_show(box);
+  gtk_widget_show(window);
+}
+
+
+
+
+/**
+ * Insert a single file.
+ *
+ * @param filename the name of the file to insert
+ * @param fid resulting file identifier for the node
+ * @returns OK on success, SYSERR on error
+ **/
+static int gtkInsertDirectoryWrapper(GNUNET_TCP_SOCKET * sock,
+                                    char * filename,
+                                    FileIdentifier * fid,
+                                    InsertDirectoryModel * ilm) {
+  Block * top;
+  cron_t startTime;
+  InsertModel ifm;
+  SetAdj adj;
+
+  ifm.fileName = filename;
+  ifm.fileNameRoot = NULL;
+  ifm.description = NULL;
+  ifm.mimetype = NULL;
+  ifm.keywords = NULL;
+  ifm.num_keywords = 0;
+  memcpy(ifm.opDescription,
+        ilm->opDescription,
+        sizeof(ilm->opDescription));
+  ifm.indexContent = ilm->indexContent;
+  ifm.progressBar = ilm->progressBar;
+  ifm.progressBarWindow = ilm->progressBarWindow;
+  ifm.deleteAfterInsert = ilm->deleteAfterInsert;
+
+  adj.bar = ilm->progressBar;
+  adj.value = getFileSize(filename);
+  
+  gtkSaveCall((GtkFunction) updateAdjustment,
+             &adj);
+ 
+  cronTime(&startTime);
+  top = insertFile(sock,
+                  filename, 
+                  (ProgressModel) &insertDirectoryModelCallback,
+                  &ifm);
+  if (top == NULL) {
+    /* print error message here?  Probably better once
+       at the top-level... */
+    return SYSERR;
+  } else {
+    SetStat stat;
+
+    memcpy(&fid->chk, 
+          &top->chk, 
+          sizeof(CHK_Hashes));
+    fid->crc = htonl(crc32N(top->data, top->len));
+    fid->file_length = htonl(top->filesize);
+    if (NO == isDirectory(filename)) {
+      if (top->filesize != getFileSize(filename))
+       abort();
+      ilm->pos += top->filesize;
+      stat.bar = ilm->progressBar2;
+      stat.progress = ilm->pos;
+      gtkSaveCall((GtkFunction) setInsertProgressVal,
+                 &stat);
+    }
+    top->vtbl->done(top, NULL);
+    return OK;
+  }
+}
+
+
+/**
+ * A function to be run by the insert thread. Does the
+ * actual insertion.
+ *
+ * @param ilm Collected data related to the insertion
+ */
+void insertDirectoryGtkThread(InsertDirectoryModel * ilm) {
+  int res;
+  GNUNET_TCP_SOCKET * sock;
+  RootNode * top;
+  int i;
+  SetStat stat;
+  FileIdentifier fid;
+#if USE_LIBEXTRACTOR
+  EXTRACTOR_ExtractorList * extractors;
+#endif
+  
+  SEMAPHORE_DOWN(refuseToDie);
+  ilm->pos = 0;
+  stat.bar = ilm->progressBar2;
+  stat.progress = ilm->pos;
+  gtkSaveCall((GtkFunction) setInsertProgressVal,
+             &stat);
+  FREENONNULL(setConfigurationString("GNUNET-INSERT",
+                                    "BUILDDIR",
+                                    "YES"));
+  FREENONNULL(setConfigurationString("GNUNET-INSERT",
+                                    "RECURSIVE",
+                                    "YES"));
+
+  if (ilm->indexContent == YES) {
+    FREENONNULL(setConfigurationString("GNUNET-INSERT",
+                                      "INDEX-CONTENT",
+                                      "YES"));
+
+    FREENONNULL(setConfigurationString("GNUNET-INSERT",
+                                       "LINK",
+                                       ilm->copyFile == YES ? "NO" : "YES"));
+  } else {
+    FREENONNULL(setConfigurationString("GNUNET-INSERT",
+                                      "INDEX-CONTENT",
+                                      "NO"));
+  }
+  sock = getClientSocket();
+  if (sock == NULL) {
+    SEMAPHORE_UP(refuseToDie);
+    return; /* warning should have been printed */
+  }
+
+
+#if USE_LIBEXTRACTOR
+  extractors = getExtractors();
+#endif
+  top = insertRecursively(sock,
+                         ilm->fileName,
+                         &fid,
+                         ilm->gkeywords,
+                         ilm->num_gkeywords,   
+#if USE_LIBEXTRACTOR
+                         extractors,
+#else
+                         NULL,
+#endif
+                         (ProgressModel)&insertModelCallback,
+                         ilm,
+                         (InsertWrapper)&gtkInsertDirectoryWrapper,
+                         ilm);
+#if USE_LIBEXTRACTOR
+  EXTRACTOR_removeAll(extractors);
+#endif
+  if (top != NULL) {
+    unsigned int priority;
+
+    priority = getConfigurationInt("GNUNET-INSERT",
+                                  "CONTENT-PRIORITY");
+    res = OK;
+    for (i=0;i<ilm->num_keywords;i++) {
+      if (SYSERR == insertRootWithKeyword(sock,
+                                         top, 
+                                         ilm->keywords[i], 
+                                         priority))
+       res = SYSERR;
+    }
+    makeRootNodeAvailable(top, DIR_CONTEXT_INSERT);
+  } else {
+    res = SYSERR;
+  }
+
+  gtkSaveCall((GtkFunction) destroyInsertProgressBar, 
+             ilm->progressBarWindow);
+
+  refreshMenuSensitivity();
+  if (res == OK) {
+    char * fstring;
+
+    fstring = fileIdentifierToString(&top->header.fileIdentifier);
+    
+    infoMessage(NO,
+               "Successfully processed %s\n  => %s\n", 
+               ilm->fileName,
+               fstring);
+    LOG(LOG_DEBUG,
+        "DEBUG: Successfully processed %s\n  => %s\n",
+        ilm->fileName,
+        fstring);
+    FREE(fstring);
+  } else {
+    guiMessage("Insertion of %s FAILED!\n", 
+                ilm->fileName);
+  }
+  FREENONNULL(top);
+  releaseClientSocket(sock);
+
+  /* insert complete */
+  SEMAPHORE_UP(refuseToDie);
+  for (i=0;i<ilm->num_keywords;i++)
+    FREE(ilm->keywords[i]);
+  FREENONNULL(ilm->keywords);
+  if(ilm->deleteAfterInsert == YES) 
+    UNLINK(ilm->fileName);
+  FREE(ilm->fileName);
+  FREE(ilm->mimetype);
+  FREE(ilm->description);
+  FREE(ilm->fileNameRoot);
+  FREE(ilm);
+}
+
+
+
+/* end of insertprogress.c */

Added: freeway/src/org/gnu/freeway/protocol/afs/swing/insertprogress.h
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/swing/insertprogress.h     
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/swing/insertprogress.h     
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,95 @@
+/*
+     This file is part of GNUnet
+
+     GNUnet is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with GNUnet; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file applications/afs/gtkui/insertprogress.h
+ * @author Christian Grothoff
+ **/
+
+#ifndef GTKUI_INSERT_PROGRESS_H
+#define GTKUI_INSERT_PROGRESS_H
+
+#include "platform.h"
+#include <gtk/gtk.h>
+
+/**
+ * @brief state associated with an insertion
+ **/
+typedef struct {
+  char * fileName;
+  char * fileNameRoot;
+  char * description;
+  char * mimetype;
+  char ** keywords;
+  int num_keywords;
+  char opDescription[32];              /* used in progressBar */
+  int indexContent;
+  int copyFile;
+  GtkWidget * progressBar;
+  GtkWidget * progressBarWindow;
+  int deleteAfterInsert;
+} InsertModel;
+
+/**
+ * @brief state associated with an insertion
+ **/
+typedef struct {
+  char * fileName;
+  char * fileNameRoot;
+  char * description;
+  char * mimetype;
+  char ** keywords;
+  int num_keywords;
+  char opDescription[32];              /* used in progressBar */
+  int indexContent;
+  int copyFile;
+  GtkWidget * progressBar;
+  GtkWidget * progressBarWindow;
+  int deleteAfterInsert;
+  char ** gkeywords;
+  int num_gkeywords;
+  GtkObject * adjustment;
+  GtkObject * adjustment2;
+  GtkWidget * progressBar2;
+  unsigned long long pos;
+} InsertDirectoryModel;
+
+/**
+ * Main function of the insert thread.  Does the
+ * actual insertion.
+ *
+ * @param ilm Collected data related to the insertion
+ */
+void insertFileGtkThread(InsertModel * ilm);
+
+
+/**
+ * A function to be run by the insert thread. Does the
+ * actual insertion for directories.
+ *
+ * @param ilm Collected data related to the insertion
+ */
+void insertDirectoryGtkThread(InsertDirectoryModel * ilm);
+
+ 
+void createInsertProgressBar(InsertModel * ilm);
+
+void createInsertDirectoryProgressBar(InsertDirectoryModel * ilm);
+
+#endif

Added: freeway/src/org/gnu/freeway/protocol/afs/swing/main.c
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/swing/main.c       2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/swing/main.c       2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,867 @@
+/*
+     This file is part of GNUnet
+
+     GNUnet is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with GNUnet; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file src/applications/afs/gtkui/main.c
+ * @brief This is the main file for the gtk+ user interface.
+ * @author Christian Grothoff
+ * @author Igor Wronsky
+ *
+ *
+ * Basic structure of the code is this:
+ * main
+ * -> search -> saveas -> download
+ * -> insert
+ * -> directory -> insert
+ * -> pseudonyms (create|delete)
+ * -> namespace insert/update
+ * -> about
+ *
+ **/
+
+#include "gnunet_afs_esed2.h"
+#include "helper.h"
+#include "search.h"
+#include "insert.h"
+#include "delete.h"
+#include "about.h"
+#include "directory.h"
+#include "download.h"
+#include "namespace.h"
+#include "pseudonyms.h"
+#include "main.h"
+#include "statistics.h"
+
+/**
+ * This semaphore can be used to prevent the main window
+ * from killing GTK at an unhealty time. It is used by
+ * the un-interruptable but GUI-updating insert thread.
+ **/
+Semaphore * refuseToDie;
+
+/**
+ * Provides access to toggling pulldown menu shadings
+ **/
+GtkItemFactory * itemFactory = NULL;
+
+static GtkWidget * main_window_input_line = NULL;
+
+/**
+ * Shows the info window 
+ **/
+static void show_infowindow(GtkButton * button,
+                           gpointer dummy) {
+  if(infoWindow)
+    gtk_widget_show(infoWindow);
+  else
+    infoMessage(YES, "This window will show messages and the URIs of inserted 
content etc\ninfo that might require copy-pasting elsewhere to be used...\n\n");
+}
+
+/**
+ * Shows the download window if some dl has been started 
+ **/
+static void show_dlwindow(GtkButton * button,
+                         gpointer dummy) {
+  if(dlWindow)
+    gtk_widget_show(dlWindow);
+}
+
+
+/**
+ * This method is called whenever the user clicks the
+ * search button of the main window. 
+ * 
+ * @param widget not used
+ * @param notebook the notebook where the opened result window will be put.
+ **/
+static void search(GtkWidget * widget,
+                  GtkNotebook * notebook) {
+  gchar * searchString;
+  gchar * searchStringStart;
+  GtkWidget * tmp;
+  int i; 
+
+  searchString 
+    = gtk_entry_get_text(GTK_ENTRY(main_window_input_line));
+  if (searchString == NULL) {
+    guiMessage("searchString == NULL\n");
+    return;
+  }
+  searchString = STRDUP(searchString);
+
+  /* Remove heading spaces (parsing bugs if not) */  
+  i = strlen(searchString)-1;
+  while ( (i>=0) &&
+         (searchString[i]==' ') ) {
+    searchString[i] = '\0';
+    i--;
+  }
+  searchStringStart = searchString;
+  while (*searchStringStart == ' ')
+    searchStringStart++;
+  if (*searchStringStart=='\0') {
+    guiMessage("No search key given!\n");
+    FREE(searchString);
+    return;
+  }
+
+  /* add a new page in the notebook with the
+     search results. getSearchWindow returns
+     the page */
+  tmp = getSearchWindow(searchStringStart);
+  if (tmp != NULL) 
+    addToNotebook(searchStringStart,
+                 tmp);
+
+  /* reset search line to empty */
+  gtk_entry_set_text(GTK_ENTRY(main_window_input_line), 
+                    ""); 
+  FREE(searchString);
+}
+
+/**
+ * Exit the application (called when the main window
+ * is closed or the user selects File-Quit).
+ **/
+static void destroyMain(GtkWidget * widget,
+                       gpointer data) {
+  gdk_threads_leave(); /* avoid deadlock! */
+  stopCron();
+  delCronJob((CronJob)&cronCheckDaemon,
+            30 * cronSECONDS,
+            NULL);
+  startCron();
+  gdk_threads_enter();
+  if (notebook != NULL) {
+    int i = 0;    
+    while(gtk_notebook_get_nth_page(notebook, 0) != NULL) {
+      LOG(LOG_DEBUG, 
+         "DEBUG: Removing search page %d\n", 
+         i++);
+      gtk_notebook_remove_page(notebook, 0);
+    }
+  }
+  gdk_threads_leave(); /* avoid deadlock! */
+  SEMAPHORE_DOWN(refuseToDie);
+  gdk_threads_enter();
+  gtk_main_quit(); /* back to main method! */
+}
+
+/**
+ * Remove all of the root-nodes of a particular type
+ * from the directory database.
+ *
+ * @param unused GTK handle that is not used
+ * @param contexts bitmask of the databases that should be emptied.
+ **/ 
+static void emptyDirectoryDatabaseInd(GtkWidget * unused,
+                                     unsigned int contexts) {
+  emptyDirectoryDatabase(contexts);
+  refreshMenuSensitivity();
+}
+
+
+/**
+ * Method called from menu bar "File-Quit". Wrapper around
+ * destroy.
+ **/ 
+static void destroy_stub(void) {
+  destroyMain(NULL, NULL);
+}
+
+/**
+ * The pulldown menus.
+ **/
+static GtkItemFactoryEntry menu_items[] = {
+  { "/_File",         
+    NULL, 
+    NULL,
+    0,
+    "<Branch>" },
+  { "/File/_Insert",  
+    "<control>I", 
+    openSelectFile, 
+    0, 
+    NULL },
+  { "/File/_Download URI",  
+    "<control>D", 
+    fetchURI, 
+    0, 
+    NULL },
+  { "/File/Import di_rectory",  
+    "<control>r", 
+    importDirectory, 
+    0, 
+    NULL },
+  { "/File/sep1",     
+    NULL, 
+    NULL, 
+    0,
+    "<Separator>" },
+  { "/File/_Unindex file", 
+    "<control>U", 
+    openDeleteFile, 
+    0, 
+    NULL },
+  { "/File/sep1",     
+    NULL, 
+    NULL, 
+    0,
+    "<Separator>" },
+  { "/File/Show downloads",      
+    "<control>w", 
+    show_dlwindow, 
+    0, 
+    NULL },
+  { "/File/Show messages", 
+    "<control>m", 
+    show_infowindow, 
+    0, 
+    NULL },
+  { "/File/Show gnunetd stats", 
+    NULL,
+    showStats, 
+    0, 
+    NULL },
+  { "/File/_Plot gnunetd stats", 
+    NULL,
+    NULL,
+    0,
+    "<Branch>" },
+  { "/File/Plot gnunetd stats/_Connectivity", 
+    NULL,
+    displayStatistics, 
+    STAT_CONNECTIVITY,
+    NULL },
+  { "/File/Plot gnunetd stats/C_PU Load", 
+    NULL,
+    displayStatistics, 
+    STAT_CPU_LOAD,
+    NULL },
+  { "/File/Plot gnunetd stats/_Inbound Traffic", 
+    NULL,
+    displayStatistics, 
+    STAT_IN_TRAFFIC,
+    NULL },
+  { "/File/Plot gnunetd stats/_Outbound Traffic", 
+    NULL,
+    displayStatistics, 
+    STAT_OUT_TRAFFIC,
+    NULL },
+  { "/File/sep1",     
+    NULL, 
+    NULL, 
+    0,
+    "<Separator>" },
+  { "/File/_Quit",     
+    "<control>Q", 
+    destroy_stub,
+    0, 
+    NULL },
+  { "/_Advanced",
+    NULL,
+    NULL,
+    0,
+    "<Branch>" },
+
+  { "/Advanced/_Assemble Directory",
+    NULL,
+    NULL,
+    0,
+    "<Branch>" },
+  { "/Advanced/Assemble Directory/from _search results",
+    NULL,
+    openAssembleDirectoryDialog,
+    DIR_CONTEXT_SEARCH,
+    NULL },
+  { "/Advanced/Assemble Directory/from _inserted files",
+    NULL,
+    openAssembleDirectoryDialog,
+    DIR_CONTEXT_INSERT,
+    NULL },
+  { "/Advanced/Assemble Directory/from local _namespaces",
+    NULL,
+    openAssembleDirectoryDialog,
+    DIR_CONTEXT_INSERT_SB,
+    NULL },
+  { "/Advanced/Assemble Directory/from file identifiers from downloaded 
_directories",
+    NULL,
+    openAssembleDirectoryDialog,
+    DIR_CONTEXT_DIRECTORY,
+    NULL }, 
+  { "/Advanced/Assemble Directory/sepx1",     
+    NULL, 
+    NULL, 
+    0,
+    "<Separator>" },
+  { "/Advanced/Assemble Directory/from _all known file identifiers",
+    NULL,
+    openAssembleDirectoryDialog,
+    DIR_CONTEXT_ALL,
+    NULL },
+
+  { "/Advanced/sep1",     
+    NULL, 
+    NULL, 
+    0,
+    "<Separator>" },
+
+  { "/Advanced/Manage _Pseudonyms",     
+    NULL,  
+    NULL, 
+    0,
+    "<Branch>" },
+  { "/Advanced/Manage Pseudonyms/_Create new pseudonym",     
+    NULL,  
+    &openCreatePseudonymDialog,
+    0,
+    NULL },
+  { "/Advanced/Manage Pseudonyms/_Delete pseudonym",     
+    NULL,  
+    &openDeletePseudonymDialog,
+    0,
+    NULL },
+  { "/Advanced/_Insert into Namespace",     
+    NULL,  
+    NULL,
+    0,
+    "<Branch>" },
+  { "/Advanced/Insert into Namespace/Select from _search results",     
+    NULL,  
+    &openAssembleNamespaceDialog, 
+    DIR_CONTEXT_SEARCH,
+    NULL },
+  { "/Advanced/Insert into Namespace/Select from _inserted files",     
+    NULL,  
+    &openAssembleNamespaceDialog, 
+    DIR_CONTEXT_INSERT,
+    NULL },
+  { "/Advanced/Insert into Namespace/Select from results from downloaded 
_directories",     
+    NULL,  
+    &openAssembleNamespaceDialog, 
+    DIR_CONTEXT_INSERT,
+    NULL },
+  { "/Advanced/Insert into Namespace/Select from results from local 
_namespaces",     
+    NULL,  
+    &openAssembleNamespaceDialog, 
+    DIR_CONTEXT_INSERT_SB,
+    NULL },
+  { "/Advanced/Insert into Namespace/sepx2",     
+    NULL, 
+    NULL, 
+    0,
+    "<Separator>" },
+  { "/Advanced/Insert into Namespace/Select from _all known file identifiers", 
    
+    NULL,  
+    &openAssembleNamespaceDialog, 
+    DIR_CONTEXT_ALL,
+    NULL },
+  /*  { "/Advanced/_Update content in Namespace",     
+    NULL,  
+    NULL,  
+    0,
+    NULL }, */
+  { "/Advanced/_Search Namespace",     
+    "<control>S",  
+    &searchNamespace,  
+    0,
+    NULL }, 
+
+  { "/Advanced/sep2",     
+    NULL, 
+    NULL, 
+    0,
+    "<Separator>" },
+
+  { "/Advanced/_Reset File Identifiers",
+    NULL,
+    NULL,
+    0,
+    "<Branch>" },
+  { "/Advanced/Reset File Identifiers/List of _search results",
+    NULL,
+    emptyDirectoryDatabaseInd,
+    DIR_CONTEXT_SEARCH,
+    NULL },
+  { "/Advanced/Reset File Identifiers/List of _inserted files",
+    NULL,
+    emptyDirectoryDatabaseInd,
+    DIR_CONTEXT_INSERT,
+    NULL },
+  { "/Advanced/Reset File Identifiers/List of entries in local _namespaces",
+    NULL,
+    emptyDirectoryDatabaseInd,
+    DIR_CONTEXT_INSERT_SB,
+    NULL },
+  { "/Advanced/Reset File Identifiers/List of files from downloaded 
_directories",
+    NULL,
+    emptyDirectoryDatabaseInd,
+    DIR_CONTEXT_DIRECTORY,
+    NULL }, 
+  { "/Advanced/Reset File Identifiers/sepx3",     
+    NULL, 
+    NULL, 
+    0,
+    "<Separator>" },
+  { "/Advanced/Reset File Identifiers/_All known file identifiers",
+    NULL,
+    emptyDirectoryDatabaseInd,
+    DIR_CONTEXT_ALL,
+    NULL },
+  
+  { "/Advanced/sep3",     
+    NULL, 
+    NULL, 
+    0,
+    "<Separator>" },
+  
+  { "/Advanced/Launch gnunetd",     
+    NULL, 
+    launchDaemon, 
+    0,
+    NULL },
+  
+  { "/Advanced/Kill gnunetd",     
+    NULL, 
+    killDaemon, 
+    0,
+    NULL },
+
+/*
+  { "/_Options",     
+    NULL,  
+    NULL, 
+    0,
+    "<Branch>" },
+  { "/Options/Preferences(not impl)",
+    NULL,  
+    NULL, 
+    0, 
+    NULL },
+*/
+  { "/_Help",     
+    NULL,  
+    NULL,
+    0,
+    "<LastBranch>" },
+  { "/Help/_About",   
+    NULL,
+    about,
+    0, 
+    NULL },
+};
+
+gint doRefreshMenuSensitivity(SaveCall *call) {
+  int havePseudo;
+  int haveSearch;
+  int haveInsert;
+  int haveDirect;
+  int haveNamesp;
+  int haveAny;
+  int value;
+  GtkWidget * entry;  
+
+  havePseudo = havePseudonyms(); 
+  if (NO == havePseudo)
+    value = FALSE;
+  else
+    value = TRUE;
+  haveSearch = 0 < iterateDirectoryDatabase(DIR_CONTEXT_SEARCH, NULL, NULL);
+  haveInsert = 0 < iterateDirectoryDatabase(DIR_CONTEXT_INSERT, NULL, NULL);
+  haveDirect = 0 < iterateDirectoryDatabase(DIR_CONTEXT_DIRECTORY, NULL, NULL);
+  haveNamesp = 0 < iterateDirectoryDatabase(DIR_CONTEXT_INSERT_SB, NULL, NULL);
+  haveAny = 0 < iterateDirectoryDatabase(DIR_CONTEXT_ALL, NULL, NULL);
+
+  entry = gtk_item_factory_get_widget(itemFactory,
+                                     "/Advanced/Manage Pseudonyms/"
+                                     "Delete pseudonym");
+  gtk_widget_set_sensitive(entry, value);
+  
+  
+  if ( (NO == havePseudo) || (NO == haveSearch) )
+    value = FALSE;
+  else
+    value = TRUE;
+  entry = gtk_item_factory_get_widget(itemFactory,
+                                     "/Advanced/Insert into Namespace/"
+                                     "Select from search results");
+  gtk_widget_set_sensitive(entry, value);  
+
+  if ( (NO == havePseudo) || (NO == haveInsert) )
+    value = FALSE;
+  else
+    value = TRUE;
+  entry = gtk_item_factory_get_widget(itemFactory,
+                                     "/Advanced/Insert into Namespace/"
+                                     "Select from inserted files");
+  gtk_widget_set_sensitive(entry, value);  
+  if ( (NO == havePseudo) || (NO == haveDirect) )
+    value = FALSE;
+  else
+    value = TRUE;
+  entry = gtk_item_factory_get_widget(itemFactory,
+                                     "/Advanced/Insert into Namespace/"
+                                     "Select from results from downloaded 
directories");
+  gtk_widget_set_sensitive(entry, value);  
+
+  if ( (NO == havePseudo) || (NO == haveNamesp) )
+    value = FALSE;
+  else
+    value = TRUE;
+  entry = gtk_item_factory_get_widget(itemFactory,
+                                     "/Advanced/Insert into Namespace/"
+                                     "Select from results from local 
namespaces");
+  gtk_widget_set_sensitive(entry, value);  
+
+  if ( (NO == havePseudo) || (NO == haveAny) )
+    value = FALSE;
+  else
+    value = TRUE;
+  entry = gtk_item_factory_get_widget(itemFactory,
+                                     "/Advanced/Insert into Namespace/"
+                                     "Select from all known file identifiers");
+  gtk_widget_set_sensitive(entry, value);  
+  if ( NO == haveAny) 
+    value = FALSE;
+  else
+    value = TRUE;
+  
+  entry = gtk_item_factory_get_widget(itemFactory,
+                                     "/Advanced/Assemble Directory/"
+                                     "from all known file identifiers");
+  gtk_widget_set_sensitive(entry, value);  
+  entry = gtk_item_factory_get_widget(itemFactory,
+                                     "/Advanced/Reset File Identifiers/"
+                                     "All known file identifiers");
+  gtk_widget_set_sensitive(entry, value);  
+
+
+  if ( NO == haveSearch) 
+    value = FALSE;
+  else
+    value = TRUE;
+  entry = gtk_item_factory_get_widget(itemFactory,
+                                     "/Advanced/Assemble Directory/"
+                                     "from search results");
+  gtk_widget_set_sensitive(entry, value);  
+  entry = gtk_item_factory_get_widget(itemFactory,
+                                     "/Advanced/Reset File Identifiers/"
+                                     "List of search results");
+  gtk_widget_set_sensitive(entry, value);  
+
+  if ( NO == haveInsert) 
+    value = FALSE;
+  else
+    value = TRUE;
+
+  entry = gtk_item_factory_get_widget(itemFactory,
+                                     "/Advanced/Assemble Directory/"
+                                     "from inserted files");
+  gtk_widget_set_sensitive(entry, value);  
+  entry = gtk_item_factory_get_widget(itemFactory,
+                                     "/Advanced/Reset File Identifiers/"
+                                     "List of inserted files");
+  gtk_widget_set_sensitive(entry, value);  
+
+
+  if ( NO == haveDirect) 
+     value = FALSE;
+  else
+    value = TRUE;
+  entry = gtk_item_factory_get_widget(itemFactory,
+                                     "/Advanced/Assemble Directory/"
+                                     "from file identifiers from downloaded 
directories");
+  gtk_widget_set_sensitive(entry, value);  
+  entry = gtk_item_factory_get_widget(itemFactory,
+                                     "/Advanced/Reset File Identifiers/"
+                                     "List of files from downloaded 
directories");
+  gtk_widget_set_sensitive(entry, value);  
+
+
+  if ( NO == haveNamesp) 
+    value = FALSE;
+  else
+    value = TRUE;
+  entry = gtk_item_factory_get_widget(itemFactory,
+                                     "/Advanced/Assemble Directory/"
+                                     "from local namespaces");
+  gtk_widget_set_sensitive(entry, value);  
+  entry = gtk_item_factory_get_widget(itemFactory,
+                                     "/Advanced/Insert into Namespace/"
+                                     "Select from results from local 
namespaces");
+  gtk_widget_set_sensitive(entry, value);  
+  entry = gtk_item_factory_get_widget(itemFactory,
+                                     "/Advanced/Reset File Identifiers/"
+                                     "List of entries in local namespaces");
+  gtk_widget_set_sensitive(entry, value);  
+
+  gtkSaveCallDone(call->sem);
+
+  return FALSE;
+}
+
+void refreshMenuSensitivity() {
+  gtkSaveCall((GtkFunction) doRefreshMenuSensitivity, NULL);
+}
+
+/**
+ * This creates the main window
+ **/
+static void makeMainWindow() {
+  GtkWidget * window;
+  GtkWidget * button;
+  GtkWidget * hbox;
+  GtkWidget * vbox;
+  GtkWidget * menubar;
+  GtkWidget * table;
+  GtkWidget * label;
+  GtkWidget * entry;
+  GtkAccelGroup * accel_group;
+
+  gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
+
+  /* create main window */
+  window = gtk_window_new(GTK_WINDOW_TOPLEVEL); 
+  gtk_window_set_title(GTK_WINDOW(window), 
+                      "GNUnet: gtk+ GUI");
+  gtk_widget_set_usize(GTK_WIDGET(window), 
+                      780, /* x-size */
+                      300); /* y-size */
+  vbox = gtk_vbox_new(FALSE, 1);
+  gtk_container_add(GTK_CONTAINER(window), vbox);
+  gtk_signal_connect(GTK_OBJECT(window), 
+                     "delete_event",
+                     GTK_SIGNAL_FUNC(deleteEvent), 
+                    NULL);
+  gtk_signal_connect(GTK_OBJECT(window), 
+                     "destroy",
+                     GTK_SIGNAL_FUNC(destroyMain),
+                    NULL);
+  gtk_widget_show(vbox);
+
+  
+  /* create pulldown menues */  
+  accel_group = gtk_accel_group_new ();
+  itemFactory = gtk_item_factory_new(GTK_TYPE_MENU_BAR, 
+                                    "<main>",
+                                    accel_group);
+  gtk_item_factory_create_items(itemFactory, 
+                               nmenu_items,
+                               menu_items, 
+                               NULL);
+  gtk_window_add_accel_group(GTK_WINDOW (window), 
+                            accel_group);
+  menubar = gtk_item_factory_get_widget(itemFactory, 
+                                       "<main>");
+  gtk_box_pack_start(GTK_BOX (vbox), 
+                    menubar,
+                    FALSE, 
+                    TRUE, 
+                    0);
+  
+
+
+  /* set some default options and show */
+
+  
+  entry = gtk_item_factory_get_widget(itemFactory,
+                                     "/File/Show downloads");
+  gtk_widget_set_sensitive(entry, FALSE);
+  refreshMenuSensitivity();
+
+  gtk_widget_show(menubar);
+
+  /* a table to put the search results notebook to */
+  table = gtk_table_new(6, 6, TRUE);
+  gtk_box_pack_start(GTK_BOX (vbox),
+                    table,
+                    TRUE,
+                    TRUE, 0);
+  gtk_widget_show(table);
+  
+  /* add "notebook" for search results */
+  notebook = GTK_NOTEBOOK(gtk_notebook_new());
+  gtk_notebook_set_scrollable(notebook,
+                             TRUE);
+  gtk_notebook_set_tab_pos(GTK_NOTEBOOK(notebook),
+                          GTK_POS_TOP);
+  gtk_table_attach_defaults(GTK_TABLE(table), 
+                           GTK_WIDGET(notebook),
+                           0, 6, 0, 6);
+  gtk_widget_show(GTK_WIDGET(notebook));
+
+  /* BEGIN of SEARCH BOX CODE */
+  /* At the bottom, put the search box */
+  hbox = gtk_hbox_new(FALSE, 1);
+  gtk_box_pack_start(GTK_BOX (vbox),
+                    hbox, 
+                    FALSE, 
+                    FALSE, 
+                    0);
+  gtk_widget_show(hbox);
+ 
+  /* search entry label */
+  label = gtk_label_new("Keyword(s):");
+  gtk_box_pack_start(GTK_BOX(hbox),
+                    label,
+                    FALSE, 
+                    FALSE,
+                    0);
+  gtk_widget_show(label);
+
+  /* search input line */
+  main_window_input_line = gtk_entry_new();
+  gtk_box_pack_start(GTK_BOX(hbox), 
+                    main_window_input_line, 
+                    TRUE, 
+                    TRUE, 
+                    0);
+  gtk_signal_connect(GTK_OBJECT(main_window_input_line),
+                    "activate",
+                     GTK_SIGNAL_FUNC(search),
+                     notebook);
+  gtk_widget_show(main_window_input_line);
+
+  /* search button */
+  button = gtk_button_new_with_label("Search");
+  gtk_signal_connect(GTK_OBJECT (button), 
+                    "clicked",
+                    GTK_SIGNAL_FUNC(search), notebook);
+  gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
+  gtk_widget_show(button);
+  /* END of SEARCH BOX CODE */
+
+  /* show the main window */
+  gtk_widget_show(window);
+}
+
+/**
+ * Perform option parsing from the command line. 
+ **/
+static int parseOptions(int argc, 
+                       char ** argv) {
+  int c;
+
+  while (1) {
+    int option_index=0;
+    static struct GNoption long_options[] = {
+      LONG_DEFAULT_OPTIONS,
+      { 0,0,0,0 }
+    };
+    
+    c = GNgetopt_long(argc,
+                     argv, 
+                     "vhdc:L:H:", 
+                     long_options, 
+                     &option_index);
+    
+    if (c == -1) 
+      break;  /* No more flags to process */
+    if (YES == parseDefaultOptions(c, GNoptarg))
+      continue;    
+    switch(c) {
+    case 'v': 
+      printf("GNUnet v%s, AFS v%s\n",
+            VERSION,
+            AFS_VERSION);
+      return SYSERR;
+    case 'h': {
+      static Help help[] = {
+       HELP_CONFIG,
+       HELP_HELP,
+       HELP_HOSTNAME,
+       HELP_LOGLEVEL,
+       HELP_VERSION,
+       HELP_END,
+      };
+      formatHelp("gnunet-gtk [OPTIONS]",
+                "Start GNUnet GTK user interface.",
+                help);
+      return SYSERR;
+    }
+    default:
+      LOG(LOG_FAILURE,
+         "Unknown option %c. Aborting.\n"
+         "Use --help to get a list of options.\n",
+         c);
+      return SYSERR;    
+    } /* end of parsing commandline */
+  }
+  if (GNoptind < argc) {
+    fprintf(stderr,
+           "Invalid arguments: ");
+    while (GNoptind < argc)
+      fprintf(stderr,
+             "%s ", 
+             argv[GNoptind++]);
+    fprintf(stderr, 
+           "\n");
+    return SYSERR;
+  }
+  return OK;
+}
+
+/**
+ * The main function.
+ **/
+int main(int argc,
+         char * argv[]) {
+
+  if (SYSERR == initUtil(argc, argv, &parseOptions))
+    return(0);
+  initGTKStatistics();
+   
+  startCron();
+  refuseToDie = SEMAPHORE_NEW(1);
+
+  g_thread_init(NULL);
+  /*  gdk_threads_init(); */
+  gtk_init(&argc, &argv);
+  gtkInitSaveCalls();
+
+  makeMainWindow();
+  /* Check if gnunetd is running */
+  checkForDaemon();
+ 
+  /* refresh the kill/launch sensitivities once per 30 secs */
+  addCronJob((CronJob)&cronCheckDaemon,
+            0,
+            30 * cronSECONDS,
+            NULL);
+  startAFSPriorityTracker();
+  gdk_threads_enter();  
+  setCustomLogProc(addLogEntry);
+#ifdef MINGW
+  FreeConsole();
+#endif
+  gtk_main();
+  setCustomLogProc(NULL);
+  gdk_threads_leave(); 
+  gtkDoneSaveCalls();
+  stopCron();
+  stopAFSPriorityTracker();
+  LOG(LOG_DEBUG, "DEBUG: GUI leaving...\n");
+  SEMAPHORE_FREE(refuseToDie);
+
+  doneGTKStatistics();
+  doneUtil();  
+  return 0;
+}
+
+/* end of main.c */

Added: freeway/src/org/gnu/freeway/protocol/afs/swing/main.h
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/swing/main.h       2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/swing/main.h       2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,39 @@
+/*
+     This file is part of GNUnet
+
+     GNUnet is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with GNUnet; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file applications/afs/gtkui/main.h
+ * @author Igor Wronsky
+ **/
+
+#ifndef GTKUI_MAIN_H
+#define GTKUI_MAIN_H
+
+/**
+ * This semaphore can be up'ed by threads that will
+ * need the GUI and that want to prevent gnunet-gtk
+ * from exiting while they are running.
+ **/
+extern Semaphore * refuseToDie;
+
+extern GtkItemFactory * itemFactory;
+
+void refreshMenuSensitivity();
+
+#endif

Added: freeway/src/org/gnu/freeway/protocol/afs/swing/namespace.c
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/swing/namespace.c  2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/swing/namespace.c  2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,1492 @@
+/*
+     This file is part of GNUnet
+
+     GNUnet is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with GNUnet; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file src/applications/afs/gtkui/namespace.c
+ * @brief Namespace dialog for the AFS interface
+ * @author Christian Grothoff
+ **/
+
+#include "gnunet_afs_esed2.h"
+#include "helper.h"
+#include "namespace.h"
+#include "search.h"
+#include "main.h"
+
+/**
+ * @brief state of the insert into namespace window
+ **/
+typedef struct {
+  char * fileName;
+  GtkWidget * window;
+  GtkWidget * passwordLine;
+  GtkWidget * pseudonymList;
+  GtkWidget * sblockList;
+  GtkWidget * availableList;
+  GtkWidget * updateInterval;
+  GtkWidget * currentKey;
+  GtkWidget * nextKey;
+  SBlock ** updateableEntries;
+  int updateableCount;
+  HashCode160 selectedPseudonym;
+} NamespaceInsertWindowModel;
+
+static int parseTime(char * t) {
+  int pos;
+  int start;
+  int ret;
+  unsigned int val;
+  char * tmp;
+  
+  ret = 0;
+  pos = 0;
+
+  while (t[pos] != '\0') {
+    start = pos;
+    while ( (t[pos] != ' ') &&
+           (t[pos] != '\0') )
+      pos++;
+    tmp = STRNDUP(&t[start],
+                 pos - start);
+    if (1 != sscanf(tmp,
+                   "%u",
+                   &val) ) 
+      return -1; /* parse error */
+    FREE(tmp);
+    while ( t[pos] == ' ')
+      pos++;
+    start = pos;
+    while ( (t[pos] != ' ') &&
+           (t[pos] != '\0') )
+      pos++;
+    if (0 == strncasecmp(&t[start],
+                        "minute",
+                        strlen("minute")))
+      ret += 60 * val;
+    else if (0 == strncasecmp(&t[start],
+                             "second",
+                             strlen("second")))
+      ret += val;
+    else if (0 == strncasecmp(&t[start],
+                             "hour",
+                             strlen("hour")))
+      ret += 60 * 60 * val;
+    else if (0 == strncasecmp(&t[start],
+                             "day",
+                             strlen("day")))
+      ret += 24 * 60 * 60 * val;
+    else
+      return -1; /* parse error */ 
+    while ( t[pos] == ' ')
+      pos++;
+  }  
+  return ret;
+}
+
+/**
+ * Collects the results of the assembly dialog, creates an insertion 
+ * progressbar and launches the insertion thread.
+ *
+ * @param dummy not used
+ * @param ewm the state of the edit window
+ **/
+static void buildNSEntry(GtkWidget * dummy, 
+                        NamespaceInsertWindowModel * ewm) {
+  GList * tmp;
+  int row;
+  gchar * key[1];
+  char * currentKey;
+  char * nextKey;
+  char * name;
+  char * pass;
+  char * message;
+  Hostkey pseudo;
+  HashCode160 k; /* = key, next - increment */
+  HashCode160 n; /* = next or namespace ID depending where in code */
+  TIME_T interval;
+  GNUNET_TCP_SOCKET * sock;
+  SBlock * sb;
+  RootNode * rn;
+  HexName hex1;
+  HexName hex2;
+  char * updateInterval;
+  TIME_T now;
+  TIME_T creationTime;
+  char * desc;
+  char * mime;
+
+  updateInterval = 
gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(ewm->updateInterval)->entry));
+  if (updateInterval == NULL) {
+    guiMessage("ERROR: you must specify an update frequency.\n");
+    return;
+  }
+  if (strcmp(updateInterval, "--no updates--") == 0) {
+    interval = 0;    
+  } else if (strcmp(updateInterval, "--sporadic updates--") == 0) {
+    interval = -1;
+  } else {
+    interval = parseTime(updateInterval);
+    if (interval == -1) {
+      guiMessage("ERROR: parsing of time interval failed. "
+                "Use \"(INT [seconds|minutes|hours])*\" format.\n");
+      return;
+    }
+  } 
+  tmp = GTK_CLIST(ewm->pseudonymList)->selection;
+  if (NULL == tmp) {
+    guiMessage("ERROR: you must select a pseudonym (Error #1).\n");    
+    return;  
+  }
+  row = (int) tmp->data;
+  if ( row < 0 ) {
+    guiMessage("ERROR: you must select a pseudonym (Error #2).\n");    
+    return; 
+  }
+
+  key[0] = NULL;
+  gtk_clist_get_text(GTK_CLIST(ewm->pseudonymList),
+                    row,
+                    0,
+                    &key[0]);
+  name = key[0];
+  if (name == NULL) {
+    guiMessage("ERROR: you must select a pseudonym (Error #3).\n");    
+    return;  /* should never happen... */
+  }
+  pass = gtk_entry_get_text(GTK_ENTRY(ewm->passwordLine));
+  if (strlen(pass) == 0)
+    pass = NULL;
+  pseudo = readPseudonym(name, pass);
+  if (pseudo == NULL) {
+    guiMessage("ERROR: password specified invalid for pseudonym.\n");
+    return;
+  }
+
+  currentKey = gtk_entry_get_text(GTK_ENTRY(ewm->currentKey));
+  nextKey = gtk_entry_get_text(GTK_ENTRY(ewm->nextKey));
+
+  tmp = GTK_CLIST(ewm->availableList)->selection;
+  if (NULL == tmp) {
+    guiMessage("ERROR: you must select a file.\n");
+    return;  
+  }
+  row = (int) tmp->data;
+  if ( row < 0 ) {
+    guiMessage("ERROR: you must select a file.\n");
+    freeHostkey(pseudo);
+    return;
+  }
+  rn = gtk_clist_get_row_data(GTK_CLIST(ewm->availableList),
+                             row);
+
+  /* select/derive current & next IDs! */
+  tmp = GTK_CLIST(ewm->sblockList)->selection;
+  if ( (NULL == tmp) || 
+       ( ((int)tmp->data) == 0) ) {
+    /* "--no update--" or nothing selected, pick random IDs */
+    tryhex2hashOrHashString(currentKey,
+                           &k);
+    switch(interval) {
+      case SBLOCK_UPDATE_NONE:
+        /* no updates => next == this */
+        memcpy(&n,
+              &k,
+              sizeof(HashCode160));
+       break;
+      case SBLOCK_UPDATE_SPORADIC:
+        /* sporadic update; pick specified ID if given,
+           otherwise go random */
+        tryhex2hashOrHashString(nextKey,
+                                &n);
+       break;
+      default:
+        /* periodic update, the very first next id will be random */
+        makeRandomId(&n);
+       break;
+    } 
+
+    TIME(&creationTime);
+  } else {
+    SBlock * pred;
+
+    row = ((int) tmp->data) - 1; /* -1: first item is always "no update" */
+    if (row >= ewm->updateableCount) {
+      guiMessage("ERROR: this should never happen.\n");    
+      freeHostkey(pseudo);
+      return; 
+    }
+    pred = ewm->updateableEntries[row];
+    /* now, compute CURRENT ID and next ID */
+    TIME(&now);
+    computeIdAtTime(pred, 
+                   now, 
+                   &k);
+    if ( (interval != SBLOCK_UPDATE_NONE) &&
+        (interval != SBLOCK_UPDATE_SPORADIC) ) {
+      int delta;
+      /* periodic update */
+      delta = now - ntohl(pred->creationTime);
+      delta = delta / ntohl(pred->updateInterval);
+      if (delta == 0)
+       delta = 1; /* force to be in the future from the updated block! */
+      creationTime = ntohl(pred->creationTime) + delta * 
ntohl(pred->updateInterval);
+
+      /* periodic update, compute _next_ ID as increment! */
+      addHashCodes(&k,
+                  &pred->identifierIncrement,
+                  &n); /* n = k + inc */      
+    } else {
+      if (interval == SBLOCK_UPDATE_SPORADIC) {
+       /* sporadic update, pick random next ID 
+          if not specified! */
+       tryhex2hashOrHashString(nextKey, 
+                               &n);
+       TIME(&creationTime);
+      } else {
+       /* updating non-updateable SBlock!? Should
+          never happen! */
+       guiMessage("ERROR: attempt to update an non-updateble SBlock, this 
should never happen!\n"); 
+       BREAK();
+       return;
+      }
+    }
+  } /* end of selecting k and n based on update mechanism */
+  
+  name = getFilenameFromNode(rn);
+  desc = getDescriptionFromNode(rn);
+  mime = getMimetypeFromNode(rn);
+  sb = buildSBlock(pseudo,
+                  &rn->header.fileIdentifier,
+                  desc,
+                  name,
+                  mime,
+                  creationTime,
+                  interval,
+                  &k,
+                  &n);
+  FREE(desc);
+  FREE(mime);
+  freeHostkey(pseudo);
+  if (sb == NULL) {
+    FREE(name);
+    guiMessage("ERROR: failed to build SBlock. Consult logs.");
+    return;
+  }
+  sock = getClientSocket();
+  if (sock == NULL) {
+    FREE(sb);
+    FREE(name);
+    guiMessage("ERROR: could not connect to gnunetd.");
+    return;
+  }
+  if (OK != insertSBlock(sock,
+                        sb)) {
+    guiMessage("ERROR: failed to insert SBlock. "
+              "Consult logs.");    
+    releaseClientSocket(sock);
+    FREE(name);
+    FREE(sb);
+    return;
+  }
+  releaseClientSocket(sock);
+  /* obtain "n = S", the namespace ID */
+  hash(&sb->subspace,
+       sizeof(PublicKey),
+       &n);
+  FREE(sb);
+ 
+  currentKey = STRDUP(currentKey); /* would be destroyed with window */ 
+
+  /* destroy the window */
+  gtk_widget_destroy(ewm->window);
+  refreshMenuSensitivity();
+
+  hash2hex(&n, &hex1);
+  hash2hex(&k, &hex2);
+  message = MALLOC(512);
+  sprintf(message,
+         "%s inserted into namespace as\n"
+         "  gnunet://afs/%s/%s\n",
+         name,
+         (char*)&hex1,
+         (currentKey[0] != '\0') ? currentKey : (char*)&hex2);
+  LOG(LOG_DEBUG,
+      "DEBUG: %s\n",
+      message);
+  infoMessage(NO, message);
+  /* FIXME remind about the next key ... if its a verbal one,
+     it can't be reconstructed from the sblock later :( */
+  FREE(currentKey);
+  FREE(message);
+  FREE(name);  
+}  
+
+
+/**
+ * Exit the application (called when the main window
+ * is closed or the user selects File-Quit).
+ **/
+static void destroyNamespaceInsertWindow(GtkWidget * widget,
+                                        NamespaceInsertWindowModel * ewm) {
+  int i;
+  GList * tmp;
+  int row;
+
+  gtk_clist_freeze(GTK_CLIST(ewm->availableList));
+  tmp = GTK_CLIST(ewm->availableList)->selection;
+  while (tmp) {
+    row = (int)tmp->data;
+    tmp = tmp->next;
+
+    FREENONNULL(gtk_clist_get_row_data(GTK_CLIST(ewm->availableList),
+                                      row));
+    gtk_clist_remove(GTK_CLIST(ewm->availableList), 
+                    row);
+  }
+  gtk_clist_thaw(GTK_CLIST(ewm->availableList));
+  
+  for (i=0;i<ewm->updateableCount;i++)
+    FREE(ewm->updateableEntries[i]);
+  GROW(ewm->updateableEntries,
+       ewm->updateableCount,
+       0);
+  FREE(ewm);
+}
+
+static void appendToCList(RootNode * root,
+                         NamespaceInsertWindowModel * ewm) {
+  gchar * entry[1];
+  char * name;
+  char * desc;
+  char * mime;
+  RootNode * copy;
+  int row;
+ 
+  entry[0] = MALLOC(strlen(root->header.filename)+
+                   strlen(root->header.description)+
+                   strlen(root->header.mimetype)+
+                   128);
+  name = getFilenameFromNode(root);
+  desc = getDescriptionFromNode(root);
+  mime = getMimetypeFromNode(root);
+  sprintf(entry[0],
+         "%s, %s (%s, %u bytes)",
+         name,
+         desc,
+         mime,
+         (unsigned int) ntohl(root->header.fileIdentifier.file_length)); 
+  FREE(name);
+  FREE(desc);
+  FREE(mime);
+  row = gtk_clist_append(GTK_CLIST(ewm->availableList), 
+                        entry);
+  FREE(entry[0]);
+  copy = MALLOC(sizeof(RootNode));
+  memcpy(copy,
+        root,
+        sizeof(RootNode));
+  /* note: if you wish any clist to be sortable, you must
+     store the associated data in the list itself, not in an external
+     array! (same goes for all clists, but currently only "Files"
+     is sortable of the namespace related lists) - IW */
+  gtk_clist_set_row_data(GTK_CLIST(ewm->availableList),
+                        row,
+                        copy);
+}
+
+static void checkUpdateableSBlocks(SBlock * sb,
+                                  NamespaceInsertWindowModel * ewm) {
+  gchar * entry[1];
+  HashCode160 namespace;
+  SBlock * tmp;
+  int i;
+  
+  if (SBLOCK_UPDATE_NONE == ntohl(sb->updateInterval))
+    return; /* non-updateable SBlock */
+
+  /* check if namespace *matches* selected Pseudonym! */
+  hash(&sb->subspace,
+       sizeof(PublicKey),
+       &namespace);
+  if (! equalsHashCode160(&ewm->selectedPseudonym,
+                         &namespace) )
+    return;
+
+  /* check if SBlock is valid */
+  if (SYSERR == verifySBlock(sb))
+    return;
+  
+  /* skip if duplicate periodical (essentially irrelevant which
+   * of the blocks gets updated, the result is the same).
+   * FIXME? this causes trouble if two unrelated sblocks have 
+   * identical identifierIncrement, which should be unlikely? */
+  if(ntohl(sb->updateInterval)>0) {
+    for(i=0;i<ewm->updateableCount;i++) {
+      tmp = ewm->updateableEntries[i];
+      if ((0 == memcmp(&tmp->identifierIncrement,
+                              &sb->identifierIncrement,
+                      sizeof(HashCode160))) ) {
+        LOG(LOG_DEBUG, 
+            "DEBUG: skipping duplicate SBlock entry ...\n");
+        return;
+      }
+    }
+  }
+
+  /* ok, all checks pass: add */
+  sb->filename[MAX_FILENAME_LEN/2-1] = 0;
+  sb->description[MAX_DESC_LEN-1] = 0;
+  sb->mimetype[MAX_MIMETYPE_LEN/2-1] = 0;
+  entry[0] = MALLOC(strlen(sb->filename)+
+                   strlen(sb->description)+
+                   strlen(sb->mimetype)+
+                   128);
+  sprintf(entry[0],
+         "%s, %s (%s, %u bytes)",
+         sb->filename,
+         sb->description,
+         sb->mimetype,
+         (unsigned int) ntohl(sb->fileIdentifier.file_length)); 
+  gtk_clist_append(GTK_CLIST(ewm->sblockList), 
+                  entry);
+  FREE(entry[0]);
+  GROW(ewm->updateableEntries,
+       ewm->updateableCount,
+       ewm->updateableCount+1);
+  ewm->updateableEntries[ewm->updateableCount-1]
+    = MALLOC(sizeof(SBlock));
+  memcpy(ewm->updateableEntries[ewm->updateableCount-1],
+        sb,
+        sizeof(SBlock));
+}
+
+/**
+ * Only the ewm argument may be used since
+ * we may also be called from enter_callback
+ * which is called whenever the user presses
+ * ENTER in the password line.
+ */
+static void pselectCallback(GtkWidget * unused,
+                           gint rowX,
+                           gint column,
+                           GdkEventButton * event,
+                           NamespaceInsertWindowModel * ewm) {
+  GList * tmp;
+  int row;
+  gchar * key[1];
+  char * name;
+  char * pass;
+  Hostkey pseudo;
+  PublicKey pkey;
+  int i;
+  gchar * titlesNo[1] = { "--no update--" };
+
+  /* first, clear off the old sblock list */
+  gtk_clist_freeze(GTK_CLIST(ewm->sblockList));
+  gtk_clist_clear(GTK_CLIST(ewm->sblockList));
+  gtk_clist_append(GTK_CLIST(ewm->sblockList),
+                  &titlesNo[0]);
+  gtk_clist_thaw(GTK_CLIST(ewm->sblockList));   
+  
+  /* update ewm->selectedPseudonym */
+  tmp = GTK_CLIST(ewm->pseudonymList)->selection;
+  if (NULL == tmp) {
+    return;  
+  }
+  row = (int) tmp->data;
+  if ( row < 0 ) {
+    return;   
+  }
+
+  key[0] = NULL;
+  gtk_clist_get_text(GTK_CLIST(ewm->pseudonymList),
+                    row,
+                    0,
+                    &key[0]);
+  name = key[0];
+  if (name == NULL) {
+    return;   
+  }
+  pass = gtk_entry_get_text(GTK_ENTRY(ewm->passwordLine));
+  if (strlen(pass) == 0)
+    pass = NULL;
+  pseudo = readPseudonym(name, pass);
+  if (pseudo == NULL) {
+    return; /* wait for password to be entered... */  
+  }
+  getPublicKey(pseudo, 
+              &pkey);
+  freeHostkey(pseudo);
+  hash(&pkey,
+       sizeof(PublicKey),
+       &ewm->selectedPseudonym);
+  
+  /* clear entries from possible previous selection */
+  for (i=0;i<ewm->updateableCount;i++)
+    FREE(ewm->updateableEntries[i]);
+  GROW(ewm->updateableEntries,
+       ewm->updateableCount,
+       0);
+  
+  gtk_clist_freeze(GTK_CLIST(ewm->sblockList));
+  iterateDirectoryDatabase(DIR_CONTEXT_INSERT_SB,
+                          (RootNodeCallback)&checkUpdateableSBlocks,
+                          ewm);
+  gtk_clist_thaw(GTK_CLIST(ewm->sblockList));   
+}
+ 
+static void enter_callback(GtkWidget * unused,
+                          NamespaceInsertWindowModel * ewm) {
+  pselectCallback(NULL, 0, 0, NULL, ewm);
+}
+
+static void selectFrequencyCallback(GtkWidget * unused,
+                                   NamespaceInsertWindowModel * ewm) {
+  gchar * choice;
+  GList * tmp;
+
+  tmp = GTK_CLIST(ewm->sblockList)->selection;
+  if ( (NULL == tmp) || 
+       ( ((int)tmp->data) == 0) ) {    
+    choice = 
gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(ewm->updateInterval)->entry)); 
+    if (strcmp(choice, "--sporadic updates--") == 0) {
+      gtk_widget_set_sensitive(ewm->currentKey, TRUE);
+      gtk_widget_set_sensitive(ewm->nextKey, TRUE);
+    } else if (strcmp(choice, "--no updates--") == 0) {
+      gtk_widget_set_sensitive(ewm->currentKey, TRUE);
+      gtk_widget_set_sensitive(ewm->nextKey, FALSE);
+      gtk_entry_set_text(GTK_ENTRY(ewm->nextKey), 
+                        "");
+    } else {
+      /* periodic */
+      gtk_widget_set_sensitive(ewm->currentKey, TRUE);
+      gtk_widget_set_sensitive(ewm->nextKey, FALSE);
+      gtk_entry_set_text(GTK_ENTRY(ewm->nextKey), 
+                        "");
+    }
+  } else {
+    /* determined by SBlock, and SBlock has
+       already set the entries correctly */
+  }
+}
+
+/**
+ * The user selected an SBlock for an update.  Set the "update
+ * interval" field according to the update interval
+ * found in the SBlock.
+ */
+static void selectSBlockCallback(GtkWidget * unused,
+                                gint rowX,
+                                gint column,
+                                GdkEventButton * event,
+                                NamespaceInsertWindowModel * ewm) {
+  SBlock * pred;
+  TIME_T interval;
+  GList * tmp;
+  int row;
+  unsigned int days, hours, minutes, seconds;
+  char * txt;
+  HexName hex;
+  HashCode160 currentId;
+  HashCode160 nextId;
+  TIME_T now;
+    
+  gtk_entry_set_text(GTK_ENTRY(ewm->nextKey), 
+                    "");
+    
+  tmp = GTK_CLIST(ewm->sblockList)->selection;
+  if ( (NULL == tmp) || 
+       ( ((int)tmp->data) == 0) ) {
+    gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(ewm->updateInterval)->entry), 
+                      "--no updates--");
+    gtk_widget_set_sensitive(ewm->currentKey, TRUE);
+    gtk_widget_set_sensitive(ewm->nextKey, FALSE);
+    gtk_widget_set_sensitive(ewm->updateInterval, TRUE);
+    gtk_entry_set_text(GTK_ENTRY(ewm->currentKey), 
+                      "");
+    gtk_entry_set_text(GTK_ENTRY(ewm->nextKey), 
+                      "");    
+    return;
+  }
+
+  row = ((int) tmp->data) - 1; /* -1: first item is always "no update" */
+  if (row >= ewm->updateableCount) {
+    guiMessage("ERROR: this should never happen.\n");    
+    gtk_widget_set_sensitive(ewm->currentKey, FALSE);
+    gtk_widget_set_sensitive(ewm->nextKey, FALSE);
+    gtk_widget_set_sensitive(ewm->updateInterval, FALSE);
+    return; 
+  }
+  pred = ewm->updateableEntries[row];
+
+  interval = ntohl(pred->updateInterval);
+  if (interval == SBLOCK_UPDATE_SPORADIC) {
+    gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(ewm->updateInterval)->entry), 
+                      "--sporadic updates--");
+    hash2hex(&pred->nextIdentifier,
+            &hex);
+    gtk_entry_set_text(GTK_ENTRY(ewm->currentKey), 
+                      (gchar*)&hex);
+
+    gtk_widget_set_sensitive(ewm->currentKey, FALSE);
+    gtk_widget_set_sensitive(ewm->nextKey, TRUE);
+    gtk_widget_set_sensitive(ewm->updateInterval, FALSE);
+    return;
+  }
+  seconds = interval % 60;
+  interval = interval / 60;
+  minutes = interval % 60;
+  interval = interval / 60;
+  hours = interval % 24;
+  interval = interval / 24;
+  days = interval;
+  txt = MALLOC(256);
+  sprintf(txt,
+         "%u day%s %u hour%s %u minute%s %u second%s",
+         days, 
+         (days == 1) ? "" : "s",
+         hours, 
+         (hours == 1) ? "" : "s",
+         minutes,
+         (minutes == 1) ? "" : "s",
+         seconds,
+         (seconds == 1) ? "" : "s");
+  gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(ewm->updateInterval)->entry), 
+                    txt);  
+  FREE(txt);
+  /* periodic: all are pre-determined! */
+
+  TIME(&now);
+  computeIdAtTime(pred, now, &currentId);
+  computeIdAtTime(pred, now + ntohl(pred->updateInterval), &nextId);
+  hash2hex(&currentId,
+          &hex);
+  gtk_entry_set_text(GTK_ENTRY(ewm->currentKey), 
+                    (gchar*)&hex);
+  hash2hex(&nextId,
+          &hex);
+  gtk_entry_set_text(GTK_ENTRY(ewm->nextKey), 
+                    (gchar*)&hex);
+  gtk_widget_set_sensitive(ewm->nextKey, FALSE);
+  gtk_widget_set_sensitive(ewm->currentKey, FALSE);
+  gtk_widget_set_sensitive(ewm->updateInterval, FALSE);
+}
+
+
+/**
+ * Open a window to allow the user to build a namespace entry.
+ *
+ * @param unused GTK handle that is not used
+ * @param context selector for a subset of the known RootNodes
+ **/
+void openAssembleNamespaceDialog(GtkWidget * unused,
+                                unsigned int context) {
+  NamespaceInsertWindowModel * ewm;
+  GtkWidget * window;
+  GtkWidget * vbox, *vboxX, * hbox, * hboxX;
+  GtkWidget * clist;
+  GtkWidget * scrolled_window;
+  GtkWidget * label;
+  GtkWidget * separator; 
+  GtkWidget * button_ok;
+  GtkWidget * button_cancel;
+  GtkWidget * combo;
+  GList * glist;
+  int i;
+  int cnt;
+  char ** list;
+  gchar * titles[1] = { "Pseudonyms" };
+  gchar * titlesNo[1] = { "--no update--" };
+  gchar * titlesSBlocks[1] = { "Updateable SBlocks for pseudonym" };
+  gchar * titlesAvailable[1] = { "Files available" };
+
+  ewm = MALLOC(sizeof(NamespaceInsertWindowModel));
+  memset(ewm, 0, sizeof(NamespaceInsertWindowModel));
+
+  /* create new window for editing */
+  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+  ewm->window = window;
+  gtk_widget_set_usize(GTK_WIDGET(window),
+                      780,
+                      580);
+  gtk_window_set_title(GTK_WINDOW(window), 
+                      "Insert into Namespace");
+
+  /* add container for window elements */
+  vbox = gtk_vbox_new(FALSE, 15);
+  gtk_container_add(GTK_CONTAINER(window),
+                   vbox);
+  gtk_widget_show(vbox);
+
+  /* when user clicks on close box, always "destroy" */
+  gtk_signal_connect(GTK_OBJECT(window),
+                    "delete_event",
+                    GTK_SIGNAL_FUNC(deleteEvent),
+                    ewm);
+  /* whenever edit window gets destroyed, 
+     free *ALL* ewm data */
+  gtk_signal_connect(GTK_OBJECT(window),
+                    "destroy",
+                    GTK_SIGNAL_FUNC(destroyNamespaceInsertWindow),
+                    ewm);
+
+  gtk_container_set_border_width(GTK_CONTAINER(window), 
+                                10);
+
+
+  /* arrange a pseudonym box left to a "select SBlock to update" box */
+  hbox = gtk_hbox_new(FALSE, 5);
+  gtk_box_pack_start(GTK_BOX(vbox),
+                    hbox, 
+                    TRUE, 
+                    TRUE, 
+                    0);
+  gtk_widget_show(hbox);
+
+  /* add a list of pseudonyms */
+  vboxX = gtk_vbox_new(FALSE, 0);
+  gtk_container_add(GTK_CONTAINER(hbox),
+                   vboxX);
+  gtk_widget_show(vboxX);
+
+  scrolled_window = gtk_scrolled_window_new(NULL, NULL);
+  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
+                                GTK_POLICY_AUTOMATIC, 
+                                GTK_POLICY_ALWAYS);
+  gtk_box_pack_start(GTK_BOX(vboxX), 
+                    scrolled_window, 
+                    TRUE, 
+                    TRUE, 
+                    0);
+  gtk_widget_show(scrolled_window);  
+  clist = gtk_clist_new_with_titles(1, titles); 
+  ewm->pseudonymList = clist;
+  gtk_clist_set_column_width(GTK_CLIST(clist),
+                            0,
+                            150);
+  gtk_container_add(GTK_CONTAINER(scrolled_window), 
+                   clist);
+  gtk_widget_show(clist);
+  /* add the known Pseudonyms to the list */
+  list = NULL;
+  cnt = listPseudonyms(&list);
+  if (cnt > 0) {
+    gtk_clist_freeze(GTK_CLIST(clist));
+    for (i=0;i<cnt;i++) {
+      gtk_clist_append(GTK_CLIST(clist),
+                      &list[i]);
+      FREE(list[i]);
+    }
+    gtk_clist_thaw(GTK_CLIST(clist));
+  }
+  FREENONNULL(list);
+  /* add callback: if a pseudonym is
+     selected, the "updateable SBlocks" list
+     must be updated! */
+  gtk_signal_connect(GTK_OBJECT(clist),
+                    "select_row",
+                    GTK_SIGNAL_FUNC(pselectCallback),
+                    ewm);
+
+
+
+  /* Create a line to enter the password */
+  hboxX = gtk_hbox_new(FALSE, 0);
+  gtk_box_pack_start(GTK_BOX(vboxX),
+                    hboxX,
+                    FALSE,
+                    FALSE,
+                    0);
+  gtk_widget_show(hboxX);
+  label = gtk_label_new("Pseudonym Password:");
+  gtk_box_pack_start(GTK_BOX(hboxX),
+                    label, 
+                    FALSE, 
+                    FALSE, 
+                    0);
+  gtk_widget_show(label); 
+  ewm->passwordLine = gtk_entry_new();
+  gtk_entry_set_visibility(GTK_ENTRY(ewm->passwordLine), FALSE);
+  gtk_box_pack_start(GTK_BOX(hboxX),
+                    ewm->passwordLine,
+                    TRUE,
+                    TRUE,
+                    0);
+  gtk_entry_set_text(GTK_ENTRY(ewm->passwordLine), 
+                    "");
+  gtk_widget_show(ewm->passwordLine);
+  gtk_signal_connect(GTK_OBJECT(ewm->passwordLine), 
+                    "activate",
+                    GTK_SIGNAL_FUNC(enter_callback),
+                    ewm);
+
+  /* add separator */
+  separator = gtk_vseparator_new();
+  gtk_box_pack_start(GTK_BOX(hbox),
+                    separator,
+                    FALSE, 
+                    FALSE,
+                    0);
+  gtk_widget_show(separator);
+  /* ok, now another feature in the hbox:
+     select which SBlock to update! */
+
+  scrolled_window = gtk_scrolled_window_new(NULL, NULL);
+  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
+                                GTK_POLICY_AUTOMATIC, 
+                                GTK_POLICY_ALWAYS);
+  gtk_box_pack_start(GTK_BOX(hbox), 
+                    scrolled_window, 
+                    TRUE, 
+                    TRUE, 
+                    0);
+  gtk_widget_show(scrolled_window);  
+  clist = gtk_clist_new_with_titles(1, titlesSBlocks); 
+  ewm->sblockList = clist;
+  gtk_container_add(GTK_CONTAINER(scrolled_window), 
+                   clist);
+  gtk_widget_show(clist);
+  /* add the known SBlocks to the list */
+  list = NULL;
+  gtk_clist_freeze(GTK_CLIST(clist));
+  gtk_clist_append(GTK_CLIST(clist),
+                  &titlesNo[0]);
+  gtk_clist_thaw(GTK_CLIST(clist)); 
+  gtk_signal_connect(GTK_OBJECT(clist),
+                    "select_row",
+                    GTK_SIGNAL_FUNC(selectSBlockCallback),
+                    ewm);
+  gtk_signal_connect(GTK_OBJECT(clist),
+                    "unselect_row",
+                    GTK_SIGNAL_FUNC(selectSBlockCallback),
+                    ewm);
+  
+  /* add separator */
+  separator = gtk_hseparator_new();
+  gtk_box_pack_start(GTK_BOX(vbox),
+                    separator,
+                    FALSE, 
+                    FALSE,
+                    0);
+  gtk_widget_show(separator);
+
+  /* add interval / non-periodic selection */
+  hbox = gtk_hbox_new(FALSE, 10);
+  gtk_box_pack_start(GTK_BOX(vbox),
+                    hbox,
+                    FALSE,
+                    FALSE,
+                    0);
+  gtk_widget_show(hbox);
+  label = gtk_label_new("Update frequency:");
+  gtk_box_pack_start(GTK_BOX(hbox),
+                    label, 
+                    FALSE, 
+                    FALSE, 
+                    0);
+  gtk_widget_show(label); 
+
+  combo = gtk_combo_new(); 
+  ewm->updateInterval = combo;
+  gtk_container_add(GTK_CONTAINER(hbox),
+                   combo);
+  gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(combo)->entry), 
+                    "--no updates--");
+  glist = NULL;
+  glist = g_list_append(glist, "--no updates--");
+  glist = g_list_append(glist, "--sporadic updates--");
+  glist = g_list_append(glist, "12 hours"); 
+  glist = g_list_append(glist, "1 day"); 
+  glist = g_list_append(glist, "2 days"); 
+  glist = g_list_append(glist, "7 days"); 
+  glist = g_list_append(glist, "30 days"); 
+  glist = g_list_append(glist, "2 hours 30 minutes"); 
+
+  gtk_combo_set_popdown_strings(GTK_COMBO(combo), 
+                               glist) ;
+  gtk_signal_connect(GTK_OBJECT(GTK_COMBO(combo)->entry),
+                    "changed",
+                    GTK_SIGNAL_FUNC(selectFrequencyCallback),
+                    ewm);
+  gtk_widget_show(combo);
+  
+  /* add keyword boxes */ 
+  hbox = gtk_hbox_new(FALSE, 0);
+  gtk_box_pack_start(GTK_BOX(vbox),
+                    hbox,
+                    FALSE,
+                    TRUE,
+                    0);
+  gtk_widget_show(hbox);
+  label = gtk_label_new("Current keyword: ");
+  gtk_box_pack_start(GTK_BOX(hbox),
+                    label, 
+                    FALSE, 
+                    FALSE, 
+                    0);
+  gtk_widget_show(label);
+  ewm->currentKey = gtk_entry_new();
+  gtk_entry_set_text(GTK_ENTRY(ewm->currentKey), 
+                    "");
+  gtk_box_pack_start(GTK_BOX(hbox),
+                    ewm->currentKey, 
+                    TRUE, 
+                    TRUE, 
+                    0);
+  gtk_widget_show(ewm->currentKey);
+  hbox = gtk_hbox_new(FALSE, 0);
+  gtk_box_pack_start(GTK_BOX(vbox),
+                    hbox,
+                    FALSE,
+                    TRUE,
+                    0);
+  gtk_widget_show(hbox);
+  label = gtk_label_new("Future keyword: ");
+  gtk_box_pack_start(GTK_BOX(hbox),
+                    label, 
+                    FALSE, 
+                    FALSE, 
+                    0);
+  gtk_widget_show(label);
+  ewm->nextKey = gtk_entry_new();
+  gtk_entry_set_text(GTK_ENTRY(ewm->nextKey), 
+                    "");
+  gtk_box_pack_start(GTK_BOX(hbox),
+                    ewm->nextKey, 
+                    TRUE, 
+                    TRUE, 
+                    0);
+  gtk_widget_set_sensitive(ewm->nextKey, FALSE);
+  gtk_widget_show(ewm->nextKey);
+
+  /* add separator */
+  separator = gtk_hseparator_new();
+  gtk_box_pack_start(GTK_BOX(vbox),
+                    separator,
+                    FALSE, 
+                    FALSE,
+                    0);
+  gtk_widget_show(separator);
+
+
+  /* add the box for the two lists */
+  hbox = gtk_hbox_new(FALSE, 0);
+  gtk_box_pack_start(GTK_BOX(vbox),
+                    hbox,
+                    TRUE,
+                    TRUE,
+                    0);
+  gtk_widget_show(hbox);
+
+  /* add a list of available entries */
+  scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
+                                GTK_POLICY_AUTOMATIC, 
+                                GTK_POLICY_ALWAYS);
+  gtk_box_pack_start(GTK_BOX(hbox), 
+                    scrolled_window, 
+                    TRUE, 
+                    TRUE, 
+                    0);
+  gtk_widget_show(scrolled_window);  
+  clist = gtk_clist_new_with_titles(1, titlesAvailable); 
+  ewm->availableList = clist;
+  gtk_container_add(GTK_CONTAINER(scrolled_window), 
+                   clist);
+  gtk_clist_set_sort_column(GTK_CLIST(clist),0);
+  gtk_clist_set_auto_sort(GTK_CLIST(clist),TRUE);
+  /* add the known RootNodes to the list */
+  gtk_clist_freeze(GTK_CLIST(clist));
+  iterateDirectoryDatabase(context,
+                          (RootNodeCallback)&appendToCList,
+                          ewm);
+  gtk_clist_thaw(GTK_CLIST(clist));
+  gtk_widget_show(clist);
+
+
+  /* add the insertion ok/cancel buttons */
+  separator = gtk_hseparator_new();
+  gtk_box_pack_start(GTK_BOX(vbox),
+                    separator,
+                    FALSE, 
+                    FALSE,
+                    0);
+  gtk_widget_show(separator);
+
+  hbox = gtk_hbox_new(FALSE, 0);
+  gtk_box_pack_start(GTK_BOX(vbox),
+                    hbox, 
+                    FALSE, 
+                    FALSE, 
+                    0);
+  gtk_widget_show(hbox);
+  button_ok = gtk_button_new_with_label("Ok");
+  button_cancel = gtk_button_new_with_label("Cancel");
+  gtk_box_pack_start(GTK_BOX(hbox),
+                    button_ok,
+                    TRUE,
+                    TRUE,
+                    0);
+  gtk_box_pack_start(GTK_BOX(hbox), 
+                    button_cancel, 
+                    TRUE,
+                    TRUE, 
+                    0);
+  gtk_signal_connect(GTK_OBJECT(button_ok), 
+                    "clicked",
+                    GTK_SIGNAL_FUNC(buildNSEntry),
+                    ewm);
+  gtk_signal_connect(GTK_OBJECT(button_cancel),
+                    "clicked",
+                    GTK_SIGNAL_FUNC(destroyWidget),
+                    window);
+  gtk_widget_show(button_ok);
+  gtk_widget_show(button_cancel);
+
+  /* all clear, show the window */
+  gtk_widget_show(window);
+}
+
+
+/* ********************** SEARCH ******************** */
+/* ********************** SEARCH ******************** */
+/* ********************** SEARCH ******************** */
+
+/**
+ * @brief state of the namespace search window
+ **/
+typedef struct {
+  GtkWidget * window;
+  GtkWidget * namespaceCombo;
+  GtkWidget * searchkeyLine;
+} NamespaceSearchWindowModel;
+
+
+/**
+ * Exit the application (called when the main window
+ * is closed or the user selects File-Quit).
+ **/
+static void destroyNamespaceSearchWindow(GtkWidget * widget,
+                                        NamespaceSearchWindowModel * ewm) {
+  FREE(ewm);
+}
+
+typedef struct {
+  HashCode160 n;
+  HashCode160 k;
+  ListModel * model;
+  HashCode160 * seen;
+  int seenCount;
+  HashCode160 * results;
+  int resultCount;
+} NSSearchThreadData;
+
+/**
+ * The main method of the search-thread.
+ *
+ * @param n namespace to search
+ * @param k code to search for
+ * @param model Data related to the search
+ * @return OK on success, SYSERR on error
+ **/
+static int startNamespaceSearchThread(HashCode160 * n,
+                                     HashCode160 * k,
+                                     ListModel * model);
+
+/**
+ * Run the namespace search.  Starts the search
+ * thread and adds a new tab to the window list.
+ */
+static void startSearch(HashCode160 * n,
+                       HashCode160 * k) {
+  ListModel * model;  
+  int ok;
+  GtkWidget * box;
+
+  /* start search! */
+  model = (ListModel*) MALLOC(sizeof(ListModel));
+  model->type = LM_TYPE_NSSEARCH;
+  model->doTerminate = NO;
+  model->skipMenuRefresh = NO;
+  model->SEARCH_socket_ = NULL;
+
+  box = initializeSearchResultList(model);
+
+  /* start searching */
+  ok = startNamespaceSearchThread(n, 
+                                 k,
+                                 model);
+  if (ok == SYSERR) {
+    LOG(LOG_DEBUG,
+        "DEBUG: startNamespaceSearchThread failed\n");
+    releaseClientSocket(model->SEARCH_socket_);
+    gtkSaveCall((GtkFunction) doDestroyWidget, box);
+    FREE(model);
+  } else {
+    char * label;
+    HexName hex1;
+    HexName hex2;
+
+    label = MALLOC(128);    
+    hash2hex(n, &hex1);
+    hash2hex(k, &hex2);
+    sprintf(label, 
+           "%.8s/%.8s", /* %8s: otherwise MUCH too long... */
+           (char*)&hex1,
+           (char*)&hex2);
+    addToNotebook(label,
+                 box);
+    FREE(label);
+
+    LOG(LOG_DEBUG, 
+       "DEBUG: namespace search initiated for %s %s\n",
+        (char *)&hex1,
+       (char *)&hex2);
+  }
+}
+
+static void displayResultGTK_(SBlock * sb,
+                             NSSearchThreadData * sqc) {
+  HashCode160 curK;
+  int i;
+  HexName hex;
+  
+  hash(sb, sizeof(SBlock), &curK);
+  hash2hex(&curK,
+          &hex);
+  LOG(LOG_DEBUG, 
+      "DEBUG: got namespace result for %s\n", 
+      (char *)&hex);
+  for (i=0;i<sqc->resultCount;i++)
+    if (equalsHashCode160(&curK,
+                         &sqc->results[i])) {
+      LOG(LOG_DEBUG, 
+          "DEBUG: displayResultGTK_ skipping previously seen entry %s\n",
+          (char *)&hex);
+      return; /* displayed already */
+    }
+  GROW(sqc->results,
+       sqc->resultCount,
+       sqc->resultCount+1);
+  memcpy(&sqc->results[sqc->resultCount-1],
+        &curK,
+        sizeof(HashCode160));
+  displayResultGTK((RootNode*)sb, 
+                  sqc->model);
+  refreshMenuSensitivity();
+  GROW(sqc->seen,
+       sqc->seenCount,
+       sqc->seenCount+1);
+  memcpy(&sqc->seen[sqc->seenCount-1], 
+        &sqc->k, 
+        sizeof(HashCode160));
+  
+  /* now search for update if possible! */
+  computeIdAtTime(sb,
+                 TIME(NULL),
+                 &curK);
+  for (i=0;i<sqc->seenCount;i++)         
+    if (equalsHashCode160(&curK,
+                         &sqc->seen[i])) {
+      HashCode160 ns;
+      hash(&sb->subspace,
+          sizeof(PublicKey),
+          &ns);
+      hash2hex(&ns,
+              &hex);
+      guiMessage("Found the most recent version for a hit\n"
+                 "in your original search in namespace\n\n"
+                 "%s\n\nGood.",
+                STRDUP((char *)&hex));
+      LOG(LOG_DEBUG, 
+          "DEBUG: namespace result %s is the most recent\n",
+         (char*)&hex);
+      return; /* found most up-to-date / all versions! */
+    }
+  /* else: start new parallel search! */
+  LOG(LOG_DEBUG, 
+      "DEBUG: starting parallel search for the current version of %s\n",
+      (char*)&hex);
+  startSearch(&sqc->n,
+             &curK);
+}
+
+int searchSBlock_(NSSearchThreadData * sqc) {
+  LOG(LOG_DEBUG, 
+      "DEBUG: enter searchSBlock_\n");
+
+  sqc->seen = NULL;
+  sqc->seenCount = 0;
+  sqc->results = NULL;
+  sqc->resultCount = 0;
+  sqc->model->SEARCH_socket_ = getClientSocket();
+  if (sqc->model->SEARCH_socket_ != NULL) {
+    searchSBlock(sqc->model->SEARCH_socket_,
+                &sqc->n,
+                &sqc->k,
+                (TestTerminateThread)&testTermination,
+                sqc->model,
+                (NSSearchResultCallback)&displayResultGTK_,
+                sqc);
+  } else {
+    LOG(LOG_DEBUG, 
+        "DEBUG: socket was NULL in searchSBlock_\n");
+  }
+  GROW(sqc->seen,
+       sqc->seenCount,
+       0);
+  GROW(sqc->results,
+       sqc->resultCount,
+       0);
+  FREE(sqc);
+  return 0;
+}
+
+/**
+ * The main method of the search-thread.
+ *
+ * @param n namespace to search
+ * @param k code to search for
+ * @param model Data related to the search
+ * @return OK on success, SYSERR on error
+ **/
+static int startNamespaceSearchThread(HashCode160 * n,
+                                     HashCode160 * k,
+                                     ListModel * model) {
+  NSSearchThreadData * sqc;
+
+  sqc = MALLOC(sizeof(NSSearchThreadData));
+  memcpy(&sqc->n,
+        n,
+        sizeof(HashCode160));
+  memcpy(&sqc->k,
+        k,
+        sizeof(HashCode160));
+  sqc->model = model;
+  if (0 != PTHREAD_CREATE(&model->thread,
+                         (PThreadMain) &searchSBlock_,
+                         sqc,
+                         16 * 1024)) {
+    errexit("FATAL: could not create SBlock search thread (%s)!\n",
+           STRERROR(errno));    
+  }
+  return OK;
+}
+
+
+/**
+ * Start the namespace search.  This method obtains
+ * n and k from the input window and then calls
+ * the actual startSearch function.
+ *
+ * @param dummy not used
+ * @param ewm the state of the search window
+ **/
+static void searchNS(GtkWidget * dummy, 
+                    NamespaceSearchWindowModel * ewm) {
+  HashCode160 n;
+  HashCode160 k;
+  char * c;
+
+  c = gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(ewm->namespaceCombo)->entry));
+  if (SYSERR == tryhex2hash(c, &n)) {
+    guiMessage("ERROR: must specify valid HEX code for namespace."); 
+    return;
+  }
+  c = gtk_entry_get_text(GTK_ENTRY(ewm->searchkeyLine));
+  if (strlen(c) == 0) {
+    guiMessage("ERROR: must specify string (or HEX code) for search key."); 
+    return;
+  }  
+  tryhex2hashOrHashString(c, &k);
+
+  /* destroy the window */
+  gtk_widget_destroy(ewm->window);
+  startSearch(&n, &k);
+}
+
+/**
+ * Open a window to allow the user to search a namespace
+ *
+ * @param unused GTK handle that is not used
+ * @param unused2 argument that is always 0
+ **/
+void searchNamespace(GtkWidget * unused,
+                    unsigned int unused2) {
+  NamespaceSearchWindowModel * ewm;
+  GtkWidget * window;
+  GtkWidget * vbox, * hbox;
+  GtkWidget * label;
+  GtkWidget * button_ok;
+  GtkWidget * button_cancel;
+  GList * glist;
+  HashCode160 * list;
+  int ret;
+  int i;
+
+  ewm = MALLOC(sizeof(NamespaceSearchWindowModel));
+  
+  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+  ewm->window = window;
+  gtk_widget_set_usize(GTK_WIDGET(window),
+                      650,
+                      120);
+  gtk_window_set_title(GTK_WINDOW(window), 
+                      "Search Namespace");
+
+  /* add container for window elements */
+  vbox = gtk_vbox_new(FALSE, 10);
+  gtk_container_add(GTK_CONTAINER(window),
+                   vbox);
+  gtk_widget_show(vbox);
+
+  /* when user clicks on close box, always "destroy" */
+  gtk_signal_connect(GTK_OBJECT(window),
+                    "delete_event",
+                    GTK_SIGNAL_FUNC(deleteEvent),
+                    ewm);
+  /* whenever edit window gets destroyed, 
+     free *ALL* ewm data */
+  gtk_signal_connect(GTK_OBJECT(window),
+                    "destroy",
+                    GTK_SIGNAL_FUNC(destroyNamespaceSearchWindow),
+                    ewm);
+
+  gtk_container_set_border_width(GTK_CONTAINER(window), 
+                                10);
+
+
+  /* Create a line to enter the namespace identifier */
+  hbox = gtk_hbox_new(FALSE, 0);
+  gtk_box_pack_start(GTK_BOX(vbox),
+                    hbox,
+                    TRUE,
+                    TRUE,
+                    0);
+  gtk_widget_show(hbox);
+  label = gtk_label_new("Namespace identifier:");
+  gtk_box_pack_start(GTK_BOX(hbox),
+                    label, 
+                    FALSE, 
+                    FALSE, 
+                    0);
+  gtk_widget_show(label); 
+
+  ewm->namespaceCombo = gtk_combo_new();
+  gtk_box_pack_start(GTK_BOX(hbox),
+                    ewm->namespaceCombo,
+                    TRUE,
+                    TRUE,
+                    0);
+  gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(ewm->namespaceCombo)->entry), 
+                    "");
+  glist = NULL;
+  glist = g_list_append(glist, 
+                       "");
+  list = NULL;
+  ret = listNamespaces(&list);
+
+  for (i=0;i<ret;i++) {
+    HexName hex;
+    hash2hex(&list[i], 
+             &hex);
+    LOG(LOG_DEBUG, 
+        "DEBUG: appending namespace id %s\n", 
+        (char*)&hex);
+    glist = g_list_append(glist, 
+                         STRDUP((char*)&hex));
+    /* FIXME: memory from STRDUP above is probably not
+     * freed anywhere - but must be DUPED or gnunet-gtk
+     * will display only a single hash multiple times */
+  }
+  if (ret > 0)
+    FREE(list);
+  gtk_combo_set_popdown_strings(GTK_COMBO(ewm->namespaceCombo), 
+                               glist) ;
+  gtk_widget_show(ewm->namespaceCombo);
+
+
+  /* Create a line to enter the search key identifier */
+  hbox = gtk_hbox_new(FALSE, 0);
+  gtk_box_pack_start(GTK_BOX(vbox),
+                    hbox,
+                    TRUE,
+                    TRUE,
+                    0);
+  gtk_widget_show(hbox);
+  label = gtk_label_new("Search key identifier:");
+  gtk_box_pack_start(GTK_BOX(hbox),
+                    label, 
+                    FALSE, 
+                    FALSE, 
+                    0);
+  gtk_widget_show(label); 
+  ewm->searchkeyLine = gtk_entry_new();
+  gtk_box_pack_start(GTK_BOX(hbox),
+                    ewm->searchkeyLine,
+                    TRUE,
+                    TRUE,
+                    0);
+  gtk_entry_set_text(GTK_ENTRY(ewm->searchkeyLine), 
+                    "");
+  gtk_signal_connect(GTK_OBJECT(ewm->searchkeyLine),
+                     "activate",
+                    GTK_SIGNAL_FUNC(searchNS),
+                    ewm);
+  gtk_widget_show(ewm->searchkeyLine);
+ 
+  /* add the ok/cancel buttons */
+
+  hbox = gtk_hbox_new(FALSE, 0);
+  gtk_box_pack_start(GTK_BOX(vbox),
+                    hbox, 
+                    FALSE, 
+                    FALSE, 
+                    0);
+  gtk_widget_show(hbox);
+  button_ok = gtk_button_new_with_label("Search");
+  button_cancel = gtk_button_new_with_label("Cancel");
+  gtk_box_pack_start(GTK_BOX(hbox),
+                    button_ok,
+                    TRUE,
+                    TRUE,
+                    0);
+  gtk_box_pack_start(GTK_BOX(hbox), 
+                    button_cancel, 
+                    TRUE,
+                    TRUE, 
+                    0);
+  gtk_signal_connect(GTK_OBJECT(button_ok), 
+                    "clicked",
+                    GTK_SIGNAL_FUNC(searchNS),
+                    ewm);
+  gtk_signal_connect(GTK_OBJECT(button_cancel),
+                    "clicked",
+                    GTK_SIGNAL_FUNC(destroyWidget),
+                    window);
+  gtk_widget_show(button_ok);
+  gtk_widget_show(button_cancel);
+
+  /* all clear, show the window */
+  gtk_widget_show(window);
+
+}
+
+
+/* end of namespace.c */

Added: freeway/src/org/gnu/freeway/protocol/afs/swing/namespace.h
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/swing/namespace.h  2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/swing/namespace.h  2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,48 @@
+/*
+     This file is part of GNUnet
+
+     GNUnet is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with GNUnet; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file applications/afs/gtkui/namespace.h
+ * @brief namespace insert and search entry points
+ * @author Christian Grothoff
+ **/
+
+#ifndef GTKUI_NAMESPACE_H
+#define GTKUI_NAMESPACE_H
+
+
+
+/**
+ * Open a window to allow the user to build a namespace.
+ *
+ * @param context selector for a subset of the known RootNodes
+ **/
+void openAssembleNamespaceDialog(GtkWidget * unused,
+                                unsigned int unused2);
+
+/**
+ * Open a window to allow the user to search a namespace
+ *
+ * @param unused GTK handle that is not used
+ * @param unused2 argument that is always 0
+ **/
+void searchNamespace(GtkWidget * unused,
+                    unsigned int unused2);
+
+#endif

Added: freeway/src/org/gnu/freeway/protocol/afs/swing/pseudonyms.c
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/swing/pseudonyms.c 2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/swing/pseudonyms.c 2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,402 @@
+/*
+     This file is part of GNUnet
+
+     GNUnet is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with GNUnet; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file src/applications/afs/gtkui/pseudonyms.c
+ * @brief dialogs for creating and deleting pseudonyms
+ * @author Christian Grothoff
+ **/
+#include "gnunet_afs_esed2.h"
+#include "helper.h"
+#include "insertprogress.h"
+#include "pseudonyms.h"
+#include "main.h"
+
+/**
+ * @brief state of the CreatePseudonym window
+ **/
+typedef struct {
+  GtkWidget * window;
+  GtkWidget * pseudonymLine;
+  GtkWidget * passwordLine;
+} CreatePseudonymWindowModel;
+
+
+/**
+ * Collects the results of the assembly dialog, creates an insertion 
+ * progressbar and launches the insertion thread.
+ *
+ * @param dummy not used
+ * @param ewm the state of the edit window
+ **/
+static void create_ok(GtkWidget * dummy, 
+                     CreatePseudonymWindowModel * ewm) {
+  char * name;
+  char * pass;
+  Hostkey ps;
+  
+  name = gtk_entry_get_text(GTK_ENTRY(ewm->pseudonymLine));
+  if (name == NULL) {
+    guiMessage("WARNING: cowardly refusing to create pseudonym without 
name.\n");
+    return;
+  }
+  name = STRDUP(name);
+  pass = gtk_entry_get_text(GTK_ENTRY(ewm->passwordLine));
+  if (pass != NULL) 
+    pass = STRDUP(pass);
+  gtk_widget_destroy(ewm->window);
+
+  /* we may want to do this in another thread
+     to keep the event manager running (and potentially
+     even give feedback in the form of a popup window).
+     After all, this can take a while... */
+  ps = createPseudonym(name, pass);
+  if (ps == NULL)
+    guiMessage("WARNING: failed to create pseudonym (see logs).\n");
+  else
+    freeHostkey(ps);
+  refreshMenuSensitivity();
+  FREE(name);
+  FREENONNULL(pass);
+}
+
+/**
+ * Exit the application (called when the main window
+ * is closed or the user selects File-Quit).
+ **/
+static void destroyPCWindow(GtkWidget * widget,
+                           CreatePseudonymWindowModel * ewm) {
+  FREE(ewm);
+}
+
+
+/**
+ * Open a window to allow the user to create a pseudonym
+ *
+ * @param unused GTK handle that is not used
+ * @param unused2 not used
+ **/
+void openCreatePseudonymDialog(GtkWidget * unused,
+                              unsigned int unused2) {
+  CreatePseudonymWindowModel * ewm;
+  GtkWidget * vbox;
+  GtkWidget * hbox;
+  GtkWidget * label;
+  GtkWidget * button_ok;
+  GtkWidget * button_cancel;
+  GtkWidget * separator;
+
+  ewm = MALLOC(sizeof(CreatePseudonymWindowModel));
+  /* create new window for editing */
+  ewm->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+  gtk_widget_set_usize(GTK_WIDGET(ewm->window),
+                      400,
+                      120);
+  gtk_window_set_title(GTK_WINDOW(ewm->window), 
+                      "Create Pseudonym");
+
+  /* add container for window elements */
+  vbox = gtk_vbox_new(FALSE, 0);
+  gtk_container_add(GTK_CONTAINER(ewm->window),
+                   vbox);
+  gtk_widget_show(vbox);
+
+  /* when user clicks on close box, always "destroy" */
+  gtk_signal_connect(GTK_OBJECT(ewm->window),
+                    "delete_event",
+                    GTK_SIGNAL_FUNC(deleteEvent),
+                    ewm);
+  /* whenever edit window gets destroyed, 
+     free *ALL* ewm data */
+  gtk_signal_connect(GTK_OBJECT(ewm->window),
+                    "destroy",
+                    GTK_SIGNAL_FUNC(destroyPCWindow),
+                    ewm);
+
+  gtk_container_set_border_width(GTK_CONTAINER(ewm->window), 
+                                10);
+
+  /* Create a line to change the pseudonym */
+  hbox = gtk_hbox_new(FALSE, 0);
+  gtk_box_pack_start(GTK_BOX(vbox),
+                    hbox,
+                    FALSE,
+                    TRUE,
+                    0);
+  gtk_widget_show(hbox);
+  label = gtk_label_new("Pseudonym:");
+  gtk_box_pack_start(GTK_BOX(hbox),
+                    label, 
+                    FALSE, 
+                    FALSE, 
+                    0);
+  gtk_widget_show(label); 
+  ewm->pseudonymLine = gtk_entry_new();
+  gtk_box_pack_start(GTK_BOX(hbox),
+                    ewm->pseudonymLine,
+                    TRUE,
+                    TRUE,
+                    0);
+  gtk_entry_set_text(GTK_ENTRY(ewm->pseudonymLine), 
+                    "");
+  gtk_widget_show(ewm->pseudonymLine);
+  
+  /* Create a line to change the description */
+  hbox = gtk_hbox_new(FALSE, 0);
+  gtk_box_pack_start(GTK_BOX(vbox),
+                    hbox,
+                    FALSE,
+                    TRUE,
+                    0);
+  gtk_widget_show(hbox);
+  label = gtk_label_new("Password:");
+  gtk_box_pack_start(GTK_BOX(hbox),
+                    label, 
+                    FALSE, 
+                    FALSE, 
+                    0);
+  gtk_widget_show(label);  
+  ewm->passwordLine = gtk_entry_new();
+  gtk_box_pack_start(GTK_BOX(hbox),
+                    ewm->passwordLine, 
+                    TRUE, 
+                    TRUE,
+                    0);
+  gtk_entry_set_text(GTK_ENTRY(ewm->passwordLine), 
+                    "");
+  gtk_widget_show(ewm->passwordLine);
+  
+  separator = gtk_hseparator_new();
+  gtk_box_pack_start(GTK_BOX(vbox),
+                    separator,
+                    TRUE, 
+                    TRUE,
+                    0);
+  gtk_widget_show(separator);
+
+  /* add the insertion ok/cancel buttons */
+  hbox = gtk_hbox_new(FALSE, 0);
+  gtk_box_pack_start(GTK_BOX(vbox),
+                    hbox, 
+                    FALSE, 
+                    TRUE, 
+                    0);
+  gtk_widget_show(hbox);
+  button_ok = gtk_button_new_with_label("Ok");
+  button_cancel = gtk_button_new_with_label("Cancel");
+  gtk_box_pack_start(GTK_BOX(hbox),
+                    button_ok,
+                    TRUE,
+                    TRUE,
+                    0);
+  gtk_box_pack_start(GTK_BOX(hbox), 
+                    button_cancel, 
+                    TRUE,
+                    TRUE, 
+                    0);
+  gtk_signal_connect(GTK_OBJECT(button_ok), 
+                    "clicked",
+                    GTK_SIGNAL_FUNC(create_ok),
+                    ewm);
+  gtk_signal_connect(GTK_OBJECT(button_cancel),
+                    "clicked",
+                    GTK_SIGNAL_FUNC(destroyWidget),
+                    ewm->window);
+  gtk_widget_show(button_ok);
+  gtk_widget_show(button_cancel);
+
+  /* all clear, show the window */
+  gtk_widget_show(ewm->window);
+}
+
+
+
+/**
+ * @brief state of the DeletePseudonym window
+ **/
+typedef struct {
+  GtkWidget * window;
+  char * selected;
+  GtkWidget * pseudonymList;
+} DeletePseudonymWindowModel;
+
+/**
+ * Exit the application (called when the main window
+ * is closed or the user selects File-Quit).
+ **/
+static void destroyDPWindow(GtkWidget * widget,
+                           DeletePseudonymWindowModel * ewm) {
+  FREE(ewm);
+}
+
+/**
+ * The keyword delete button was clicked. Delete the 
+ * currently selected pseudonym.
+ *
+ * @param w not used
+ * @param ewm state of the edit window
+ **/
+static void button_del_clicked(GtkWidget * w, 
+                              DeletePseudonymWindowModel * ewm) {
+  GList * tmp;
+  gchar * key[1];
+  int row;
+ 
+  tmp = GTK_CLIST(ewm->pseudonymList)->selection;
+  if (NULL == tmp) {
+    /* message that keyword must be selected to delete one? */
+    return;
+  }  
+  row = (int) tmp->data;
+  if (row < 0) 
+    return; /* should never happen... */
+  key[0] = NULL;
+  gtk_clist_get_text(GTK_CLIST(ewm->pseudonymList),
+                    row,
+                    0,
+                    &key[0]);
+  if (key[0] == NULL)
+    return;
+  if (OK != deletePseudonym(key[0]))
+    guiMessage("WARNING: failed to delete pseudonym (see logs).\n");
+  gtk_clist_remove(GTK_CLIST(ewm->pseudonymList),
+                  row);
+  refreshMenuSensitivity();
+}
+
+/**
+ * Open a window to allow the user to delete a pseudonym
+ *
+ * @param unused GTK handle that is not used
+ * @param unused2 not used
+ **/
+void openDeletePseudonymDialog(GtkWidget * unused,
+                              unsigned int unused2) {
+  DeletePseudonymWindowModel * ewm;
+  GtkWidget * window;
+  GtkWidget * vbox, * hbox;
+  GtkWidget * clist;
+  GtkWidget * scrolled_window;
+  GtkWidget * button_delete;
+  GtkWidget * button_cancel;
+  gchar * titles[1] = { "Pseudonyms" };
+  int i;
+  int cnt;
+  char ** list;
+
+  ewm = MALLOC(sizeof(DeletePseudonymWindowModel));
+  /* create new window for editing */
+  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+  ewm->window = window;
+  gtk_widget_set_usize(GTK_WIDGET(window),
+                      250,
+                      300);
+  gtk_window_set_title(GTK_WINDOW(window), 
+                      "Delete Pseudonym");
+
+  /* add container for window elements */
+  vbox = gtk_vbox_new(FALSE, 0);
+  gtk_container_add(GTK_CONTAINER(window),
+                   vbox);
+  gtk_widget_show(vbox);
+
+  /* when user clicks on close box, always "destroy" */
+  gtk_signal_connect(GTK_OBJECT(window),
+                    "delete_event",
+                    GTK_SIGNAL_FUNC(deleteEvent),
+                    ewm);
+  /* whenever edit window gets destroyed, 
+     free *ALL* ewm data */
+  gtk_signal_connect(GTK_OBJECT(window),
+                    "destroy",
+                    GTK_SIGNAL_FUNC(destroyDPWindow),
+                    ewm);
+
+  gtk_container_set_border_width(GTK_CONTAINER(window), 
+                                10);
+
+  /* add a list of pseudonyms */
+  scrolled_window = gtk_scrolled_window_new(NULL, NULL);
+  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
+                                GTK_POLICY_AUTOMATIC, 
+                                GTK_POLICY_ALWAYS);
+  gtk_box_pack_start(GTK_BOX(vbox), 
+                    scrolled_window, 
+                    TRUE, 
+                    TRUE, 
+                    0);
+  gtk_widget_show(scrolled_window);  
+  clist = gtk_clist_new_with_titles(1, titles); 
+  ewm->pseudonymList = clist;
+  gtk_container_add(GTK_CONTAINER(scrolled_window), 
+                   clist);
+  gtk_widget_show(clist);
+  /* add the known RootNodes to the list */
+  list = NULL;
+  cnt = listPseudonyms(&list);
+  if (cnt > 0) {
+    gtk_clist_freeze(GTK_CLIST(clist));
+    for (i=0;i<cnt;i++) {
+      gtk_clist_append(GTK_CLIST(clist),
+                      &list[i]);
+      FREE(list[i]);
+    }
+    gtk_clist_thaw(GTK_CLIST(clist));
+  }
+  FREENONNULL(list);
+
+  /* add the buttons to add and delete keywords */
+  hbox = gtk_hbox_new(FALSE, 0);
+  gtk_box_pack_start(GTK_BOX(vbox),
+                    hbox,
+                    FALSE,
+                    TRUE,
+                    0);
+  gtk_widget_show(hbox);
+  button_delete = gtk_button_new_with_label("Delete Pseudonym");
+  gtk_box_pack_start(GTK_BOX(hbox), 
+                    button_delete, 
+                    TRUE, 
+                    TRUE, 
+                    0);
+  gtk_signal_connect(GTK_OBJECT(button_delete), 
+                    "clicked",
+                    GTK_SIGNAL_FUNC(button_del_clicked),
+                    ewm);
+  gtk_widget_show(button_delete);
+
+
+  button_cancel = gtk_button_new_with_label("Cancel");
+  gtk_box_pack_start(GTK_BOX(hbox), 
+                    button_cancel, 
+                    TRUE,
+                    TRUE, 
+                    0);
+  gtk_signal_connect(GTK_OBJECT(button_cancel),
+                    "clicked",
+                    GTK_SIGNAL_FUNC(destroyWidget),
+                    window);
+  gtk_widget_show(button_cancel);
+
+  /* all clear, show the window */
+  gtk_widget_show(window);
+}
+
+
+/* end of pseudonyms.c */

Added: freeway/src/org/gnu/freeway/protocol/afs/swing/pseudonyms.h
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/swing/pseudonyms.h 2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/swing/pseudonyms.h 2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,46 @@
+/*
+     This file is part of GNUnet
+
+     GNUnet is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with GNUnet; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file applications/afs/gtkui/pseudonyms.h
+ * @brief Pseudonym creation and deletion dialogs
+ * @author Christian Grothoff
+ **/
+#ifndef GTKUI_PSEUDONYMS_H
+#define GTKUI_PSEUDONYMS_H
+
+/**
+ * Open a window to allow the user to create a pseudonym
+ *
+ * @param unused GTK handle that is not used
+ * @param unused2 not used
+ **/
+void openCreatePseudonymDialog(GtkWidget * unused,
+                              unsigned int unused2);
+
+/**
+ * Open a window to allow the user to delete a pseudonym
+ *
+ * @param unused GTK handle that is not used
+ * @param unused2 not used
+ **/
+void openDeletePseudonymDialog(GtkWidget * unused,
+                              unsigned int unused2);
+
+#endif

Added: freeway/src/org/gnu/freeway/protocol/afs/swing/saveas.c
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/swing/saveas.c     2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/swing/saveas.c     2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,192 @@
+/*
+     This file is part of GNUnet
+
+     GNUnet is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with GNUnet; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+/**
+ * @file src/applications/afs/gtkui/saveas.c
+ * @brief open a save-as window.
+ * @author Christian Grothoff
+ * @author Igor Wronsky
+ **/
+
+#include "gnunet_afs_esed2.h"
+#include "helper.h"
+#include "download.h"
+#include "saveas.h"
+
+/**
+ * @brief state of the SaveAs window
+ **/
+typedef struct {
+  RootNode root;
+  GtkWidget * w;
+  char * filename;
+} SaveAs;
+
+/**
+ * Destroy the DownloadModel data structure of the
+ * saveas dialog.
+ *
+ * @param widget not used
+ * @param saveas state associated with the SaveAs window
+ **/
+static gint destroySaveAs(GtkWidget * widget,
+                         SaveAs * saveas) {
+  LOG(LOG_DEBUG, 
+      "DEBUG: destroying saveas window (%x)\n", 
+      saveas);
+  FREENONNULL(saveas->filename);
+  FREE(saveas);
+  return TRUE;
+}
+ 
+/**
+ * Get the selected filename and start downloading 
+ *
+ * @param okButton not used
+ * @param saveas state of the saveas window
+ **/
+static gint file_ok_sel(GtkWidget * okButton,
+                       SaveAs * saveas) {
+  gchar * filename;
+
+  LOG(LOG_DEBUG, 
+      "DEBUG: file_ok_sel (%x)\n", 
+      saveas);
+  filename
+    = gtk_file_selection_get_filename(GTK_FILE_SELECTION(saveas->w));
+  startDownload(filename, &saveas->root);
+  gtk_widget_destroy(saveas->w);
+  /* destroySaveAs does: "FREE(saveas);" */
+  return FALSE;
+}
+
+/**
+ * Open the window that prompts the user for the filename.  This
+ * method must open the window, copy the arguments and return.  After
+ * the method returns, the arguments passed to it will be freed, so
+ * pointer should not be retained.  The method executes during a
+ * signal handler, so a GTK lock is not required to to GUI operations.
+ * 
+ * @param root search result of the file to download
+ **/
+void openSaveAs(RootNode * root) {
+  SaveAs * saveas;
+  int i;
+  
+  saveas = MALLOC(sizeof(SaveAs));
+  memcpy(&saveas->root,
+        root,
+        sizeof(RootNode));
+  saveas->filename = NULL;
+
+  /* if the search result specified a suggested
+     filename, fill it in! */
+  switch (ntohs(root->header.major_formatVersion)) {
+  case ROOT_MAJOR_VERSION:
+    /* if it is a GNUnet directory, replace suffix '/' with ".gnd" */
+    if (0 == strcmp(root->header.mimetype,
+                   GNUNET_DIRECTORY_MIME)) {
+      saveas->filename = expandDirectoryName(root->header.filename);
+    } else 
+      saveas->filename = STRDUP(root->header.filename);
+    break;
+  case SBLOCK_MAJOR_VERSION:
+    if (0 == strcmp(&((SBlock*)root)->mimetype[0],
+                   GNUNET_DIRECTORY_MIME)) {      
+      saveas->filename = expandDirectoryName(root->header.filename);
+    } else 
+      saveas->filename = STRDUP(((SBlock*)root)->filename);
+    break;
+  default:
+    LOG(LOG_WARNING,
+       "WARNING: unknown format version: %d\n",
+       ntohs(root->header.major_formatVersion));
+    /* how did we get here!? */
+    break;
+  }
+
+  if ( (saveas->filename == NULL) ||
+       (saveas->filename[0] == 0) ||
+       (testConfigurationString("GNUNET-GTK",
+                               "ALWAYS-ASK-SAVEAS",
+                               "YES")) ) {
+    GtkWidget * window;
+
+    window = gtk_file_selection_new("save as");
+    saveas->w = window;
+        
+    /* callbacks (destroy, ok, cancel) */
+    gtk_signal_connect(GTK_OBJECT(window),
+                      "destroy",
+                      GTK_SIGNAL_FUNC(destroySaveAs),
+                      saveas);
+    /* Connect the ok_button to file_ok_sel function */
+    gtk_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(window)->ok_button),
+                      "clicked", 
+                      GTK_SIGNAL_FUNC(file_ok_sel), 
+                      saveas);
+    /* Connect the cancel_button to destroy the widget */
+    gtk_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(window)->cancel_button),
+                      "clicked", 
+                      GTK_SIGNAL_FUNC(destroyWidget), 
+                      window);
+    gtk_widget_show(window);
+  } else {
+    char * tmp;
+    char * downloadDir;
+
+    /* sanity check the filename */
+    for(i=0;i<strlen(saveas->filename);i++) {
+      switch(saveas->filename[i]) {
+      case '*':
+      case '/':
+      case '\\':
+      case '?':
+      case ':':
+       saveas->filename[i]='_';
+       break;
+      default:
+       break;
+      }
+    }
+
+    downloadDir = getConfigurationString("AFS",
+                                         "DOWNLOADDIR");
+    if(downloadDir != NULL) {
+      char * expanded;
+  
+      expanded = expandFileName(downloadDir);
+      if((SYSERR == mkdirp(expanded)))
+        LOG(LOG_WARNING, 
+                 "WARNING: Unable to create %s\n", 
+         expanded);
+      CHDIR(expanded);
+      FREE(expanded);
+    }
+    FREENONNULL(downloadDir);
+
+    tmp = expandFileName(saveas->filename);
+    FREE(saveas->filename);
+    startDownload(tmp,
+                 &saveas->root);
+    FREE(saveas);
+    FREE(tmp);
+  }
+}
+
+/* end of saveas.c */

Added: freeway/src/org/gnu/freeway/protocol/afs/swing/saveas.h
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/swing/saveas.h     2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/swing/saveas.h     2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,43 @@
+/*
+     This file is part of GNUnet
+
+     GNUnet is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with GNUnet; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file applications/afs/gtkui/saveas.h
+ * @author Christian Grothoff
+ **/
+
+#ifndef GTKUI_SAVEAS_H
+#define GTKUI_SAVEAS_H
+
+#include "platform.h"
+
+/**
+ * Open the window that prompts the user for the 
+ * filename.
+ * This method must open the window,
+ * copy the arguments and return. After the method
+ * returns, the arguments passed to it will be
+ * freed, so pointer should not be retained.
+ * The method executes during a signal handler,
+ * so a GTK lock is not required to to GUI 
+ * operations.
+ **/
+void openSaveAs(RootNode * root);
+
+#endif

Added: freeway/src/org/gnu/freeway/protocol/afs/swing/search.c
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/swing/search.c     2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/swing/search.c     2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,852 @@
+/*
+     This file is part of GNUnet
+
+     GNUnet is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with GNUnet; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file src/applications/afs/gtkui/search.c
+ * @brief box displaying search results for the gtk+ client.
+ * @author Christian Grothoff
+ * @author Igor Wronsky
+ **/
+
+#include "gnunet_afs_esed2.h"
+#include "helper.h"
+#include "download.h"
+#include "saveas.h"
+#include "search.h"
+#include "main.h"
+
+static void searchSelectAll(void);
+static void searchSelectNone(void);
+static void searchSelectByName(void);
+static void searchSelectByDesc(void);
+static void searchSelectByMime(void);
+static void searchClose(void);
+static void searchDownloadSelected(void); 
+
+static GtkItemFactoryEntry searchWindowMenu[] = {
+  { "/Select all",            NULL,   searchSelectAll,      0, "<Item>" },
+  { "/Unselect all",          NULL,   searchSelectNone,     0, "<Item>" },
+  { "/sep1",                  NULL,   NULL,                 0, "<Separator>" },
+  { "/Select by filename",    NULL,   searchSelectByName,   0, "<Item>" },
+  { "/Select by description", NULL,   searchSelectByDesc,   0, "<Item>" },
+  { "/Select by mimetype",    NULL,   searchSelectByMime,   0, "<Item>" },
+  { "/sep2",                  NULL,   NULL,                 0, "<Separator>" },
+  { "/Download selected",     NULL,   searchDownloadSelected,  0, "<Item>" },
+  { "/sep3",                  NULL,   NULL,                 0, "<Separator>" },
+  { "/Abort search",          NULL,   searchClose,          0, "<Item>" }
+};
+
+static gint searchWindowMenuItems
+  = sizeof (searchWindowMenu) / sizeof (searchWindowMenu[0]);
+
+/**
+ * Selects all search results from the current search page.
+ **/
+static void searchSelectAll(void)
+{
+  gint pagenr;
+  GtkWidget * page;
+  ListModel * model;
+
+  pagenr = gtk_notebook_get_current_page(notebook);
+  if(pagenr<0)
+    return;
+  page = gtk_notebook_get_nth_page(notebook,
+                                   pagenr);
+  if(!page)
+    return;
+  model = gtk_object_get_data(GTK_OBJECT(page),
+                              "MODEL");
+  if(model)
+    gtk_clist_select_all(GTK_CLIST(model->search_result_list));
+}
+
+/**
+ * Unselects all search results from the current search page.
+ **/
+static void searchSelectNone(void)
+{
+  gint pagenr;
+  GtkWidget * page;
+  ListModel * model;
+
+  pagenr = gtk_notebook_get_current_page(notebook);
+  if(pagenr<0)
+    return;
+  page = gtk_notebook_get_nth_page(notebook,
+                                   pagenr);
+  if(!page)
+    return;
+  model = gtk_object_get_data(GTK_OBJECT(page),
+                              "MODEL");
+  if(model)
+    gtk_clist_unselect_all(GTK_CLIST(model->search_result_list));
+}
+
+static void selectByCallback(GtkWidget * dummy,
+                            GtkWidget * entry) {
+  gchar * nameString;
+  gint pagenr;
+  GtkWidget * page;
+  GtkCList * clist;
+  ListModel * model;
+  int i;
+  int j; 
+  int * data;
+  int column;
+
+  data = gtk_object_get_data(GTK_OBJECT(entry),
+                            "COLUMNID");
+  column = *data;
+  FREE(data);
+
+  nameString = gtk_entry_get_text(GTK_ENTRY(entry));
+  if(nameString == NULL) {
+    guiMessage("nameString == NULL\n");
+    return;
+  }
+  if(nameString[0] == 0) {
+    guiMessage("Naah...\n");
+    return;
+  }
+ 
+  pagenr = gtk_notebook_get_current_page(notebook);
+  if(pagenr<0)
+    return;
+  page = gtk_notebook_get_nth_page(notebook,
+                                   pagenr);
+  if(!page)                        
+    return;
+  model = gtk_object_get_data(GTK_OBJECT(page),
+                              "MODEL");
+  if(model) {
+    gchar * tmp;
+    gchar * haystack;
+    gchar * needle;  
+    int hits = 0;
+
+    needle = STRDUP(nameString);
+    for(i=0;i<strlen(needle);i++)
+      needle[i]=tolower(needle[i]);
+
+    clist = GTK_CLIST(model->search_result_list);
+
+    gtk_clist_freeze(clist);
+    for(i=0;i<clist->rows;i++) {
+      gtk_clist_get_text(clist,
+                         i,
+                         column,
+                         &tmp);
+      haystack = STRDUP(tmp);
+      for(j=0;j<strlen(haystack);j++)
+        haystack[j]=tolower(haystack[j]);
+ 
+      if(strstr(haystack, needle)!=NULL) {
+         gtk_clist_select_row(clist,
+                              i,
+                              1);
+         hits++;
+      }
+      FREE(haystack);
+    }
+     
+    gtk_clist_thaw(clist);
+    if(hits==0)
+      guiMessage("No matches...");
+    FREE(needle);
+  }
+}
+
+
+static void searchSelectByColumn(int column)
+{
+  GtkWidget *window;
+  GtkWidget *vbox;
+  GtkWidget *label; 
+  GtkWidget *entry;
+  GtkWidget *button;
+  int * data;
+
+  data = MALLOC(sizeof(int));
+  *data = column;
+
+  window = gtk_window_new(GTK_WINDOW_DIALOG);
+  vbox = gtk_vbox_new(FALSE, 0);
+  gtk_container_add(GTK_CONTAINER(window),
+                    vbox);
+  label = gtk_label_new("Pattern? ");
+  gtk_container_add (GTK_CONTAINER(vbox),
+                     label);
+  entry = gtk_entry_new();
+  gtk_object_set_data(GTK_OBJECT(entry),
+                     "COLUMNID",
+                     data);
+  gtk_entry_set_text(GTK_ENTRY(entry), 
+                    "");
+  gtk_signal_connect(GTK_OBJECT(entry),
+                    "activate",
+                    GTK_SIGNAL_FUNC(selectByCallback),
+                    entry);
+  gtk_signal_connect(GTK_OBJECT(entry),
+                    "activate",
+                    GTK_SIGNAL_FUNC(destroyWidget),
+                    window);
+  gtk_container_add(GTK_CONTAINER(vbox),
+                   entry);
+  button = gtk_button_new_with_label("Ok");
+  gtk_container_add(GTK_CONTAINER(vbox),
+                   button);
+  gtk_signal_connect(GTK_OBJECT(button),
+                    "clicked",
+                    GTK_SIGNAL_FUNC(selectByCallback),
+                    entry);
+  gtk_signal_connect(GTK_OBJECT(button),
+                    "clicked",
+                    GTK_SIGNAL_FUNC(destroyWidget),
+                    window);
+  gtk_window_set_position(GTK_WINDOW(window),
+                         GTK_WIN_POS_MOUSE);
+  gtk_widget_show_all(window);
+  gtk_widget_grab_focus(entry);
+
+}
+
+static void searchSelectByName(void)
+{
+  searchSelectByColumn(2);
+}
+
+static void searchSelectByDesc(void)
+{
+  searchSelectByColumn(0);
+}
+
+static void searchSelectByMime(void) {
+  searchSelectByColumn(6);
+}
+
+/**
+ * Remove the active page from the search results notebook.
+ * The respective search will be stopped as well
+ * (by a callback assigned to the page earlier on).
+ **/
+void searchClose(void) {
+  gint pagenr;
+  
+  pagenr = gtk_notebook_get_current_page(notebook);
+  gtk_notebook_remove_page(notebook, pagenr);
+  /* Need to refresh the widget --
+     This forces the widget to redraw itself. */
+  gtk_widget_draw(GTK_WIDGET(notebook), NULL);
+}
+
+/**
+ * This method is called whenever the user clicks the
+ * download button.  It opens the "save-as" dialog.
+ *
+ * @param widget not used
+ * @param listModel Data related to the search
+ **/
+static void downloadGTK(GtkWidget * widget,
+                       ListModel * listModel);
+
+
+static void searchDownloadSelected(void) {
+  gint pagenr;
+  GtkWidget * page;
+  ListModel * model;
+
+  pagenr = gtk_notebook_get_current_page(notebook);
+  if(pagenr<0)
+    return;
+  page = gtk_notebook_get_nth_page(notebook,
+                                   pagenr);
+  if(!page)
+    return;
+  model = gtk_object_get_data(GTK_OBJECT(page),
+                              "MODEL");
+  if(model)
+    downloadGTK(NULL, model);
+}
+
+/**
+ * This method is called whenever the user clicks the
+ * download button.  It opens the "save-as" dialog.
+ *
+ * @param widget not used
+ * @param listModel Data related to the search
+ **/
+static void downloadGTK(GtkWidget * widget,
+                       ListModel * listModel) {  
+  gint row;
+  GList * tmp;
+  
+  tmp=GTK_CLIST(listModel->search_result_list)->selection;
+         
+  if ( !tmp ) {
+    guiMessage("Nothing selected!\n");
+    return;
+  }
+    
+  gtk_clist_freeze(GTK_CLIST(listModel->search_result_list));
+
+  /* download all selected entries */
+  while (tmp) {
+    RootNode * rootNode;
+    
+    row = (int)tmp->data;
+    tmp = tmp->next;
+    rootNode = (RootNode *)
+      gtk_clist_get_row_data(GTK_CLIST(listModel->search_result_list),
+                             row);
+    openSaveAs(rootNode);
+  
+    /* Remove entry from search results.  Yes,
+       if the user cancel's the download, the
+       entry does not re-appear.  That's intended,
+       after all, if you cancel, it's probably because
+       it took too long to download anyway... 
+       If you really need it back, just search again! */
+    gtk_clist_remove(GTK_CLIST(listModel->search_result_list),
+                    row);
+
+    FREE(rootNode);
+  }
+  gtk_clist_thaw(GTK_CLIST(listModel->search_result_list));
+}
+
+static void freeSearchList(GtkWidget * dummy,
+                          GtkCList * clist) {
+  int i;
+
+  /* Free clist stored rootNode data */
+  gtk_clist_freeze(clist);
+  for(i=0;i<clist->rows;i++) {
+    RootNode * rootNode;
+    
+    rootNode = (RootNode *)
+      gtk_clist_get_row_data(clist,
+                             i);
+    FREE(rootNode);
+  }
+  gtk_clist_clear(clist);
+  gtk_clist_thaw(clist);
+}
+
+static gint doDisplayResult(SaveCall *call) {
+  int newrow;
+  GtkWidget *search_result_list = ((Result *) call->args)->search_result_list;
+
+  gtk_clist_freeze(GTK_CLIST(search_result_list));
+  newrow = gtk_clist_append(GTK_CLIST(search_result_list),
+                                     ((Result *) call->args)->results);
+  gtk_clist_set_row_data(GTK_CLIST(search_result_list),
+                                        newrow,
+                                          ((Result *) call->args)->rootCopy);
+  gtk_clist_thaw(GTK_CLIST(search_result_list));
+
+  gtkSaveCallDone(call->sem);
+  
+  return FALSE;
+}
+
+/**
+ * Display results.  This is a callback from receiveResults that is
+ * called on every new result.
+ *
+ * @param rootNode Data about a file
+ * @param model Data related to the search
+ **/
+void displayResultGTK(RootNode * rootNode,
+                     ListModel * model) {
+  RootNode * rootCopy;
+  SBlock * sb;
+  gchar * results[7];
+  Result result;
+  int i;
+  
+  if(model->doTerminate == YES)
+    return;
+
+  rootCopy = MALLOC(sizeof(RootNode));
+  memcpy(rootCopy,
+         rootNode,
+        sizeof(RootNode));
+
+  switch (ntohs(rootNode->header.major_formatVersion)) {
+  case ROOT_MAJOR_VERSION:
+    /* ensure well-formed strings */
+    rootNode->header.description[MAX_DESC_LEN-1] = 0;
+    rootNode->header.filename[MAX_FILENAME_LEN-1] = 0;
+    rootNode->header.mimetype[MAX_MIMETYPE_LEN-1] = 0;
+    
+    results[0] = STRDUP(rootNode->header.description);
+    results[1] = MALLOC(32);
+    sprintf(results[1],
+           "%u", 
+           (unsigned int) ntohl(rootNode->header.fileIdentifier.file_length));
+    if ( (0 == strcmp(rootNode->header.mimetype,
+                     GNUNET_DIRECTORY_MIME)) &&
+        (rootNode->header.filename[strlen(rootNode->header.filename)-1] != 
DIR_SEPARATOR) ) {
+      results[2] = MALLOC(strlen(rootNode->header.filename)+2);
+      strcpy(results[2], rootNode->header.filename);
+      strcat(results[2], "/");      
+    } else
+      results[2] = STRDUP(rootNode->header.filename);
+    results[3] = MALLOC(12);
+    sprintf(results[3], 
+           "%X", 
+           (unsigned int) ntohl(rootNode->header.fileIdentifier.crc));
+    results[4] = MALLOC(sizeof(HexName));
+    hash2hex(&rootNode->header.fileIdentifier.chk.query,
+            (HexName*) results[4]);
+    results[5] = MALLOC(sizeof(HexName));
+    hash2hex(&rootNode->header.fileIdentifier.chk.key,
+            (HexName*) results[5]);
+    results[6] = STRDUP(rootNode->header.mimetype);
+    break;
+  case SBLOCK_MAJOR_VERSION:
+    sb = (SBlock*) rootNode;
+    sb->description[MAX_DESC_LEN-1] = 0;
+    sb->filename[MAX_FILENAME_LEN/2-1] = 0;
+    sb->mimetype[MAX_MIMETYPE_LEN/2-1] = 0;
+    
+    results[0] = STRDUP(rootNode->header.description);
+    results[1] = MALLOC(32);
+    sprintf(results[1], 
+           "%u", 
+           (unsigned int) ntohl(sb->fileIdentifier.file_length));
+    results[2] = STRDUP(sb->filename);
+    results[3] = MALLOC(32);
+    sprintf(results[3],
+           "%u", 
+           (unsigned int) ntohl(sb->fileIdentifier.crc));
+    results[4] = MALLOC(sizeof(HexName));
+    hash2hex(&sb->fileIdentifier.chk.query,
+            (HexName*) results[4]);
+    results[5] = MALLOC(sizeof(HexName));
+    hash2hex(&sb->fileIdentifier.chk.key,
+            (HexName*) results[5]);
+    results[6] = STRDUP(sb->mimetype);    
+    break;
+  default:
+    LOG(LOG_ERROR,
+       "ERROR: search result received of unsupported type %d\n",
+       ntohs(rootNode->header.major_formatVersion));
+    return;
+  }
+  result.search_result_list = model->search_result_list;
+  result.rootCopy = rootCopy;
+  memcpy(&result.results, &results, sizeof(results));
+  gtkSaveCall((GtkFunction) doDisplayResult, &result);
+  if (model->skipMenuRefresh != YES)
+    refreshMenuSensitivity();
+
+  for (i=0;i<7;i++)
+    FREE(results[i]);
+}
+
+/**
+ * Struct to pass a couple of arguments to a new
+ * thread "receiveResults_".
+ **/
+typedef struct {
+  char * searchString;
+  ListModel * model;
+} _receiveResultArgs_;
+
+/**
+ * Should the receive thread abort? (has the window been closed?)
+ * This is a callback for the receiveResults method (since
+ * not every "error" on the socket corresponds to a closed
+ * window!).
+ *
+ * @return YES if we should abort
+ **/
+int testTermination(ListModel * model) {
+  return model->doTerminate;
+}
+
+/**
+ * Main method of the receiveResults-threads.
+ * Runs the receiveResults method and frees some
+ * data structures once the search is aborted.
+ * See also "stopSearch".
+ **/
+static void * receiveResults_(_receiveResultArgs_ * args) {
+  char ** keywords;
+  int num_Words;
+  int inWord;
+  char * c;
+
+
+  num_Words = 0;
+  for (inWord = 0, c = args->searchString; *c != '\0'; ++c) {
+    if (isspace(*c)) {
+      inWord = 0;
+    } else if (!inWord) {
+      inWord = 1;
+      ++num_Words;
+    }
+  }
+
+  if (num_Words == 0) {
+    FREENONNULL(args->searchString);
+    FREE(args);
+    LOG(LOG_FAILURE, "No keywords specified!\n");
+    return NULL;
+  }
+  keywords = MALLOC(num_Words * sizeof(char *));
+  num_Words = 0;
+  for (inWord = 0, c = args->searchString; *c != '\0'; ++c) {
+    if (isspace(*c)) {
+      inWord = 0;
+      *c = '\0';
+    } else if (!inWord) {
+      keywords[num_Words] = c;
+      inWord = 1;
+      ++num_Words;
+    }
+  }
+  searchRBlock(args->model->SEARCH_socket_,
+              keywords,
+              num_Words,
+              (SearchResultCallback) &displayResultGTK,
+              args->model,
+              (TestTerminateThread)&testTermination,
+              args->model);  
+  FREE(args->searchString);
+  FREE(keywords);
+  FREE(args);
+  return NULL;
+}
+
+/**
+ * The main method of the search-thread.
+ *
+ * @param searchString What to look for
+ * @param model Data related to the search
+ * @return OK on success, SYSERR on error
+ **/
+static int startSearchThread(char * searchString,
+                            ListModel * model) {
+  _receiveResultArgs_ * receiveArgs;
+
+  receiveArgs = MALLOC(sizeof(_receiveResultArgs_));
+  receiveArgs->searchString = STRDUP(searchString);
+  receiveArgs->model = model;  
+  if (0 != PTHREAD_CREATE(&model->thread,
+                         (PThreadMain) &receiveResults_,
+                         receiveArgs,
+                         16 * 1024)) {
+    errexit("FATAL: could not create receive thread (%s)!\n",
+           STRERROR(errno));    
+  }
+  return OK;
+}
+
+/**
+ * Cron job that stops the search.
+ */
+static void stopSearch_(ListModel * model) {
+  void * unused;
+
+  switch (model->type) {
+  case LM_TYPE_DIRECTORY:
+    break;
+  case LM_TYPE_SEARCH:
+    /* the terminated search thread ups this semaphore
+       once it is done and we can free data structures */
+    model->doTerminate = YES;
+    
+    /* this signals the download thread to terminate */
+    closeSocketTemporarily(model->SEARCH_socket_);
+    
+    /* wait for download thread signal */
+    PTHREAD_JOIN(&model->thread,
+                &unused);
+    
+    /* Now we can finally free the shared data structures.
+       Note that the terminated search thread freed 
+       some of the memory that was allocated in
+       startSearchThread (see receiveResults_)
+       we free the rest. */
+    releaseClientSocket(model->SEARCH_socket_);  
+    break;
+  case LM_TYPE_NSSEARCH:
+    model->doTerminate = YES;
+    /* this signals the download thread to terminate */
+    closeSocketTemporarily(model->SEARCH_socket_);    
+    /* wait for download thread signal */
+    PTHREAD_JOIN(&model->thread,
+                &unused);
+    releaseClientSocket(model->SEARCH_socket_);
+    break;
+  default:
+    LOG(LOG_ERROR, 
+       "ERROR: Unknown model->type %d\n",
+       model->type);
+    break;
+  }
+  if (model->sem != NULL)
+    SEMAPHORE_UP(model->sem);
+  FREE(model);  
+}
+
+/**
+ * Stop the search thread and free the model.  This method MUST always
+ * be called when the search ends, either because the search was
+ * aborted or because gnunet-gtk exists as a whole.  
+ *
+ * @param widget the window (not used)
+ * @param model the model with the socket 
+ **/
+static void stopSearch(GtkWidget * widget,
+                      ListModel * model) { 
+  Semaphore * cs;
+  LOG(LOG_DEBUG, 
+      "DEBUG: stopSearch called\n");
+  /* this must be done as a cron-job, since otherwise
+     it may deadlock (this is called from the
+     gtk event thread, and cron may be waiting for
+     the gtk event lock, so we can't delete a cron
+     job in this thread */
+  model->doTerminate = YES;
+  cs = SEMAPHORE_NEW(0);
+  model->sem = cs;
+  addCronJob((CronJob) &stopSearch_,
+            0, 0, model);
+  /* this is always the gtk-thread, so we must
+     not block; instead, run gtk-savecalls! */
+  while (SYSERR == SEMAPHORE_DOWN_NONBLOCKING(cs)) 
+    gtkRunSomeSaveCalls();
+  SEMAPHORE_FREE(cs);
+}
+
+/**
+ * Changes the current sort column and sorts the list.
+ **/
+static void sort_column_callback(GtkCList * clist,
+                                gint column,
+                                gpointer data) {
+  static int sortOrder[7]={0,0,0,0,0,0,0};
+
+  sortOrder[column]^=1;
+
+  if(sortOrder[column]==1)
+    gtk_clist_set_sort_type(clist,
+                            GTK_SORT_ASCENDING);
+  else
+    gtk_clist_set_sort_type(clist,
+                            GTK_SORT_DESCENDING);
+
+  /* Sort all columns as strings except 1 (size) */
+  if (column == 1)
+    gtk_clist_set_compare_func(clist, 
+                              (GtkCListCompareFunc)numericComp);
+  else
+    gtk_clist_set_compare_func(clist, 
+                              (GtkCListCompareFunc)alphaComp);  
+  gtk_clist_set_sort_column(clist, column);
+  gtk_clist_freeze(clist);
+  gtk_clist_sort(clist);
+  gtk_clist_thaw(clist);
+}
+
+static gint doInitSearchResultList(SaveCall *call) {
+  ListModel *model;
+  GtkWidget * scrolled_window;
+  GtkWidget * button;
+  GtkWidget * box;
+  GtkWidget * search_result_list;
+  GtkWidget * menu;
+  GtkItemFactory * popupFactory;
+  static gchar * descriptions[] = {
+    "Description",
+    "Size",
+    "Filename",
+    "CRC",
+    "HASH1",
+    "HASH2",
+    "Mimetype"
+  };
+  /* widths of the colums in the search results */
+  static int widths[] = {
+    470, 70, 200, 40, 40, 40, 200,
+  };
+  int i;
+  
+  model = ((InitResultList *)call->args)->model;
+
+  box = gtk_vbox_new(FALSE, 0); 
+  /* scrolled window for the list */
+  scrolled_window = gtk_scrolled_window_new(NULL, NULL);
+  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
+                                 GTK_POLICY_AUTOMATIC, 
+                                GTK_POLICY_ALWAYS);
+  gtk_box_pack_start(GTK_BOX(box), 
+                    scrolled_window, 
+                    TRUE,
+                    TRUE, 
+                    0);
+  gtk_widget_show(scrolled_window);
+  
+  /* result set list */
+  search_result_list 
+    = gtk_clist_new_with_titles(7, descriptions);
+  model->search_result_list 
+    = search_result_list;
+  gtk_signal_connect(GTK_OBJECT(search_result_list), 
+                     "destroy",
+                    GTK_SIGNAL_FUNC(freeSearchList), 
+                    search_result_list); 
+  gtk_container_add(GTK_CONTAINER(scrolled_window), 
+                   search_result_list);
+  gtk_widget_show(search_result_list);
+
+  gtk_clist_set_selection_mode
+    (GTK_CLIST(search_result_list),
+     GTK_SELECTION_EXTENDED);
+  /* set passive titles as default */
+  gtk_clist_column_titles_passive
+    (GTK_CLIST(search_result_list));
+
+  /* allow sorting by description, size, filename and mimetype */
+  gtk_clist_column_title_active(GTK_CLIST(search_result_list),
+                               0);
+  gtk_clist_column_title_active(GTK_CLIST(search_result_list),
+                               1);
+  gtk_clist_column_title_active(GTK_CLIST(search_result_list),
+                               2);
+  gtk_clist_column_title_active(GTK_CLIST(search_result_list),
+                               6);
+  gtk_signal_connect(GTK_OBJECT(search_result_list),
+                    "click-column",
+                    GTK_SIGNAL_FUNC(sort_column_callback),
+                    NULL);
+
+  /* description left, size right justification */
+  gtk_clist_set_column_justification
+    (GTK_CLIST(search_result_list),
+     0,
+     GTK_JUSTIFY_LEFT);
+  gtk_clist_set_column_justification
+    (GTK_CLIST(search_result_list),
+     1,
+     GTK_JUSTIFY_RIGHT);
+
+  /* set column widths */
+  for (i=0;i<7;i++)
+    gtk_clist_set_column_width(GTK_CLIST(search_result_list), 
+                              i,
+                              widths[i]);
+
+  /* download button */
+  button = gtk_button_new_with_label("Download");
+  gtk_signal_connect (GTK_OBJECT(button), 
+                     "clicked",
+                     GTK_SIGNAL_FUNC(downloadGTK), 
+                     model);
+  gtk_box_pack_start(GTK_BOX(box), 
+                    button,
+                    FALSE,
+                    FALSE,
+                    0);
+  gtk_widget_show(button);
+  /* generic: on delete request, just do it (always OK) */
+  gtk_signal_connect(GTK_OBJECT(scrolled_window), 
+                     "delete_event",
+                     GTK_SIGNAL_FUNC(deleteEvent), 
+                     NULL);
+  /* when we are destroyed (e.g. search aborted), stop the search thread */
+  gtk_signal_connect(GTK_OBJECT(scrolled_window), 
+                     "destroy",
+                     GTK_SIGNAL_FUNC(stopSearch), 
+                     model); 
+  /* store a pointer to the model for main-menu access */
+  gtk_object_set_data(GTK_OBJECT(box),
+                     "MODEL",
+                     model);
+
+  /* add a right button popup menu */
+  popupFactory = gtk_item_factory_new (GTK_TYPE_MENU, "<main>",
+                                       NULL);
+  gtk_item_factory_create_items(popupFactory,
+                                searchWindowMenuItems,
+                                searchWindowMenu,
+                                NULL);
+  menu = gtk_item_factory_get_widget (popupFactory, "<main>");
+  gtk_signal_connect(GTK_OBJECT(box),
+                     "event",
+                     GTK_SIGNAL_FUNC(popupCallback),
+                     menu);
+
+  ((InitResultList *)call->args)->ret = box;
+
+  gtkSaveCallDone(call->sem);
+  
+  return FALSE;
+}
+
+GtkWidget * initializeSearchResultList(ListModel * model) {
+  InitResultList init;
+  
+  init.model = model;
+  gtkSaveCall((GtkFunction) doInitSearchResultList,
+             &init);
+  return init.ret;
+}
+
+/** 
+ * Returns a box containing the search results list.
+ **/
+GtkWidget * getSearchWindow(gchar * title) {
+  GtkWidget * box;
+  ListModel * model;  
+  int ok;
+
+  model = (ListModel*) MALLOC(sizeof(ListModel));
+  model->sem = NULL;
+  model->type = LM_TYPE_SEARCH;
+  model->doTerminate = NO;
+  model->skipMenuRefresh = NO;
+  model->SEARCH_socket_
+    = getClientSocket();
+  if (model->SEARCH_socket_ == NULL) {
+    FREE(model);
+    return NULL;
+  }
+
+  box = initializeSearchResultList(model);
+
+  /* start searching */
+  ok = startSearchThread(title,
+                        model);
+  if (ok == SYSERR) {
+    releaseClientSocket(model->SEARCH_socket_);
+    gtk_widget_destroy(box);
+    FREE(model);
+    return NULL;
+  } else {
+    /* return complete box such that it gets displayed */
+    return box;
+  }
+}
+
+/* end of search.c */

Added: freeway/src/org/gnu/freeway/protocol/afs/swing/search.h
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/swing/search.h     2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/swing/search.h     2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,101 @@
+/*
+     This file is part of GNUnet
+
+     GNUnet is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with GNUnet; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file applications/afs/gtkui/search.h
+ * @author Christian Grothoff
+ **/
+
+#ifndef GTKUI_SEARCH_H
+#define GTKUI_SEARCH_H
+
+/**
+ * Data for a search process
+ **/
+typedef struct {
+  int type;
+  GtkWidget * search_result_list;  
+  int doTerminate;
+  GNUNET_TCP_SOCKET * SEARCH_socket_;
+  PTHREAD_T thread; /* search thread */
+  int skipMenuRefresh; /* don't refresh gtk menus (its slow)? (YES/NO) */
+  Semaphore * sem; /* shutdown signaling */
+} ListModel;
+
+typedef struct {
+  GtkWidget *search_result_list;
+  RootNode *rootCopy;
+  gchar * results[7];
+} Result;
+
+typedef struct {
+  ListModel *model;
+  GtkWidget *ret;
+} InitResultList;
+
+#define LM_TYPE_SEARCH 1
+#define LM_TYPE_DIRECTORY 2
+#define LM_TYPE_NSSEARCH 3
+
+/**
+ * Get the window with the search results.
+ **/
+GtkWidget * getSearchWindow(gchar * searchString);
+
+GtkWidget * initializeSearchResultList(ListModel * model);
+
+/**
+ * Should the receive thread abort? (has the window been closed?)
+ * This is a callback for the receiveResults method (since
+ * not every "error" on the socket corresponds to a closed
+ * window!).
+ *
+ * @return YES if we should abort
+ **/
+int testTermination(ListModel * model);
+
+/**
+ * Display results.  This is a callback from receiveResults that is
+ * called on every new result.
+ *
+ * @param rootNode Data about a file
+ * @param model Data related to the search
+ **/
+void displayResultGTK(RootNode * rootNode,
+                     ListModel * model);
+ 
+/**
+ * Some search list row has been unselected
+ **/
+void unselect_row_callbackGTK(GtkWidget *widget,
+                             gint row,
+                             gint column,
+                             GdkEventButton *event,
+                             gpointer data);
+
+/**
+ * User has selected some search list row
+ **/
+void select_row_callbackGTK(GtkWidget *widget,
+                           gint row,
+                           gint column,
+                           GdkEventButton *event,
+                           gpointer data);
+
+#endif

Added: freeway/src/org/gnu/freeway/protocol/afs/swing/statistics.c
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/swing/statistics.c 2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/swing/statistics.c 2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,963 @@
+/*
+     This file is part of GNUnet
+
+     GNUnet is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with GNUnet; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+
+     Portions of this code were adopted from the
+     gnome-system-monitor v2.0.5, (C) 2001 Kevin Vandersloot
+
+
+     Todo:
+     - add any more StatEntries, update menu accordingly.
+*/
+
+#include "gnunet_afs_esed2.h"
+#include "statistics.h"
+#include "helper.h"
+#include "main.h"
+#include <glib.h>
+
+#define UPDATE_INTERVAL (30 * cronSECONDS)
+
+typedef struct {
+  char * statName;
+  long long value;
+  long long lvalue;
+  cron_t delta;
+} StatPair;
+
+static StatPair * lastStatValues;
+static unsigned int lsv_size;
+static cron_t lastUpdate;
+static Mutex lock;
+
+static void updateStatValues(GNUNET_TCP_SOCKET * sock) {
+  STATS_CS_MESSAGE * statMsg;
+  CS_HEADER csHdr;
+  unsigned int count;
+  unsigned int i;
+  int j;
+  int mpos;
+  int found;
+  char * optName;
+  cron_t now;
+  cron_t prev;
+    
+  cronTime(&now);
+  MUTEX_LOCK(&lock);
+  if (now - lastUpdate < UPDATE_INTERVAL) { 
+    MUTEX_UNLOCK(&lock);
+    return;
+  }
+  prev = lastUpdate;
+  lastUpdate = now;
+  csHdr.size 
+    = htons(sizeof(CS_HEADER));
+  csHdr.tcpType
+    = htons(STATS_CS_PROTO_GET_STATISTICS);
+  if (SYSERR == writeToSocket(sock,
+                             &csHdr)) {
+    MUTEX_UNLOCK(&lock);
+    return;
+  }
+  statMsg 
+    = MALLOC(MAX_BUFFER_SIZE);
+  statMsg->totalCounters 
+    = htonl(1); /* to ensure we enter the loop */
+  count = 0;
+  while ( count < ntohl(statMsg->totalCounters) ) {
+    if (SYSERR == readFromSocket(sock,
+                                (CS_HEADER**)&statMsg)) {
+      FREE(statMsg);
+      MUTEX_UNLOCK(&lock);
+      return;    
+    }
+    if (ntohs(statMsg->header.size) < sizeof(STATS_CS_MESSAGE)) {
+      LOG(LOG_WARNING,
+         "WARNING: received malformed stats message (%d < %d)\n",
+         ntohs(statMsg->header.size), 
+         sizeof(STATS_CS_MESSAGE) );
+      break;
+    }
+    mpos = sizeof(unsigned long long) * ntohl(statMsg->statCounters);
+    if ( ((char*)(((STATS_CS_MESSAGE_GENERIC*)statMsg)->values))
+        [ntohs(statMsg->header.size) - sizeof(STATS_CS_MESSAGE) - 1] != '\0') {
+      LOG(LOG_WARNING,
+         "WARNING: received malformed stats message (does not end with 
0-termination)\n");
+      break;
+    }      
+    for (i=0;i<ntohl(statMsg->statCounters);i++) {
+      optName = &((char*)(((STATS_CS_MESSAGE_GENERIC*)statMsg)->values))[mpos];
+      if ( (mpos > ntohs(statMsg->header.size) - sizeof(STATS_CS_MESSAGE)) ||
+          (mpos + strlen(optName) + 1 > 
+           ntohs(statMsg->header.size) - sizeof(STATS_CS_MESSAGE)) ) {
+       LOG(LOG_WARNING,
+           "WARNING: received malformed stats message (%d > %d)\n",
+           mpos+strlen(optName)+1,
+           ntohs(statMsg->header.size) - sizeof(STATS_CS_MESSAGE));
+       break; /* out of bounds! */      
+      }
+      found = -1;
+      for (j=0;j<lsv_size;j++) 
+       if (0 == strcmp(optName,
+                       lastStatValues[j].statName))
+         found = j;
+      if (found == -1) {
+       found = lsv_size;
+       GROW(lastStatValues,
+            lsv_size,
+            lsv_size+1);
+       lastStatValues[found].statName
+         = STRDUP(optName);
+      }
+      lastStatValues[found].lvalue
+       = lastStatValues[found].value;
+      lastStatValues[found].value
+       = ntohll(((STATS_CS_MESSAGE_GENERIC*)statMsg)->values[i]);      
+      lastStatValues[found].delta
+       = now-prev;
+      mpos += strlen(optName)+1;
+    }    
+    count += ntohl(statMsg->statCounters);
+  } /* end while */
+  FREE(statMsg);
+  MUTEX_UNLOCK(&lock);
+}
+
+static int getStatValue(long long * value,
+                       long long * lvalue,
+                       cron_t * dtime,
+                       GNUNET_TCP_SOCKET * sock,
+                       const char * optName) {
+  unsigned int i;
+
+  *value = 0;
+  if (lvalue != NULL)
+    *lvalue = 0;
+  updateStatValues(sock);
+  MUTEX_LOCK(&lock);
+  for (i=0;i<lsv_size;i++) {
+    if (0 == strcmp(optName,
+                   lastStatValues[i].statName)) {
+      *value = lastStatValues[i].value;
+      if (lvalue != NULL)
+       *lvalue = lastStatValues[i].lvalue;
+      if (dtime != NULL)
+       *dtime = lastStatValues[i].delta;
+      MUTEX_UNLOCK(&lock);
+      return OK;
+    }      
+  }
+  MUTEX_UNLOCK(&lock);
+  return SYSERR;
+}
+
+/**
+ * Callback function to obtain the latest stats
+ * data for this stat display.
+ */
+typedef int (*UpdateData)(GNUNET_TCP_SOCKET * sock,
+                         const void * closure,
+                         gfloat ** data);
+
+static int getConnectedNodesStat(GNUNET_TCP_SOCKET * sock,
+                                const void * closure,
+                                gfloat ** data) {
+  long long val;
+  char * cmh;
+  long long cval;
+
+  cmh = getConfigurationOptionValue(sock,
+                                   "gnunetd",
+                                   "connection-max-hosts");
+  if (cmh == NULL)
+    return SYSERR;
+  cval = atoll(cmh);
+  FREE(cmh);
+  if (OK != getStatValue(&val,
+                        NULL,
+                        NULL,
+                        sock,
+                        "# currently connected nodes")) 
+    return SYSERR;
+  data[0][0] = 0.8 * val / cval;
+  return OK;
+}
+
+static int getCPULoadStat(GNUNET_TCP_SOCKET * sock,
+                         const void * closure,
+                         gfloat ** data) {
+  long long val;
+
+  if (OK != getStatValue(&val,
+                        NULL,
+                        NULL,
+                        sock,
+                        "% of allowed cpu load"))
+    return SYSERR;
+  data[0][0] = val / 125.0;
+  return OK;
+}
+
+static int getTrafficRecvStats(GNUNET_TCP_SOCKET * sock,
+                              const void * closure,
+                              gfloat ** data) {
+  long long total;
+  long long noise;
+  long long content;
+  long long queries;
+  long long ltotal;
+  long long lnoise;
+  long long lcontent;
+  long long lqueries;
+  long long band;
+  long long tmp;
+  long long ltmp;
+  cron_t dtime;
+  char * available;
+
+  MUTEX_LOCK(&lock);
+  if (OK != getStatValue(&total,       
+                        &ltotal,
+                        &dtime,
+                        sock,
+                        "# bytes decrypted"))
+    return SYSERR;
+  if (OK != getStatValue(&noise,
+                        &lnoise,
+                        NULL,
+                        sock,
+                        "# bytes of noise received"))
+    return SYSERR;
+  content = lcontent = 0;
+  if (OK == getStatValue(&tmp,
+                        &ltmp,
+                        NULL,
+                        sock,
+                        "# bytes received of type 17")) {
+    content += tmp;
+    lcontent += ltmp;
+  }
+  if (OK == getStatValue(&tmp,
+                        &ltmp,
+                        NULL,
+                        sock,
+                        "# bytes received of type 18")) {
+    content += tmp;
+    lcontent += ltmp;
+  }
+  if (OK == getStatValue(&tmp,
+                        &ltmp,
+                        NULL,
+                        sock,
+                        "# bytes received of type 20")) {
+    content += tmp;
+    lcontent += ltmp;
+  }
+  queries = lqueries = 0;
+  if (OK == getStatValue(&tmp,
+                        &ltmp,
+                        NULL,
+                        sock,
+                        "# bytes received of type 16")) {
+    queries += tmp;
+    lqueries += ltmp;
+  }
+  if (OK == getStatValue(&tmp,
+                        &ltmp,
+                        NULL,
+                        sock,
+                        "# bytes received of type 19")) {
+    queries += tmp;
+    lqueries += ltmp;
+  }
+  
+  MUTEX_UNLOCK(&lock);
+  available = getConfigurationOptionValue(sock,
+                                         "LOAD",
+                                         "MAXNETDOWNBPSTOTAL");
+  if (available == NULL)
+    return SYSERR; 
+  band = atoll(available) * dtime / cronSECONDS;
+  FREE(available);
+  total -= ltotal;
+  noise -= lnoise;
+  queries -= lqueries;
+  content -= lcontent;
+  if (band <= 0) {
+    data[0][0] = 0.0;
+    data[0][1] = 0.0;
+    data[0][2] = 0.0;
+    data[0][3] = 0.0;
+    return OK;
+  }
+  data[0][0] = 0.8 * noise / band; /* red */
+  data[0][1] = 0.8 * (content+noise) / band; /* green */
+  data[0][2] = 0.8 * (queries+content+noise) / band; /* yellow */
+  data[0][3] = 0.8 * total / band; /* blue */
+  /*printf("I: %f %f %f\n", 
+        data[0][0],
+        data[0][1],
+        data[0][2]);*/
+
+  return OK;
+}
+
+
+static int getTrafficSendStats(GNUNET_TCP_SOCKET * sock,
+                              const void * closure,
+                              gfloat ** data) {
+  long long total;
+  long long noise;
+  long long content;
+  long long queries;
+  long long ltotal;
+  long long lnoise;
+  long long lcontent;
+  long long lqueries;
+  long long band;
+  long long tmp;
+  long long ltmp;
+  cron_t dtime;
+  char * available;
+
+  MUTEX_LOCK(&lock);
+  if (OK != getStatValue(&total,       
+                        &ltotal,
+                        &dtime,
+                        sock,
+                        "# encrypted bytes sent"))
+    return SYSERR;
+  if (OK != getStatValue(&noise,
+                        &lnoise,
+                        NULL,
+                        sock,
+                        "# bytes noise sent"))
+    return SYSERR;
+  content = lcontent = 0;
+  if (OK == getStatValue(&tmp,
+                        &ltmp,
+                        NULL,
+                        sock,
+                        "# bytes transmitted of type 17")) {
+    content += tmp;
+    lcontent += ltmp;
+  }
+  if (OK == getStatValue(&tmp,
+                        &ltmp,
+                        NULL,
+                        sock,
+                        "# bytes transmitted of type 18")) {
+    content += tmp;
+    lcontent += ltmp;
+  }
+  if (OK == getStatValue(&tmp,
+                        &ltmp,
+                        NULL,
+                        sock,
+                        "# bytes transmitted of type 20")) {
+    content += tmp;
+    lcontent += ltmp;
+  }
+  queries = lqueries = 0;
+  if (OK == getStatValue(&tmp,
+                        &ltmp,
+                        NULL,
+                        sock,
+                        "# bytes transmitted of type 16")) {
+    queries += tmp;
+    lqueries += ltmp;
+  }
+  if (OK == getStatValue(&tmp,
+                        &ltmp,
+                        NULL,
+                        sock,
+                        "# bytes transmitted of type 19")) {
+    queries += tmp;
+    lqueries += ltmp;
+  }
+  MUTEX_UNLOCK(&lock);
+  available = getConfigurationOptionValue(sock,
+                                         "LOAD",
+                                         "MAXNETUPBPSTOTAL");
+  if (available == NULL)
+    return SYSERR;
+  band = atoll(available) * dtime / cronSECONDS;
+  FREE(available);
+  total -= ltotal;
+  noise -= lnoise;
+  queries -= lqueries;
+  content -= lcontent;
+  if (band <= 0) {
+    data[0][0] = 0.0;
+    data[0][1] = 0.0;
+    data[0][2] = 0.0;
+    data[0][3] = 0.0;
+    return OK;
+  }
+  data[0][0] = 0.8 * noise / band; /* red */
+  data[0][1] = 0.8 * (noise + content) / band; /* green */
+  data[0][2] = 0.8 * (noise + content + queries) / band; /* yellow */
+  data[0][3] = 0.8 * total / band; /* yellow */
+  /* printf("O: %f %f %f\n", 
+     data[0][0],
+     data[0][1],
+        data[0][2]);*/
+  
+  return OK;
+}
+
+
+
+typedef struct SE_ {
+  char * paneName;
+  char * frameName;
+  UpdateData getData;
+  void * get_closure;
+  unsigned int count;
+  int fill; /* YES / NO */
+} StatEntry;
+
+#define STATS_COUNT 4
+
+static StatEntry stats[] = {
+  { 
+    "Connectivity",
+    "# connected nodes (100% = connection table size)",
+    &getConnectedNodesStat,
+    NULL,
+    1,
+    NO,
+  }, 
+  { 
+    "CPU load",
+    "CPU load (in percent of allowed load)",
+    &getCPULoadStat,
+    NULL,
+    1,
+    NO,
+  },
+  { 
+    "Inbound Traffic",
+    "Noise (red), Content (green), Queries (yellow), other (blue)",
+    &getTrafficRecvStats,
+    NULL,
+    4,
+    YES,
+  },
+  { 
+    "Outbound Traffic",
+    "Noise (red), Content (green), Queries (yellow), other (blue)",
+    &getTrafficSendStats,
+    NULL,
+    4,
+    YES,
+  },
+  {
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    1,
+    NO,
+  },
+};
+
+static void statClose(void);
+
+static GtkItemFactoryEntry statWindowMenu[] = {
+  { "/Close display",   
+    NULL, 
+    statClose, 
+    0, 
+    "<Item>" 
+  }
+};
+static gint statWindowMenuItems 
+  = sizeof (statWindowMenu) / sizeof (statWindowMenu[0]);
+
+typedef struct {
+  gint type;
+  guint count;
+  guint speed;
+  guint draw_width, draw_height;
+  guint num_points; 
+  guint allocated;  
+  GdkColor *colors;
+  gfloat **data, **odata;
+  guint data_size;
+  gint colors_allocated;
+  GtkWidget *main_widget;
+  GtkWidget *disp;
+  GtkWidget *label;
+  GdkPixmap *pixmap;
+  GdkGC *gc;
+  int timer_index;  
+  gboolean draw;
+  GNUNET_TCP_SOCKET * sock;
+  int statIdx;
+} LoadGraph;
+
+#define MAX_COLOR 4
+
+typedef struct {
+  gint            graph_update_interval;
+  GdkColor        bg_color;
+  GdkColor        frame_color;
+  GdkColor        mem_color[MAX_COLOR];
+} ProcConfig;
+
+typedef struct ProcData {
+  ProcConfig      config;
+  LoadGraph       *mem_graph;
+  int statIdx;
+} ProcData;
+
+#define GNOME_PAD_SMALL 2
+#define FRAME_WIDTH 0
+
+/**
+ * Remove the active page from the notebook.
+ **/
+static void statClose(void) {
+  gint pagenr;
+
+  pagenr = gtk_notebook_get_current_page(notebook);
+  gtk_notebook_remove_page(notebook, pagenr);
+  /* Need to refresh the widget --
+     This forces the widget to redraw itself. */
+  gtk_widget_draw(GTK_WIDGET(notebook), NULL);
+}
+
+/* Redraws the backing pixmap for the load graph and updates the window */
+static void load_graph_draw(LoadGraph *g) {
+  guint i;
+  guint j;
+  gint dely;
+  float delx;
+  
+  if (!g->disp->window)
+    return;
+  
+  g->draw_width = g->disp->allocation.width;
+  g->draw_height = g->disp->allocation.height;
+  
+  if (!g->pixmap)
+    g->pixmap = gdk_pixmap_new (g->disp->window,
+                               g->draw_width, g->draw_height,
+                               gtk_widget_get_visual (g->disp)->depth);
+  
+  /* Create GC if necessary. */
+  if (!g->gc) {
+    g->gc = gdk_gc_new (g->disp->window);
+    gdk_gc_copy (g->gc, g->disp->style->white_gc);
+  }
+  
+  /* Allocate colors. */
+  if (!g->colors_allocated) {
+    GdkColormap *colormap;
+    
+    colormap = gdk_window_get_colormap (g->disp->window);
+    for (i=0;i<2+g->count;i++) 
+      gdk_color_alloc (colormap, &(g->colors [i]));    
+    
+    g->colors_allocated = 1;
+  }
+  /* Erase Rectangle */
+  gdk_gc_set_foreground (g->gc, &(g->colors [0]));
+  gdk_draw_rectangle (g->pixmap,
+                     g->gc,
+                     TRUE, 0, 0,
+                     g->disp->allocation.width,
+                     g->disp->allocation.height);
+  
+  /* draw frame */
+  gdk_gc_set_foreground (g->gc, &(g->colors [1]));
+  gdk_draw_rectangle (g->pixmap,
+                     g->gc,
+                     FALSE, 0, 0,
+                     g->draw_width,
+                     g->disp->allocation.height);
+  
+  dely = g->draw_height / 5;
+  for (i = 1; i <5; i++) {
+    gint y1 = g->draw_height + 1 - i * dely;
+    gdk_draw_line (g->pixmap, g->gc,
+                  0, y1, g->draw_width, y1);
+  }
+  
+  gdk_gc_set_line_attributes (g->gc, 2, GDK_LINE_SOLID, GDK_CAP_ROUND, 
GDK_JOIN_MITER );
+  delx = (float)g->draw_width / ( g->num_points - 1);
+  
+  for (j=0;j<g->count;j++) {
+    gdk_gc_set_foreground (g->gc, &(g->colors [j + 2]));
+    for (i = 0; i < g->num_points - 1; i++) {    
+      gint x1 = i * delx;
+      gint x2 = (i + 1) * delx;
+      gint y1 = g->data[i][j] * g->draw_height - 1;
+      gint y2 = g->data[i+1][j] * g->draw_height - 1;
+      
+      if ((g->data[i][j] != -1) && (g->data[i+1][j] != -1)) {
+       if (stats[g->statIdx].fill == NO) {
+         gdk_draw_line(g->pixmap, g->gc,
+                       g->draw_width - x2, 
+                       g->draw_height - y2,
+                       g->draw_width - x1,
+                       g->draw_height - y1);
+       } else {
+         GdkPoint points[4];
+         
+         points[0].x = g->draw_width - x2;
+         points[0].y = g->draw_height - y2;
+         points[1].x = g->draw_width - x1;
+         points[1].y = g->draw_height - y1;
+         points[2].x = g->draw_width - x1;
+         points[3].x = g->draw_width - x2;
+         if (j == 0) {
+           points[2].y = g->draw_height;
+           points[3].y = g->draw_height;
+         } else {
+           gint ly1 = g->data[i][j-1] * g->draw_height - 1;
+           gint ly2 = g->data[i+1][j-1] * g->draw_height - 1;
+           points[2].y = g->draw_height - ly1;
+           points[3].y = g->draw_height - ly2;
+         }
+         gdk_draw_polygon(g->pixmap,
+                          g->gc,
+                          1,
+                          points,
+                          4);  
+       }
+      }
+    }
+  }
+
+  gdk_gc_set_line_attributes (g->gc, 1, GDK_LINE_SOLID, GDK_CAP_ROUND, 
GDK_JOIN_MITER );
+  
+  gdk_draw_pixmap (g->disp->window,
+                  g->disp->style->fg_gc [GTK_WIDGET_STATE(g->disp)],
+                  g->pixmap,
+                  0, 0,
+                  0, 0,
+                  g->disp->allocation.width,
+                  g->disp->allocation.height);
+}
+
+
+/* Updates the load graph when the timeout expires */
+static int load_graph_update(LoadGraph *g) {
+  guint i;  
+  guint j;
+
+  for (i=0;i<g->num_points;i++)
+    memcpy(g->odata[i], 
+          g->data[i],
+          g->data_size * g->count); 
+  stats[g->statIdx].getData(g->sock,
+                           stats[g->statIdx].get_closure,
+                           g->data);
+  for (i=0;i<g->num_points-1;i++)
+    for (j=0;j<g->count;j++)
+      g->data[i+1][j] = g->odata[i][j];  
+  if (g->draw)
+    load_graph_draw (g);  
+  return TRUE;
+}
+
+static void load_graph_unalloc (LoadGraph *g) {
+  int i;
+  if (!g->allocated)
+    return;
+  for (i = 0; i < g->num_points; i++) {
+    FREE(g->data[i]);
+    FREE(g->odata[i]);
+  }
+  FREE(g->data);
+  FREE(g->odata);
+  g->data = g->odata = NULL;
+  if (g->pixmap) {
+    gdk_pixmap_unref(g->pixmap);
+    g->pixmap = NULL;
+  }
+  g->allocated = FALSE;
+}
+
+static void load_graph_alloc (LoadGraph *g) {
+  int i;
+  int j;
+
+  if (g->allocated)
+    return;
+  
+  g->data = MALLOC(sizeof(gfloat *) * g->num_points);
+  g->odata = MALLOC(sizeof(gfloat*) * g->num_points);
+  g->data_size = sizeof (gfloat);  
+  for (i = 0; i < g->num_points; i++) {
+    g->data[i] = MALLOC(g->data_size * g->count);
+    g->odata[i] = MALLOC(g->data_size * g->count);
+  }  
+  for (i=0;i<g->num_points;i++) 
+    for (j=0;j<g->count;j++)
+      g->data[i][j] = -1;
+  g->allocated = TRUE;
+}
+
+static gint load_graph_configure(GtkWidget *widget, 
+                                GdkEventConfigure *event,
+                                gpointer data_ptr) {
+  LoadGraph *c = (LoadGraph *) data_ptr;
+  
+  if (c->pixmap) {
+    gdk_pixmap_unref (c->pixmap);
+    c->pixmap = NULL;
+  }
+  
+  if (!c->pixmap)
+    c->pixmap = gdk_pixmap_new (widget->window,
+                               widget->allocation.width,
+                               widget->allocation.height,
+                               gtk_widget_get_visual (c->disp)->depth);
+  
+  gdk_draw_rectangle (c->pixmap,
+                     widget->style->black_gc,
+                     TRUE, 0,0,
+                     widget->allocation.width,
+                     widget->allocation.height);
+  gdk_draw_pixmap (widget->window,
+                  c->disp->style->fg_gc [GTK_WIDGET_STATE(widget)],
+                  c->pixmap,
+                  0, 0,
+                  0, 0,
+                  c->disp->allocation.width,
+                  c->disp->allocation.height);  
+  load_graph_draw (c); 
+  return TRUE;
+}
+
+static gint load_graph_expose(GtkWidget *widget,
+                             GdkEventExpose *event,
+                             gpointer data_ptr) {
+  LoadGraph *g = (LoadGraph *) data_ptr;
+  
+  gdk_draw_pixmap (widget->window,
+                  widget->style->fg_gc [GTK_WIDGET_STATE(widget)],
+                  g->pixmap,
+                  event->area.x, event->area.y,
+                  event->area.x, event->area.y,
+                  event->area.width, event->area.height);
+  return FALSE;
+}
+
+static void load_graph_stop (LoadGraph *g) {
+  if (g->timer_index != -1) {
+    gtk_timeout_remove (g->timer_index);
+    g->timer_index = -1;
+  }
+  if (!g)
+    return;
+  g->draw = FALSE;
+}
+
+static void load_graph_destroy(GtkWidget *widget, 
+                              gpointer data_ptr) {
+  LoadGraph *g = (LoadGraph *) data_ptr;  
+  load_graph_stop(g);  
+  if (g->timer_index != -1)
+    gtk_timeout_remove (g->timer_index);
+  if (g->sock != NULL)
+    releaseClientSocket(g->sock);
+  load_graph_unalloc(g);
+  FREE(g->colors);
+  FREE(g);
+}
+
+static LoadGraph * load_graph_new(ProcData *procdata) {
+  LoadGraph *g;
+  unsigned int i;
+
+  if ( (procdata->statIdx < 0) ||
+       (procdata->statIdx >= STATS_COUNT) ) {
+    LOG(LOG_ERROR,
+       "ERROR: invalid statIdex %d!",
+       procdata->statIdx);
+    return NULL;
+  }
+  if (stats[procdata->statIdx].count > MAX_COLOR) {
+    LOG(LOG_ERROR,
+       "ERROR: more colors requested than available!\n");
+    return NULL;
+  }
+  
+  g = MALLOC(sizeof(LoadGraph));
+  g->sock = getClientSocket();
+  g->statIdx = procdata->statIdx;
+  g->count = stats[g->statIdx].count;
+  g->speed = procdata->config.graph_update_interval;
+  g->num_points = 600;
+  g->colors = MALLOC(sizeof(GdkColor) * (2+g->count));
+  g->colors[0] = procdata->config.bg_color;
+  g->colors[1] = procdata->config.frame_color;  
+  for (i=0;i<g->count;i++) 
+    g->colors[2+i] = procdata->config.mem_color[i];
+  g->timer_index = -1;
+  g->draw = FALSE;
+  g->main_widget = gtk_vbox_new (FALSE, FALSE);
+  gtk_widget_show (g->main_widget);
+  g->disp = gtk_drawing_area_new();
+  gtk_widget_show (g->disp);
+  gtk_signal_connect (GTK_OBJECT (g->disp),
+                     "expose_event",
+                     GTK_SIGNAL_FUNC (load_graph_expose), g);
+  gtk_signal_connect (GTK_OBJECT(g->disp), 
+                     "configure_event",
+                     GTK_SIGNAL_FUNC (load_graph_configure), g);
+  gtk_signal_connect (GTK_OBJECT(g->disp),
+                     "destroy",
+                     GTK_SIGNAL_FUNC (load_graph_destroy), g); 
+  gtk_widget_set_events(g->disp, GDK_EXPOSURE_MASK);  
+  gtk_box_pack_start(GTK_BOX (g->main_widget), g->disp, TRUE, TRUE, 0);  
+  load_graph_alloc(g);  
+  gtk_widget_show_all (g->main_widget);  
+  g->timer_index = gtk_timeout_add(g->speed,
+                                  (GtkFunction) load_graph_update, g);
+  
+  return g;
+}
+
+static void load_graph_start(LoadGraph *g) {
+  if (!g)
+    return;
+  
+  if (g->timer_index == -1)
+    g->timer_index = gtk_timeout_add(g->speed,
+                                    (GtkFunction) load_graph_update, g);
+  
+  g->draw = TRUE;
+}
+
+static GtkWidget * create_sys_view(ProcData * procdata) {
+  GtkWidget * vbox;
+  GtkWidget * mem_frame;
+  GtkWidget * menu;
+  GtkItemFactory * popupFactory;
+  LoadGraph * mem_graph;
+
+  mem_graph = load_graph_new(procdata);
+  if (mem_graph == NULL)
+    return NULL; /* oops */
+  vbox = gtk_vbox_new (FALSE, 0);
+
+  mem_frame = gtk_frame_new (stats[procdata->statIdx].frameName);
+  gtk_container_set_border_width (GTK_CONTAINER (mem_frame), GNOME_PAD_SMALL);
+  gtk_box_pack_start (GTK_BOX (vbox), mem_frame, TRUE, TRUE, 0);
+  
+ 
+  gtk_container_add (GTK_CONTAINER (mem_frame), mem_graph->main_widget);
+  gtk_container_set_border_width (GTK_CONTAINER (mem_graph->main_widget),
+                                 GNOME_PAD_SMALL);
+  procdata->mem_graph = mem_graph;
+
+  /* FIXME: this should add a right-button popup-menu 
+     but does not. Somehow popupCallback() never gets called,
+     i.e. event does not occur or has been blocked or 
+     overridden somewhere. Trying to connect to some other
+     widget instead of vbox stays equally unresponsive */
+  popupFactory = gtk_item_factory_new (GTK_TYPE_MENU, "<main>",
+                                      NULL);
+  gtk_item_factory_create_items(popupFactory,
+                               statWindowMenuItems,
+                               statWindowMenu,
+                               NULL);
+  menu = gtk_item_factory_get_widget(popupFactory, "<main>");
+  gtk_signal_connect(GTK_OBJECT(vbox),
+                    "event",
+                    GTK_SIGNAL_FUNC(popupCallback),
+                    menu);
+  gtk_widget_show_all (vbox);
+  
+  return vbox;
+}
+
+
+static GtkWidget * create_main_window(int stat) {
+  GtkWidget *sys_box;
+  ProcData procdata;
+
+    
+  memset(&procdata, 0, sizeof(ProcData));
+  procdata.config.graph_update_interval 
+    = UPDATE_INTERVAL / cronMILLIS;
+  procdata.statIdx = stat;
+  gdk_color_parse("black",
+                 &procdata.config.bg_color);
+  gdk_color_parse("gray",
+                 &procdata.config.frame_color);
+  gdk_color_parse("red",
+                 &procdata.config.mem_color[0]);
+  gdk_color_parse("green",
+                 &procdata.config.mem_color[1]);
+  gdk_color_parse("yellow",
+                 &procdata.config.mem_color[2]);
+  gdk_color_parse("blue",
+                 &procdata.config.mem_color[3]);
+  if (MAX_COLOR != 4)
+    errexit("Assertion failed! MAX_COLOR wrong!");
+  sys_box = create_sys_view(&procdata);
+  if (sys_box == NULL)
+    return NULL;
+  load_graph_start(procdata.mem_graph); 
+  return sys_box;
+}
+
+
+/**
+ * Display the statistics.
+ */
+void displayStatistics(GtkWidget * widget,
+                      gpointer data) {
+  int dptr;
+  GtkWidget * wid;
+
+  dptr = (int) data;
+  if ( (dptr < 0) ||
+       (dptr >= STATS_COUNT) ) {
+    LOG(LOG_ERROR,
+       "ERROR: displayStatistics called with invalid argument (%d)\n",
+       dptr);
+  } else {    
+    wid = create_main_window(dptr);
+    if (wid != NULL)
+      addToNotebook(stats[dptr].paneName,
+                   wid);
+  }
+}
+
+void initGTKStatistics() {
+  MUTEX_CREATE_RECURSIVE(&lock);
+}
+
+void doneGTKStatistics() {
+  unsigned int i;
+  for (i=0;i<lsv_size;i++)
+    FREE(lastStatValues[i].statName);
+  GROW(lastStatValues,
+       lsv_size,
+       0);
+  MUTEX_DESTROY(&lock);
+}
+
+ 
+/* end of statistics.c */

Added: freeway/src/org/gnu/freeway/protocol/afs/swing/statistics.h
===================================================================
--- freeway/src/org/gnu/freeway/protocol/afs/swing/statistics.h 2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/afs/swing/statistics.h 2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,52 @@
+/*
+     This file is part of GNUnet
+
+     GNUnet is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with GNUnet; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file applications/afs/gtkui/statistics.h
+ * @author Christian Grothoff 
+ **/
+
+#ifndef GTKUI_STATISTICS_H
+#define GTKUI_STATISTICS_H
+
+/* for GTK 2 */
+#define GTK_ENABLE_BROKEN
+
+#include "platform.h"
+#include <gtk/gtk.h>
+#include <gtk/gtktext.h>
+
+/**
+ * Display the statistics.
+ */
+void displayStatistics(GtkWidget * widget,
+                      gpointer data);
+
+
+void initGTKStatistics();
+ 
+void doneGTKStatistics();
+
+#define STAT_CONNECTIVITY 0
+#define STAT_CPU_LOAD 1
+#define STAT_IN_TRAFFIC 2
+#define STAT_OUT_TRAFFIC 3
+
+
+#endif

Added: freeway/src/org/gnu/freeway/protocol/chat/CSChatMessage.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/chat/CSChatMessage.java        
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/chat/CSChatMessage.java        
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,103 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.chat;
+
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+import java.util.*;
+
+/**
+ */
+
+public class CSChatMessage extends CSMessage
+{
+       private static final int        CHAT_NICK_LENGTH        =       32;
+       private static final int        CHAT_MSG_LENGTH         =       1024;
+
+       public static final int SIZE    =       
CHAT_NICK_LENGTH+CHAT_MSG_LENGTH+4;
+
+       private byte[]  nick;
+       private byte[]  message;
+
+
+       public CSChatMessage()
+       {
+               super(IS_CHAT_MESSAGE);
+               nick=new byte[CHAT_NICK_LENGTH];
+               Arrays.fill(nick,(byte) 0);
+               message=new byte[CHAT_MSG_LENGTH];
+               Arrays.fill(message,(byte) 0);
+       }
+
+       public String toString()
+       {
+               return "Chat c/s message [nick="+getNickName()+", 
message="+getMessage()+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public String getNickName()
+       {
+               int     i;
+
+               for (i=0; i<nick.length && nick[i]!=0; i++) {}
+               return new String(nick,0,i);
+       }
+
+       public void setNickName( String str )
+       {
+               byte[]  b;
+
+               b=str.getBytes();
+               Arrays.fill(nick,(byte) 0);
+               System.arraycopy(b,0,nick,0,Math.min(b.length,nick.length-1));
+       }
+
+       public String getMessage()
+       {
+               int     i;
+
+               for (i=0; i<message.length && message[i]!=0; i++) {}
+               return new String(message,0,i);
+       }
+
+       public void setMessage( String str )
+       {
+               byte[]  b;
+
+               b=str.getBytes();
+               Arrays.fill(message,(byte) 0);
+               
System.arraycopy(b,0,message,0,Math.min(b.length,message.length-1));
+       }
+
+       public int getByteSize()
+       {
+               return SIZE;
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               int     size,type;
+
+               size=buf.getShort() & 0x0000ffff;
+               err.reportIf(size!=SIZE,"bad size");
+
+               type=buf.getShort() & 0x0000ffff;
+               err.reportIf(type!=IS_CHAT_MESSAGE,"bad type !");
+
+               buf.get(nick);
+               buf.get(message);
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               buf.putShort((short) SIZE);
+               buf.putShort((short) IS_CHAT_MESSAGE);
+               buf.put(nick);
+               buf.put(message);
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/chat/ChatProtocol.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/chat/ChatProtocol.java 2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/chat/ChatProtocol.java 2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,225 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.chat;
+
+import org.gnu.freeway.*;
+import org.gnu.freeway.protocol.*;
+import org.gnu.freeway.server.*;
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.util.logging.*;
+
+/**
+ * CHAT CORE. This is the code that is plugged
+ * into the GNUnet core to enable chatting.
+ */
+
+public class ChatProtocol extends AbstractProtocol
+{
+       public static final int MAX_LAST_MESSAGES       =       12;
+       public static final int MAX_CLIENTS                     =       4;
+
+       private CoreForProtocol coreAPI;
+       private CSSession[]     clients;
+       private int                             clientCount;
+       private HashCode160[]           lastMsgs;
+       private int                             ringIndex;
+       private Object                  chatMutex;
+
+
+       public ChatProtocol()
+       {
+               super("CHAT");
+               clients=new CSSession[MAX_CLIENTS];
+               lastMsgs=new HashCode160[MAX_LAST_MESSAGES];
+       }
+
+       public String toString()
+       {
+               return "Chat";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       /**
+        * Initialize the AFS module. This method name must match
+        * the library name (libgnunet_XXX => initialize_XXX).
+        * @param capi
+        * @return false on errors
+        */
+
+       public boolean init( CoreForProtocol capi )
+       {
+               boolean ok = true;
+
+               super.init(capi);
+               if (P2PMessage.IS_CHAT_MESSAGE != CSMessage.IS_CHAT_MESSAGE) {
+                       log(Level.SEVERE,"Message type ids for p2p and CS CHAT 
must be equal! ("+P2PMessage.IS_CHAT_MESSAGE+" != 
"+CSMessage.IS_CHAT_MESSAGE+")");
+                       return false;
+                       }
+               if (P2PChatMessage.SIZE != CSChatMessage.SIZE) {
+                       log(Level.SEVERE,"Message sizes for p2p and CS chat 
must be equals! ("+P2PChatMessage.SIZE+" != "+CSChatMessage.SIZE+")");
+                       return false;
+                       }
+
+               chatMutex=new Object();
+               clientCount = 0;
+               coreAPI = capi;
+               log(Level.FINEST,"Registering handlers 
"+P2PMessage.IS_CHAT_MESSAGE+" and "+CSMessage.IS_CHAT_MESSAGE);
+
+               addP2PHandler(P2PMessage.IS_CHAT_MESSAGE,P2PChatMessage.class);
+               if (!((Server) 
coreAPI.getApplication()).registerCSExitHandler(new ClientExitHandler() {
+                       public void handle( CSSession client )
+                       {
+                               chatClientExitHandler(client);
+                       }
+                       }))
+                       ok = false;
+               if (!((Server) 
coreAPI.getApplication()).registerCSHandler(CSMessage.IS_CHAT_MESSAGE,CSChatMessage.class,new
 CSHandler() {
+                       public boolean handle( CSSession client, CSMessage 
message )
+                       {
+                               return csHandleChatRequest(client,message);
+                       }
+                       }))
+                       ok = false;
+               return ok;
+       }
+
+       public void done()
+       {
+               ((Server) coreAPI.getApplication()).unregisterCSExitHandler(new 
ClientExitHandler() {
+                       public void handle( CSSession client )
+                       {
+                               chatClientExitHandler(client);
+                       }
+                       });
+               ((Server) 
coreAPI.getApplication()).unregisterCSHandler(CSMessage.IS_CHAT_MESSAGE,new 
CSHandler() {
+                       public boolean handle( CSSession client, CSMessage 
message )
+                       {
+                               return csHandleChatRequest(client,message);
+                       }
+                       });
+               chatMutex=null;
+               coreAPI = null;
+               super.done();
+       }
+
+       public PersistentDecoder createCSDecoder()
+       {
+               PersistentDecoder       dec;
+
+               dec=new PersistentDecoder();
+               dec.add(CSMessage.IS_CHAT_MESSAGE,CSChatMessage.class);
+               return dec;
+       }
+
+       protected void markSeen( HashCode160 hc )
+       {
+               if (++ringIndex >= MAX_LAST_MESSAGES)
+                       ringIndex = 0;
+               lastMsgs[ringIndex]=(HashCode160) PersistentHelper.copy(hc);
+       }
+
+       protected boolean onP2PMessage( HostIdentity sender, P2PMessage msg )
+       {
+               if (msg instanceof P2PChatMessage) {
+                       return handleChatMSG(sender,msg);
+                       }
+               return false;
+       }
+
+       protected boolean handleChatMSG( HostIdentity sender, P2PMessage 
message )
+       {
+               int                                     i,j;
+               CSChatMessage           cmsg;
+               P2PChatMessage  pmsg;
+               HashCode160                     hc;
+
+               if (message.getByteSize() != P2PChatMessage.SIZE) {
+                       log(Level.WARNING,"WARNING: message received from peer 
is invalid.");
+                       return false;
+                       }
+
+               pmsg = (P2PChatMessage) message;
+               cmsg = (CSChatMessage) 
PersistentHelper.readFully(CSChatMessage.class,PersistentHelper.toBuffer(message));
+
+               /* check if we have seen this message already */
+               hc=HashCode160.create(PersistentHelper.toBytes(pmsg));
+
+               j = -1;
+               synchronized(chatMutex) {
+                       for (i=0;i<MAX_LAST_MESSAGES;i++)
+                               if (hc.equals(lastMsgs[i]))
+                                       j = i;
+                       if (j == -1) {
+                               /* we have not seen it before, send to all TCP 
clients
+                                and broadcast to all peers */
+                               markSeen(hc);
+                               for (j=0; j<clientCount; j++) {
+                                       clients[j].send(cmsg);
+                                       }
+                               coreAPI.broadcastToConnected(message, 5, 1);
+
+                               debug("DEBUG: CHAT: received new message from 
"+pmsg.getNickName()+": "+pmsg.getMessage());
+                               }
+                       }
+               return true;
+       }
+
+       protected boolean csHandleChatRequest( CSSession client, CSMessage 
message )
+       {
+               int                                     i,j;
+               P2PChatMessage  pmsg;
+               HashCode160                     hc;
+
+               if (message.getByteSize()!=CSChatMessage.SIZE) {
+                       log(Level.WARNING,"WARNING: message received from 
client is invalid");
+                       return false; /* invalid message */
+                       }
+
+               pmsg = (P2PChatMessage) 
PersistentHelper.readFully(P2PChatMessage.class,PersistentHelper.toBuffer(message));
+
+               hc=HashCode160.create(PersistentHelper.toBytes(pmsg));
+               synchronized(chatMutex) {
+                       markSeen(hc);
+
+                       /* forward to all other TCP chat clients */
+                       j = -1; /* marker to check if this is a new client */
+                       for (i=0;i<clientCount;i++)
+                               if (clients[i] == client)
+                                       j = i;
+                               else
+                                       clients[i].send(message);
+                       if (j == -1) {
+                               if (clientCount == MAX_CLIENTS)
+                                       log(Level.WARNING,"Maximum number of 
chat clients reached.");
+                               else {
+                                       clients[clientCount++] = client;
+                                       log(Level.FINEST,"Now "+clientCount+" 
of "+MAX_CLIENTS+" chat clients at this node.");
+                                       }
+                               }
+                       /* forward to all other nodes in the network */
+                       coreAPI.broadcastToConnected(pmsg,5,1);
+                       }
+               return true;
+       }
+
+       protected void chatClientExitHandler( CSSession client )
+       {
+               int     i;
+
+               synchronized(chatMutex) {
+                       for (i=0; i<clientCount; i++) {
+                               if (clients[i] == client) {
+                                       log(Level.FINEST,"DEBUG: Chat client 
exits.");
+                                       clients[i] = clients[--clientCount];
+                                       break;
+                                       }
+                               }
+                       }
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/chat/P2PChatMessage.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/chat/P2PChatMessage.java       
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/chat/P2PChatMessage.java       
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,103 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.chat;
+
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+import java.util.*;
+
+/**
+ */
+
+public class P2PChatMessage extends P2PMessage
+{
+       private static final int        CHAT_NICK_LENGTH        =       32;
+       private static final int        CHAT_MSG_LENGTH         =       1024;
+
+       public static final int SIZE    =       
4+CHAT_NICK_LENGTH+CHAT_MSG_LENGTH;
+
+       private byte[]  nick;
+       private byte[]  message;
+
+
+       public P2PChatMessage()
+       {
+               super(IS_CHAT_MESSAGE);
+               nick=new byte[CHAT_NICK_LENGTH];
+               Arrays.fill(nick,(byte) 0);
+               message=new byte[CHAT_MSG_LENGTH];
+               Arrays.fill(message,(byte) 0);
+       }
+
+       public String toString()
+       {
+               return "Chat p2p message [nick="+getNickName()+", 
message="+getMessage()+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public String getNickName()
+       {
+               int     i;
+
+               for (i=0; i<nick.length && nick[i]!=0; i++) {}
+               return new String(nick,0,i);
+       }
+
+       public void setNickName( String str )
+       {
+               byte[]  b;
+
+               b=str.getBytes();
+               Arrays.fill(nick,(byte) 0);
+               System.arraycopy(b,0,nick,0,Math.min(b.length,nick.length-1));
+       }
+
+       public String getMessage()
+       {
+               int     i;
+
+               for (i=0; i<message.length && message[i]!=0; i++) {}
+               return new String(message,0,i);
+       }
+
+       public void setMessage( String str )
+       {
+               byte[]  b;
+
+               b=str.getBytes();
+               Arrays.fill(message,(byte) 0);
+               
System.arraycopy(b,0,message,0,Math.min(b.length,message.length-1));
+       }
+
+       public int getByteSize()
+       {
+               return SIZE;
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               int     size,type;
+
+               size=buf.getShort() & 0x0000ffff;
+               err.reportIf(size!=SIZE,"bad size");
+
+               type=buf.getShort() & 0x0000ffff;
+               err.reportIf(type!=IS_CHAT_MESSAGE,"bad type !");
+
+               buf.get(nick);
+               buf.get(message);
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               buf.putShort((short) SIZE);
+               buf.putShort((short) IS_CHAT_MESSAGE);
+               buf.put(nick);
+               buf.put(message);
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/dht/DHTConstants.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/dht/DHTConstants.java  2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/dht/DHTConstants.java  2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,95 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.dht;
+
+/**
+ *
+ */
+
+public interface DHTConstants
+{
+       /** sleeping time to be used when waiting for data to be received */
+       public static final int API_SLEEP_MILLIS                                
        =       50;
+
+       /** sleeping time between receive thread polling for any operation 
waiting for data */
+       public static final int RECEIVE_THREAD_SLEEP_MILLIS             =       
50;
+
+       /* timeouts (ms) for different operations or suboperations */
+       public static final int API_INITIALIZE_RESULTS_TIMEOUT  =       2000;
+
+       public static final int API_CREATE_ACK_TIMEOUT                  =       
1000;
+       public static final int API_CREATE_RESULTS_TIMEOUT              =       
4000;
+
+       public static final int API_INSERT_ACK_TIMEOUT                  =       
1000;
+       public static final int API_INSERT_RESULTS_TIMEOUT              =       
4000;
+
+       public static final int API_FETCH_ACK_TIMEOUT                           
=       1000;
+       public static final int API_FETCH_RESULTS_TIMEOUT                       
=       9000;
+
+       public static final int API_JOIN_ACK_TIMEOUT                            
=       1000;
+       public static final int API_JOIN_RESULTS_TIMEOUT                        
=       9000;
+
+       public static final int API_LEAVE_ACK_TIMEOUT                           
=       1000;
+       public static final int API_LEAVE_RESULTS_TIMEOUT                       
=       4000;
+
+       public static final int API_TABLES_ACK_TIMEOUT                  =       
1000;
+       public static final int API_TABLES_RESULTS_TIMEOUT              =       
9000;
+
+       public static final int API_INSERTED_ACK_TIMEOUT                        
=       1000;
+       public static final int API_INSERTED_RESULTS_TIMEOUT            =       
4000;
+
+       public static final int API_DROP_ACK_TIMEOUT                            
=       1000;
+       public static final int API_DROP_RESULTS_TIMEOUT                        
=       4000;
+
+       /* operation status numbering, we are only interested in 
waitfor-statuses */
+       public static final int OPERATION_STATUS_OTHER                  =       
0;
+       public static final int OPERATION_STATUS_WAITFOR_ACK            =       
1;
+       public static final int OPERATION_STATUS_WAITFOR_RESULTS        =       
2;
+       public static final int OPERATION_STATUS_WAITFOR_STATUS =       3;
+
+       /* ************* DHT-operation's errorcodes *********** */
+       public static final int DHT_ERRORCODE_BASE                              
                                                =       1000;
+       public static final int DHT_ERRORCODES__OP_REQUEST_REJECTED             
                                =       (DHT_ERRORCODE_BASE+1);
+
+       /* ************* API specific errorcodes *********** */
+       public static final int API_ERRORCODE_BASE                              
                                                =       50;
+       public static final int DHT_ERRORCODES__API_ERROR_UNKNOWN               
                                        =       (API_ERRORCODE_BASE+1);
+       public static final int 
DHT_ERRORCODES__API_CS_PROTO_RECEIVER_THREAD_CREATE_FAILED      =       
(API_ERRORCODE_BASE+2);
+       public static final int 
DHT_ERRORCODES__API_CS_PROTO_WRITE_TO_SOCKET_FAILED             =       
(API_ERRORCODE_BASE+3);
+       public static final int DHT_ERRORCODES__API_MEMORY_ALLOCATION_FAILED    
                        =       (API_ERRORCODE_BASE+4);
+       public static final int DHT_ERRORCODES__API_NO_SUCH_ELEMENT             
                                =       (API_ERRORCODE_BASE+5);
+
+       /* ************* DHT Client-Server protocol errorcodes *********** */
+               public static final int CS_ERRORCODE_BASE                       
                                                =       100;
+       public static final int 
DHT_ERRORCODES__CS_PROTO_UNEXPECTED_MESSAGE_RECEIVED            =       
(CS_ERRORCODE_BASE+1);
+       public static final int DHT_ERRORCODES__CS_PROTO_ACK_TIMEOUT            
                                =       (CS_ERRORCODE_BASE+2);
+       public static final int DHT_ERRORCODES__CS_PROTO_RESULTS_TIMEOUT        
                                =       (CS_ERRORCODE_BASE+3);
+       public static final int DHT_ERRORCODES__CS_PROTO_INVALID_RESULTS        
                                =       (CS_ERRORCODE_BASE+4);
+       public static final int DHT_ERRORCODES__CS_PROTO_PAYLOAD_EXCEEDED       
                                =       (CS_ERRORCODE_BASE+5);
+
+       /* *************** Datalayer specific error codes *************** */
+       public static final int DL_ERRORCODE_BASE                               
                                                =       150;
+       public static final int DHT_ERRORCODES__DATALAYER_STORAGE_FULL          
                                =       (DL_ERRORCODE_BASE+1);
+       public static final int DHT_ERRORCODES__DATALAYER_KEYSTORAGE_FULL       
                                =       (DL_ERRORCODE_BASE+2);
+       public static final int DHT_ERRORCODES__TABLE_NOT_FOUND                 
                                =       (DL_ERRORCODE_BASE+3);
+       public static final int DHT_ERRORCODES__EMPTY_RESULT                    
                                        =       (DL_ERRORCODE_BASE+4);
+       public static final int DHT_ERRORCODES__DUPLICATE_DATA_UNIT             
                                =       (DL_ERRORCODE_BASE+5);
+       public static final int DHT_ERRORCODES__DUPLICATE_TABLE_ID              
                                =       (DL_ERRORCODE_BASE+6);
+
+       /* these constants are exchanged between gnunetd and the clients (APIs) 
*/
+       public static final int REQUEST_STATUS_ACCEPTED                         
=       0;
+       public static final int REQUEST_STATUS_REJECTED                         
=       1;
+
+       public static final int FAILURE_REASON_UNKNOWN                          
=       2;
+       public static final int FAILURE_REASON_INTERNAL                         
=       3;
+       public static final int FAILURE_REASON_P2P_PROTOCOL                     
=       4;
+       public static final int FAILURE_REASON_MSG_LENGTH_MISMATCH      =       
5;
+
+       public static final int OPERATION_STATUS_UNKNOWN_OPERATION      =       
6;
+       public static final int OPERATION_STATUS_IN_PROGRESS                    
=       7;
+       public static final int OPERATION_STATUS_WILL_SUCCEED                   
=       8;
+       public static final int OPERATION_STATUS_SUCCEEDED                      
=       9;
+       public static final int OPERATION_STATUS_FAILED                         
=       10;
+}

Added: freeway/src/org/gnu/freeway/protocol/dht/DHTDataContainer.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/dht/DHTDataContainer.java      
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/dht/DHTDataContainer.java      
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,15 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.dht;
+
+/**
+ *
+ */
+
+public class DHTDataContainer extends Object
+{
+       public int              dataLength;
+       public byte[]   data;
+}

Added: freeway/src/org/gnu/freeway/protocol/dht/DHTDataList.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/dht/DHTDataList.java   2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/dht/DHTDataList.java   2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,15 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.dht;
+
+/**
+ *
+ */
+
+public class DHTDataList extends Object
+{
+       public int                              errorCode;
+       public DHTDataListItem  firstItem;
+}

Added: freeway/src/org/gnu/freeway/protocol/dht/DHTDataListItem.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/dht/DHTDataListItem.java       
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/dht/DHTDataListItem.java       
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,17 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.dht;
+
+/**
+ *
+ */
+
+public class DHTDataListItem extends Object
+{
+       public DHTDataContainer                 key;
+       public DHTDataContainer                 value;
+       public DHTStoredDataReference   uniqueReference;
+       public DHTDataListItem                  nextItem;
+}

Added: freeway/src/org/gnu/freeway/protocol/dht/DHTFetchResult.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/dht/DHTFetchResult.java        
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/dht/DHTFetchResult.java        
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,19 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.dht;
+
+/**
+ * this struct is exchanged in CS messages between gnunetd and the clients 
(APIs)
+ */
+
+public class DHTFetchResult extends Object
+{
+       /** number of values that are returned */
+       public int              valueCount;
+
+       /** Start of data. First there will be array of valueCount shorts 
(telling length of each returned value)
+        and immediately after that actual data will follow (concatenated) */
+       public byte[]   data;
+}

Added: freeway/src/org/gnu/freeway/protocol/dht/DHTMessageHeader.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/dht/DHTMessageHeader.java      
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/dht/DHTMessageHeader.java      
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,15 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.dht;
+
+/**
+ * this struct is exchanged in CS messages between gnunetd and the clients 
(APIs)
+ */
+
+public class DHTMessageHeader extends Object
+{
+       public int      apiId;
+       public int      requestId;
+}

Added: freeway/src/org/gnu/freeway/protocol/dht/DHTProtocol.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/dht/DHTProtocol.java   2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/dht/DHTProtocol.java   2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,214 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.dht;
+
+import org.gnu.freeway.protocol.*;
+import org.gnu.freeway.server.*;
+
+/**
+ * API to the DHT-module.  This API is what will be used by
+ *     DHT clients that run as modules within gnunetd.  If you
+ *     are writing a client look at either gnunet_dht.h (if you
+ *     want to handle the communication with gnunetd yourself) or
+ *     at gnunet_dht_lib to use the convenience library.
+ *
+ * convenience API to the DHT infrastructure for use by clients
+ *        (for library in applications/dht/tools/dht_api.c)
+ *
+ * data structures exchanged between between DHT clients and the GNUnet DHT 
module
+ */
+
+public class DHTProtocol extends AbstractProtocol implements DHTConstants
+{
+       public DHTProtocol()
+       {
+               super("DHT");
+       }
+
+       public String toString()
+       {
+               return "DHT";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public boolean init( CoreForProtocol capi )
+       {
+               return false;
+       }
+
+       public void done()
+       {
+       }
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       /**
+        * Initializes API for application use.
+        *
+        * @return errorcode for the execution, see error_handling.h
+        */
+
+       public int initializeApi()
+       {
+               throw new UnsupportedOperationException();
+       }
+
+       /**
+        * Destroys API and free's resources that it uses.
+        *
+        * @return errorcode for the execution, see error_handling.h
+        */
+
+       public int destroyApi()
+       {
+               throw new UnsupportedOperationException();
+       }
+
+       /**
+        * Creates a new DHT-table.
+        *
+        * @param meta description for the table
+        * @param config configuration for the table
+        * @return handle to the created table
+        */
+
+       public DHTTableHandle create( DHTTableMetaData meta, DHTTableConfig 
config )
+       {
+               throw new UnsupportedOperationException();
+       }
+
+       /**
+        * Joins to a DHT-table.
+        *
+        * @param table_id id of the table that is to be joined to
+        * @return handle to the joined table
+        */
+
+       public DHTTableHandle join( DHTTableId table_id )
+       {
+               throw new UnsupportedOperationException();
+       }
+
+       /**
+        * Leaves a DHT-table.
+        *
+        * @param table handle of the table that is to be leaved from
+        * @return errorcode for the execution, see error_handling.h
+        */
+
+       public int leave( DHTTableHandle table )
+       {
+               throw new UnsupportedOperationException();
+       }
+
+       /**
+        * Inserts a <key,value>-mapping into a DHT-table.
+        *
+        * @param table handle of the table where insertion is to be done
+        * @param key key of the <key,value>-mapping
+        * @param value value of the <key,value>-mapping
+        * @return errorcode for the execution, see error_handling.h
+        */
+
+       public int insert( DHTTableHandle table, DHTDataContainer key, 
DHTDataContainer value )
+       {
+               throw new UnsupportedOperationException();
+       }
+
+       /**
+        * Retrieves values that are mapped to given key in a DHT-table.
+        *
+        * @param table handle of the table where retrieval is to be done
+        * @param key key that is used when searching mappings
+        * @return set that contains values that are mapped to the key
+        */
+
+       public DHTResultSet fetch( DHTTableHandle table, DHTDataContainer key )
+       {
+               throw new UnsupportedOperationException();
+       }
+
+       /**
+        * Retrieves a listing of tables to whom a DHT-node is joined. 
Information is
+        * received only about public tables.
+        *
+        * @return set that contains information about tables to whom DHT-node 
is joined.
+        */
+
+       public DHTTableSet listTables()
+       {
+               throw new UnsupportedOperationException();
+       }
+
+       /**
+        * Lists all <key,value>-pairs that are inserted to a DHT-table by 
DHT-node.
+        *
+        * @param table handle of the table where data is inserted
+        * @return set that contains information about all inserted keys.
+        */
+
+       public DHTDataList listInsertedData( DHTTableHandle table )
+       {
+               throw new UnsupportedOperationException();
+       }
+
+       /**
+        * Drops data that is inserted to a DHT-table by DHT-node.
+        *
+        * @param table table where <key,value>-pair is
+        * @param uniqueReference       reference to the <key,value>-mapping to 
be dropped
+        * @return errorcode for the execution, see error_handling.h
+        */
+
+       public int dropInsertedData( DHTTableHandle table, 
DHTStoredDataReference uniqueReference )
+       {
+               throw new UnsupportedOperationException();
+       }
+
+       /**
+        * Returns a boolean value that indicates if errorcode returned by a
+        * DHT's API function is actually an error.
+        *
+        * @param errorCode     errorcode to check
+        * @return 1 on error, 0 otherwise
+        */
+
+       public int isError( int errorCode )
+       {
+               throw new UnsupportedOperationException();
+       }
+
+       /**
+        * Sets dataPointer to point to the next data item that is in the 
resultset.
+        * While iterating through a resultset with this method, resultset items
+        * contained in the resultset will be freed (returned ones will not).
+        *
+        * @param resultSet resultset whose next dataitem is to be returned
+        * @param dataPointer pointer that will be set to point to next dataitem
+        * @return errorcode for the execution, see error_handling.h.
+        */
+
+       public int resultSetNext( DHTResultSet resultSet, DHTDataContainer[] 
dataPointer )
+       {
+               throw new UnsupportedOperationException();
+       }
+
+       /**
+        * Sets tablePointer to point to the next table that is in the tableset.
+        * While iterating through a tableset with this method, tableset items
+        * contained in the tableset will be freed (returned ones will not)
+        *
+        * @param tableSet tableset whose next table is to be returned
+        * @param tablePointer pointer that will be set to point to next table
+        * @return errorcode for the execution, see error_handling.h.
+        */
+
+       public int tableSetNext( DHTTableSet tableSet, DHTTableSetItem[] 
tablePointer )
+       {
+               throw new UnsupportedOperationException();
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/dht/DHTReplyFailure.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/dht/DHTReplyFailure.java       
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/dht/DHTReplyFailure.java       
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,51 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.dht;
+
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * TCP communication: gnunetd to client: Failure notification for a request.
+ */
+
+public class DHTReplyFailure extends CSMessage
+{
+       /** The DHT CS Message header */
+       public DHTMessageHeader dhtCSHeader;
+
+       /** Errornumber for the failure reason - see CONSTANTS in this file. */
+       public int                              failureReasonNumber;
+
+
+       public DHTReplyFailure()
+       {
+               super(IS_DHT_REPLY_FAILURE);
+       }
+
+       public String toString()
+       {
+               return "xxx";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int getByteSize()
+       {
+               throw new UnsupportedOperationException();
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               throw new UnsupportedOperationException();
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               throw new UnsupportedOperationException();
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/dht/DHTReplyResults.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/dht/DHTReplyResults.java       
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/dht/DHTReplyResults.java       
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,51 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.dht;
+
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * TCP communication: gnunetd to client: Results for a request.
+ */
+
+public class DHTReplyResults extends CSMessage
+{
+       /** The DHT CS Message header */
+       public DHTMessageHeader dhtCSHeader;
+
+       /** Results data */
+       public byte[]                   data;
+
+
+       public DHTReplyResults()
+       {
+               super(IS_DHT_REPLY_RESULTS);
+       }
+
+       public String toString()
+       {
+               return "xxx";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int getByteSize()
+       {
+               throw new UnsupportedOperationException();
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               throw new UnsupportedOperationException();
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               throw new UnsupportedOperationException();
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/dht/DHTReplyStandard.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/dht/DHTReplyStandard.java      
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/dht/DHTReplyStandard.java      
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,51 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.dht;
+
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * TCP communication: gnunetd to client: ACK or DENIAL reply for a request.
+ */
+
+public class DHTReplyStandard extends CSMessage
+{
+       /** The DHT CS Message header */
+       public DHTMessageHeader dhtCSHeader;
+
+       /** Status number for the request - see CONSTANTS in this file. */
+       public int                              requestStatusNumber;
+
+
+       public DHTReplyStandard()
+       {
+               super(IS_DHT_REPLY_STANDARD);
+       }
+
+       public String toString()
+       {
+               return "xxx";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int getByteSize()
+       {
+               throw new UnsupportedOperationException();
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               throw new UnsupportedOperationException();
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               throw new UnsupportedOperationException();
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/dht/DHTReplyStatus.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/dht/DHTReplyStatus.java        
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/dht/DHTReplyStatus.java        
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,51 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.dht;
+
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * TCP communication: gnunetd to client: Operation status information.
+ */
+
+public class DHTReplyStatus extends CSMessage
+{
+       /** The DHT CS Message header */
+       public DHTMessageHeader dhtCSHeader;
+
+       /** Operation status number - see CONSTANTS in this file */
+       public int                              operationStatusNumber;
+
+
+       public DHTReplyStatus()
+       {
+               super(IS_DHT_REPLY_STATUS);
+       }
+
+       public String toString()
+       {
+               return "xxx";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int getByteSize()
+       {
+               throw new UnsupportedOperationException();
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               throw new UnsupportedOperationException();
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               throw new UnsupportedOperationException();
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/dht/DHTRequestAPIId.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/dht/DHTRequestAPIId.java       
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/dht/DHTRequestAPIId.java       
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,48 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.dht;
+
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * TCP communication: client to gnunetd: create apiId
+ */
+
+public class DHTRequestAPIId extends CSMessage
+{
+       /** The DHT CS Message header */
+       public DHTMessageHeader dhtCSHeader;
+
+
+       public DHTRequestAPIId()
+       {
+               super(IS_DHT_REQUEST_API_ID);
+       }
+
+       public String toString()
+       {
+               return "xxx";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int getByteSize()
+       {
+               throw new UnsupportedOperationException();
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               throw new UnsupportedOperationException();
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               throw new UnsupportedOperationException();
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/dht/DHTRequestCreate.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/dht/DHTRequestCreate.java      
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/dht/DHTRequestCreate.java      
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,54 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.dht;
+
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * TCP communication: client to gnunetd: create new table
+ */
+
+public class DHTRequestCreate extends CSMessage
+{
+       /** The DHT CS Message header */
+       public DHTMessageHeader dhtCSHeader;
+
+       /** Table settings */
+       public DHTTableConfig   tableConfig;
+
+       /** Table name */
+       public String                   name;
+
+
+       public DHTRequestCreate()
+       {
+               super(IS_DHT_REQUEST_CREATE);
+       }
+
+       public String toString()
+       {
+               return "xxx";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int getByteSize()
+       {
+               throw new UnsupportedOperationException();
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               throw new UnsupportedOperationException();
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               throw new UnsupportedOperationException();
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/dht/DHTRequestDrop.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/dht/DHTRequestDrop.java        
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/dht/DHTRequestDrop.java        
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,55 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.dht;
+
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * TCP communication: client to gnunetd: stop republishing a <key,value>-pair
+ * that is inserted by DHT node at given DHT-Table
+ */
+
+public class DHTRequestDrop extends CSMessage
+{
+       /** The DHT CS Message header */
+       public DHTMessageHeader                 dhtCSHeader;
+
+       /** Id of the table whose inserted <key,value>-pair should be dropped */
+       public DHTTableId                               insertedTableId;
+
+       /** Reference to the data that should be dropped */
+       public DHTStoredDataReference   droppedDataReference;
+
+
+       public DHTRequestDrop()
+       {
+               super(IS_DHT_REQUEST_DROP);
+       }
+
+       public String toString()
+       {
+               return "xxx";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int getByteSize()
+       {
+               throw new UnsupportedOperationException();
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               throw new UnsupportedOperationException();
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               throw new UnsupportedOperationException();
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/dht/DHTRequestFetch.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/dht/DHTRequestFetch.java       
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/dht/DHTRequestFetch.java       
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,58 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.dht;
+
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * TCP communication: client to gnunetd: fetch <key,value>-mappings
+ * for given key
+ */
+
+public class DHTRequestFetch extends CSMessage
+{
+       /** The DHT CS Message header */
+       public DHTMessageHeader dhtCSHeader;
+
+       /** The id of target table */
+       public DHTTableId               targetTableId;
+
+       /** Length of the key to be used for searching */
+       public int                              keyLength;
+
+       /** Bytes that contain the key. */
+       public byte[]                   key;
+
+
+       public DHTRequestFetch()
+       {
+               super(IS_DHT_REQUEST_FETCH);
+       }
+
+       public String toString()
+       {
+               return "xxx";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int getByteSize()
+       {
+               throw new UnsupportedOperationException();
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               throw new UnsupportedOperationException();
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               throw new UnsupportedOperationException();
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/dht/DHTRequestInsert.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/dht/DHTRequestInsert.java      
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/dht/DHTRequestInsert.java      
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,60 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.dht;
+
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * TCP communication: client to gnunetd: insert <key,value>-mapping to table
+ */
+
+public class DHTRequestInsert extends CSMessage
+{
+       /** The DHT CS Message header */
+       public DHTMessageHeader dhtCSHeader;
+
+       /** The id of target table */
+       public DHTTableId               targetTableId;
+
+       /** Length of the key to be inserted */
+       public int                              keyLength;
+
+       /** Length of the value to be inserted */
+       public int                              valueLength;
+
+       /** Bytes that contain first key and then value (concatenated). */
+       public byte[]                   keyAndValue;
+
+
+       public DHTRequestInsert()
+       {
+               super(IS_DHT_REQUEST_INSERT);
+       }
+
+       public String toString()
+       {
+               return "xxx";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int getByteSize()
+       {
+               throw new UnsupportedOperationException();
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               throw new UnsupportedOperationException();
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               throw new UnsupportedOperationException();
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/dht/DHTRequestInserted.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/dht/DHTRequestInserted.java    
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/dht/DHTRequestInserted.java    
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,52 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.dht;
+
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * TCP communication: client to gnunetd: fetch list of <key,value>-pairs that
+ * are inserted by DHT node at given DHT-Table
+ */
+
+public class DHTRequestInserted extends CSMessage
+{
+       /** The DHT CS Message header */
+       public DHTMessageHeader dhtCSHeader;
+
+       /** Id of the table whose inserted <key,value>-pairs should be listed */
+       public DHTTableId               insertedTableId;
+
+
+       public DHTRequestInserted()
+       {
+               super(IS_DHT_REQUEST_INSERTED);
+       }
+
+       public String toString()
+       {
+               return "xxx";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int getByteSize()
+       {
+               throw new UnsupportedOperationException();
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               throw new UnsupportedOperationException();
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               throw new UnsupportedOperationException();
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/dht/DHTRequestJoin.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/dht/DHTRequestJoin.java        
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/dht/DHTRequestJoin.java        
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,55 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.dht;
+
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * TCP communication: client to gnunetd: join table
+ */
+
+public class DHTRequestJoin extends CSMessage
+{
+       /** The DHT CS Message header */
+       public DHTMessageHeader dhtCSHeader;
+
+       /** Address of the node that is the helper-when-joining node's address 
*/
+       public HostIdentity             helperNodeAddress;
+
+       /** Id of the to-be-joined table */
+       public DHTTableId               joinedTableId;
+
+
+       public DHTRequestJoin()
+       {
+               super(IS_DHT_REQUEST_JOIN);
+       }
+
+       public String toString()
+       {
+               return "xxx";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int getByteSize()
+       {
+               throw new UnsupportedOperationException();
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               throw new UnsupportedOperationException();
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               throw new UnsupportedOperationException();
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/dht/DHTRequestLeave.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/dht/DHTRequestLeave.java       
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/dht/DHTRequestLeave.java       
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,51 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.dht;
+
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * TCP communication: client to gnunetd: leave table
+ */
+
+public class DHTRequestLeave extends CSMessage
+{
+       /** The DHT CS Message header */
+       public DHTMessageHeader dhtCSHeader;
+
+       /** Id of the to-be-leaved table */
+       public DHTTableId               leavedTableId;
+
+
+       public DHTRequestLeave()
+       {
+               super(IS_DHT_REQUEST_LEAVE);
+       }
+
+       public String toString()
+       {
+               return "xxx";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int getByteSize()
+       {
+               throw new UnsupportedOperationException();
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               throw new UnsupportedOperationException();
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               throw new UnsupportedOperationException();
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/dht/DHTRequestStatus.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/dht/DHTRequestStatus.java      
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/dht/DHTRequestStatus.java      
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,48 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.dht;
+
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * TCP communication: client to gnunetd: query operation status
+ */
+
+public class DHTRequestStatus extends CSMessage
+{
+       /** The DHT CS Message header. Note that old requestId is used in this 
header to tell which operation is in question */
+       public DHTMessageHeader dhtCSHeader;
+
+
+       public DHTRequestStatus()
+       {
+               super(IS_DHT_REQUEST_STATUS);
+       }
+
+       public String toString()
+       {
+               return "xxx";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int getByteSize()
+       {
+               throw new UnsupportedOperationException();
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               throw new UnsupportedOperationException();
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               throw new UnsupportedOperationException();
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/dht/DHTRequestTables.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/dht/DHTRequestTables.java      
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/dht/DHTRequestTables.java      
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,53 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.dht;
+
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * TCP communication: client to gnunetd: fetch info about joined tables
+ * from given dht node
+ */
+
+public class DHTRequestTables extends CSMessage
+{
+       /** The DHT CS Message header */
+       public DHTMessageHeader dhtCSHeader;
+
+       /** Address of the DHT node whose tables are to be fetched */
+       public HostIdentity             nodeAddress;
+
+
+       public DHTRequestTables()
+       {
+               super(IS_DHT_REQUEST_TABLES);
+       }
+
+       public String toString()
+       {
+               return "xxx";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int getByteSize()
+       {
+               throw new UnsupportedOperationException();
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               throw new UnsupportedOperationException();
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               throw new UnsupportedOperationException();
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/dht/DHTResultSet.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/dht/DHTResultSet.java  2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/dht/DHTResultSet.java  2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,15 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.dht;
+
+/**
+ *
+ */
+
+public class DHTResultSet extends Object
+{
+       public int                              errorCode;
+       public DHTResultSetItem firstItem;
+}

Added: freeway/src/org/gnu/freeway/protocol/dht/DHTResultSetItem.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/dht/DHTResultSetItem.java      
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/dht/DHTResultSetItem.java      
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,15 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.dht;
+
+/**
+ *
+ */
+
+public class DHTResultSetItem extends Object
+{
+       public DHTDataContainer data;
+       public DHTResultSetItem nextItem;
+}

Added: freeway/src/org/gnu/freeway/protocol/dht/DHTStoredDataReference.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/dht/DHTStoredDataReference.java        
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/dht/DHTStoredDataReference.java        
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,17 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.dht;
+
+import org.gnu.freeway.util.crypto.*;
+
+/**
+ *
+ */
+
+public class DHTStoredDataReference extends Object
+{
+       public HashCode160      hashCode;
+       public long                     insertionTime;
+}

Added: freeway/src/org/gnu/freeway/protocol/dht/DHTTableConfig.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/dht/DHTTableConfig.java        
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/dht/DHTTableConfig.java        
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,19 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.dht;
+
+/**
+ *
+ */
+
+public class DHTTableConfig extends Object
+{
+       public int      replicationCount;
+       public int      parallelismCount;
+       public int      maximumValuesPerKey;
+       public int      expirationTimeSeconds;
+       public int      flags;
+       public float    cacheTimeMultiplier;
+}

Added: freeway/src/org/gnu/freeway/protocol/dht/DHTTableHandle.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/dht/DHTTableHandle.java        
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/dht/DHTTableHandle.java        
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,15 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.dht;
+
+/**
+ *
+ */
+
+public class DHTTableHandle extends Object
+{
+       public int                      errorCode;
+       public DHTTableId       tableId;
+}

Added: freeway/src/org/gnu/freeway/protocol/dht/DHTTableId.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/dht/DHTTableId.java    2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/dht/DHTTableId.java    2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,16 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.dht;
+
+import org.gnu.freeway.util.crypto.*;
+
+/**
+ *
+ */
+
+public class DHTTableId extends Object
+{
+       public HashCode160      id;
+}

Added: freeway/src/org/gnu/freeway/protocol/dht/DHTTableMetaData.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/dht/DHTTableMetaData.java      
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/dht/DHTTableMetaData.java      
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,14 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.dht;
+
+/**
+ *
+ */
+
+public class DHTTableMetaData extends Object
+{
+       public String   name;
+}

Added: freeway/src/org/gnu/freeway/protocol/dht/DHTTableSet.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/dht/DHTTableSet.java   2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/dht/DHTTableSet.java   2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,15 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.dht;
+
+/**
+ *
+ */
+
+public class DHTTableSet extends Object
+{
+       public int                              errorCode;
+       public DHTTableSetItem  firstItem;
+}

Added: freeway/src/org/gnu/freeway/protocol/dht/DHTTableSetItem.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/dht/DHTTableSetItem.java       
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/dht/DHTTableSetItem.java       
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,16 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.dht;
+
+/**
+ *
+ */
+
+public class DHTTableSetItem extends Object
+{
+       public DHTTableId               tableId;
+       public DHTTableMetaData tableMetaData;
+       public DHTTableSetItem  nextItem;
+}

Added: freeway/src/org/gnu/freeway/protocol/tbench/CSBenchReply.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/tbench/CSBenchReply.java       
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/tbench/CSBenchReply.java       
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,80 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.tbench;
+
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ *
+ */
+
+public class CSBenchReply extends CSMessage
+{
+       public static final int SIZE            =       36;
+
+       public int              max_loss;
+       public int              min_loss;
+       public float            mean_loss;
+       public float            variance_loss;
+       public int              max_time;
+       public int              min_time;
+       public float            mean_time;
+       public float            variance_time;
+
+
+       public CSBenchReply()
+       {
+               super(IS_BENCH_REPLY);
+       }
+
+       public String toString()
+       {
+               return "xxx";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int getByteSize()
+       {
+               return SIZE;
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               int     size,type;
+
+               size=buf.getShort() & 0x0000ffff;
+               err.reportIf(size!=SIZE,"bad size !");
+
+               type=buf.getShort() & 0x0000ffff;
+               err.reportIf(type!=IS_BENCH_REPLY,"bad type !");
+
+               max_loss=buf.getInt();
+               min_loss=buf.getInt();
+               mean_loss=buf.getFloat();
+               variance_loss=buf.getFloat();
+               max_time=buf.getInt();
+               min_time=buf.getInt();
+               mean_time=buf.getFloat();
+               variance_time=buf.getFloat();
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               buf.putShort((short) SIZE);
+               buf.putShort((short) IS_BENCH_REPLY);
+               buf.putInt(max_loss);
+               buf.putInt(min_loss);
+               buf.putFloat(mean_loss);
+               buf.putFloat(variance_loss);
+               buf.putInt(max_time);
+               buf.putInt(min_time);
+               buf.putFloat(mean_time);
+               buf.putFloat(variance_time);
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/tbench/CSBenchRequest.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/tbench/CSBenchRequest.java     
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/tbench/CSBenchRequest.java     
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,93 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.tbench;
+
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ *
+ */
+
+public class CSBenchRequest extends CSMessage
+{
+       public static final int SIZE            =       28+HostIdentity.SIZE;
+
+       /** */
+       public int                      msgSize;
+
+       /** */
+       public int                      msgCnt;
+
+       /** */
+       public int                      iterations;
+
+       /** */
+       public HostIdentity     receiverId;
+
+       /** Inter packet space in milliseconds */
+       public int                      intPktSpace;
+
+       public int                      trainSize;
+
+       /** Time to wait for the arrival of a reply in secs */
+       public int                      timeOut;
+
+
+       public CSBenchRequest()
+       {
+               super(IS_BENCH_REQUEST);
+       }
+
+       public String toString()
+       {
+               return "xxx";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int getByteSize()
+       {
+               return SIZE;
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               int     size,type;
+
+               size=buf.getShort() & 0x0000ffff;
+               err.reportIf(size!=SIZE,"bad size !");
+
+               type=buf.getShort() & 0x0000ffff;
+               err.reportIf(type!=IS_BENCH_REQUEST,"bad type !");
+
+               msgSize=buf.getInt();
+               msgCnt=buf.getInt();
+               iterations=buf.getInt();
+
+               receiverId=new HostIdentity();
+               receiverId.readBytes(buf,err);
+
+               intPktSpace=buf.getInt();
+               trainSize=buf.getInt();
+               timeOut=buf.getInt();
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               buf.putShort((short) SIZE);
+               buf.putShort((short) IS_BENCH_REQUEST);
+               buf.putInt(msgSize);
+               buf.putInt(msgCnt);
+               buf.putInt(iterations);
+               receiverId.writeBytes(buf);
+               buf.putInt(intPktSpace);
+               buf.putInt(trainSize);
+               buf.putInt(timeOut);
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/tbench/P2PBenchReply.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/tbench/P2PBenchReply.java      
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/tbench/P2PBenchReply.java      
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,70 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.tbench;
+
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ *
+ */
+
+public class P2PBenchReply extends P2PMessage
+{
+       public static final int SIZE                                    =       
12;
+       public static final int TBENCH_MSG_LENGTH       =       1024;
+
+       public int              iterationNum;
+       public int              packetNum;
+       public byte[]   message;
+
+
+       public P2PBenchReply()
+       {
+               super(IS_BENCH_REPLY);
+               iterationNum=0;
+               packetNum=0;
+               message=new byte[0];
+       }
+
+       public String toString()
+       {
+               return "xxx";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int getByteSize()
+       {
+               return SIZE+message.length;
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               int     size,type;
+
+               size=buf.getShort() & 0x0000ffff;
+               err.reportIf(size>=SIZE,"bad size");
+
+               type=buf.getShort() & 0x0000ffff;
+               err.reportIf(type!=IS_BENCH_REPLY,"bad type");
+
+               iterationNum=buf.getInt();
+               packetNum=buf.getInt();
+               message=new byte[size-SIZE];
+               buf.get(message);
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               buf.putShort((short) getByteSize());
+               buf.putShort((short) IS_BENCH_REPLY);
+               buf.putInt(iterationNum);
+               buf.putInt(packetNum);
+               buf.put(message);
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/tbench/P2PBenchRequest.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/tbench/P2PBenchRequest.java    
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/tbench/P2PBenchRequest.java    
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,75 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.tbench;
+
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ *
+ */
+
+public class P2PBenchRequest extends P2PMessage
+{
+       public static final int SIZE                                    =       
12;
+       public static final int TBENCH_MSG_LENGTH       =       1024;
+
+       public int              iterationNum;
+       public int              packetNum;
+       public byte[]   message;
+
+
+       public P2PBenchRequest()
+       {
+               super(IS_BENCH_REQUEST);
+               iterationNum=0;
+               packetNum=0;
+               message=new byte[0];
+       }
+
+       public String toString()
+       {
+               return "xxx";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public void setMessageSize( int size )
+       {
+               message=new byte[size];
+       }
+
+       public int getByteSize()
+       {
+               return SIZE+message.length;
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               int     size,type;
+
+               size=buf.getShort() & 0x0000ffff;
+               err.reportIf(size>=SIZE,"bad size");
+
+               type=buf.getShort() & 0x0000ffff;
+               err.reportIf(type!=IS_BENCH_REQUEST,"bad type");
+
+               iterationNum=buf.getInt();
+               packetNum=buf.getInt();
+               message=new byte[size-SIZE];
+               buf.get(message);
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               buf.putShort((short) getByteSize());
+               buf.putShort((short) IS_BENCH_REQUEST);
+               buf.putInt(iterationNum);
+               buf.putInt(packetNum);
+               buf.put(message);
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/tbench/TBenchProtocol.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/tbench/TBenchProtocol.java     
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/tbench/TBenchProtocol.java     
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,282 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.tbench;
+
+import org.gnu.freeway.protocol.*;
+import org.gnu.freeway.server.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import EDU.oswego.cs.dl.util.concurrent.*;
+
+import java.util.logging.*;
+
+/**
+ * TBench CORE. This is the code that is plugged
+ * into the GNUnet core to enable transport profiling.
+ */
+
+public class TBenchProtocol extends AbstractProtocol
+{
+       private Scheduler       scheduler;
+       private Object          lock;
+       private Object          lockCnt;
+       private HostIdentity    receiverIdent;
+       private Semaphore       sem;
+       private long                    startTime;
+       private long                    endTime;
+       private int                     msgCnt;
+       private int                     msgIter;
+       private int                     receiveCnt;
+       private int                     currIteration;
+
+
+       public TBenchProtocol()
+       {
+               super("TBENCH");
+               startTime=0;
+               endTime=0;
+               msgCnt=1;
+               msgIter=1;
+       }
+
+       public String toString()
+       {
+               return "TBench protocol";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       /**
+        * Initialize the TBench module. This method name must match
+        * the library name (libgnunet_XXX => initialize_XXX).
+        *
+        * @param capi
+        * @return false on errors
+        */
+
+       public boolean init( CoreForProtocol capi )
+       {
+               super.init(capi);
+               lock=new Object();
+               lockCnt=new Object();
+
+               scheduler=capi.getApplication().getScheduler();
+
+               addCSHandler(CSMessage.IS_BENCH_REQUEST,CSBenchRequest.class);
+               
addP2PHandler(P2PMessage.IS_BENCH_REQUEST,P2PBenchRequest.class);
+               addP2PHandler(P2PMessage.IS_BENCH_REPLY,P2PBenchReply.class);
+               return true;
+       }
+
+       public void done()
+       {
+               super.done();
+               lock=null;
+               lockCnt=null;
+       }
+
+       protected boolean onCSMessage( CSSession client, CSMessage msg )
+       {
+               if (msg instanceof CSBenchRequest) {
+                       return csHandleTBenchRequest(client,(CSBenchRequest) 
msg);
+                       }
+               return false;
+       }
+
+       protected boolean onP2PMessage( HostIdentity sender, P2PMessage msg )
+       {
+               if (msg instanceof P2PBenchRequest) {
+                       return handleTBenchReq(sender,(P2PBenchRequest) msg);
+                       }
+               if (msg instanceof P2PBenchReply) {
+                       return handleTBenchReply(sender,(P2PBenchReply) msg);
+                       }
+               return false;
+       }
+
+       protected void semaUp( Semaphore semx )
+       {
+               semx.release();
+       }
+
+       protected boolean handleTBenchReq( HostIdentity sender, P2PBenchRequest 
pmsg )
+       {
+               P2PBenchReply   reply;
+
+               log(Level.FINEST,"DEBUG: handleTBenchReq received iteration 
"+pmsg.iterationNum+", message "+pmsg.packetNum);
+
+               reply=new P2PBenchReply();
+               reply.iterationNum=pmsg.iterationNum;
+               reply.packetNum=pmsg.packetNum;
+               reply.message=new byte[pmsg.message.length];
+               
System.arraycopy(pmsg.message,0,reply.message,0,pmsg.message.length);
+
+               coreAPI.sendToNode(sender,reply,5,0);
+               return true;
+       }
+
+       protected boolean handleTBenchReply( HostIdentity sender, P2PBenchReply 
pmsg )
+       {
+               log(Level.FINEST,"DEBUG: handleTBenchReply");
+
+               synchronized(lockCnt) {
+                       if (pmsg.iterationNum==currIteration) {
+                               endTime=Scheduler.now();
+                               receiveCnt++;
+                               log(Level.FINEST,"DEBUG: iteration 
"+currIteration+", received reply, "+receiveCnt);
+                               if (receiveCnt >= msgCnt) {
+                                       sem.release();
+                                       }
+                               }
+                       else {
+                               log(Level.FINEST,"DEBUG: Old Reply: iteration 
"+currIteration+", received reply, "+receiveCnt);
+                               }
+                       }
+               return true;
+       }
+
+       protected boolean csHandleTBenchRequest( CSSession client, 
CSBenchRequest icmsg )
+       {
+               Result[]                        results;
+               P2PBenchRequest opmsg;
+               CSBenchReply            ocmsg;
+               double                  sum_variance_time,sum_variance_loss;
+               long                            sum_loss,sum_time;
+               int                             i,j;
+               ScheduledTask   upJob = new ScheduledTask("SEMAPHORE-UP",new 
AbstractAction("SEMUP") {
+                       public void perform()
+                       {
+                               semaUp(sem);
+                       }
+                       },0);
+
+               log(Level.FINEST,"DEBUG: handleTBenchRequest");
+
+               opmsg=new P2PBenchRequest();
+               ocmsg=new CSBenchReply();
+
+               synchronized(lock) {    // only one benchmark run at a time
+                       msgCnt  = icmsg.msgCnt;
+                       msgIter = icmsg.iterations;
+                       results = new Result[msgIter];
+
+                       log(Level.FINEST,"DEBUG: TBENCH: msgCnt "+msgCnt+" 
msgIter "+msgIter);
+                       sem = new Semaphore(0);
+
+                       receiveCnt = 0;
+                       receiverIdent=(HostIdentity) 
PersistentHelper.copy(icmsg.receiverId);
+
+                       /* set up opmsg */
+                       opmsg.setMessageSize(icmsg.msgSize);
+                       opmsg.iterationNum = opmsg.packetNum = 0;
+
+                       for (currIteration = 0; currIteration < msgIter; 
currIteration++) {
+                               opmsg.iterationNum = currIteration;
+                               receiveCnt = 0;
+
+                               log(Level.FINEST,"DEBUG: Timeout after 
"+icmsg.timeOut+" ms");
+                               
scheduler.addJob(upJob,Scheduler.millis(icmsg.timeOut));
+
+                               startTime=Scheduler.now();
+                               endTime = startTime;
+                               for (j=0; j<msgCnt; j++){
+                                       if 
(Scheduler.now()>startTime+Scheduler.millis(icmsg.timeOut)) {
+                                               break;
+                                               }
+                                       opmsg.packetNum = j;
+                                       
coreAPI.sendToNode(receiverIdent,opmsg,5,0);
+
+                                       if (icmsg.intPktSpace!=0 && (j % 
icmsg.trainSize) == 0) {
+                                               long            sec,nsec;
+
+                                               sec = 
Scheduler.toSeconds(icmsg.intPktSpace);
+                                               nsec = (icmsg.intPktSpace - 
Scheduler.seconds(sec)) * 1000 * 1000;
+                                               try {
+                                                       
Thread.sleep(sec*1000,(int) nsec);
+                                                       }
+                                               catch( InterruptedException x ) 
{
+                                                       err("",x);
+                                                       }
+                                               }
+                                       }
+
+                               try {
+                                       sem.acquire();
+                                       }
+                               catch( InterruptedException x ) {
+                                       err("",x);
+                                       }
+
+                               scheduler.suspend();
+                               scheduler.deleteJob(upJob);
+                               scheduler.resume();
+                               results[currIteration].time = endTime-startTime;
+                               results[currIteration].packets = receiveCnt;
+                               }
+                       sem=null;
+                       }
+
+               // let's see what the raw results are
+               for (i=0; i<msgIter; i++) {
+                       log(Level.FINEST,"iter["+i+"], packets 
"+results[i].packets+"/"+msgCnt+", time "+results[i].time+" ms");
+                       }
+
+               sum_loss = msgCnt - results[0].packets;
+               ocmsg.max_loss = msgCnt - results[0].packets;
+               ocmsg.min_loss = msgCnt - results[0].packets;
+               sum_time=results[0].time;
+               ocmsg.max_time=(int) results[0].time;
+               ocmsg.min_time=(int) results[0].time;
+               for(i = 1; i < msgIter; i++) {
+                       log(Level.FINEST,"EVERYTHING: iteration="+i);
+                       sum_loss += msgCnt - results[i].packets;
+                       if(msgCnt-results[i].packets > ocmsg.max_loss)
+                               ocmsg.max_loss = msgCnt - results[i].packets;
+
+                       if(msgCnt-results[i].packets < ocmsg.min_loss)
+                               ocmsg.min_loss = msgCnt - results[i].packets;
+
+                       sum_time += results[i].time;
+                       if (results[i].time > ocmsg.max_time) {
+                               ocmsg.max_time=(int) results[i].time;
+                               }
+                       if (results[i].time < ocmsg.min_time) {
+                               ocmsg.min_time=(int) results[i].time;
+                               }
+                       }
+               ocmsg.mean_loss = ((float)sum_loss/(float)msgIter);
+               ocmsg.mean_time = ((float)sum_time/(float)msgIter);
+
+               sum_variance_time = 0.0;
+               sum_variance_loss = 0.0;
+               for(i = 0; i < msgIter; i++){
+                       log(Level.FINEST,"DEBUG: TBENCH: iteration="+i+" 
msgIter="+msgIter);
+                       sum_variance_time += (results[i].time - 
ocmsg.mean_time)*
+                       (results[i].time - ocmsg.mean_time);
+
+                       sum_variance_loss += ((msgCnt - results[i].packets) - 
ocmsg.mean_loss)*
+                       ((msgCnt - results[i].packets) - ocmsg.mean_loss);
+               }
+               ocmsg.variance_time=(float) (sum_variance_time/(msgIter-1));
+               ocmsg.variance_loss=(float) (sum_variance_loss/(msgIter-1));
+
+               log(Level.FINEST,"DEBUG: calling writeToSocket");
+
+               if (!client.send(ocmsg))
+                       return false;
+
+               log(Level.FINEST,"DEBUG: finishing benchmark");
+               return true;
+       }
+}
+
+class Result extends Object
+{
+       public long     time;
+       public int      packets;
+}

Added: freeway/src/org/gnu/freeway/protocol/testbed/commands.c
===================================================================
--- freeway/src/org/gnu/freeway/protocol/testbed/commands.c     2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/testbed/commands.c     2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,1763 @@
+/*
+     This file is part of GNUnet.
+     (C) 2003, 2004 Christian Grothoff (and other contributing authors)
+
+     GNUnet is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with GNUnet; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file applications/testbed/commands.c 
+ * @brief the commands available in the testbed
+ * @author Ronaldo Alves Ferreira
+ * @author Christian Grothoff
+ * @author Murali Krishan Ramanathan
+ *
+ * Todo:
+ * - test add-ssh-node
+ * - implement shutdown (in particular, kill ssh connections / processes!)
+ * - design and implement better topology management
+ * - test
+ **/
+
+#include "platform.h"
+#include "testbed.h"
+#include "commands.h"
+#include "socket.h"
+#include "get-stats.h"
+
+/**
+ * @brief struct keeping per-peer information for the testbed
+ */
+typedef struct {
+  /** IP address of the peer */
+  IPaddr            ip;
+  /** CS port of the peer */
+  unsigned short     port;
+  /** string describing the peer address */
+  char * ips;
+  /** socket to communicate with the peer */
+  GNUNET_TCP_SOCKET sock;
+  /** HELO message identifying the peer in the network */
+  HELO_Message * helo;
+  /** if we're using ssh, what is the PID of the
+      ssh process? (-1 for unencrypted direct connections) */
+  pid_t ssh;
+} NODE_INFO;
+
+/**
+ * List of nodes known to the testbed.
+ */
+static NODE_INFO * nodes = NULL;
+
+/**
+ * Number of known nodes, size of the nodes array.
+ */
+static int nnodes = 0;
+
+/**
+ * Should the driver exit?
+ */
+int do_quit = NO;
+
+/**
+ * Convert the strings ss and ds to peer-identifiers (ints) s and d
+ * respectively.  Aborts the method containing the macro with an error
+ * message if the peer-IDs are not within range.
+ */
+#define CHECK_SRC_DST(s, d, ss, ds)    \
+  s = atoi(ss);                        \
+  d = atoi(ds);                                    \
+  if (s < 0 || s >= nnodes || d < 0 || d >= nnodes) {  \
+    PRINTF("Invalid src (%s) or dst (%s)\n", ss, ds);  \
+    return -1;                                        \
+  }
+
+/**
+ * Convert the string ps to a peer-id p and abort the current method
+ * with an error message if ps is not a valid peer-id.
+ */
+#define CHECK_PEER(p, ps)      \
+  p = atoi(ps);                                \
+  if (p < 0 || p >= nnodes) {                  \
+    PRINTF("Invalid peer value %s\n", ps);     \
+    return -1;                                 \
+  }
+
+/**
+ * Send a message to peer 'peer' of type 'msgType'
+ * the given size and data.
+ */
+static int sendMessage(unsigned msgType, 
+                      int peer, 
+                      unsigned short argSize, 
+                      void *arg) {
+  TESTBED_CS_MESSAGE * msg;
+  int msgsz;
+  
+  /* Assume peer value is valid. */
+  if (argSize + sizeof(TESTBED_CS_MESSAGE) > 65535) 
+    errexit("Message body too big for sendMessage: %s\n",
+           argSize);
+  
+  msgsz = sizeof(TESTBED_CS_MESSAGE)+argSize;
+  msg = MALLOC(msgsz);
+  msg->header.size 
+    = htons(msgsz);
+  msg->header.tcpType 
+    = htons(TESTBED_CS_PROTO_REQUEST);
+  msg->msgType
+    = htonl(msgType);
+  memcpy(&((TESTBED_CS_MESSAGE_GENERIC*)msg)->data[0], 
+        arg, 
+        argSize);
+  msgsz = writeToSocket(&nodes[peer].sock,
+                       &msg->header);
+  FREE(msg);
+  if (msgsz == SYSERR) {
+    PRINTF("ERROR: Could not send message to peer %s.\n", 
+          nodes[peer].ips);
+    return SYSERR;
+  }
+  return OK;
+}
+
+/**
+ * Read a result from the given peer.  Print
+ * an error message if the peer fails to respond.
+ *
+ * @return OK on success, SYSERR on error
+ */
+static int readResult(int peer,
+                     int * result) {
+  if (OK != readTCPResult(&nodes[peer].sock,
+                         result)) {
+    PRINTF("ERROR: peer %s is not responding.\n",
+          nodes[peer].ips);
+    return SYSERR;
+  }
+  return OK;
+}
+
+/* ****************** individual commands ********** */
+
+
+/**
+ * Add a node to the configuration.
+ * Arguments must be IP PORT of the peer.
+ */
+static int addNode(int argc, char * argv[]) {
+  int currindex;
+  TESTBED_HELO_MESSAGE * hdr;
+  TESTBED_GET_HELO_MESSAGE req;  
+  int port;
+  int i;
+
+  if (argc != 2) {
+    PRINTF("Syntax: add-node IP PORT.\n");
+    return -1;
+  }
+  port = atoi(argv[1]);
+  for (i=0;i<nnodes;i++) {
+    if ( (0 == strcmp(argv[0],
+                     nodes[i].ips)) &&
+        (port == nodes[i].port) ) {
+      PRINTF("Node already in use!\n");
+      return -1;
+    }
+  }
+  
+  
+  req.proto = 0;
+  req.reserved = 0;
+  /* connect */
+  currindex = nnodes;
+  GROW(nodes, nnodes, nnodes+1);
+  nodes[currindex].ips = STRDUP(argv[0]);
+  nodes[currindex].port = atoi(argv[1]);
+  nodes[currindex].ssh = -1;
+#ifndef MINGW
+  inet_aton(argv[0], (struct in_addr*) &nodes[currindex].ip);
+#else
+  nodes[currindex].ip.addr.S_un.S_addr = inet_addr(argv[0]);
+#endif
+  
+  if (SYSERR == initGNUnetClientSocket(nodes[currindex].port,
+                                      nodes[currindex].ips,
+                                      &nodes[currindex].sock)) {
+    PRINTF("ERROR: could not connect to %s:%d.\n",
+          nodes[currindex].ips,
+          nodes[currindex].port);
+    return -1;
+  }
+  
+  /* request HELO */
+  if (OK != sendMessage(TESTBED_GET_HELO,
+                       currindex,
+                       
sizeof(TESTBED_GET_HELO_MESSAGE)-sizeof(TESTBED_CS_MESSAGE),
+                       &req.proto)) {
+    /* send message already printed an error message */
+    destroySocket(&nodes[currindex].sock);
+    FREE(nodes[currindex].ips);
+    GROW(nodes, 
+        nnodes, 
+        nnodes-1);
+    return -1;
+  }
+  
+  hdr = NULL;
+  if (SYSERR == readFromSocket(&nodes[currindex].sock, 
+                              (CS_HEADER**)&hdr)) {
+    PRINTF("ERROR: peer %s is not responding.\n",
+          nodes[currindex].ips);
+    destroySocket(&nodes[currindex].sock);
+    FREE(nodes[currindex].ips);
+    GROW(nodes, nnodes, nnodes-1);
+    return -1;
+  }
+  if ( (ntohs(hdr->header.header.tcpType) == TESTBED_CS_PROTO_REPLY) &&
+       (ntohs(hdr->header.header.size) >= sizeof(TESTBED_HELO_MESSAGE)) &&
+       (ntohl(hdr->header.msgType) == TESTBED_HELO_RESPONSE) &&
+       (ntohs(hdr->header.header.size) - sizeof(TESTBED_CS_MESSAGE) >= 
sizeof(HELO_Message)) &&
+       (ntohs(hdr->header.header.size) - sizeof(TESTBED_CS_MESSAGE) == 
HELO_Message_size(&hdr->helo)) ) {
+    nodes[currindex].helo 
+      = MALLOC(HELO_Message_size(&hdr->helo));
+    memcpy(nodes[currindex].helo, 
+          &hdr->helo, 
+          HELO_Message_size(&hdr->helo));
+  } else {
+    FREE(hdr);
+    destroySocket(&nodes[currindex].sock);
+    PRINTF("ERROR: peer %s did not respond with proper HELO.\n",
+          nodes[currindex].ips);
+    FREE(nodes[currindex].ips);
+    GROW(nodes, nnodes, nnodes-1);    
+    return -1;
+  }
+  FREE(hdr);
+  PRINTF("%d\n",
+        currindex);
+  return 0;
+}
+
+
+
+/**
+ * Add an node reachable via ssh-tunnel to the configuration.
+ * Arguments must be LOGIN, IP PORT of the peer.  Sshd must
+ * be running on the default port.
+ */
+static int addSshNode(int argc, char * argv[]) {
+  int currindex;
+  TESTBED_HELO_MESSAGE * hdr;
+  TESTBED_GET_HELO_MESSAGE req;  
+  int port;
+  int i;
+  pid_t pid;
+  unsigned short lport;
+  int rtc;
+  int ret;
+  int status;
+    
+  if (argc != 3) {
+    PRINTF("Syntax: add-ssh-node LOGIN IP PORT.\n");
+    return -1;
+  }
+  port = atoi(argv[2]);
+  for (i=0;i<nnodes;i++) {
+    if ( (0 == strcmp(argv[1],
+                     nodes[i].ips)) &&
+        (port == nodes[i].port) ) {
+      PRINTF("Node already in use!\n");
+      return -1;
+    }
+  }
+
+  /* find available local port to bind to */
+  for (lport=10000;lport<65535;lport++) {
+    struct sockaddr_in addr;
+    int s;
+    const int on = 1;
+
+    s = SOCKET(PF_INET, SOCK_STREAM, 0);
+    if (s == -1) {
+      PRINTF("Can not open socket: %s\n",
+            strerror(errno));
+      return -1;
+    }
+    if ( SETSOCKOPT(s,
+                   SOL_SOCKET, 
+                   SO_REUSEADDR, 
+                   &on, sizeof(on)) < 0 )
+      perror("setsockopt");
+    memset(&addr,
+          0,
+          sizeof(addr));
+    addr.sin_family 
+      = AF_INET;
+    addr.sin_addr.s_addr
+      = htonl(INADDR_ANY);
+    addr.sin_port
+      = htons(lport);
+    if (0 == BIND(s,
+                 &addr,
+                 sizeof(addr))) {
+      CLOSE(s);
+      break; /* found port! */
+    } else {
+      CLOSE(s); /* not available, try another one... */
+    }    
+  }
+  if (lport == 65535) {
+    PRINTF("ERROR: Can not find available local port!\n");
+    return -1;
+  }
+  
+  pid = fork();
+  if (pid == 0) {
+    char * sargv[7];
+    char pohopo[64];
+    sargv[0] = "ssh";
+    sargv[1] = "-l";
+    sargv[2] = argv[0]; /* login */
+    sargv[3] = "-L";
+    sprintf(pohopo,
+           "%d:%s:%d",
+           lport, /* local port */
+           "localhost", /* loopback on remote host */
+           port /* remote port */);
+    sargv[4] = pohopo;
+    sargv[5] = argv[1]; /* remote hostname */
+    sargv[6] = NULL; /* last argument */
+    execvp("ssh",
+          sargv);
+    LOG(LOG_ERROR,
+       "ERROR: execvp failed: %s\n",
+       strerror(errno));
+    exit(-1);
+  } 
+  if (pid == -1) {
+    PRINTF("Failed to fork: %s\n",
+          strerror(errno));
+    return -1;
+  }
+  
+
+  req.proto = 0;
+  req.reserved = 0;
+  /* connect */
+  currindex = nnodes;
+  GROW(nodes, nnodes, nnodes+1);
+  nodes[currindex].ips = STRDUP("localhost");
+  nodes[currindex].port = lport;
+  nodes[currindex].ssh = pid;
+#ifndef MINGW
+  inet_aton(argv[0], (struct in_addr*) &nodes[currindex].ip);
+#else
+  nodes[currindex].ip.addr.S_un.S_addr = inet_addr(argv[0]);
+#endif
+
+  /* FIXME: wait a bit to give ssh a chance to connect... */
+  rtc = 0; /* number of retries */
+  while (rtc < 5) {
+    ret = initGNUnetClientSocket(nodes[currindex].port,
+                                nodes[currindex].ips,
+                                &nodes[currindex].sock);
+    if (ret == OK) 
+      break;
+    rtc++;
+    gnunet_util_sleep(cronSECONDS);
+  }
+  if (ret == SYSERR) {
+    PRINTF("ERROR: could not connect to %s:%d.\n",
+          nodes[currindex].ips,
+          nodes[currindex].port);
+    kill(nodes[currindex].ssh,
+        SIGTERM);
+    waitpid(nodes[currindex].ssh,
+           &status,
+           0); 
+    GROW(nodes, nnodes, nnodes-1);
+    return -1;
+  }
+  
+  /* request HELO */
+  if (OK != sendMessage(TESTBED_GET_HELO,
+                       currindex,
+                       
sizeof(TESTBED_GET_HELO_MESSAGE)-sizeof(TESTBED_CS_MESSAGE),
+                       &req.proto)) {
+    /* send message already printed an error message */
+    destroySocket(&nodes[currindex].sock);
+    FREE(nodes[currindex].ips);
+    /* fixme: check error conditions on kill/waidpid! */
+    kill(nodes[currindex].ssh,
+        SIGTERM);
+    waitpid(nodes[currindex].ssh,
+           &status,
+           0);
+    GROW(nodes, 
+        nnodes, 
+        nnodes-1);
+    return -1;
+  }
+  
+  hdr = NULL;
+  if (SYSERR == readFromSocket(&nodes[currindex].sock, 
+                              (CS_HEADER**)&hdr)) {
+    PRINTF("ERROR: peer %s is not responding.\n",
+          nodes[currindex].ips);
+    destroySocket(&nodes[currindex].sock);
+    FREE(nodes[currindex].ips);
+    /* fixme: check error conditions on kill/waidpid! */
+    kill(nodes[currindex].ssh,
+        SIGTERM);
+    waitpid(nodes[currindex].ssh,
+           &status,
+           0);
+    GROW(nodes, nnodes, nnodes-1);
+    return -1;
+  }
+  if ( (ntohs(hdr->header.header.tcpType) == TESTBED_CS_PROTO_REPLY) &&
+       (ntohs(hdr->header.header.size) >= sizeof(TESTBED_HELO_MESSAGE)) &&
+       (ntohl(hdr->header.msgType) == TESTBED_HELO_RESPONSE) &&
+       (ntohs(hdr->header.header.size) - sizeof(TESTBED_CS_MESSAGE) >= 
sizeof(HELO_Message)) &&
+       (ntohs(hdr->header.header.size) - sizeof(TESTBED_CS_MESSAGE) == 
HELO_Message_size(&hdr->helo)) ) {
+    nodes[currindex].helo 
+      = MALLOC(HELO_Message_size(&hdr->helo));
+    memcpy(nodes[currindex].helo, 
+          &hdr->helo, 
+          HELO_Message_size(&hdr->helo));
+  } else {
+    FREE(hdr);
+    destroySocket(&nodes[currindex].sock);
+    PRINTF("ERROR: peer %s did not respond with proper HELO.\n",
+          nodes[currindex].ips);
+    FREE(nodes[currindex].ips);
+    /* fixme: check error conditions on kill/waidpid! */
+    kill(nodes[currindex].ssh,
+        SIGTERM);
+    waitpid(nodes[currindex].ssh,
+           &status,
+           0);
+    GROW(nodes, nnodes, nnodes-1);    
+    return -1;
+  }
+  FREE(hdr);
+  PRINTF("%d\n",
+        currindex);
+  return 0;
+}
+
+
+/**
+ * Tear down the connection between two peers.
+ */
+static int delConnection(int argc,
+                        char * argv[]) {
+  int src, dst, ack;
+  
+  if (argc != 2) {
+    PRINTF("Syntax: disconnect PEERID PEERID\n");
+    return -1;
+  }
+  CHECK_SRC_DST(src, dst, argv[0], argv[1]);
+  if (OK != sendMessage(TESTBED_DEL_PEER, 
+                       src,
+                       sizeof(HostIdentity),
+                       &nodes[dst].helo->senderIdentity)) 
+    return -1;
+  if (OK != readResult(src,
+                      &ack))
+    return -1;
+  if (ack == OK) {
+    PRINTF("OK.\n");
+    return 0;
+  } else {
+    PRINTF("ERROR: Connection NOT deleted.\n");
+    return -1;
+  }
+}
+
+/**
+ * Tear down all connections of a peer.
+ */
+static int delAllConnections(int argc,
+                            char * argv[]) {
+  int dst, ack;
+  
+  if (argc != 1) {
+    PRINTF("Syntax: disconnect-all PEERID\n");
+    return -1;
+  }
+  CHECK_PEER(dst, argv[0]);
+  if (OK != sendMessage(TESTBED_DEL_ALL_PEERS, 
+                       dst,
+                       0,
+                       NULL)) 
+    return -1;
+  if (OK != readResult(dst,
+                      &ack))
+    return -1;
+  if (ack == OK) {
+    PRINTF("OK.\n");
+    return 0;
+  } else {
+    PRINTF("ERROR: Connections NOT deleted.\n");
+    return -1;
+  }
+}
+
+/**
+ * Add a connection between two peers.
+ */
+static int addConnection(int argc, 
+                        char * argv[]) {
+  int src, dst, ack;
+  
+  if (argc != 2) {
+    PRINTF("Syntax: connect PEERID PEERID\n");
+    return -1;
+  }
+  CHECK_SRC_DST(src, dst, argv[0], argv[1]);
+  if (SYSERR == sendMessage(TESTBED_ADD_PEER,
+                           src,
+                           HELO_Message_size(nodes[dst].helo),
+                           nodes[dst].helo)) 
+    return -1;
+  if (OK != readResult(src,
+                      &ack))
+    return -1;  
+  if (ack == OK) {
+    PRINTF("OK.\n");
+    return 0;
+  } else {
+    PRINTF("ERROR: peer can not connect.\n");
+    return -1;
+  }
+}
+
+/**
+ * Set the level of trust that one peer has in
+ * another.
+ */
+static int setTrust(int argc, char * argv[]) {
+  int src, dst, value, ack;
+  TESTBED_SET_TVALUE_MESSAGE msg;
+  
+  if (argc != 3) {
+    PRINTF("Syntax: set-trust PEERID PEERID TRUST\n");
+    return -1;
+  }
+  CHECK_SRC_DST(src, dst, argv[0], argv[1]);
+  value = atoi(argv[2]);
+  msg.trust = htonl(value);
+  memcpy(&msg.otherPeer,
+        &nodes[dst].helo->senderIdentity,
+        sizeof(HostIdentity));
+  if (SYSERR == sendMessage(TESTBED_SET_TVALUE,
+                           src,
+                           sizeof(HostIdentity)+sizeof(unsigned int),
+                           &msg.otherPeer)) 
+    return -1;  
+  if (OK != readResult(src,
+                      &ack)) 
+    return -1;
+  if (htonl(ack) != OK) {
+    PRINTF("ERROR: peer could not set trust value.\n");
+    return -1;
+  } else {
+    PRINTF("OK.\n");
+    return 0;
+  }
+}
+
+/**
+ * Get the amount of trust that A has in B.
+ */
+static int getTrust(int argc, char *argv[]) {
+  int src, dst, value;
+  
+  if (argc != 2) {
+    PRINTF("Syntax: get-trust PEERID PEERID\n");
+    return -1;
+  }
+  CHECK_SRC_DST(src, dst, argv[0], argv[1]);
+  if (SYSERR == sendMessage(TESTBED_GET_TVALUE, 
+                           src, 
+                           sizeof(HostIdentity),
+                           &nodes[dst].helo->senderIdentity)) 
+    return -1;
+  if (SYSERR == readResult(src, &value)) 
+    return -1;
+  if (value < 0) {
+    PRINTF("ERROR: could not get trust value.\n");
+    return -1;
+  } else {
+    PRINTF("%d\n", 
+          value);
+    return 0;
+  }
+}
+
+/**
+ * Disable HELO at a peer.
+ */
+static int disableHELO(int argc, char *argv[]) {
+  int dst, value;
+  
+  if (argc != 1) {
+    PRINTF("Syntax: helo-disable PEERID\n");
+    return -1;
+  }
+  CHECK_PEER(dst, argv[0]);
+  if (SYSERR == sendMessage(TESTBED_DISABLE_HELO, 
+                           dst, 
+                           0,
+                           NULL)) 
+    return -1;
+  if (SYSERR == readResult(dst, &value)) 
+    return -1;
+  if (value != OK) {
+    PRINTF("ERROR: could disable HELO\n");
+    return -1;
+  } else {
+    PRINTF("OK.\n");
+    return 0;
+  }
+}
+
+/**
+ * Enable HELO at a peer.
+ */
+static int enableHELO(int argc, char *argv[]) {
+  int dst, value;
+  
+  if (argc != 1) {
+    PRINTF("Syntax: helo-enable PEERID\n");
+    return -1;
+  }
+  CHECK_PEER(dst, argv[0]);
+  if (SYSERR == sendMessage(TESTBED_ENABLE_HELO, 
+                           dst, 
+                           0,
+                           NULL)) 
+    return -1;
+  if (SYSERR == readResult(dst, &value)) 
+    return -1;
+  if (value != OK) {
+    PRINTF("ERROR: could enable HELO\n");
+    return -1;
+  } else {
+    PRINTF("OK.\n");
+    return 0;
+  }
+}
+
+/**
+ * Disable AUTOCONNECT at a peer.
+ */
+static int disableAUTOCONNECT(int argc, char *argv[]) {
+  int dst, value;
+  
+  if (argc != 1) {
+    PRINTF("Syntax: autoconnect-disable PEERID\n");
+    return -1;
+  }
+  CHECK_PEER(dst, argv[0]);
+  if (SYSERR == sendMessage(TESTBED_DISABLE_AUTOCONNECT, 
+                           dst, 
+                           0,
+                           NULL)) 
+    return -1;
+  if (SYSERR == readResult(dst, &value)) 
+    return -1;
+  if (value != OK) {
+    PRINTF("ERROR: could disable AUTOCONNECT\n");
+    return -1;
+  } else {
+    PRINTF("OK.\n");
+    return 0;
+  }
+}
+
+/**
+ * Enable AUTOCONNECT at a peer.
+ */
+static int enableAUTOCONNECT(int argc, char *argv[]) {
+  int dst, value;
+  
+  if (argc != 1) {
+    PRINTF("Syntax: autoconnect-enable PEERID\n");
+    return -1;
+  }
+  CHECK_PEER(dst, argv[0]);
+  if (SYSERR == sendMessage(TESTBED_ENABLE_AUTOCONNECT, 
+                           dst, 
+                           0,
+                           NULL)) 
+    return -1;
+  if (SYSERR == readResult(dst, &value)) 
+    return -1;
+  if (value != OK) {
+    PRINTF("ERROR: could enable AUTOCONNECT\n");
+    return -1;
+  } else {
+    PRINTF("OK.\n");
+    return 0;
+  }
+}
+
+
+static int allowDenyConnectHelper(unsigned int argc,
+                                 char * argv[],
+                                 int type) {
+  int dst, value;
+  HostIdentity * list;
+  int i;
+  int idx = 0;
+  
+  CHECK_PEER(dst, argv[0]);
+  if (argc > (65532 - sizeof(TESTBED_CS_MESSAGE)) / sizeof(HostIdentity)) {
+    PRINTF("Too many peers specified.  Ask a wizard to enlarge limit.\n");
+    return -1;
+  }
+
+  list = NULL;
+  for (i=1;i<argc;i++) 
+    CHECK_PEER(idx, argv[i]); /* may return, do before MALLOC! */
+  if (argc > 1)
+    list = MALLOC(sizeof(HostIdentity)*(argc-1));
+  for (i=1;i<argc;i++) {
+    CHECK_PEER(idx, argv[i]);
+    memcpy(&list[i-1],
+          &nodes[idx].helo->senderIdentity,
+          sizeof(HostIdentity));
+  }
+  if (SYSERR == sendMessage(type,
+                           dst, 
+                           sizeof(HostIdentity)*(argc-1),
+                           list)) {
+    FREENONNULL(list);
+    return -1;  
+  }
+  FREENONNULL(list);
+  if (SYSERR == readResult(dst, &value)) 
+    return -1;
+  if (value != OK) {
+    PRINTF("ERROR: could change setting.\n");
+    return -1;
+  } else {
+    PRINTF("OK.\n");
+    return 0;
+  }
+}
+
+/**
+ * Deny connections to certain peers at a peer.
+ */
+static int denyConnect(int argc, char *argv[]) {
+  if (argc < 1) {
+    PRINTF("Syntax: connect-deny PEERID [PEERID]*\n");
+    return -1;
+  }
+  return allowDenyConnectHelper(argc, argv, 
+                               TESTBED_DENY_CONNECT);
+}
+
+/**
+ * Allow connections to certain peers at a peer.
+ */
+static int allowConnect(int argc, char *argv[]) {
+  if (argc < 1) {
+    PRINTF("Syntax: connect-allow PEERID [PEERID]*\n");
+    return -1;
+  }
+  return allowDenyConnectHelper(argc, argv,
+                               TESTBED_ALLOW_CONNECT);
+}
+
+/**
+ * Helper function for (un)loadModule.
+ * @param type load or unload requested?
+ */
+static int loadModuleHelper(unsigned short type, 
+                           char * peerId,
+                           char * modulename) {
+  int ok, dst;
+  
+  CHECK_PEER(dst, peerId);
+  if (OK != sendMessage(type, 
+                       dst, 
+                       strlen(modulename), 
+                       modulename)) 
+    return -1;  
+  if (OK != readResult(dst, 
+                      &ok)) 
+    return -1;
+  if (ok != OK) {
+    PRINTF("ERROR: peer %s refused.\n",
+          nodes[dst].ips);
+    return -1;
+  }
+  PRINTF("OK.\n");
+  return 0;
+}
+
+/**
+ * Load an application module at the given peer.
+ */
+static int loadModule(int argc, char *argv[]) {
+  if (argc != 2) {
+    PRINTF("Syntax: load-module PEERID MODULENAME\n");
+    return -1;
+  }
+  return loadModuleHelper(TESTBED_LOAD_MODULE, 
+                         argv[0],
+                         argv[1]);
+}
+
+/**
+ * Unload an application module.
+ */
+static int unloadModule(int argc, char *argv[]) {
+  if (argc != 2) {
+    PRINTF("Syntax: unload-module PEERID MODULENAME\n");
+    return -1;
+  }
+  return loadModuleHelper(TESTBED_UNLOAD_MODULE,
+                         argv[0],
+                         argv[1]);
+}
+
+/**
+ * Fork a client process.  Captures the output and makes it
+ * available via process-output.  The client can be killed
+ * using process-signal.  The process identifier is printed.
+ */
+static int startProcess(int argc, 
+                       char *argv[]) {
+  char * cmdLine;
+  int size;
+  int i;
+  int dst;
+  int ack;
+  int pos;
+
+  if (argc < 2) {
+    PRINTF("Syntax: process-start PEERID COMMAND [ARGUMENTS]\n");
+    return -1;
+  }
+  CHECK_PEER(dst, argv[0]);
+  size = 0;
+  for (i=1;i<argc;i++)
+    size += 1 + strlen(argv[i]);
+  cmdLine = MALLOC(size);
+  pos = 0;
+  for (i=1;i<argc;i++) {
+    memcpy(&cmdLine[pos],
+          argv[i],
+          strlen(argv[i])+1);
+    pos += strlen(argv[i])+1;
+  }
+  
+  if (OK != sendMessage(TESTBED_EXEC, 
+                       dst,
+                       size,
+                       cmdLine)) {
+    FREE(cmdLine);
+    return -1;
+  }
+  FREE(cmdLine);
+  if (OK != readResult(dst,
+                      &ack))
+    return -1;
+  if (ack != SYSERR) {
+    PRINTF("%d\n",
+          ack);
+    return 0;
+  } else {
+    PRINTF("ERROR: Peer could not fork process.\n");
+    return -1;
+  }  
+}
+
+/**
+ * Send a signal to a client process.  Use signal
+ * 0 to test if the process is still live.  Use
+ * -1 to obtain the return value from a dead 
+ * process and to free all associated resources.
+ * For -1 the return value is printed, otherwise OK.
+ * Note that if the signal is -1 and the process
+ * is still running, -1 is returned (which can then
+ * NOT be distinguished from the process returning -1)
+ */
+static int signalProcess(int argc, char *argv[]) {
+  int dst;
+  int ack;
+  TESTBED_SIGNAL_MESSAGE msg;
+
+  if (argc != 3) {
+    PRINTF("Syntax: process-signal PEERID PROCESSID SIGNAL\n");
+    return -1;
+  }
+  CHECK_PEER(dst, argv[0]);
+  msg.pid = htonl(atoi(argv[1]));
+  msg.signal = htonl(atoi(argv[2]));
+  if (OK != sendMessage(TESTBED_SIGNAL, 
+                       dst,
+                       sizeof(TESTBED_SIGNAL_MESSAGE) - 
sizeof(TESTBED_CS_MESSAGE),
+                       &msg.pid))
+    return -1;
+  if (OK != readResult(dst,
+                      &ack))
+    return -1;
+  if (ntohl(msg.signal) == -1) {
+    PRINTF("%d\n", ack);
+    return 0;
+  }
+  if (ack == OK) {
+    PRINTF("OK.\n");
+    return 0;
+  } else {
+    PRINTF("ERROR: Peer could not signal process.\n");
+    return -1;
+  }  
+}
+
+/**
+ * Get the recorded output of a process.
+ */
+static int dumpProcessOutput(int argc, 
+                            char * argv[]) {
+  int dst;
+  int pid;
+  unsigned int ack;
+
+  if (argc != 2) {
+    PRINTF("Syntax: process-output PEERID PROCESSID\n");
+    return -1;
+  }
+  CHECK_PEER(dst, argv[0]);
+  pid = htonl(atoi(argv[1]));
+  if (OK != sendMessage(TESTBED_GET_OUTPUT, 
+                       dst,
+                       sizeof(int),
+                       &pid))
+    return -1;
+  if (OK != readResult(dst,
+                      &ack))
+    return -1;
+  if (ack != SYSERR) {
+    char * tmp;
+    unsigned int pos = 0;
+    while (pos < ack) {
+      unsigned short size;
+      TESTBED_OUTPUT_REPLY_MESSAGE * reply;   
+
+      reply = NULL;
+      if (SYSERR == readFromSocket(&nodes[dst].sock, 
+                                  (CS_HEADER**)&reply)) {
+       PRINTF("ERROR: peer %s is not responding after %d of %d bytes.\n",
+              nodes[dst].ips,
+              pos,
+              ack);
+       return -1;
+      }
+      /* FIXME: check that this is the correct reply format */
+      size = ntohs(reply->header.header.size) - 
sizeof(TESTBED_OUTPUT_REPLY_MESSAGE); 
+      tmp = MALLOC(size+1);
+      memcpy(tmp,
+            &((TESTBED_OUTPUT_REPLY_MESSAGE_GENERIC*)reply)->data[0],
+            size);
+      tmp[size] = '\0';
+      PRINTF("%s", 
+            tmp);           
+      FREE(tmp);
+      FREE(reply);
+      pos += size;      
+    }    
+    return 0;
+  } else {
+    PRINTF("ERROR: Peer could not return process output.\n");
+    return -1;
+  }  
+}
+
+/**
+ * Set bandwidth limitations for a peer.
+ */
+static int setBW(int argc, char * argv[]) {
+  TESTBED_SET_BW_MESSAGE msg;
+  int dst, in, out, ack;
+  
+  if (argc != 3) {
+    PRINTF("Syntax: set-bw PEERID DOWN-BPS UP-BPS\n");
+    return -1;
+  }
+  CHECK_PEER(dst, argv[0]);
+  in  = atoi(argv[1]);
+  out = atoi(argv[2]);
+  if ((in < 0) || (out < 0)) {
+    PRINTF("ERROR: Invalid bandwidth specification.\n");
+    return -1;
+  }
+  msg.in_bw  = htonl(in);
+  msg.out_bw = htonl(out);  
+  if (SYSERR == sendMessage(TESTBED_SET_BW,
+                           dst,
+                           sizeof(TESTBED_SET_BW_MESSAGE) - 
sizeof(TESTBED_CS_MESSAGE),
+                           &msg.in_bw)) 
+    return -1;
+  if (OK != readResult(dst, &ack)) 
+    return -1;
+  if (ack != OK) {
+    PRINTF("ERROR: peer could not set the specified bandwith.\n");
+    return -1;
+  } else {
+    PRINTF("OK.\n");
+    return 0;
+  }
+}
+
+/**
+ * Set artifical message loss rates for a peer.
+ */
+static int setLoss(int argc, char * argv[]) {
+  int dst;
+  int ack;
+  TESTBED_SET_LOSS_RATE_MESSAGE msg;
+
+  if (argc != 3) {
+    PRINTF("Syntax: set-loss PEERID DOWN-LOSS UP-LOSS\n");
+    return -1;
+  }  
+  CHECK_PEER(dst, argv[0]);
+  msg.percentageLossInbound 
+    = htonl(atoi(argv[1]));
+  msg.percentageLossOutbound 
+    = htonl(atoi(argv[2]));
+  
+  if (SYSERR == sendMessage(TESTBED_SET_LOSS_RATE,
+                           dst,
+                           sizeof(TESTBED_SET_LOSS_RATE_MESSAGE) - 
sizeof(TESTBED_CS_MESSAGE),
+                           &msg.percentageLossInbound))
+    return -1;
+  if (OK != readResult(dst, &ack)) 
+    return -1;
+  if (ack != OK) {
+    PRINTF("ERROR: peer could not set the specified loss rates.\n");
+    return -1;
+  } else {
+    PRINTF("OK.\n");
+    return 0;
+  }
+}
+
+/**
+ * Obtain statistics from a peer.
+ *
+ * @param argc number of arguments from the command line
+ * @param argv command line arguments
+ * @return 0 ok, 1 on error
+ **/   
+static int getStat(int argc, char ** argv) {
+  int res, peer, printProtocols;
+  
+  printProtocols = NO;
+  if (argc != 2) {
+    PRINTF("Syntax: get-stat PEERID STATID\n");
+    return -1;
+  }
+  CHECK_PEER(peer, argv[0]);
+  res = requestAndPrintStatistic(&nodes[peer].sock,
+                                argv[1]);
+  if (res == OK)
+    return 0;
+  else
+    return -1;
+}
+
+/**
+ * Obtain statistics from a peer.
+ *
+ * @param argc number of arguments from the command line
+ * @param argv command line arguments
+ * @return 0 ok, 1 on error
+ **/   
+static int getStats(int argc, char ** argv) {
+  int res, peer, printProtocols;
+  
+  printProtocols = NO;
+  if (argc == 2) {
+    if (strcmp(argv[0], "-P")) {
+      PRINTF("Syntax: get-stats [-P] PEERID\n");
+      return -1;
+    }
+    printProtocols = YES;
+    CHECK_PEER(peer, argv[1]);
+  } else if (argc != 1) {
+    PRINTF("Syntax: get-stats [-P] PEERID\n");
+    return -1;
+  } else
+    CHECK_PEER(peer, argv[0]);
+  res = requestAndPrintStatistics(&nodes[peer].sock);
+  if ( (printProtocols == YES) && 
+       (res == OK)) {
+    res = requestAndPrintProtocols(&nodes[peer].sock);
+  }
+  if (res == OK)
+    return 0;
+  else
+    return -1;
+}
+
+
+/**
+ * Obtain option from a peer.
+ *
+ * @param argc number of arguments from the command line
+ * @param argv command line arguments
+ * @return 0 ok, 1 on error
+ **/   
+static int getOption(int argc, char ** argv) {
+  int peer;
+  CS_GET_OPTION_REQUEST req;
+  CS_GET_OPTION_REPLY * reply;
+  int res;
+  
+  if (argc != 3) {
+    PRINTF("Syntax: get-option PEERID SECTION OPTION\n");
+    return -1;
+  }
+  CHECK_PEER(peer, argv[0]);
+  memset(&req,
+        0,
+        sizeof(CS_GET_OPTION_REQUEST));
+  req.header.tcpType = htons(CS_PROTO_GET_OPTION_REQUEST);
+  req.header.size = htons(sizeof(CS_GET_OPTION_REQUEST));
+  if ( (strlen(argv[1]) >= CS_GET_OPTION_REQUEST_OPT_LEN) ||
+       (strlen(argv[2]) >= CS_GET_OPTION_REQUEST_OPT_LEN) ) {
+    PRINTF("Illegal length of arguments (>= %d characters)",
+          CS_GET_OPTION_REQUEST_OPT_LEN);
+    return -1;
+  }
+  strcpy(&req.section[0],
+        argv[1]);
+  strcpy(&req.option[0],
+        argv[2]);
+  res = writeToSocket(&nodes[peer].sock,
+                     &req.header);
+  if (res != OK) {
+    PRINTF("Error sending request to peer %d\n",
+          peer);
+    return -1;
+  }
+  reply = NULL;
+  res = readFromSocket(&nodes[peer].sock,
+                      (CS_HEADER**)&reply);
+  if (res != OK) {
+    PRINTF("Error receiving reply from peer %d\n",
+          peer);
+    return -1;
+  }
+  PRINTF("%*s\n",
+        ntohs(reply->header.size) - sizeof(CS_HEADER),
+        &reply->value[0]);
+  FREE(reply);
+  if (res == OK)
+    return 0;
+  else
+    return -1;
+}
+
+
+/**
+ * Upload a file to a peer.
+ */
+static int uploadFile(int argc, 
+                     char *argv[]) {
+  int peer, nbytes, flen, ack;
+  char * buf;
+  FILE * infile;
+  TESTBED_UPLOAD_FILE_MESSAGE * msg;
+    
+  if (argc != 3) {
+    PRINTF("Syntax: load-file PEERID LOCAL_FILENAME DEST_FILENAME\n");
+    return -1;
+  }
+  CHECK_PEER(peer, argv[0]);
+  infile = FOPEN(argv[1], "r");
+  if (infile == NULL) {
+    PRINTF("ERROR: Could not open file %s\n",
+          argv[1]);
+    return -1;
+  }
+  flen = strlen(argv[2]) + 1; /* '\0' added in the flen */
+  if (flen > TESTBED_FILE_BLK_SIZE) {
+    PRINTF("ERROR: destination file name too long (%d characters, limit 
%d).\n", 
+          flen-1,
+          TESTBED_FILE_BLK_SIZE);
+    return -1;
+  }
+
+  msg = MALLOC(sizeof(TESTBED_UPLOAD_FILE_MESSAGE) + TESTBED_FILE_BLK_SIZE);
+  msg->header.header.size 
+    = htons(sizeof(TESTBED_UPLOAD_FILE_MESSAGE)+flen);
+  msg->header.header.tcpType 
+    = htons(TESTBED_CS_PROTO_REQUEST);
+  msg->header.msgType
+    = htonl(TESTBED_UPLOAD_FILE);
+  msg->type 
+    = htonl(TESTBED_FILE_DELETE);
+  memcpy(((TESTBED_UPLOAD_FILE_MESSAGE_GENERIC*)msg)->buf, argv[2], flen);
+  
+  if (SYSERR == writeToSocket(&nodes[peer].sock, 
+                             &msg->header.header)) {
+    fclose(infile);
+    FREE(msg);
+    PRINTF("ERROR: Could not send message to peer %s.\n", 
+          nodes[peer].ips);
+    return -1;
+  }
+  /* Read ack from the peer */
+  if (OK != readTCPResult(&nodes[peer].sock, &ack)) {
+    fclose(infile);
+    FREE(msg);
+    PRINTF("Peer is not responding\n");
+    return -1;
+  }
+  if (ack != OK) {
+    fclose(infile);
+    FREE(msg);
+    PRINTF("ERROR: Peer returned error (delete existing file).\n");
+    return -1;
+  }
+  msg->type = htonl(TESTBED_FILE_APPEND);
+  buf = ((TESTBED_UPLOAD_FILE_MESSAGE_GENERIC*)msg)->buf + flen;
+  while ((nbytes = GN_FREAD(buf, 1, 
+                           (TESTBED_FILE_BLK_SIZE -
+                            sizeof(TESTBED_UPLOAD_FILE_MESSAGE) - flen),
+                           infile)) > 0) {
+    if (ferror(infile))
+      break;
+    msg->header.header.size = htons(sizeof(TESTBED_UPLOAD_FILE_MESSAGE) +
+                                   nbytes + flen);
+    if (SYSERR == writeToSocket(&nodes[peer].sock, &msg->header.header)) {
+      fclose(infile);
+      FREE(msg);
+      PRINTF("ERROR: could not send file to node %s.\n",
+            nodes[peer].ips);
+      return -1;
+    }
+    if (OK != readResult(peer, &ack)) {
+      fclose(infile);
+      FREE(msg);
+      return -1;
+    }
+    if (ack != OK) {
+      fclose(infile);
+      FREE(msg);
+      PRINTF("ERROR: peer returned error.\n");
+      return -1;
+    }
+  }
+  if (ferror(infile)) {
+    fclose(infile);
+    FREE(msg);
+    PRINTF("ERROR: could not read source file. Transmission aborted.\n");
+    return -1;
+  }
+  fclose(infile);
+  FREE(msg);
+  PRINTF("OK.\n");
+  return 0;
+}
+
+/**
+ * Print the list of commands.
+ */
+static int printOnlineHelp(int argc, 
+                          char * argv[]) {
+  int i;
+  i = 0;
+  while (commands[i].command != NULL) {
+    PRINTF("%-30s%s\n",
+          commands[i].command,
+          commands[i].help);
+    i++;
+  }
+  return 0;
+}
+
+static int processCommands(char * buffer,
+                          unsigned int * available) {
+  int ip[4];
+  int port;
+  unsigned int end;
+  unsigned int start;
+  char * up;
+  int err;
+
+  err = 0;
+  end = 0;
+  start = 0;
+  while (end < *available) {   
+    while ( (buffer[end] != '\n') &&
+           (end < *available) )
+      end++;
+    if (buffer[end] != '\n') {
+      if (start == 0) {
+       PRINTF("Received invalid response from HTTP server!\n");
+       return -1;
+      } else {
+       memmove(buffer,
+               &buffer[start],
+               *available - start);
+       *available -= start;
+       return 0;
+      }
+    }    
+    up = MALLOC(end-start+1);
+    memcpy(up,
+          &buffer[start],
+          end - start);
+    up[end-start] = '\0';
+    port = 2087; /* default port */
+    if (4 <= sscanf(up,
+                   "add-node %d.%d.%d.%d %d",
+                   &ip[0],
+                   &ip[1],
+                   &ip[2],
+                   &ip[3],
+                   &port)) {
+      char ports[12];
+      char ips[128];
+      char * argv[2];
+      sprintf(ports,
+             "%d",
+             port);
+      sprintf(ips,
+             "%d.%d.%d.%d",
+             ip[0],
+             ip[1],
+             ip[2],
+             ip[3]);
+      argv[0] = ips;
+      argv[1] = ports;
+      if (0 != addNode(2, argv)) 
+       err = 2;
+    } else {      
+      char * login;
+      login = MALLOC(64);
+      if (5 <= sscanf(up,
+                     "add-node %63s %d.%d.%d.%d %d",
+                     login,
+                     &ip[0],
+                     &ip[1],
+                     &ip[2],
+                     &ip[3],
+                     &port)) {
+       char ports[12];
+       char ips[128];
+       char * argv[3];
+       sprintf(ports,
+               "%d",
+               port);
+       sprintf(ips,
+               "%d.%d.%d.%d",
+               ip[0],
+               ip[1],
+               ip[2],
+               ip[3]);
+       argv[0] = login;
+       argv[1] = ips;
+       argv[2] = ports;
+       if (0 != addSshNode(3, argv)) 
+         err = 2;
+      }
+      FREE(login);
+    }
+    FREE(up);
+    end++;
+    start = end;
+  }
+  return err;
+}
+
+
+#define GET_COMMAND "GET %s/display.php3 HTTP/1.0\r\n\r\n"
+#define HTTP_URL "http://";
+
+/**
+ * add-nodes that are listed as available at the
+ * TESTBED-HTTP registry.  Optional argument:
+ * URL of the registry (by default we use the
+ * value from the configuration file value).
+ **/
+static int addAvailable(int argc,
+                       char * argv[]) {
+  char * reg = NULL;
+  long int port;
+  char * hostname;
+  unsigned int curpos;
+  struct hostent *ip_info;
+  struct sockaddr_in soaddr;
+  int sock;
+  int ret;
+  char * command;
+  cron_t start;
+  char c;
+  char * buffer;
+  int i;
+  int j;
+  int k;
+  struct sockaddr_in theProxy;
+  char *proxy, *proxyPort;
+  struct hostent *ip;
+
+  if (argc == 0) {
+    reg = getConfigurationString("GNUNET-TESTBED",
+                                "REGISTERURL");
+    if (reg == NULL) {
+      PRINTF("ERROR: no testbed registration URL given.\n");
+      return -1;
+    }
+  } else
+    reg = STRDUP(argv[0]);
+  
+  
+
+  proxy = getConfigurationString("GNUNETD", 
+                                "HTTP-PROXY");
+  if (proxy != NULL) {
+    ip = GETHOSTBYNAME(proxy);
+    if (ip == NULL) {
+      PRINTF("ERROR: Couldn't resolve name of HTTP proxy %s\n",
+            proxy);
+      theProxy.sin_addr.s_addr = 0;
+    } else {
+      theProxy.sin_addr.s_addr 
+       = ((struct in_addr *)ip->h_addr)->s_addr;
+      proxyPort = getConfigurationString("GNUNETD",
+                                        "HTTP-PROXY-PORT");
+      if (proxyPort == NULL) {
+       theProxy.sin_port = htons(8080);
+      } else {
+       theProxy.sin_port = htons(atoi(proxyPort));
+       FREE(proxyPort);
+      }
+    }
+    FREE(proxy);
+  } else {
+    theProxy.sin_addr.s_addr = 0;
+  }
+
+  if (0 != strncmp(HTTP_URL,
+                  reg, 
+                  strlen(HTTP_URL)) ) {
+    PRINTF("WARNING: invalid URL %s (must begin with %s)\n",
+          reg, 
+          HTTP_URL);
+    return -1;
+  }
+  port = 80; /* default http port */
+
+  hostname = STRDUP(&reg[strlen(HTTP_URL)]);
+  buffer = NULL;
+  j = -1;
+  k = -1;
+  for (i=0;i<strlen(hostname);i++) {
+    if (hostname[i] == ':') 
+      j = i;
+    if (hostname[i] == '/') {
+      k = i;
+      if (j == -1)
+       j = i;      
+      break;
+    }
+  }
+  if ( (j != -1) && (j < k) ) {   
+    char * pstring;
+    if (k == -1) {
+      pstring = MALLOC(strlen(hostname)-j+1);
+      memcpy(pstring,
+            &hostname[j],
+            strlen(hostname)-j+1);
+      pstring[strlen(hostname)-j] = '\0';
+    } else {
+      pstring = MALLOC(k-j+1);
+      memcpy(pstring,
+            &hostname[j],
+            k-j);
+      pstring[k-j] = '\0';
+    }
+    port = strtol(pstring, &buffer, 10);
+    if ( (port < 0) || (port > 65536) ) {
+      PRINTF("ERROR: malformed http URL: %s at %s.\n",
+            reg,
+            buffer);
+      FREE(hostname);
+      FREE(reg);
+      FREE(pstring);
+      return -1;
+    }
+    FREE(pstring);
+  }
+  hostname[k] = '\0';
+
+#if DEBUG_TESTBED
+  LOG(LOG_INFO,
+      "INFO: Trying to download a hostlist from %s\n",
+      reg);
+#endif
+
+
+
+  sock = SOCKET(PF_INET, 
+               SOCK_STREAM,
+               0);
+  if (sock < 0) {
+    PRINTF("ERROR: could not open socket for hostlist download (%s).\n",
+          STRERROR(errno));
+    FREE(hostname);   
+    FREE(reg);
+    return -1;
+  }
+
+  /* Do we need to connect through a proxy? */
+  if (theProxy.sin_addr.s_addr == 0) {
+    /* no proxy */
+    ip_info = GETHOSTBYNAME(hostname);
+    if (ip_info == NULL) {
+      PRINTF("WARNING: could not download hostlist, host %s unknown\n",
+            hostname);
+      FREE(reg);
+      FREE(hostname);
+      return -1;
+    }    
+    soaddr.sin_addr.s_addr 
+      = ((struct in_addr*)(ip_info->h_addr))->s_addr;
+    soaddr.sin_port 
+      = htons((unsigned short)port);
+  } else {
+    /* proxy */
+    soaddr.sin_addr.s_addr 
+      = theProxy.sin_addr.s_addr;
+    soaddr.sin_port 
+      = theProxy.sin_port;
+  }
+  soaddr.sin_family = AF_INET;
+  if (CONNECT(sock, 
+             (struct sockaddr*)&soaddr, 
+             sizeof(soaddr)) < 0) {
+    PRINTF("WARNING: failed to send HTTP request to host %s: %s\n",
+          hostname,
+          STRERROR(errno));
+    FREE(reg);
+    FREE(hostname);
+    CLOSE(sock);
+    return -1;
+  }
+  
+  command = MALLOC(strlen(GET_COMMAND) 
+                  + strlen(reg));
+  sprintf(command, 
+         GET_COMMAND,
+         reg);
+  FREE(reg);
+  curpos = strlen(command)+1;
+  curpos = SEND_BLOCKING_ALL(sock,
+                            command,
+                            curpos);
+  if (SYSERR == (int)curpos) {
+    PRINTF("WARNING: failed so send HTTP request %s to host %s (%u - %d) - 
%s\n",
+          command,
+          hostname,
+          curpos, 
+          sock,
+          STRERROR(errno));
+    FREE(command);    
+    FREE(hostname);
+    CLOSE(sock);
+    return -1;
+  }
+  FREE(command);
+  FREE(hostname);
+  cronTime(&start);
+
+  /* we first have to read out the http_response*/
+  /* it ends with four line delimiters: "\r\n\r\n" */
+  curpos = 0;
+  while (curpos < 4) {
+    if (start + 5 * cronMINUTES < cronTime(NULL))
+      break; /* exit after 5m */
+    ret = RECV_NONBLOCKING(sock,
+                          &c,
+                          sizeof(c));
+    if ( (ret == SYSERR) &&
+        (errno == EAGAIN) ) {
+      gnunet_util_sleep(100 * cronMILLIS);
+      continue;    
+    }
+    if (ret <= 0)
+      break; /* end of transmission or error */
+    if ((c=='\r') || (c=='\n')) 
+      curpos += ret;
+    else 
+      curpos=0;    
+  }
+  if (curpos < 4) { /* invalid response */
+    PRINTF("WARNING: exit register (error: no http response read)\n");
+    CLOSE(sock);
+    return -1;
+  }
+
+  /* now read peer list */
+  buffer = MALLOC(65536);
+  
+
+  while (1) {
+    if (start + 300 * cronSECONDS < cronTime(NULL))
+      break; /* exit after 300s */
+    curpos = 0;
+    while (curpos < 65536) {
+      if (start + 300 * cronSECONDS < cronTime(NULL))
+       break; /* exit after 300s */
+      ret = RECV_NONBLOCKING(sock,
+                            &buffer[curpos],
+                            65536-curpos);      
+      if ( (ret == SYSERR) &&
+          (errno == EAGAIN) )
+       continue;
+      if (ret <= 0)
+       break; /* end of file or error*/
+      curpos += ret;
+    
+      if (0 != processCommands(buffer, &curpos)) {
+       FREE(buffer);
+       CLOSE(sock);
+       return -1;
+      }
+    }
+  }
+  if (0 != processCommands(buffer, &curpos)) {
+    FREE(buffer);
+    CLOSE(sock);
+    return -1;
+  }
+  FREE(buffer);
+  CLOSE(sock);  
+  return 0;
+}
+
+/**
+ * Print the list of available peers.
+ */
+static int listPeers(int argc, char * argv[]) {
+  int i;
+  for (i=0;i<nnodes;i++)
+    PRINTF("%4d - %s:%d\n",
+          i,
+          nodes[i].ips,
+          nodes[i].port);
+  return 0;
+}
+
+/**
+ * Exit gnunet-testbed shell.
+ */
+static int doExit(int argc, char * argv[]) {
+  do_quit = YES;
+  return 0;
+}
+
+/* ****************** command set ****************** */
+
+CMD_ENTRY commands[] = {
+  { "help", 
+    "print this help text", 
+    &printOnlineHelp },
+  { "get-trust",
+    "", 
+    &getTrust },
+  { "set-bw", 
+    "", 
+    &setBW },
+  { "set-trust", 
+    "", 
+    &setTrust },
+  { "add-node", 
+    "add node to testbed, arguments: IP PORT", 
+    &addNode },
+  { "add-ssh-node", 
+    "add node to testbed, arguments: LOGIN IP PORT", 
+    &addSshNode },
+  { "connect",
+    "connect two peers", 
+    &addConnection },
+  { "disconnect", 
+    "disconnect two peers", 
+    &delConnection },
+  { "disconnect-all",
+    "destroy all connections between peers", 
+    &delAllConnections },
+  { "helo-disable", 
+    "disable HELO advertisements",
+    &disableHELO },
+  { "helo-enable", 
+    "enable HELO advertisements", 
+    &enableHELO },
+  { "autoconnect-disable", "", &disableAUTOCONNECT },
+  { "autoconnect-enable", "", &enableAUTOCONNECT },
+  { "process-start", 
+    "Start a process on a given peer.  Prints the process-ID on success.",
+    &startProcess },
+  { "process-signal",
+    "Send a signal to a process running at a peer.  Use signal 0 to test if 
the process is still running.  Use -1 to obtain the exit code of a process that 
terminated.", 
+    &signalProcess },
+  { "process-output", 
+    "Obtain the process output from a process at a peer.",
+    &dumpProcessOutput },
+  { "exit", 
+    "exit the testbed shell", 
+    &doExit },
+  { "list-peers", "", &listPeers}, 
+  { "set-loss", "", &setLoss} ,
+  { "get-stats",
+    "get all stats values from peer", 
+    &getStats }, 
+  { "get-stat",
+    "get one specific stats value from peer",
+    &getStat }, 
+  { "get-option",
+    "Get configuration value from peer.", 
+    &getOption },
+  { "load-module", "", &loadModule },
+  { "unload-module", "", &unloadModule },
+  { "add-available", 
+    "Check http server for available testbed peers and add"
+    " all available nodes.  An optional argument can be"
+    " passed to specify the URL of the http server.",
+    &addAvailable },
+  { "upload", 
+    "", 
+    &uploadFile }, 
+  { "connect-deny", "", &denyConnect },
+  { "connect-allow", "", &allowConnect },
+  { NULL, NULL }, /* termination */
+};
+
+
+/* end of commands.c */

Added: freeway/src/org/gnu/freeway/protocol/testbed/commands.h
===================================================================
--- freeway/src/org/gnu/freeway/protocol/testbed/commands.h     2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/testbed/commands.h     2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,51 @@
+/*
+     This file is part of GNUnet.
+     (C) 2003 Christian Grothoff (and other contributing authors)
+
+     GNUnet is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with GNUnet; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @author Ronaldo Alves Ferreira
+ * @author Christian Grothoff
+ * @author Murali Krishna Ramanathan
+ * @file applications/testbed/testbed.h
+ **/
+#ifndef TESTBED_COMMANDS_H
+#define TESTBED_COMMANDS_H
+
+/**
+ * Signature of a command -- just like main()!
+ */
+typedef int (*CommandHandler)(int argc,
+                             char * argv[]);
+
+typedef struct CMD_ENTRY_ {
+  /* the name of the command (what the user enters
+     in the shell */
+  char * command;
+  /* help text */
+  char * help;
+  /* the function to run */
+  CommandHandler handler;
+} CMD_ENTRY;
+
+extern CMD_ENTRY commands[];
+
+/* flag used to signal "end of execution" */
+extern int do_quit;
+
+#endif

Added: freeway/src/org/gnu/freeway/protocol/testbed/connect.php3
===================================================================
--- freeway/src/org/gnu/freeway/protocol/testbed/connect.php3   2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/testbed/connect.php3   2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,11 @@
+<?php
+$connection = @mysql_connect("localhost",
+                            "testbed",
+                            "password");
+if ($connection) {
+  mysql_select_db("testbedDB",
+                 $connection);
+  $query = "CREATE TABLE IF NOT EXISTS peers (trusted BLOB, login BLOB, ip 
BLOB, port BLOB, startup TIMESTAMP)";
+  mysql_query($query);
+}
+?>
\ No newline at end of file

Added: freeway/src/org/gnu/freeway/protocol/testbed/display.php3
===================================================================
--- freeway/src/org/gnu/freeway/protocol/testbed/display.php3   2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/testbed/display.php3   2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,67 @@
+<?php
+include("connect.php3");
+if (!$connection) {
+  echo "<HTML><HEAD><TITLE>GNUnet-testbed: display</TITLE></HEAD><BODY>";
+  echo "Database is down. Can not list available peers.";
+  echo "</body></html>";
+  die(-1);
+}
+
+$printed=0;
+$ip=$_SERVER['REMOTE_ADDR'];
+$fipa = explode(".", $ip);
+$rip = $fipa[0] << 24 + $fipa[1] << 16 + $fipa[2] << 8 + $fipa[3];
+$query = "SELECT * FROM peers;";
+$result = mysql_query($query, $connection);
+if ($result) {
+ $num = mysql_numrows($result);
+
+ for ($i=0;$i<$num;$i++) {
+   $row = mysql_fetch_array($result);
+   // add here: filtering by trusted IP,
+   // filtering by date of entry
+   $ok=0;
+   $trusted = $row["trusted"];
+   $tok = strtok($trusted, "@");
+   while ($tok) {
+     $ipnm = explode("/", $tok);
+     $fipa = explode(".", $ipnm[0]);
+     // we shift by 23 (and so on) because PHP does not have unsigned ints and 
going
+     // bignum would be overkill.  So instead we just ignore the last bit in 
the IP,
+     // which should hardly give us any mismatches in practice anyway.
+     $fip = ($fipa[0] << 23) + ($fipa[1] << 15) + ($fipa[2] << 7) + ($fipa[3] 
>> 1);
+     if (is_int($ipnm[1])) {
+       $fnm = 0;
+       $ipnm[1]--;
+       while ($ipnm[1] > 0) {
+        $fnm = ($fnm >> 1) | 0x80000000;
+         $ipnm[1]--;
+       }
+     } else {
+       $fipa = explode(".", $ipnm[1]);
+       $fnm = ($fipa[0] << 23) + ($fipa[1] << 15) + ($fipa[2] << 7) + 
($fipa[3] >> 1);
+     }
+     /*printf("<br>IP: %x (%s) have %x/%x (%s/%s)<br>", 
+      $rip, $ip, $fip, $fnm, $ipnm[0], $ipnm[1]);*/
+     if ( ($rip & $fnm) == ($fip & $fnm) )
+       $ok = 1;
+     $tok = strtok("@");
+   }
+   $login = $row["login"];
+   if ($ok != 0) {
+     printf("add-node %s %s\n", $row["ip"], $row["port"]);
+     $printed=1;
+   }
+   if ( ($ok == 0) && ($login) ) {
+     printf("add-ssh-node %s %s %s\n", $login, $row["ip"], $row["port"]);
+     $printed=1;
+   }
+ }
+ if ($num == 0) 
+   printf("# No peers available at this point.\n");
+ else if ($printed == 0)
+   printf("# No peers available at this point for %s.\n", $ip);
+} else
+  printf("# Database error.\n");
+
+?>
\ No newline at end of file

Added: freeway/src/org/gnu/freeway/protocol/testbed/functions.sh
===================================================================
--- freeway/src/org/gnu/freeway/protocol/testbed/functions.sh   2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/testbed/functions.sh   2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,249 @@
+# This script defines some basic helper functions
+# that can be used with gnunet-testbed.
+#
+# Source the script in the testbed-shell to make
+# the functions available.
+#
+# Author: Christian Grothoff <address@hidden>
+
+# How many peers are available in the testbed?
+testbedsize() {
+  ok = 0;
+  PEERS=`list-peers || ok=1`;
+  if test $ok == 1
+  then
+    return 0;
+  else
+    return `cat $PEERS | wc -l`
+  fi
+}
+
+# Return a list of all PEER IDs.
+allpeerids() {
+  size=`expr testbedsize - 1`
+  if test $size -ge 0
+  then
+    return `seq 0 $size`;
+  else
+    return "";
+  fi
+}
+
+# Isolate the testbed from the rest of the network
+# by limiting connections to other testbed peers.
+isolate() {
+  for n in allpeerids
+  do
+    connect-allow $n allpeerids
+    disconnect-all $n
+  done
+}
+
+# switch everything to manual topology management
+manualtopology() {
+  isolate;
+  for n in allpeerids
+  do
+    helo-disable $n
+  done
+}
+
+# switch everything to automatic topology management
+autotopology() {
+  isolate;
+  for n in allpeerids
+  do
+    helo-enable $n
+  done
+}
+
+# create a circular topology
+circulartopology() {
+  manualtopology;
+  LAST=`testbedsize`;
+  LAST=`expr $LAST - 1`
+  for n in allpeerids
+  do
+    connect $n $LAST
+    LAST=$n
+  done
+}
+
+# create a clique topology
+cliquetopology() {
+  manualtopology;
+  for n in allpeerids
+  do
+    for m in allpeerids 
+    do
+      connect $n $m
+    done
+  done
+}
+
+# wait until peer $1 has connectivity degree exactly $2
+waitConnected() {
+  EG="-1"
+  while test $EG != $2
+  do
+    EG=`get-stat $1 "# currently connected nodes"`
+    if test $EG != $2
+    then  
+      sleep 1
+    fi
+  done  
+}
+
+
+# upload a random file of $1 kb to peer $2 under the name $3
+# Prints "OK." on success, otherwise the error message.
+uploadrandomfile() {
+  size=`expr $1 \* 2`
+  TMPFILE=`mktemp -t randomXXXXXX` && {
+    dd if=/dev/urandom of=/$TMPFILE count=$size &> /dev/null
+    upload $2 $TMPFILE $3
+    rm $TMPFILE
+  }
+}
+
+# Delete file $2 on peer $1
+deleteFile() {
+  PID=`process-start -- $1 rm $2`
+  last="OK.";
+  while test "$last" == "OK."
+  do
+    sleep 1
+    last=`process-signal -- $1 $PID 0`
+  done
+  OUT=`process-output $1 $PID`
+  RET=`process-signal -- $1 $PID -1`
+  if test $RET != "0"
+  then
+    echo "deleteFile failed: $OUT"
+    return -1
+  else
+    return 0
+  fi
+
+}
+
+# insert file $2 on peer $1, print GNUnet URI.
+# On error, returns -1 and prints full gnunet-insert output.
+# $2 may contain options, like -i for full insertion.
+afsInsert() {
+  PID=`process-start -- $1 gnunet-insert -u $2`
+  last="OK.";
+  while test "$last" == "OK."
+  do
+    sleep 1
+    last=`process-signal -- $1 $PID 0`
+  done
+  OUT=`process-output $1 $PID`
+  RET=`process-signal -- $1 $PID -1`
+  if test $RET != "0"
+  then
+    echo "gnunet-insert failed: $OUT"
+    return -1
+  fi
+  LINES=`echo $OUT | grep "gnunet://afs" | wc -l`
+  if test $LINES -ne 1
+  then
+    echo "gnunet-insert failed: $OUT"
+    return -1
+  else
+    echo $OUT
+    return 0
+  fi  
+}
+
+# download file with url $2 on peer $1 using name $3
+# Prints performance on success.
+afsDownload() {
+  PID=`process-start -- $1 gnunet-download -V -o $3 $2`
+  last="OK.";
+  while test "$last" == "OK."
+  do
+    sleep 1
+    last=`process-signal -- $1 $PID 0`
+  done
+  FOUT=`process-output $1 $PID`
+  RET=`process-signal -- $1 $PID -1`
+  OUT=`echo $FOUT | tail -n 1`
+  if test "$RET" == "0"
+  then
+    echo $OUT
+    return 0 
+  else
+    echo "Download failed: $FOUT"
+    return -1
+  fi
+}
+
+
+# The very basic testcase: insert, download, both on the same peer.
+# Argument: $1 = ID of peer to test.
+testUploadInsertDownloadLoopback() {
+  PEER0=$1
+  UPLOAD=`uploadrandomfile 1024 $PEER0 RANDOM1MB`
+  if test "OK." != "$UPLOAD"
+  then
+    echo "File upload failed: $UPLOAD"
+    return -1;
+  else 
+    URL=`afsInsert $PEER0 RANDOM1MB`
+    deleteFile $PEER0 RANDOM1MB.download &> /dev/null
+    afsDownload $PEER0 $URL RANDOM1MB.download  
+    deleteFile $PEER0 RANDOM1MB.download &> /dev/null
+  fi
+}
+
+
+# Test loopback functionality at a peer.
+# Arguments: peer IP and port ($1 = IP, $2 = port)
+testLoopback() {
+  PEER0=`add-node $1 $2`
+  if test 0 -le $PEER0 &> /dev/null
+  then
+    testUploadInsertDownloadLoopback $PEER0
+  else
+    echo "add-node $1 $2 failed: $PEER0";
+    return -1;
+  fi
+}
+
+
+# Quick test for the localhost peer.  No arguments.
+testLocalhost() {
+  testLoopback 127.0.0.1 2087
+}
+
+
+
+# Insert on one peer, download at another.
+# Argument: $1 = ID of peer for insert, $2 = ID of peer for download.
+testUploadInsertDownloadPeerPair() {
+  PEER0=$1
+  PEER1=$2
+  autoconnect-disable $PEER0
+  autoconnect-disable $PEER1
+  disconnect-all 0
+  disconnect-all 1
+  waitConnected $PEER0 0
+  waitConnected $PEER1 0
+  connect $PEER0 $PEER1
+  waitConnected $PEER0 1
+  waitConnected $PEER1 1
+  UPLOAD=`uploadrandomfile 1024 $PEER0 RANDOM1MB`
+  if test "OK." != "$UPLOAD"
+  then
+    echo "File upload failed: $UPLOAD"
+    return -1;
+  else 
+    URL=`afsInsert $PEER0 RANDOM1MB`
+    deleteFile $PEER1 RANDOM1MB.download &> /dev/null
+    afsDownload $PEER1 $URL RANDOM1MB.download  
+    deleteFile $PEER1 RANDOM1MB.download &> /dev/null
+  fi
+}
+
+

Added: freeway/src/org/gnu/freeway/protocol/testbed/get-stats.c
===================================================================
--- freeway/src/org/gnu/freeway/protocol/testbed/get-stats.c    2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/testbed/get-stats.c    2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,374 @@
+/*
+     This file is part of GNUnet.
+     (C) 2001, 2002 Christian Grothoff (and other contributing authors)
+
+     GNUnet is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with GNUnet; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file applications/stats/gnunet-stats.c 
+ * @brief tool to obtain statistics from gnunetd.
+ * @author Christian Grothoff
+ * @author Igor Wronsky
+ */
+
+#include "gnunet_util.h"
+#include "platform.h"
+#include "socket.h"
+
+/**
+ * Return a descriptive name for a p2p message type
+ */
+static const char *p2pMessageName(unsigned short type) {
+  const char *name = NULL;
+  switch( type ) {
+  case p2p_PROTO_HELO : 
+    name = "p2p_PROTO_HELO";
+    break;
+  case p2p_PROTO_SKEY : 
+    name = "p2p_PROTO_SKEY";
+    break;
+  case p2p_PROTO_PING : 
+    name = "p2p_PROTO_PING";
+    break;
+  case p2p_PROTO_PONG : 
+    name = "p2p_PROTO_PONG";
+    break;
+  case p2p_PROTO_TIMESTAMP : 
+    name = "p2p_PROTO_TIMESTAMP";
+    break;
+  case p2p_PROTO_SEQUENCE : 
+    name = "p2p_PROTO_SEQUENCE";
+    break;
+  case p2p_PROTO_NOISE : 
+    name = "p2p_PROTO_NOISE";
+    break;
+  case p2p_PROTO_HANGUP : 
+    name = "p2p_PROTO_HANGUP";
+    break;
+  case AFS_p2p_PROTO_QUERY : 
+    name = "AFS_p2p_PROTO_QUERY";
+    break;
+  case AFS_p2p_PROTO_3HASH_RESULT : 
+    name = "AFS_p2p_PROTO_3HASH_RESULT";
+    break;
+  case AFS_p2p_PROTO_CHK_RESULT : 
+    name = "AFS_p2p_PROTO_CHK_RESULT";
+    break;
+  case CHAT_p2p_PROTO_MSG : 
+    name = "CHAT_p2p_PROTO_MSG";
+    break;
+  case TRACEKIT_p2p_PROTO_PROBE : 
+    name = "TRACEKIT_p2p_PROTO_PROBE";
+    break;
+  case TRACEKIT_p2p_PROTO_REPLY : 
+    name = "TRACEKIT_p2p_PROTO_REPLY";
+    break;
+  case TBENCH_p2p_PROTO_REQUEST        : 
+    name = "TBENCH_p2p_PROTO_REQUEST";
+    break;
+  case TBENCH_p2p_PROTO_REPLY  : 
+    name = "TBENCH_p2p_PROTO_REPLY";
+    break;
+  default:
+    name = NULL;
+    break;
+  }
+  return name;
+}
+
+/**
+ * Return a descriptive name for a client server message type
+ */
+static const char *csMessageName( unsigned short type ) {
+  const char *name = NULL;
+  
+  switch( type ) {
+  case CS_PROTO_RETURN_VALUE : 
+    name = "CS_PROTO_RETURN_VALUE";
+    break;
+  case CS_PROTO_CLIENT_COUNT : 
+    name = "CS_PROTO_CLIENT_COUNT";
+    break;
+  case CS_PROTO_TRAFFIC_QUERY : 
+    name = "CS_PROTO_TRAFFIC_QUERY";
+    break;
+  case CS_PROTO_TRAFFIC_INFO : 
+    name = "CS_PROTO_TRAFFIC_INFO";
+    break;
+  case STATS_CS_PROTO_GET_STATISTICS : 
+    name = "STATS_CS_PROTO_GET_STATISTICS";
+    break;
+  case STATS_CS_PROTO_STATISTICS : 
+    name = "STATS_CS_PROTO_STATISTICS";
+    break;
+  case STATS_CS_PROTO_GET_CS_MESSAGE_SUPPORTED : 
+    name = "STATS_CS_PROTO_GET_CS_MESSAGE_SUPPORTED";
+    break;
+  case STATS_CS_PROTO_GET_P2P_MESSAGE_SUPPORTED : 
+    name = "STATS_CS_PROTO_GET_P2P_MESSAGE_SUPPORTED";
+    break;
+  case AFS_CS_PROTO_QUERY: 
+    name = "AFS_CS_PROTO_QUERY";
+    break;
+  case AFS_CS_PROTO_RESULT_3HASH: 
+    name = "AFS_CS_PROTO_RESULT_3HASH";
+    break;
+  case AFS_CS_PROTO_RESULT_CHK: 
+    name = "AFS_CS_PROTO_RESULT_CHK";
+    break;
+  case AFS_CS_PROTO_INSERT_CHK: 
+    name = "AFS_CS_PROTO_INSERT_CHK";
+    break;
+  case AFS_CS_PROTO_INSERT_3HASH : 
+    name = "AFS_CS_PROTO_INSERT_3HASH";
+    break;
+  case AFS_CS_PROTO_INDEX_BLOCK : 
+    name = "AFS_CS_PROTO_INDEX_BLOCK";
+    break;
+  case AFS_CS_PROTO_INDEX_FILE : 
+    name = "AFS_CS_PROTO_INDEX_FILE";
+    break;
+  case AFS_CS_PROTO_INDEX_SUPER : 
+    name = "AFS_CS_PROTO_INDEX_SUPER";
+    break;
+  case CHAT_CS_PROTO_MSG : 
+    name = "CHAT_CS_PROTO_MSG";
+    break;
+  case TRACEKIT_CS_PROTO_PROBE : 
+    name = "TRACEKIT_CS_PROTO_PROBE";
+    break;
+  case TRACEKIT_CS_PROTO_REPLY : 
+    name = "TRACEKIT_CS_PROTO_REPLY";
+    break;
+  case TBENCH_CS_PROTO_REQUEST : 
+    name = "TBENCH_CS_PROTO_REQUEST";
+    break;
+  case TBENCH_CS_PROTO_REPLY : 
+    name = "TBENCH_CS_PROTO_REPLY";
+    break;
+  default:
+    name = NULL;
+    break;
+  }
+  return name;
+}
+
+/**
+ * Print statistics received from TCP socket.
+ * @param stream where to print the statistics
+ * @param sock the socket to use 
+ * @return OK on success, SYSERR on error
+ **/
+int requestAndPrintStatistics(GNUNET_TCP_SOCKET * sock) {
+  STATS_CS_MESSAGE * statMsg;
+  CS_HEADER csHdr;
+  unsigned int count;
+  unsigned int i;
+  int mpos;
+  
+  csHdr.size = htons(sizeof(CS_HEADER));
+  csHdr.tcpType = htons(STATS_CS_PROTO_GET_STATISTICS);
+  if (SYSERR == writeToSocket(sock, &csHdr)) {
+    PRINTF("Error sending request for statistics to peer.\n");
+    return SYSERR;
+  }
+  statMsg = MALLOC(MAX_BUFFER_SIZE);
+  statMsg->totalCounters = htonl(1); /* to ensure we enter the loop */
+  count = 0;
+  while ( count < ntohl(statMsg->totalCounters) ) {
+    /* printf("reading from socket starting %u of %d\n",
+       count, ntohl(statMsg->totalCounters) );*/
+    if (SYSERR == readFromSocket(sock,
+                                (CS_HEADER**)&statMsg)) {
+      PRINTF("Error receiving reply for statistics from peer.\n");
+      FREE(statMsg);
+      return SYSERR;    
+    }
+    if (ntohs(statMsg->header.size) < sizeof(STATS_CS_MESSAGE)) {
+      LOG(LOG_WARNING,
+         "WARNING: received malformed stats message (%d < %d)\n",
+         ntohs(statMsg->header.size), 
+         sizeof(STATS_CS_MESSAGE) );
+      break;
+    }
+    mpos = sizeof(unsigned long long) * ntohl(statMsg->statCounters);
+    if (count == 0) {
+      PRINTF("%-60s: %16u\n",
+            "Uptime (seconds)",
+            (unsigned int) 
+            ((cronTime(NULL) - ntohll(statMsg->startTime))/
+             cronSECONDS));
+    }
+    for (i=0; i < ntohl(statMsg->statCounters); i++) {
+      if 
(mpos+strlen(&((char*)(((STATS_CS_MESSAGE_GENERIC*)statMsg)->values))[mpos])+1 
> 
+         ntohs(statMsg->header.size) - sizeof(STATS_CS_MESSAGE)) {
+       LOG(LOG_WARNING,
+           "WARNING: received malformed stats message (%d > %d)\n",
+           
mpos+strlen(&((char*)(((STATS_CS_MESSAGE_GENERIC*)statMsg)->values))[mpos])+1,
+           ntohs(statMsg->header.size)-sizeof(STATS_CS_MESSAGE));
+       break; /* out of bounds! */      
+      }
+      PRINTF("%-60s: %16llu\n",
+            &((char*)(((STATS_CS_MESSAGE_GENERIC*)statMsg)->values))[mpos],
+                  ntohll(((STATS_CS_MESSAGE_GENERIC*)statMsg)->values[i]));
+      mpos += 
strlen(&((char*)(((STATS_CS_MESSAGE_GENERIC*)statMsg)->values))[mpos])+1;
+    }    
+    count += ntohl(statMsg->statCounters);
+  } /* end while */
+  FREE(statMsg);
+  return OK;
+}
+
+/**
+ * Print statistics received from TCP socket.
+ * @param stream where to print the statistics
+ * @param sock the socket to use 
+ * @return OK on success, SYSERR on error
+ **/
+int requestAndPrintStatistic(GNUNET_TCP_SOCKET * sock,
+                            char * name) {
+  STATS_CS_MESSAGE * statMsg;
+  CS_HEADER csHdr;
+  unsigned int count;
+  unsigned int i;
+  int mpos;
+  
+  csHdr.size = htons(sizeof(CS_HEADER));
+  csHdr.tcpType = htons(STATS_CS_PROTO_GET_STATISTICS);
+  if (SYSERR == writeToSocket(sock, &csHdr)) {
+    PRINTF("Error sending request for statistics to peer.\n");
+    return SYSERR;
+  }
+  statMsg = MALLOC(MAX_BUFFER_SIZE);
+  statMsg->totalCounters = htonl(1); /* to ensure we enter the loop */
+  count = 0;
+  while ( count < ntohl(statMsg->totalCounters) ) {
+    /* printf("reading from socket starting %u of %d\n",
+       count, ntohl(statMsg->totalCounters) );*/
+    if (SYSERR == readFromSocket(sock,
+                                (CS_HEADER**)&statMsg)) {
+      PRINTF("Error receiving reply for statistics from peer.\n");
+      FREE(statMsg);
+      return SYSERR;    
+    }
+    if (ntohs(statMsg->header.size) < sizeof(STATS_CS_MESSAGE)) {
+      PRINTF("Error receiving reply for statistics from peer.\n");
+      LOG(LOG_WARNING,
+         "WARNING: received malformed stats message (%d < %d)\n",
+         ntohs(statMsg->header.size), 
+         sizeof(STATS_CS_MESSAGE) );
+      break;
+    }
+    mpos = sizeof(unsigned long long) * ntohl(statMsg->statCounters);
+    if (count == 0) {
+      if (0 == strcmp(name,
+                     "Uptime (seconds)"))
+       PRINTF("%u\n",
+              (unsigned int) 
+              ((cronTime(NULL) - ntohll(statMsg->startTime))/
+               cronSECONDS));
+    }
+    for (i=0; i<ntohl(statMsg->statCounters); i++) {
+      if 
(mpos+strlen(&((char*)(((STATS_CS_MESSAGE_GENERIC*)statMsg)->values))[mpos])+1 
> 
+         ntohs(statMsg->header.size) - sizeof(STATS_CS_MESSAGE)) {
+       LOG(LOG_WARNING,
+           "WARNING: received malformed stats message (%d > %d)\n",
+           
mpos+strlen(&((char*)(((STATS_CS_MESSAGE_GENERIC*)statMsg)->values))[mpos])+1,
+           ntohs(statMsg->header.size)-sizeof(STATS_CS_MESSAGE));
+       break; /* out of bounds! */      
+      }
+      if (0 == strcmp(name,
+                     
&((char*)(((STATS_CS_MESSAGE_GENERIC*)statMsg)->values))[mpos]))
+       PRINTF("%llu\n",
+              ntohll(((STATS_CS_MESSAGE_GENERIC*)statMsg)->values[i]));
+      mpos += 
strlen(&((char*)(((STATS_CS_MESSAGE_GENERIC*)statMsg)->values))[mpos])+1;
+    }    
+    count += ntohl(statMsg->statCounters);
+  } /* end while */
+  FREE(statMsg);
+  return OK;
+}
+
+/**
+ * Queries the server for what protocol messages are
+ * supported and prints a list of them
+ * @param stream where to print the statistics
+ * @param sock the socket to use 
+ * @return OK on success, SYSERR on error
+ **/
+int requestAndPrintProtocols(GNUNET_TCP_SOCKET * sock) {
+  STATS_CS_GET_MESSAGE_SUPPORTED csStatMsg;
+  int i = 0;
+  int supported = NO;
+  const char *name = NULL;
+  
+  csStatMsg.header.size 
+    = htons(sizeof(STATS_CS_GET_MESSAGE_SUPPORTED));  
+  PRINTF("Supported Peer to Peer messages:\n");
+  csStatMsg.header.tcpType
+    = htons(STATS_CS_PROTO_GET_P2P_MESSAGE_SUPPORTED);
+  for (i=0;i<65536;i++) {
+    csStatMsg.tcpType = htons(i);
+    
+    if (SYSERR == writeToSocket(sock, &csStatMsg.header)) {
+      PRINTF("Error sending request for p2p protocol "
+            "status to gnunetd.\n");
+      return SYSERR;
+    }
+    if (SYSERR == readTCPResult(sock, &supported)) {
+      PRINTF("Error reading p2p protocol status to gnunetd.\n");
+      return SYSERR;
+    }
+    
+    if (supported == YES) {
+      PRINTF("\t%d", i);
+      name = p2pMessageName( i );
+      if (name != NULL) {
+       PRINTF("\t(%s)", name);
+      }
+      PRINTF("\n");
+    }
+  }
+  PRINTF("Supported Client Server messages:\n");
+  csStatMsg.header.tcpType
+    = htons(STATS_CS_PROTO_GET_CS_MESSAGE_SUPPORTED);
+  for (i=0;i<65536;i++) {
+    csStatMsg.tcpType = htons(i);   
+    if (SYSERR == writeToSocket(sock, &csStatMsg.header)) {
+      PRINTF("Error sending request for client-server "
+            "protocol status to gnunetd.\n");
+      return SYSERR;
+    }
+    if (SYSERR == readTCPResult(sock, &supported)) {
+      PRINTF("Error reading client-server protocol "
+            "status to gnunetd.\n");
+      return SYSERR;
+    }    
+    if (supported == YES) {
+      PRINTF("\t%d", i);
+      name = csMessageName( i );
+      if (name != NULL) {
+       PRINTF("\t(%s)", name);
+      }
+      PRINTF("\n");
+    }
+  }
+  return OK;
+}
+
+
+/* end of get-stats.c */

Added: freeway/src/org/gnu/freeway/protocol/testbed/get-stats.h
===================================================================
--- freeway/src/org/gnu/freeway/protocol/testbed/get-stats.h    2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/testbed/get-stats.h    2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,4 @@
+int requestAndPrintStatistics(GNUNET_TCP_SOCKET *sock);
+int requestAndPrintStatistic(GNUNET_TCP_SOCKET *sock,
+                            char * name);
+int requestAndPrintProtocols(GNUNET_TCP_SOCKET * sock);

Added: freeway/src/org/gnu/freeway/protocol/testbed/socket.c
===================================================================
--- freeway/src/org/gnu/freeway/protocol/testbed/socket.c       2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/testbed/socket.c       2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,173 @@
+/*
+     This file is part of GNUnet.
+     (C) 2003 Christian Grothoff (and other contributing authors)
+
+     GNUnet is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with GNUnet; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file applications/testbed/socket.c 
+ * @brief socket operation for communication between the testbed processes
+ * @author Ronaldo Alves Ferreira
+ * @author Christian Grothoff
+ * @author Murali Krishan Ramanathan
+ **/
+
+#include "platform.h"
+#include "socket.h"
+#include "testbed.h"
+
+/* ********* helper methods to implement primitive commands *********** */
+
+#define DEBUG NO
+
+int sock = -1;
+
+static void writeAll(int fd, 
+                    char * data, 
+                    unsigned int len) {
+  unsigned int pos;
+  int ret;
+  pos = 0;
+  ret = 42;
+  while ( (ret > 0) && (pos < len) ) {
+    ret = WRITE(sock, &data[pos], len-pos);
+    if (ret > 0) 
+      pos += ret;
+    else
+      LOG(LOG_WARNING,
+         "WARNING: write to socket failed: %s\n",
+         STRERROR(errno));
+  }
+}
+
+/* ************** socket operations (IPC) ************* */
+
+typedef struct {
+  unsigned int size;
+  unsigned int type;
+  char data[0];
+} ExchangeBuffer;
+
+void socketSend(unsigned int len, 
+               unsigned int type, 
+               void * data) {
+  ExchangeBuffer * buf;
+  unsigned int tlen;
+#if DEBUG
+  unsigned int i;
+#endif
+  
+  tlen = len + sizeof(ExchangeBuffer); 
+  buf = MALLOC(tlen);
+  if (len > 0)
+    memcpy(&buf->data[0], data, len);
+  buf->size = htonl(tlen);
+  buf->type = htonl(type);
+  
+#if DEBUG
+  printf("Sending %u bytes: ", tlen);
+  for (i=0;i<tlen;i++)
+    printf("%d ", ((char*)buf)[i]);
+  printf("\n");
+#endif
+  
+  writeAll(sock, (void*)buf, tlen);
+  FREE(buf);
+}
+
+/**
+ * Read a message from the socket.
+ * @return the type of the message
+ */
+unsigned int readSocket(char ** rbuf, 
+                       unsigned int * len) {
+  unsigned int type;  
+  ExchangeBuffer * buf;
+  unsigned int pos;
+  int ret;
+  unsigned int mlen;
+  
+#if DEBUG
+  unsigned int i;
+#endif  
+  
+  pos = 0;
+  ret = 42;
+  while ( (pos < sizeof(unsigned int)) && (ret >= 0) ) {
+    ret = READ(sock, &((char*)&mlen)[pos], sizeof(unsigned int)-pos);
+    if (ret >= 0) 
+      pos += ret;
+    else
+      errexit("FATAL: socket protocol error (%s)\n", STRERROR(errno));
+  }
+  mlen = ntohl(mlen);
+  
+  buf = MALLOC(mlen);
+  while ( (pos < mlen) && (ret >= 0) ) {
+    ret = READ(sock, &((char*)buf)[pos], mlen-pos);
+    if (ret >= 0) 
+      pos += ret;
+    else
+      errexit("FATAL: socket protocol error (%s)\n", STRERROR(errno));
+  }
+  
+#if DEBUG
+  buf->size = htonl(mlen);
+  printf("Reading %u bytes: ", mlen);
+  for (i=0;i<mlen;i++)
+    printf("%d ", ((char*)buf)[i]);
+  printf("\n");
+#endif
+  
+  type = ntohl(buf->type);
+  *rbuf = MALLOC(mlen - sizeof(ExchangeBuffer));
+  memcpy(*rbuf, &buf->data[0], mlen - sizeof(ExchangeBuffer));
+  FREE(buf);
+  *len = mlen - sizeof(ExchangeBuffer);
+  return type;
+}
+
+/**
+ * Print a message in the testbed-shell.
+ */
+void PRINTF(char * fmt, ...) {  
+  va_list      args;  
+  int n;
+  int size = 1024;
+  char * p;
+  
+  p = MALLOC(size);
+  while (1) {
+    /* Try to print in the allocated space. */
+    va_start(args, fmt);
+    n = vsnprintf(p, size, fmt, args);
+    va_end(args);
+    /* If that worked, return the string. */
+    if ( (n > -1) && (n < size) ) {
+      socketSend(n, SOCKET_PRINTF, p);
+      FREE(p);
+      return;
+    }
+    /* Else try again with more space. */
+    if (n > -1)    /* glibc 2.1 */
+      GROW(p, size, n+1); /* precisely what is needed */
+    else           /* glibc 2.0 */
+      GROW(p, size, size*2);  /* twice the old size */
+  }
+}
+
+/* end of socket communication primitives */

Added: freeway/src/org/gnu/freeway/protocol/testbed/socket.h
===================================================================
--- freeway/src/org/gnu/freeway/protocol/testbed/socket.h       2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/testbed/socket.h       2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,57 @@
+/*
+     This file is part of GNUnet.
+     (C) 2003 Christian Grothoff (and other contributing authors)
+
+     GNUnet is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with GNUnet; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @author Ronaldo Alves Ferreira
+ * @author Christian Grothoff
+ * @author Murali Krishna Ramanathan
+ * @file applications/testbed/socket.h
+ **/
+#ifndef TESTBED_SOCKET_H
+#define TESTBED_SOCKET_H
+
+
+/* the server or client socket for
+   communication */
+extern int sock;
+
+/* types for socketSend / read */
+
+#define SOCKET_PRINTF 0
+#define SOCKET_RETVAL 1
+#define SOCKET_BEGIN_COMMAND 2
+#define SOCKET_ADD_ARGUMENT 3
+#define SOCKET_END_COMMAND 4
+
+void socketSend(unsigned int len,
+               unsigned int type,
+               void * data);
+ 
+/**
+ * Read a message from the socket.
+ * @return the type of the message
+ */
+unsigned int readSocket(char ** rbuf,
+                       unsigned int * len);
+ 
+void PRINTF(char * fmt,
+           ...);
+
+#endif

Added: freeway/src/org/gnu/freeway/protocol/testbed/startup.php3
===================================================================
--- freeway/src/org/gnu/freeway/protocol/testbed/startup.php3   2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/testbed/startup.php3   2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,14 @@
+<?php
+global $trusted;
+include("connect.php3");
+if (!$connection) {
+  echo "<HTML><HEAD><TITLE>GNUnet-testbed registration: 
startup</TITLE></HEAD><BODY>";
+  echo "Database is down. Can not register peers.";
+  echo "</body></html>";
+  die(-1);
+}
+
+$query = "INSERT INTO peers VALUES(\"$trusted\", \"$port\", \"$secure\", \"" . 
$_SERVER['REMOTE_ADDR'] . "\", NOW());";
+mysql_query($query, $connection);
+
+?>
\ No newline at end of file

Added: freeway/src/org/gnu/freeway/protocol/testbed/testbed.c
===================================================================
--- freeway/src/org/gnu/freeway/protocol/testbed/testbed.c      2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/testbed/testbed.c      2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,1419 @@
+/*
+     This file is part of GNUnet.
+     (C) 2003, 2004 Christian Grothoff (and other contributing authors)
+
+     GNUnet is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with GNUnet; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * Testbed CORE.  This is the code that is plugged
+ * into the GNUnet core to enable transport profiling.
+ *
+ * @author Ronaldo Alves Ferreira
+ * @author Christian Grothoff
+ * @author Murali Khrisna Ramanathan
+ * @file applications/testbed/testbed.c
+ **/
+
+
+#include "testbed.h"
+#include "platform.h"
+
+#include <sys/types.h>
+
+#define DEBUG_TESTBED YES
+
+#define GET_COMMAND "GET %s/%s.php3?trusted=%s&port=%s&secure=%s 
HTTP/1.0\r\n\r\n"
+#define HTTP_URL "http://";
+
+/* */
+static CoreAPIForApplication * coreAPI = NULL;
+
+static void sendAcknowledgement(ClientHandle client,
+                               int ack) {
+  if (OK != coreAPI->sendTCPResultToClient(client, ack)) {
+    LOG(LOG_WARNING,
+       "WARNING: could not send ack back to client.\n");
+  }
+}
+
+/**
+ * Handler that is called for "message not understood" cases.
+ */
+static void tb_undefined(ClientHandle client,
+                        TESTBED_CS_MESSAGE * msg) {
+  LOG(LOG_WARNING,
+      "WARNING: received unknown testbed message of type %u\n",
+      ntohl(msg->msgType));
+}
+
+/**
+ * Connect to another peer.
+ */
+static void tb_ADD_PEER(ClientHandle client, 
+                       TESTBED_CS_MESSAGE * msg) {
+  p2p_HEADER noise;
+  TESTBED_ADD_PEER_MESSAGE * hm 
+    = (TESTBED_ADD_PEER_MESSAGE*) msg;
+  
+  LOG(LOG_DEBUG, 
+      "DEBUG: tb_ADD_PEER\n");
+  if (sizeof(TESTBED_ADD_PEER_MESSAGE) >
+      ntohs(msg->header.size) ) {
+    LOG(LOG_ERROR,
+       "ERROR: size of ADD_PEER message is too short.  Ignoring.\n");
+    return;
+  }
+  if (HELO_Message_size(&hm->helo) !=
+      ntohs(msg->header.size) - sizeof(TESTBED_CS_MESSAGE) ) {
+    LOG(LOG_ERROR,
+       "ERROR: size of ADD_PEER message is wrong.  Ignoring.\n");
+    return;
+  }
+  
+  coreAPI->bindAddress(&hm->helo);
+  noise.size = htons(sizeof(p2p_HEADER));
+  noise.requestType = htons(p2p_PROTO_NOISE);
+  coreAPI->sendToNode(&hm->helo.senderIdentity,
+                     &noise,
+                     EXTREME_PRIORITY, 
+                     0);
+  sendAcknowledgement(client, OK);
+}
+
+/**
+ * Disconnect from another peer.
+ */
+static void tb_DEL_PEER(ClientHandle client,
+                       TESTBED_DEL_PEER_MESSAGE * msg) {
+  coreAPI->disconnectFromPeer(&msg->host);
+  sendAcknowledgement(client, OK);
+}
+
+/**
+ * Disconnect from all other peers.
+ */
+static void tb_DEL_ALL_PEERS(ClientHandle client,
+                            TESTBED_DEL_ALL_PEERS_MESSAGE * msg) {
+  coreAPI->disconnectPeers(); 
+  sendAcknowledgement(client, OK);
+}
+
+/**
+ * Get a HELO message for this peer.
+ */
+static void tb_GET_HELO(ClientHandle client,
+                       TESTBED_GET_HELO_MESSAGE * msg) {
+  HELO_Message * helo;
+  unsigned int proto = ntohs(msg->proto);
+  
+  if (SYSERR == coreAPI->identity2Helo(coreAPI->myIdentity, 
+                                      proto,
+                                      NO, 
+                                      &helo)) {
+    LOG(LOG_WARNING, 
+       "WARNING: TESTBED could not generate HELO"      \
+       "message for protocol %u\n",
+       proto);
+    sendAcknowledgement(client, SYSERR);
+  } else {
+    TESTBED_HELO_MESSAGE * reply 
+      = MALLOC(ntohs(helo->header.size) + sizeof(TESTBED_CS_MESSAGE));
+    reply->header.header.size 
+      = htons(ntohs(helo->header.size) + sizeof(TESTBED_CS_MESSAGE));
+    reply->header.header.tcpType 
+      = htons(TESTBED_CS_PROTO_REPLY);
+    reply->header.msgType
+      = htonl(TESTBED_HELO_RESPONSE);
+    memcpy(&reply->helo, 
+          helo, 
+          ntohs(helo->header.size));
+    coreAPI->sendToClient(client,
+                         &reply->header.header);
+    LOG(LOG_DEBUG,
+       "DEBUG: tb_GET_HELO: returning from writeToSocket\n");
+    FREE(helo);
+    FREE(reply);
+  }    
+}
+
+/**
+ * Set a trust value.
+ */
+static void tb_SET_TVALUE(ClientHandle client,
+                         TESTBED_SET_TVALUE_MESSAGE * msg) {
+  int trust, chg;
+  
+  trust = ntohl(msg->trust);
+  chg = coreAPI->changeTrust(&msg->otherPeer, trust);
+  if (chg != trust) {
+    LOG(LOG_WARNING,
+       "WARNING: trust change=%d, required=%d\n",
+       chg,
+       trust);
+  }
+  sendAcknowledgement(client, OK);
+}      
+                 
+/**
+ * Get a trust value.
+ */
+static void tb_GET_TVALUE(ClientHandle client,
+                         TESTBED_GET_TVALUE_MESSAGE * msg) {    
+  unsigned int trust;
+  
+  trust = coreAPI->getTrust(&msg->otherPeer);
+  sendAcknowledgement(client, trust);
+}      
+
+/**
+ * Change the bandwidth limitations.
+ */
+static void tb_SET_BW(ClientHandle client,
+                     TESTBED_SET_BW_MESSAGE * msg) {
+  LOG(LOG_DEBUG,
+      "DEBUG: gnunet-testbed: tb_SET_BW\n");
+  setConfigurationInt("LOAD",
+                     "MAXNETDOWNBPSTOTAL", 
+                     ntohl(msg->in_bw));
+  setConfigurationInt("LOAD",
+                     "MAXNETUPBPSTOTAL", 
+                     ntohl(msg->out_bw));
+  triggerGlobalConfigurationRefresh();
+  sendAcknowledgement(client, OK);
+}                
+
+/**
+ * Load an application module.
+ */
+static void tb_LOAD_MODULE(ClientHandle client,
+                          TESTBED_CS_MESSAGE * msg) {
+  unsigned short size;
+  char * name;
+  int ok;
+  
+  size = ntohs(msg->header.size);
+  if (size <= sizeof(TESTBED_CS_MESSAGE) ) {
+    LOG(LOG_WARNING,
+       "WARNING: received invalid LOAD_MODULE message\n");
+    return;
+  } 
+  
+  if (! testConfigurationString("TESTBED",
+                               "ALLOW_MODULE_LOADING",
+                               "YES")) {
+    sendAcknowledgement(client, SYSERR);
+    return;
+  }
+  
+  name = STRNDUP(&((TESTBED_LOAD_MODULE_MESSAGE_GENERIC*)msg)->modulename[0],
+                size - sizeof(TESTBED_CS_MESSAGE));
+  if (strlen(name) == 0) {
+    LOG(LOG_WARNING,
+       "WARNING: received invalid LOAD_MODULE" \
+       "message (empty module name)\n");
+    return;
+  }
+  ok = coreAPI->loadApplicationModule(name);
+  if (ok != OK)
+    LOG(LOG_WARNING,
+       "WARNING: loading module failed.  Notifying client.\n");
+  FREE(name);
+  sendAcknowledgement(client, ok);
+}
+
+/**
+ * Unload an application module.
+ */
+static void tb_UNLOAD_MODULE(ClientHandle client,
+                            TESTBED_CS_MESSAGE * msg) {
+  unsigned short size;
+  char * name;
+  int ok;
+  
+  size = ntohs(msg->header.size);
+  if (size <= sizeof(TESTBED_CS_MESSAGE) ) {
+    LOG(LOG_WARNING,
+       "WARNING: received invalid UNLOAD_MODULE message\n");
+    return;
+  }
+  
+  if (! testConfigurationString("TESTBED",
+                               "ALLOW_MODULE_LOADING",
+                               "YES")) {      
+    sendAcknowledgement(client, SYSERR);
+    return;
+  }
+  
+  name = STRNDUP(&((TESTBED_UNLOAD_MODULE_MESSAGE_GENERIC*)msg)->modulename[0],
+                size - sizeof(TESTBED_CS_MESSAGE));
+  if (strlen(name) == 0) {
+    LOG(LOG_WARNING,
+       "WARNING: received invalid UNLOAD_MODULE"       \
+       "message (empty module name)\n");
+    return;
+  }
+  ok = coreAPI->unloadApplicationModule(name);
+  if (ok != OK)
+    LOG(LOG_WARNING,
+       "WARNING: unloading module failed.  Notifying client.\n");
+  FREE(name);
+  sendAcknowledgement(client, ok);
+}
+
+/**
+ * Set the reliability of the inbound and outbound transfers for this
+ * peer (by making it drop a certain percentage of the messages at
+ * random).
+ */
+static void tb_SET_LOSS_RATE(ClientHandle client,
+                            TESTBED_SET_LOSS_RATE_MESSAGE * msg) {
+  coreAPI->setPercentRandomInboundDrop
+    (ntohl(msg->percentageLossInbound));
+  coreAPI->setPercentRandomOutboundDrop
+    (ntohl(msg->percentageLossOutbound));
+  sendAcknowledgement(client, OK);
+}
+
+/**
+ * Set the reliability of the inbound and outbound transfers for this
+ * peer (by making it drop a certain percentage of the messages at
+ * random).
+ */
+static void tb_DISABLE_AUTOCONNECT(ClientHandle client,
+                                  TESTBED_DISABLE_AUTOCONNECT_MESSAGE * msg) {
+  FREENONNULL(setConfigurationString("GNUNETD",
+                                    "DISABLE-AUTOCONNECT",
+                                    "YES"));
+  triggerGlobalConfigurationRefresh();
+  sendAcknowledgement(client, OK);
+}
+
+/**
+ * Set the reliability of the inbound and outbound transfers for this
+ * peer (by making it drop a certain percentage of the messages at
+ * random).
+ */
+static void tb_ENABLE_AUTOCONNECT(ClientHandle client,
+                                 TESTBED_ENABLE_AUTOCONNECT_MESSAGE * msg) {
+  FREENONNULL(setConfigurationString("GNUNETD",
+                                    "DISABLE-AUTOCONNECT",
+                                    "NO"));
+  triggerGlobalConfigurationRefresh();
+  sendAcknowledgement(client, OK);
+}
+
+/**
+ * Set the reliability of the inbound and outbound transfers for this
+ * peer (by making it drop a certain percentage of the messages at
+ * random).
+ */
+static void tb_DISABLE_HELO(ClientHandle client,
+                           TESTBED_DISABLE_HELO_MESSAGE * msg) {
+  FREENONNULL(setConfigurationString("NETWORK",
+                                    "DISABLE-ADVERTISEMENTS",
+                                    "YES"));
+  FREENONNULL(setConfigurationString("NETWORK",
+                                    "HELOEXCHANGE",
+                                    "NO"));
+  triggerGlobalConfigurationRefresh();
+  sendAcknowledgement(client, OK);
+}
+
+/**
+ * Set the reliability of the inbound and outbound transfers for this
+ * peer (by making it drop a certain percentage of the messages at
+ * random).
+ */
+static void tb_ENABLE_HELO(ClientHandle client,
+                          TESTBED_ENABLE_HELO_MESSAGE * msg) {
+  FREENONNULL(setConfigurationString("NETWORK",
+                                    "DISABLE-ADVERTISEMENTS",
+                                    "NO"));
+  FREENONNULL(setConfigurationString("NETWORK",
+                                    "HELOEXCHANGE",
+                                    "YES"));
+  triggerGlobalConfigurationRefresh();
+  sendAcknowledgement(client, OK);
+}
+
+/**
+ * Allow only certain peers to connect.
+ */
+static void tb_ALLOW_CONNECT(ClientHandle client,
+                            TESTBED_ALLOW_CONNECT_MESSAGE * msg) {
+  char * value;
+  unsigned short size;
+  unsigned int count;
+  unsigned int i;
+  HexName hex;
+
+  size = ntohs(msg->header.header.size);
+  if (size <= sizeof(TESTBED_CS_MESSAGE) ) {
+    LOG(LOG_WARNING,
+       "WARNING: received invalid ALLOW_CONNECT message\n");
+    return;
+  }
+  count = (size - sizeof(TESTBED_CS_MESSAGE)) / sizeof(HostIdentity);
+  if (count * sizeof(HostIdentity) + sizeof(TESTBED_CS_MESSAGE) != size) {
+    LOG(LOG_WARNING,
+       "WARNING: received invalid ALLOW_CONNECT message\n");
+    return;
+  }
+  if (count == 0) {
+    value = NULL;
+  } else {
+    value = MALLOC(count * sizeof(HostIdentity)*2 + 1);
+    value[0] = '\0';
+    for (i=0;i<count;i++) {
+      
hash2hex(&((TESTBED_ALLOW_CONNECT_MESSAGE_GENERIC*)msg)->peers[i].hashPubKey,
+              &hex);
+      strcat(value, (char*)&hex);
+    }
+  }
+  FREENONNULL(setConfigurationString("GNUNETD",
+                                    "LIMIT-ALLOW",
+                                    value));
+  FREENONNULL(value);
+  triggerGlobalConfigurationRefresh();
+  sendAcknowledgement(client, OK);
+}
+
+/**
+ * Deny certain peers the right to connect.
+ */
+static void tb_DENY_CONNECT(ClientHandle client,
+                           TESTBED_DENY_CONNECT_MESSAGE * msg) {
+  char * value;
+  unsigned short size;
+  unsigned int count;
+  unsigned int i;
+  HexName hex;
+
+  size = ntohs(msg->header.header.size);
+  if (size <= sizeof(TESTBED_CS_MESSAGE) ) {
+    LOG(LOG_WARNING,
+       "WARNING: received invalid DENY_CONNECT message\n");
+    return;
+  }
+  count = (size - sizeof(TESTBED_CS_MESSAGE)) / sizeof(HostIdentity);
+  if (count * sizeof(HostIdentity) + sizeof(TESTBED_CS_MESSAGE) != size) {
+    LOG(LOG_WARNING,
+       "WARNING: received invalid DENY_CONNECT message\n");
+    return;
+  }
+  if (count == 0) {
+    value = NULL;
+  } else {
+    value = MALLOC(count * sizeof(HostIdentity)*2 + 1);
+    value[0] = '\0';
+    for (i=0;i<count;i++) {
+      
hash2hex(&((TESTBED_DENY_CONNECT_MESSAGE_GENERIC*)msg)->peers[i].hashPubKey,
+              &hex);
+      strcat(value, (char*)&hex);
+    }
+  }
+  FREENONNULL(setConfigurationString("GNUNETD",
+                                    "LIMIT-DENY",
+                                    value));
+  FREENONNULL(value);
+  triggerGlobalConfigurationRefresh();
+  sendAcknowledgement(client, OK);
+}
+
+/**
+ * Information about processes that we have forked off.
+ */
+typedef struct {
+  /** the unique identifier of the PI */
+  unsigned int uid;
+  /** the PID of the process */
+  pid_t pid; 
+  /** stdout and stderr of the process */
+  int outputPipe;
+  /** thread that reads the output of the process */
+  PTHREAD_T reader;
+  /** how many bytes of output did the process produce? */
+  int outputSize;
+  /** the output of the process */ 
+  char * output; 
+  /** did the process exit? (YES/NO) */
+  int hasExited; 
+  /** if the process did exit, what was the status? (or errno of execve) */
+  int exitStatus; 
+  /** semaphore used to communicate thread-start */
+  Semaphore * sem;
+  /** Client responsible for this process 
+      (if that client disconnects, the process
+      will be killed!) */
+  ClientHandle client;
+  /** arguments for exec */
+  char ** argv;
+  int argc;
+} ProcessInfo;
+
+static unsigned int uidCounter = 0;
+
+/**
+ * The process table.
+ */
+static ProcessInfo ** pt = NULL;
+
+/**
+ * Number of entries in the process table.
+ */
+static int ptSize = 0;
+
+/**
+ * Lock for accessing the PT
+ */
+static Mutex lock;
+
+/**
+ * Thread that captures the output from a child-process
+ * and stores it in the process info.
+ */
+static int pipeReaderThread(ProcessInfo * pi) {
+  int ret = 1;
+  char * buffer;
+  int pos;
+  int fd[2];
+  int i;
+  char * dir;
+  char * tmp;
+
+  if (0 != PIPE(fd)) {
+    LOG(LOG_WARNING,
+       "WARNING: could not create pipe: %s.\n",
+       strerror(errno));
+    pi->pid = SYSERR;
+    SEMAPHORE_UP(pi->sem);
+    MUTEX_UNLOCK(&lock);
+    return -1;
+  }
+  LOG(LOG_DEBUG,
+      "DEBUG: exec'ing: %s with %d arguments\n",
+      pi->argv[0],
+      pi->argc-1);
+  for (i=1;i<pi->argc;i++)
+    LOG(LOG_DEBUG,
+       "DEBUG: exec argument %d is %s\n",
+       i, pi->argv[i]);
+  tmp = getConfigurationString("TESTBED",
+                              "UPLOAD-DIR");
+  if (tmp == NULL)
+    tmp = STRDUP("/");
+  dir = expandFileName(tmp);
+  mkdirp(dir);
+  FREE(tmp);
+
+  MUTEX_LOCK(&lock);
+  pi->pid = fork();
+  if (pi->pid == 0) {
+    /* make pipe stdout/stderr */
+    
+    CLOSE(fd[0]);
+    CLOSE(1);
+    CLOSE(2);
+    if (-1 == dup2(fd[1], 1))
+      LOG(LOG_ERROR,
+         "ERROR: could not dup2 pipe to be stdout (%s)!\n",
+         strerror(errno));
+    if (-1 == dup2(fd[1], 2))
+      LOG(LOG_ERROR,
+         "ERROR: could not dup2 pipe to be stdout (%s)!\n",
+         strerror(errno));
+    
+    CLOSE(fd[1]); 
+    CHDIR(dir);
+    FREE(dir);
+    execvp(pi->argv[0],
+          &pi->argv[0]);
+    LOG(LOG_ERROR,
+       "ERROR: execvp %s failed: %s\n",
+       pi->argv[0],
+       strerror(errno));
+    fprintf(stderr,
+           "ERROR: execvp %s failed: %s\n",
+           pi->argv[0],
+           strerror(errno));
+    exit(errno);
+  }
+  FREE(dir);
+  CLOSE(fd[1]);
+  for (pos=0;pos<pi->argc;pos++)
+    FREE(pi->argv[pos]);
+  FREE(pi->argv);
+  if (pi->pid == -1) {
+    CLOSE(fd[0]);
+    SEMAPHORE_UP(pi->sem);
+    MUTEX_UNLOCK(&lock);
+    return -1;
+  }
+  pi->uid = uidCounter++;
+  pi->outputPipe = fd[0];
+  pi->outputSize = 0;
+  pi->output = NULL;
+  pi->hasExited = NO;
+  pi->exitStatus = 0;
+
+  GROW(pt,
+       ptSize,
+       ptSize+1);
+  pt[ptSize-1] = pi;  
+  SEMAPHORE_UP(pi->sem);
+  MUTEX_UNLOCK(&lock);
+
+#define PRT_BUFSIZE 65536
+  buffer = MALLOC(PRT_BUFSIZE);
+  while (ret > 0) {
+    ret = read(pi->outputPipe,
+              buffer,
+              PRT_BUFSIZE);
+    if (ret <= 0)
+      break;
+    MUTEX_LOCK(&lock);
+    if (pi->outputSize == -1) {
+      MUTEX_UNLOCK(&lock);
+      break;
+    }
+    GROW(pi->output,
+        pi->outputSize,
+        pi->outputSize + ret);
+    memcpy(&pi->output[pi->outputSize-ret],
+          buffer,
+          ret);
+    MUTEX_UNLOCK(&lock);
+  }
+  CLOSE(pi->outputPipe);
+  MUTEX_LOCK(&lock);
+
+  ret = waitpid(pi->pid,
+               &pi->exitStatus,
+               0);
+  if (ret != pi->pid) {
+    LOG(LOG_WARNING,
+       "WARNING: waidpid failed: %d: %s\n",
+       ret,
+       strerror(errno));
+    pi->exitStatus = errno;
+  }
+  pi->hasExited = YES;    
+  MUTEX_UNLOCK(&lock);
+  return 0;
+}
+
+/**
+ * Execute a command.
+ */
+static void tb_EXEC(ClientHandle client,
+                   TESTBED_CS_MESSAGE * msg) {
+  int argc2;
+  unsigned short size;
+  unsigned int uid;
+  int pos;
+  TESTBED_EXEC_MESSAGE * emsg;
+  ProcessInfo * pi;
+  char * clientConfig;
+  char * mainName;
+
+  emsg = (TESTBED_EXEC_MESSAGE*)msg;
+  size = htons(msg->header.size);
+  if ( (size <= sizeof(TESTBED_CS_MESSAGE)) ||
+       
(((TESTBED_EXEC_MESSAGE_GENERIC*)emsg)->commandLine[size-sizeof(TESTBED_CS_MESSAGE)-1]
 != '\0') ) {
+    LOG(LOG_WARNING,
+       "WARNING: received invalid EXEC message: %s.\n",
+       (size <= sizeof(TESTBED_CS_MESSAGE)) 
+       ? "size smaller or equal than TESTBED_CS_MESSAGE"
+       : "last character in command line is not zero-terminator"); 
+    sendAcknowledgement(client, SYSERR);
+    return;
+  }
+  size -= sizeof(TESTBED_CS_MESSAGE);
+  pi = MALLOC(sizeof(ProcessInfo));
+  pi->argc = 0;
+  for (pos=0;pos<size;pos++)
+    if (((TESTBED_EXEC_MESSAGE_GENERIC*)emsg)->commandLine[pos] == '\0')
+      pi->argc++;
+  mainName = STRDUP(&((TESTBED_EXEC_MESSAGE_GENERIC*)emsg)->commandLine[0]);
+  clientConfig = NULL;
+  if (0 == strncmp("gnunet",
+                  mainName,
+                  strlen("gnunet"))) 
+    clientConfig = getConfigurationString("TESTBED",
+                                         "CLIENTCONFIG"); 
+  if (clientConfig != NULL)
+    pi->argc +=2;
+  argc2 = pi->argc;
+  pi->argv = MALLOC(sizeof(char*)*(pi->argc+1));
+  pi->argv[0] = mainName;
+  pi->argv[pi->argc] = NULL; /* termination! */
+  for (pos=size-2;pos>=0;pos--)
+    if (((TESTBED_EXEC_MESSAGE_GENERIC*)emsg)->commandLine[pos] == '\0')
+      pi->argv[--argc2] = 
STRDUP(&((TESTBED_EXEC_MESSAGE_GENERIC*)emsg)->commandLine[pos+1]);
+  if (clientConfig != NULL) {
+    pi->argv[--argc2] = clientConfig;
+    pi->argv[--argc2] = STRDUP("-c");
+  }
+  MUTEX_LOCK(&lock);
+
+  pi->sem = SEMAPHORE_NEW(0);
+  if (0 != PTHREAD_CREATE(&pi->reader,
+                         (PThreadMain) &pipeReaderThread,
+                         pi,
+                         8*1024)) {
+    LOG(LOG_WARNING,
+       "WARNING: pthread_create failed: %s\n",
+       strerror(errno));
+    SEMAPHORE_FREE(pi->sem);
+    MUTEX_UNLOCK(&lock);
+    FREE(pi);
+    sendAcknowledgement(client, SYSERR);
+    return;
+  }
+  MUTEX_UNLOCK(&lock);
+  SEMAPHORE_DOWN(pi->sem);
+  SEMAPHORE_FREE(pi->sem);
+  uid = pi->uid;
+  if (uid == -1) {
+    LOG(LOG_WARNING,
+       "WARNING: fork failed: %s\n",
+       strerror(errno));
+    FREE(pi);
+    uid = SYSERR;
+  }
+  sendAcknowledgement(client, uid);
+}
+
+/**
+ * Send a signal to a process or obtain the status of the
+ * process on exit.
+ */
+static void tb_SIGNAL(ClientHandle client,
+                     TESTBED_SIGNAL_MESSAGE * msg) {
+  int ret;
+  int i;
+  unsigned int uid;
+  int sig;
+  void * unused;
+  ProcessInfo * pi;
+
+  ret = SYSERR;
+  uid = ntohl(msg->pid);
+  sig = ntohl(msg->signal);
+  MUTEX_LOCK(&lock);
+  for (i=0;i<ptSize;i++) {
+    pi = pt[i];
+    if (pi->uid != uid) 
+      continue;
+    if (sig == -1) {   
+      if (pi->hasExited == NO) {
+       ret = SYSERR;
+      } else { 
+       ret = WEXITSTATUS(pi->exitStatus);
+       /* free resources... */
+       GROW(pi->output,
+            pi->outputSize,
+            0);
+       PTHREAD_JOIN(&pi->reader,
+                    &unused);
+       FREE(pi);
+       pt[i] = pt[ptSize-1];
+       GROW(pt,
+            ptSize,
+            ptSize-1);
+      }
+    } else {
+      if (pi->hasExited == NO) {      
+       if (0 == kill(pi->pid, 
+                     ntohl(msg->signal)))
+         ret = OK;
+       else
+         LOG(LOG_WARNING,
+             "WARNING: could not send signal to process %d: %s\n",
+             pi->pid,
+             strerror(errno));
+      }
+    }
+    break;
+  }
+  MUTEX_UNLOCK(&lock);
+  sendAcknowledgement(client, ret);
+}
+
+/**
+ * Get the output of a process.
+ */
+static void tb_GET_OUTPUT(ClientHandle client,
+                         TESTBED_GET_OUTPUT_MESSAGE * msg) {
+  int i;
+  unsigned int uid;
+
+  uid = ntohl(msg->pid);
+  MUTEX_LOCK(&lock);
+  for (i=0;i<ptSize;i++) {
+    ProcessInfo * pi;
+
+    pi = pt[i];
+    if (pi->uid == uid) {
+      unsigned int pos;
+      TESTBED_OUTPUT_REPLY_MESSAGE * msg;
+
+      msg = MALLOC(65532);
+      msg->header.header.tcpType 
+       = htons(TESTBED_CS_PROTO_REPLY);
+      msg->header.msgType 
+       = htonl(TESTBED_OUTPUT_RESPONSE);
+      
+      sendAcknowledgement(client, pi->outputSize);
+      pos = 0;
+      while (pos < pi->outputSize) {   
+       unsigned int run = pi->outputSize - pos;
+       if (run > 65532 - sizeof(TESTBED_OUTPUT_REPLY_MESSAGE))
+         run = 65532 - sizeof(TESTBED_OUTPUT_REPLY_MESSAGE);
+       msg->header.header.size
+         = htons(run+sizeof(TESTBED_OUTPUT_REPLY_MESSAGE));
+       memcpy(&((TESTBED_OUTPUT_REPLY_MESSAGE_GENERIC*)msg)->data[0],
+              &pi->output[pos],
+              run);
+       coreAPI->sendToClient(client,
+                             &msg->header.header);
+       pos += run;
+      }
+      FREE(msg);
+      /* reset output buffer */
+      GROW(pi->output,
+          pi->outputSize,
+          0);
+      MUTEX_UNLOCK(&lock);
+      return;
+    }
+  } 
+  MUTEX_UNLOCK(&lock);
+  sendAcknowledgement(client, SYSERR);
+}
+
+/**
+ * The client is uploading a file to this peer.
+ */
+static void tb_UPLOAD_FILE(ClientHandle client,
+                          TESTBED_UPLOAD_FILE_MESSAGE * msg) {
+  int ack;
+  unsigned int size;
+  char * filename, *gnHome, *s;
+  char * end;
+  char * tmp;
+  FILE *outfile;
+  
+  LOG(LOG_DEBUG, 
+      "DEBUG: tb_UPLOAD_FILE\n");
+  if (sizeof(TESTBED_UPLOAD_FILE_MESSAGE) > ntohs(msg->header.header.size)) {
+    LOG(LOG_ERROR,
+       "ERROR: size of UPLOAD_FILE message is too short. Ignoring.\n");
+    sendAcknowledgement(client, SYSERR);
+    return;
+  }
+  end = &((char*)msg)[ntohs(msg->header.header.size)];
+  s = filename = ((TESTBED_UPLOAD_FILE_MESSAGE_GENERIC*)msg)->buf;
+  while ( (*s) && (s != end) ) {
+    if (*s == '.' && *(s+1) == '.') {
+      LOG(LOG_ERROR,
+         "ERROR: \'..\' is not allowed in file name (%s).\n",
+         filename);
+      return;
+    }
+    s++;
+  }
+  if (s == filename) {
+    /* filename empty, not allowed! */
+    LOG(LOG_ERROR,
+       "ERROR: empty filename for UPLOAD_FILE message is illegal!\n"); 
+    sendAcknowledgement(client, SYSERR);
+    return;
+  }
+  if (s == end) {
+    /* filename empty, not allowed! */
+    LOG(LOG_ERROR,
+       "ERROR: filename for UPLOAD_FILE message is not null-terminated 
(illegal!)\n"); 
+    sendAcknowledgement(client, SYSERR);
+    return;
+  }
+  tmp = getConfigurationString("TESTBED",
+                                   "UPLOAD-DIR");
+  if (tmp == NULL) {
+    LOG(LOG_ERROR,
+       "ERROR: upload refused!");
+    sendAcknowledgement(client, SYSERR);
+    return;
+  }
+  gnHome = expandFileName(tmp);
+  FREE(tmp);
+  mkdirp(gnHome);
+  
+  filename = MALLOC(strlen(filename) + strlen(gnHome) + 2); /*2: /, \0 */ 
+  strcpy(filename, gnHome);
+  strcat(filename, "/");
+  strncat(filename, 
+         ((TESTBED_UPLOAD_FILE_MESSAGE_GENERIC*)msg)->buf,
+         end - ((TESTBED_UPLOAD_FILE_MESSAGE_GENERIC*)msg)->buf);
+  if (htonl(msg->type) == TESTBED_FILE_DELETE) {
+    if (remove(filename) && errno != ENOENT) {
+      LOG(LOG_WARNING,
+         "WARNING: could not remove file %s (%s)\n",
+         filename,
+         STRERROR(errno));
+      ack = SYSERR;
+    } else
+      ack = OK;
+    FREE(filename);
+    sendAcknowledgement(client, ack);
+    return;
+  }   
+  if (htonl(msg->type) != TESTBED_FILE_APPEND) {
+    LOG(LOG_ERROR,
+       "ERROR: Invalid message received at UPLOAD_FILE.\n");
+    FREE(filename);
+    return;
+  }   
+  outfile = FOPEN(filename, "ab");
+  if (outfile == NULL) {
+    /* Send nack back to control point. */
+    LOG(LOG_ERROR, 
+       "ERROR: could not create file %s\n",
+       filename);
+    sendAcknowledgement(client, SYSERR);
+    FREE(filename);
+    return;
+  }
+  FREE(filename);
+  s    = ((TESTBED_UPLOAD_FILE_MESSAGE_GENERIC*)msg)->buf 
+    + strlen(((TESTBED_UPLOAD_FILE_MESSAGE_GENERIC*)msg)->buf) + 1; /* \0 
added */
+  size = ntohs(msg->header.header.size) -
+    sizeof(TESTBED_UPLOAD_FILE_MESSAGE) - 
+    (strlen(((TESTBED_UPLOAD_FILE_MESSAGE_GENERIC*)msg)->buf)+1);
+  if (GN_FWRITE(s, 1, size, outfile) != size)
+    ack = SYSERR;
+  else
+    ack = OK;
+  fclose(outfile);
+  sendAcknowledgement(client, ack);
+}
+
+/**
+ * General type of a message handler.
+ */                      
+typedef void (*THandler)(ClientHandle client,
+                        TESTBED_CS_MESSAGE * msg);
+
+/**
+ * @brief Entry in the handlers array that describes a testbed message handler.
+ */
+typedef struct HD_ {
+    /**
+     * function that handles these types of messages 
+     **/
+    THandler handler; 
+
+    /**
+     * Expected size of messages for this handler.  Checked by caller.  Use
+     * 0 for variable size, in that case, the handler must check.
+     */
+    unsigned short expectedSize; 
+
+    /**
+     * Textual description of the handler for debugging 
+     */
+    char * description; 
+    
+    /**
+     * The message-ID of the handler.  Used only for checking that 
+     * the handler array matches the message IDs defined in testbed.h.
+     * Must be equal to the index in the handler array that yields
+     * this entry.
+     */
+    unsigned int msgId; 
+} HD;
+
+/* some macros to make initializing the handlers array extremely brief. */
+
+#define TBDENTRY(a)  {(THandler)&tb_##a, 0, "##a##", TESTBED_##a }
+#define TBSENTRY(a)  {(THandler)&tb_##a, sizeof(TESTBED_##a##_MESSAGE),\
+                     "##a##", TESTBED_##a}
+
+/**
+ * The array of message handlers.  Add new handlers here.
+ */
+static HD handlers[] = {
+  TBSENTRY(undefined), /* For IDs that should never be received */
+  TBDENTRY(ADD_PEER),  /* RF: Why was this as TBDENTRY? Because HELO is 
variable size! */
+  TBSENTRY(DEL_PEER), 
+  TBSENTRY(DEL_ALL_PEERS),
+  TBSENTRY(GET_HELO), 
+  TBSENTRY(SET_TVALUE), 
+  TBSENTRY(GET_TVALUE), 
+  TBSENTRY(undefined), 
+  TBSENTRY(SET_BW), 
+  TBSENTRY(SET_LOSS_RATE),
+  TBDENTRY(LOAD_MODULE),
+  TBDENTRY(UNLOAD_MODULE),
+  TBDENTRY(UPLOAD_FILE),
+  TBSENTRY(DISABLE_HELO),
+  TBSENTRY(ENABLE_HELO),
+  TBSENTRY(DISABLE_AUTOCONNECT),
+  TBSENTRY(ENABLE_AUTOCONNECT),
+  TBDENTRY(ALLOW_CONNECT),
+  TBDENTRY(DENY_CONNECT),
+  TBDENTRY(EXEC),
+  TBSENTRY(SIGNAL),
+  TBSENTRY(GET_OUTPUT),
+  { NULL, 0, NULL, 0 },        /* this entry is used to ensure that
+                          a wrong TESTBED_MAX_MSG will abort
+                          insted of possibly segfaulting.
+                          This must always be the LAST entry. */
+};
+
+
+/**
+ * Global handler called by the GNUnet core.  Does the demultiplexing
+ * on the testbed-message type.
+ */
+static void csHandleTestbedRequest(ClientHandle client,
+                                  CS_HEADER * message) {
+  TESTBED_CS_MESSAGE * msg;
+  unsigned short size;
+  unsigned int id;
+  
+#if DEBUG_TESTBED
+  LOG(LOG_DEBUG, 
+      "DEBUG: TESTBED handleTestbedRequest\n");
+#endif
+  size = ntohs(message->size);
+  if (size < sizeof(TESTBED_CS_MESSAGE)) {
+    LOG(LOG_WARNING,
+       "WARNING: received invalid testbed message of size %u\n",
+       size);
+    return;
+  }
+  msg = (TESTBED_CS_MESSAGE *)message;
+  id = ntohl(msg->msgType);
+  if (id < TESTBED_MAX_MSG) {
+    if ( (handlers[id].expectedSize == 0) ||
+        (handlers[id].expectedSize == size) ) {
+      LOG(LOG_DEBUG, 
+         "DEBUG: TESTBED received message of type %u.\n",
+         id);
+      
+      handlers[id].handler(client, msg);
+      
+    } else {
+      LOG(LOG_ERROR,
+         "ERROR: received testbed message of type %u but "
+         "unexpected size %u, expected %u\n",
+         id, 
+         size,
+         handlers[id].expectedSize);
+    }
+  } else {
+    tb_undefined(client, msg);
+  }
+} 
+
+/**
+ * Register this testbed peer with the central testbed server.
+ * Yes, the testbed has a central server.  There's nothing wrong
+ * with that.  It's a testbed.
+ */
+static void httpRegister(char * cmd) {
+  char * reg;
+  long int port;
+  char * hostname;
+  unsigned int curpos;
+  struct hostent *ip_info;
+  struct sockaddr_in soaddr;
+  int sock;
+  int ret;
+  char * command;
+  char * secure;
+  char * trusted;
+  unsigned short tport;
+  char sport[6];
+  cron_t start;
+  char c;
+  char * buffer;
+  int i;
+  int j;
+  int k;
+  struct sockaddr_in theProxy;
+  char *proxy, *proxyPort;
+  struct hostent *ip;
+
+  reg = getConfigurationString("TESTBED",
+                              "REGISTERURL");
+  if (reg == NULL) {
+    LOG(LOG_DEBUG,
+       "DEBUG: no testbed URL given, not registered.\n");
+    return;
+  }
+
+  proxy = getConfigurationString("GNUNETD", 
+                                "HTTP-PROXY");
+  if (proxy != NULL) {
+    ip = GETHOSTBYNAME(proxy);
+    if (ip == NULL) {
+      LOG(LOG_ERROR, 
+         "Couldn't resolve name of HTTP proxy %s\n",
+         proxy);
+      theProxy.sin_addr.s_addr = 0;
+    } else {
+      theProxy.sin_addr.s_addr 
+       = ((struct in_addr *)ip->h_addr)->s_addr;
+      proxyPort = getConfigurationString("GNUNETD",
+                                        "HTTP-PROXY-PORT");
+      if (proxyPort == NULL) {
+       theProxy.sin_port = htons(8080);
+      } else {
+       theProxy.sin_port = htons(atoi(proxyPort));
+       FREE(proxyPort);
+      }
+    }
+    FREE(proxy);
+  } else {
+    theProxy.sin_addr.s_addr = 0;
+  }
+
+  if (0 != strncmp(HTTP_URL,
+                  reg, 
+                  strlen(HTTP_URL)) ) {
+    LOG(LOG_WARNING, 
+       "WARNING: invalid URL %s (must begin with %s)\n", 
+       reg, 
+       HTTP_URL);
+    return;
+  }
+  port = 80; /* default http port */
+
+  hostname = STRDUP(&reg[strlen(HTTP_URL)]);
+  buffer = NULL;
+  j = -1;
+  k = -1;
+  for (i=0;i<strlen(hostname);i++) {
+    if (hostname[i] == ':') 
+      j = i;
+    if (hostname[i] == '/') {
+      k = i;
+      if (j == -1)
+       j = i;      
+      break;
+    }
+  }
+  if ( (j != -1) && (j < k) ) {   
+    char * pstring;
+    if (k == -1) {
+      pstring = MALLOC(strlen(hostname)-j+1);
+      memcpy(pstring,
+            &hostname[j],
+            strlen(hostname)-j+1);
+      pstring[strlen(hostname)-j] = '\0';
+    } else {
+      pstring = MALLOC(k-j+1);
+      memcpy(pstring,
+            &hostname[j],
+            k-j);
+      pstring[k-j] = '\0';
+    }
+    port = strtol(pstring, &buffer, 10);
+    if ( (port < 0) || (port > 65536) ) {
+      LOG(LOG_ERROR,
+         "ERROR: malformed http URL: %s at %s.  Testbed-client not 
registered.\n",
+         reg,
+         buffer);
+      FREE(hostname);
+      FREE(reg);
+      FREE(pstring);
+      return;
+    }
+    FREE(pstring);
+  }
+  hostname[k] = '\0';
+
+#if DEBUG_TESTBED
+  LOG(LOG_INFO,
+      "INFO: Trying to (un)register testbed client at %s\n",
+      reg);
+#endif
+
+
+
+  sock = SOCKET(PF_INET, 
+               SOCK_STREAM,
+               0);
+  if (sock < 0) {
+    LOG(LOG_ERROR,
+       "ERROR: could not open socket for testbed registration (%s).\n",
+       STRERROR(errno));
+    FREE(hostname);   
+    FREE(reg);
+    return;
+  }
+
+  /* Do we need to connect through a proxy? */
+  if (theProxy.sin_addr.s_addr == 0) {
+    /* no proxy */
+    ip_info = GETHOSTBYNAME(hostname);
+    if (ip_info == NULL) {
+      LOG(LOG_WARNING,
+         "WARNING: could not register testbed, host %s unknown\n",
+         hostname);
+      FREE(reg);
+      FREE(hostname);
+      return;
+    }    
+    soaddr.sin_addr.s_addr 
+      = ((struct in_addr*)(ip_info->h_addr))->s_addr;
+    soaddr.sin_port 
+      = htons((unsigned short)port);
+  } else {
+    /* proxy */
+    soaddr.sin_addr.s_addr 
+      = theProxy.sin_addr.s_addr;
+    soaddr.sin_port 
+      = theProxy.sin_port;
+  }
+  soaddr.sin_family = AF_INET;
+  if (CONNECT(sock, 
+             (struct sockaddr*)&soaddr, 
+             sizeof(soaddr)) < 0) {
+    LOG(LOG_WARNING,
+       "WARNING: failed to send HTTP request to host %s: %s\n",
+       hostname,
+       STRERROR(errno));
+    FREE(reg);
+    FREE(hostname);
+    CLOSE(sock);
+    return;
+  }
+  
+
+  trusted = getConfigurationString("NETWORK",
+                                  "TRUSTED");
+  if (trusted == NULL)
+    trusted = STRDUP("127.0.0.0/8;");
+  i = 0;
+  while (trusted[i] != '\0') {
+    if (trusted[i] == ';')
+      trusted[i] = '@';
+    i++;
+  }
+  tport = getGNUnetPort();
+  sprintf(sport,
+         "%u",
+         tport);
+  secure = getConfigurationString("TESTBED",
+                                 "LOGIN");
+  if (secure == NULL)
+    secure = STRDUP("");
+  command = MALLOC(strlen(GET_COMMAND) 
+                  + strlen(cmd) 
+                  + strlen(reg) 
+                  + strlen(trusted) 
+                  + strlen(sport)
+                  + strlen(secure));
+  sprintf(command, 
+         GET_COMMAND,
+         reg,
+         cmd,
+         trusted,
+         sport,
+         secure);
+  FREE(trusted);
+  FREE(secure);
+  FREE(reg);
+  curpos = strlen(command)+1;
+  curpos = SEND_BLOCKING_ALL(sock,
+                            command,
+                            curpos);
+  if (SYSERR == (int)curpos) {
+    LOG(LOG_WARNING,
+       "WARNING: failed so send HTTP request %s to host %s (%u - %d) - %s\n",
+       command,
+       hostname,
+       curpos, 
+       sock,
+       STRERROR(errno));
+    FREE(command);    
+    FREE(hostname);
+    CLOSE(sock);
+    return;
+  }
+  FREE(command);
+  FREE(hostname);
+  cronTime(&start);
+
+  /* we first have to read out the http_response*/
+  /* it ends with four line delimiters: "\r\n\r\n" */
+  curpos = 0;
+  while (curpos < 4) {
+    if (start + 5 * cronMINUTES < cronTime(NULL))
+      break; /* exit after 5m */
+    ret = RECV_NONBLOCKING(sock,
+                          &c,
+                          sizeof(c));
+    if ( (ret == SYSERR) &&
+        (errno == EAGAIN) ) {
+      gnunet_util_sleep(100 * cronMILLIS);
+      continue;    
+    }
+    if (ret <= 0)
+      break; /* end of transmission or error */
+    if ((c=='\r') || (c=='\n')) 
+      curpos += ret;
+    else 
+      curpos=0;    
+  }
+  CLOSE(sock);  
+  if (curpos < 4) { /* invalid response */
+    LOG(LOG_WARNING, 
+       "WARNING: exit register (error: no http response read)\n");
+  }
+#if DEBUG_HELOEXCHANGE
+  LOG(LOG_INFO,
+      "INFO: exit register (%d seconds before timeout)\n",
+      (int)(start + 300 * cronSECONDS - cronTime(NULL))/cronSECONDS);
+#endif
+} 
+
+/**
+ * When a client exits, kill all associated processes.
+ */
+static void testbedClientExitHandler(ClientHandle client) {
+  int i;
+  int pding;
+  void * unused;
+
+  pding = 0;
+  /* kill all processes */
+  MUTEX_LOCK(&lock);
+  for (i=ptSize-1;i>=0;i--) {
+    if (pt[i]->client == client) {
+      pding++;
+      if (pt[i]->hasExited == NO)
+       kill(pt[i]->pid, SIGKILL); /* die NOW */
+    }
+  }
+  MUTEX_UNLOCK(&lock);
+  /* join on all pthreads, but since they may be
+     blocking on the same lock, unlock from time
+     to time for a while to let them leave... 
+     FIXME: not really elegant, better use
+     semaphores... */
+  while (pding > 0) {
+    pding = 0;
+    gnunet_util_sleep(50);
+    MUTEX_LOCK(&lock);
+    for (i=ptSize-1;i>=0;i--) {
+      if (pt[i]->client == client) {
+       if (pt[i]->hasExited == YES) {
+         PTHREAD_JOIN(&pt[i]->reader,
+                      &unused);
+         GROW(pt[i]->output,
+              pt[i]->outputSize,
+              0);
+         FREE(pt[i]);
+         pt[i] = pt[ptSize-1];
+         GROW(pt,
+              ptSize,
+              ptSize-1);
+       } else {
+         pding++;
+       }
+      }
+    }
+    MUTEX_UNLOCK(&lock);
+  }
+}
+
+/**
+ * Initialize the TESTBED module. This method name must match
+ * the library name (libgnunet_XXX => initialize_XXX).
+ * @return SYSERR on errors
+ **/
+int initialize_testbed_protocol(CoreAPIForApplication * capi) {
+  unsigned int i;
+  int ok = OK;
+  
+  /* some checks */
+  for (i=0;i<TESTBED_MAX_MSG;i++)
+    if ( (handlers[i].msgId != i) &&
+        (handlers[i].handler != &tb_undefined) )
+      errexit("FATAL: Assertion failed: "
+             "Malformed handlers array in %s:%d. "
+             "Aborting. (%d)\n",
+             __FILE__,
+             __LINE__,
+             i);
+  if (handlers[TESTBED_MAX_MSG].handler != NULL)
+    errexit("FATAL: Assertion failed: "
+           "TESTBED_MAX_MSG in testbed.c is wrong."
+           "Aborting.\n");
+  MUTEX_CREATE(&lock);
+  LOG(LOG_DEBUG,
+      "DEBUG: TESTBED registering handler %d!\n",
+      TESTBED_CS_PROTO_REQUEST);
+  coreAPI = capi;
+  if (SYSERR == capi->registerClientExitHandler(&testbedClientExitHandler))
+    ok = SYSERR;
+  if (SYSERR == capi->registerClientHandler(TESTBED_CS_PROTO_REQUEST,
+                                           (CSHandler)&csHandleTestbedRequest))
+    ok = SYSERR;
+  httpRegister("startup");
+  return ok;
+}
+
+/**
+ * Shutdown the testbed module.
+ */
+void done_testbed_protocol() {
+  int i;
+
+  /* kill all child-processes */
+  for (i=0;i<ptSize;i++) {
+    ProcessInfo * pi;
+    void * unused;
+
+    pi = pt[i];
+    if (pi->hasExited != NO)
+      kill(pi->pid, SIGKILL);    
+    PTHREAD_JOIN(&pi->reader,
+                &unused);
+    FREENONNULL(pi->output);
+    FREE(pi);    
+  }
+  GROW(pt,
+       ptSize,
+       0);
+  
+  httpRegister("shutdown");
+  MUTEX_DESTROY(&lock);
+  LOG(LOG_DEBUG,
+      "DEBUG: TESTBED unregistering handler %d\n",
+      TESTBED_CS_PROTO_REQUEST);
+  coreAPI->unregisterClientHandler(TESTBED_CS_PROTO_REQUEST,
+                                  (CSHandler)&csHandleTestbedRequest);
+  coreAPI->unregisterClientExitHandler(&testbedClientExitHandler);
+  coreAPI = NULL;
+}
+
+/* end of testbed.c */

Added: freeway/src/org/gnu/freeway/protocol/testbed/testbed.h
===================================================================
--- freeway/src/org/gnu/freeway/protocol/testbed/testbed.h      2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/testbed/testbed.h      2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,237 @@
+/*
+     This file is part of GNUnet.
+     (C) 2003, 2004 Christian Grothoff (and other contributing authors)
+
+     GNUnet is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with GNUnet; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @author Ronaldo Alves Ferreira
+ * @author Christian Grothoff
+ * @author Murali Krishna Ramanathan
+ * @file applications/testbed/testbed.h
+ **/
+#ifndef TESTBED_TESTBED_H
+#define TESTBED_TESTBED_H
+
+#include <sys/wait.h>
+
+#include "gnunet_core.h"
+
+/* */
+#define TESTBED_HELO_RESPONSE   0       /* peer responds with a HELO */
+#define TESTBED_ADD_PEER       1       /* Add a peer to a peer connection pool 
                */
+#define TESTBED_DEL_PEER       2       /* Delete a peer from a peer connection 
pool            */
+#define TESTBED_DEL_ALL_PEERS  3       /* Delete all peers from a peer 
connection pool         */
+#define TESTBED_GET_HELO       4       /* Get the complete host information 
(ID, IP, ...)      */
+#define TESTBED_SET_TVALUE     5       /* Set trust value for a peer           
                */
+#define TESTBED_GET_TVALUE     6       /* Get trust value of a peer            
                */
+#define TESTBED_OUTPUT_RESPONSE        7       /* Reply to GET_OUTPUT          
                        */
+#define TESTBED_SET_BW         8       /* Set in/outbound bandwidth            
                */
+#define TESTBED_SET_LOSS_RATE  9       /* Set the drop probability of a 
connection             */
+#define TESTBED_LOAD_MODULE     10      /* load a module                       
                 */
+#define TESTBED_UNLOAD_MODULE   11      /* unload a module                     
                 */
+#define TESTBED_UPLOAD_FILE    12      /* Upload a file to a peer              
                */
+#define TESTBED_DISABLE_HELO    13      /* stop sending HELOs */
+#define TESTBED_ENABLE_HELO     14      /* start sending HELOs */
+#define TESTBED_DISABLE_AUTOCONNECT    15      /* stop automatically 
connecting to other peers */
+#define TESTBED_ENABLE_AUTOCONNECT     16      /* start trying to 
automatically connect to other peers */
+#define TESTBED_ALLOW_CONNECT   17      /* only allow connections from a 
certain group of peers */
+#define TESTBED_DENY_CONNECT    18      /* deny connections from a certain 
group of peers */
+#define TESTBED_EXEC            19      /* execute process */
+#define TESTBED_SIGNAL          20      /* send signal to process */
+#define TESTBED_GET_OUTPUT      21      /* get output from process */
+#define TESTBED_undefined       22
+
+/**
+ * Number of entries in handlers array.  Checked automatically when the
+ * module is initialized.
+ */
+#define TESTBED_MAX_MSG 22
+
+/*
+  TODO LIST:
+  Make loss rate to work. (CG: DONE)
+  Upload a file to a specific machine.  (RF: DONE)
+  Revisit statistics (don't re-invent that wheel!)
+  AFS download a file on a specific machine.
+  AFS insert a file on a specific machine.
+ */
+
+typedef struct {
+  CS_HEADER header;  
+  unsigned int msgType;        /* The message types listed above       */
+} TESTBED_CS_MESSAGE;
+
+typedef struct {
+  TESTBED_CS_MESSAGE testbed_cs_message;  
+  char data[1];                /* Value is dependent on the type field */
+} TESTBED_CS_MESSAGE_GENERIC;
+
+typedef struct {
+  TESTBED_CS_MESSAGE header;
+} TESTBED_undefined_MESSAGE;
+
+typedef struct {
+  TESTBED_CS_MESSAGE header;
+  unsigned short proto;
+  unsigned short reserved; /* for alignment */
+} TESTBED_GET_HELO_MESSAGE;
+
+typedef struct {
+  TESTBED_CS_MESSAGE header;
+  unsigned int in_bw;          /* Inbound bandwidth            */
+  unsigned int out_bw; /* Outbound bandwidth           */
+} TESTBED_SET_BW_MESSAGE;
+
+typedef struct {
+  TESTBED_CS_MESSAGE header;
+  HELO_Message helo;
+} TESTBED_HELO_MESSAGE;
+
+typedef struct {
+  TESTBED_CS_MESSAGE header;
+  HELO_Message helo;
+} TESTBED_ADD_PEER_MESSAGE;
+
+typedef struct {
+    TESTBED_CS_MESSAGE header;
+    HostIdentity host;
+} TESTBED_DEL_PEER_MESSAGE;
+
+typedef struct {
+    TESTBED_CS_MESSAGE header;
+} TESTBED_DEL_ALL_PEERS_MESSAGE;
+
+typedef struct {
+    TESTBED_CS_MESSAGE header;
+    HostIdentity otherPeer;
+} TESTBED_GET_TVALUE_MESSAGE;
+
+typedef struct {
+    TESTBED_CS_MESSAGE header;
+    HostIdentity otherPeer;
+    unsigned int trust;
+} TESTBED_SET_TVALUE_MESSAGE;
+
+typedef struct {
+    TESTBED_CS_MESSAGE header;
+    HostIdentity otherPeer;
+} TESTBED_BLACKLIST_MESSAGE;
+
+typedef struct {
+  TESTBED_CS_MESSAGE header;
+  unsigned int percentageLossInbound;
+  unsigned int percentageLossOutbound;
+} TESTBED_SET_LOSS_RATE_MESSAGE;
+
+typedef struct {
+    TESTBED_CS_MESSAGE header;
+} TESTBED_LOAD_MODULE_MESSAGE;
+
+typedef struct {
+    TESTBED_LOAD_MODULE_MESSAGE load_module_message;
+    char modulename[1];
+} TESTBED_LOAD_MODULE_MESSAGE_GENERIC;
+
+typedef struct {
+  TESTBED_CS_MESSAGE header;
+} TESTBED_UNLOAD_MODULE_MESSAGE;
+
+typedef struct {
+  TESTBED_UNLOAD_MODULE_MESSAGE unload_module_message;
+  char modulename[1];
+} TESTBED_UNLOAD_MODULE_MESSAGE_GENERIC;
+
+#define TESTBED_FILE_APPEND    1
+#define TESTBED_FILE_DELETE    2
+
+#define TESTBED_FILE_BLK_SIZE  1400
+
+typedef struct {
+  TESTBED_CS_MESSAGE header;
+  unsigned int type;
+} TESTBED_UPLOAD_FILE_MESSAGE;
+
+typedef struct {
+  TESTBED_UPLOAD_FILE_MESSAGE upload_file_message;
+  char buf[1];
+} TESTBED_UPLOAD_FILE_MESSAGE_GENERIC;
+
+typedef struct {
+    TESTBED_CS_MESSAGE header;
+} TESTBED_DISABLE_HELO_MESSAGE;
+
+typedef struct {
+    TESTBED_CS_MESSAGE header;
+} TESTBED_ENABLE_HELO_MESSAGE;
+
+typedef struct {
+    TESTBED_CS_MESSAGE header;
+} TESTBED_ENABLE_AUTOCONNECT_MESSAGE;
+
+typedef struct {
+    TESTBED_CS_MESSAGE header;
+} TESTBED_DISABLE_AUTOCONNECT_MESSAGE;
+
+typedef struct {
+  TESTBED_CS_MESSAGE header;
+} TESTBED_ALLOW_CONNECT_MESSAGE;
+
+typedef struct {
+  TESTBED_ALLOW_CONNECT_MESSAGE allow_connect_message;
+  HostIdentity peers[1];
+} TESTBED_ALLOW_CONNECT_MESSAGE_GENERIC;
+
+typedef struct {
+  TESTBED_CS_MESSAGE header;
+} TESTBED_DENY_CONNECT_MESSAGE;
+
+typedef struct {
+  TESTBED_DENY_CONNECT_MESSAGE deny_connect_message;
+  HostIdentity peers[1];
+} TESTBED_DENY_CONNECT_MESSAGE_GENERIC;
+
+typedef struct {
+  TESTBED_CS_MESSAGE header;
+} TESTBED_EXEC_MESSAGE;
+
+typedef struct {
+  TESTBED_EXEC_MESSAGE exec_message;
+  char commandLine[1];
+} TESTBED_EXEC_MESSAGE_GENERIC;
+
+typedef struct {
+  TESTBED_CS_MESSAGE header;
+  unsigned int pid;
+  int signal;
+} TESTBED_SIGNAL_MESSAGE;
+
+typedef struct {
+  TESTBED_CS_MESSAGE header;
+  int pid;
+} TESTBED_GET_OUTPUT_MESSAGE;
+
+typedef struct {
+  TESTBED_CS_MESSAGE header;
+} TESTBED_OUTPUT_REPLY_MESSAGE;
+
+typedef struct {
+  TESTBED_OUTPUT_REPLY_MESSAGE output_reply_message;
+  char data[1];
+} TESTBED_OUTPUT_REPLY_MESSAGE_GENERIC;
+
+#endif

Added: freeway/src/org/gnu/freeway/protocol/tracekit/CSTraceProbe.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/tracekit/CSTraceProbe.java     
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/tracekit/CSTraceProbe.java     
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,67 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.tracekit;
+
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ *
+ */
+
+public class CSTraceProbe extends CSMessage
+{
+       public static final int SIZE    =       12;
+
+       /** How many more hops should this probe go */
+       public int      hops;
+
+       /** How important is the probe for the sender? */
+       public int      priority;
+
+
+       public CSTraceProbe()
+       {
+               super(IS_TRACE_PROBE);
+               hops=0;
+               priority=0;
+       }
+
+       public String toString()
+       {
+               return "Trace kit c/s probe [hops="+hops+", 
priority="+priority+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int getByteSize()
+       {
+               return SIZE;
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               int     size,type;
+
+               size=buf.getShort() & 0x0000ffff;
+               err.reportIf(size!=SIZE,"bad size");
+
+               type=buf.getShort() & 0x0000ffff;
+               err.reportIf(type!=IS_TRACE_PROBE,"bad type !");
+
+               hops=buf.getInt();
+               priority=buf.getInt();
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               buf.putShort((short) SIZE);
+               buf.putShort((short) IS_TRACE_PROBE);
+               buf.putInt(hops);
+               buf.putInt(priority);
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/tracekit/CSTraceReply.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/tracekit/CSTraceReply.java     
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/tracekit/CSTraceReply.java     
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,119 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.tracekit;
+
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+import java.util.*;
+
+/**
+ *
+ */
+
+public class CSTraceReply extends CSMessage
+{
+       public static final int SIZE    =       HostIdentity.SIZE+4;
+
+       /** */
+       private int                     size;
+
+       /** Which peer is the ultimate responder responsible for sending this 
reply ? */
+       public HostIdentity     responderId;
+
+       /** List of peers that the responder is currently connected to. */
+       private List            peerList;
+
+
+       public CSTraceReply()
+       {
+               super(IS_TRACE_REPLY);
+               size=SIZE;
+               responderId=null;
+               peerList=new ArrayList();
+       }
+
+       public String toString()
+       {
+               return "Trace kit c/s reply [responderId="+responderId+", 
peerList.size="+peerList.size()+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int getHostsCount()
+       {
+               return peerList.size();
+       }
+
+       public HostIdentity getHost( int index )
+       {
+               return (HostIdentity) peerList.get(index);
+       }
+
+       public void addHost( HostIdentity hi )
+       {
+               peerList.add(hi);
+               size+=HostIdentity.SIZE;
+       }
+
+       public void setHosts( HostIdentity[] hosts )
+       {
+               peerList.clear();
+               peerList.addAll(Arrays.asList(hosts));
+               size=SIZE+hosts.length*HostIdentity.SIZE;
+       }
+
+       public int getByteSize()
+       {
+               return size;
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               HostIdentity    hi;
+               int                             type,i;
+
+               size=buf.getShort() & 0x0000ffff;
+               err.reportIf(size<SIZE,"bad size");
+
+               type=buf.getShort() & 0x0000ffff;
+               err.reportIf(type!=IS_TRACE_REPLY,"bad type !");
+
+               responderId=new HostIdentity();
+               responderId.readBytes(buf,err);
+
+               peerList.clear();
+               for (i=(size-SIZE)/HostIdentity.SIZE; i>0; i--) {
+                       hi=new HostIdentity();
+                       hi.readBytes(buf,err);
+                       peerList.add(hi);
+                       }
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               HostIdentity    hi;
+               int                             i;
+
+               buf.putShort((short) size);
+               buf.putShort((short) IS_TRACE_REPLY);
+
+               if (responderId!=null) {
+                       responderId.writeBytes(buf);
+                       }
+               else {
+                       for (i=0; i<HostIdentity.SIZE; i++) {
+                               buf.put((byte) 0);
+                               }
+                       }
+
+               for (i=0; i<peerList.size(); i++) {
+                       hi=(HostIdentity) peerList.get(i);
+                       hi.writeBytes(buf);
+                       }
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/tracekit/P2PTraceProbe.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/tracekit/P2PTraceProbe.java    
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/tracekit/P2PTraceProbe.java    
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,94 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.tracekit;
+
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ *
+ */
+
+public class P2PTraceProbe extends P2PMessage
+{
+       public static final int SIZE    =       4+16+HostIdentity.SIZE;
+
+       /** When was this probe started ? */
+       public int                      timestamp;
+
+       /** How many more hops should this probe go */
+       public int                      hopsToGo;
+
+       /** How important is the probe for the sender ? */
+       public int                      priority;
+
+       /** Internal client id of the sender. */
+       public int                      clientId;
+
+       /** Which peer is the ultimate receiver of this information ? */
+       public HostIdentity     initiatorId;
+
+
+       public P2PTraceProbe()
+       {
+               super(IS_TRACE_PROBE);
+       }
+
+       public String toString()
+       {
+               return "Trace kit p2p probe";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int getByteSize()
+       {
+               return SIZE;
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               int     size,type;
+
+               size=buf.getShort() & 0x0000ffff;
+               err.reportIf(size!=SIZE,"bad size");
+
+               type=buf.getShort() & 0x0000ffff;
+               err.reportIf(type!=IS_TRACE_PROBE,"bad type !");
+
+               timestamp=buf.getInt();
+               hopsToGo=buf.getInt();
+               priority=buf.getInt();
+               clientId=buf.getInt();
+
+               initiatorId=new HostIdentity();
+               initiatorId.readBytes(buf,err);
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               int     i;
+
+               buf.putShort((short) SIZE);
+               buf.putShort((short) IS_TRACE_PROBE);
+
+               buf.putInt(timestamp);
+               buf.putInt(hopsToGo);
+               buf.putInt(priority);
+               buf.putInt(clientId);
+
+               if (initiatorId!=null) {
+                       initiatorId.writeBytes(buf);
+                       }
+               else {
+                       for (i=0; i<HostIdentity.SIZE; i++) {
+                               buf.put((byte) 0);
+                               }
+                       }
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/tracekit/P2PTraceReply.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/tracekit/P2PTraceReply.java    
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/tracekit/P2PTraceReply.java    
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,134 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.tracekit;
+
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+import java.util.*;
+
+/**
+ *
+ */
+
+public class P2PTraceReply extends P2PMessage
+{
+       public static final int SIZE    =       4+HostIdentity.SIZE*2+8;
+
+       /** Which peer is the ultimate receiver of this information ? */
+       public HostIdentity     initiatorId;
+
+       /** Which peer is the ultimate responder responsible for sending this 
reply ? */
+       public HostIdentity     responderId;
+
+       /** At what time was the initator sending the request ? */
+       public int                      initiatorTimestamp;
+
+       /** Internal client Id of the sender. */
+       public int                      clientId;
+
+       /** List of peers that the responder is currently connected to. */
+       private List            peerList;
+
+
+       public P2PTraceReply()
+       {
+               super(IS_TRACE_REPLY);
+               peerList=new ArrayList();
+       }
+
+       public String toString()
+       {
+               return "Trace kit p2p reply [initiatorId="+initiatorId+", 
responderId="+responderId+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public HostIdentity[] getHosts()
+       {
+               return (HostIdentity[]) peerList.toArray(new 
HostIdentity[peerList.size()]);
+       }
+
+       public void addHost( HostIdentity hi )
+       {
+               peerList.add(PersistentHelper.copy(hi));
+       }
+
+       public void clearHosts()
+       {
+               peerList.clear();
+       }
+
+       public int getByteSize()
+       {
+               return SIZE+peerList.size()*HostIdentity.SIZE;
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               HostIdentity    hi;
+               int                             size,type,i;
+
+               size=buf.getShort() & 0x0000ffff;
+               err.reportIf(size<SIZE,"bad size");
+
+               type=buf.getShort() & 0x0000ffff;
+               err.reportIf(type!=IS_TRACE_REPLY,"bad type !");
+
+               initiatorId=new HostIdentity();
+               initiatorId.readBytes(buf,err);
+
+               responderId=new HostIdentity();
+               responderId.readBytes(buf,err);
+
+               initiatorTimestamp=buf.getInt();
+
+               clientId=buf.getInt();
+
+               peerList.clear();
+               for (i=(size-SIZE)/HostIdentity.SIZE; i>0; i--) {
+                       hi=new HostIdentity();
+                       hi.readBytes(buf,err);
+                       peerList.add(hi);
+                       }
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               HostIdentity    hi;
+               int                             i;
+
+               buf.putShort((short) getByteSize());
+               buf.putShort((short) IS_TRACE_REPLY);
+
+               if (initiatorId!=null) {
+                       initiatorId.writeBytes(buf);
+                       }
+               else {
+                       for (i=0; i<HostIdentity.SIZE; i++) {
+                               buf.put((byte) 0);
+                               }
+                       }
+
+               if (responderId!=null) {
+                       responderId.writeBytes(buf);
+                       }
+               else {
+                       for (i=0; i<HostIdentity.SIZE; i++) {
+                               buf.put((byte) 0);
+                               }
+                       }
+
+               buf.putInt(initiatorTimestamp);
+               buf.putInt(clientId);
+
+               for (i=0; i<peerList.size(); i++) {
+                       hi=(HostIdentity) peerList.get(i);
+                       hi.writeBytes(buf);
+                       }
+       }
+}

Added: freeway/src/org/gnu/freeway/protocol/tracekit/RTE.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/tracekit/RTE.java      2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/tracekit/RTE.java      2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,34 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.tracekit;
+
+import org.gnu.freeway.util.crypto.*;
+
+/**
+ *
+ */
+
+public class RTE extends Object
+{
+       public HostIdentity     initiator;
+       public HostIdentity     replyTo;
+       public int                      timestamp;
+       public int                      priority;
+
+
+       public RTE()
+       {
+               super();
+       }
+
+       public String toString()
+       {
+               return "xxx";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+}

Added: freeway/src/org/gnu/freeway/protocol/tracekit/TraceKitProtocol.java
===================================================================
--- freeway/src/org/gnu/freeway/protocol/tracekit/TraceKitProtocol.java 
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/protocol/tracekit/TraceKitProtocol.java 
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,312 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.protocol.tracekit;
+
+import org.gnu.freeway.protocol.*;
+import org.gnu.freeway.server.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.util.*;
+import java.util.logging.*;
+
+/**
+ *
+ */
+
+public class TraceKitProtocol extends AbstractProtocol
+{
+       private static final int        MAXROUTE        =       16;
+
+       private Object                  lock;
+       private int                             clientCount;
+       private CSSession[]     clients;
+       private Stat            stat_cs_requests;
+       private Stat            stat_cs_replies;
+       private Stat            stat_p2p_requests;
+       private Stat            stat_p2p_replies;
+       private RTE[]                   routeTable;
+
+
+       public TraceKitProtocol()
+       {
+               super("TRACE_KIT");
+               routeTable=new RTE[MAXROUTE];
+               clients=new CSSession[] {};
+       }
+
+       public String toString()
+       {
+               return "Trace kit protocol";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public boolean init( CoreForProtocol capi )
+       {
+               Statistics      stats;
+               int                     i;
+
+               super.init(capi);
+
+               lock=new Object();
+
+               stats=capi.getApplication().getStatistics();
+               stat_cs_requests=stats.getHandle("# client trace requests 
received",Stat.VERBOSE);
+               stat_cs_replies=stats.getHandle("# client trace replies 
sent",Stat.VERBOSE);
+               stat_p2p_requests=stats.getHandle("# p2p trace requests 
received",Stat.VERBOSE);
+               stat_p2p_replies=stats.getHandle("# p2p trace replies 
sent",Stat.VERBOSE);
+
+               for (i=0; i<routeTable.length; i++) {
+                       routeTable[i]=new RTE();
+                       }
+
+               addCSHandler(CSMessage.IS_TRACE_PROBE,CSTraceProbe.class);
+               addP2PHandler(P2PMessage.IS_TRACE_PROBE,P2PTraceProbe.class);
+               addP2PHandler(P2PMessage.IS_TRACE_REPLY,P2PTraceReply.class);
+               return true;
+       }
+
+       public void done()
+       {
+               clients=null;
+               clientCount=0;
+               lock=null;
+               super.done();
+       }
+
+       protected boolean onCSMessage( CSSession client, CSMessage msg )
+       {
+               int i;
+               int idx;
+               CSTraceProbe    csProbe;
+               P2PTraceProbe p2pProbe;
+               CSSession[]     old;
+
+               stat_cs_requests.inc();
+
+               log(Level.FINEST,"Client sends probe request.");
+
+               synchronized(lock) {
+                       idx = -1;
+                       for (i=0; i<clientCount; i++) {
+                               if (clients[i] == null)
+                                       idx = i;
+                               if (clients[i] == client) {
+                                       idx = i;
+                                       break;
+                                       }
+                               }
+
+                       if (idx == -1) {
+                               old=clients;
+                               clients=new CSSession[clients.length+1];
+                               System.arraycopy(old,0,clients,0,old.length);
+                               idx = clientCount;
+                               clients[clientCount++] = client;
+                               }
+                       else {
+                               clients[idx] = client;
+                               }
+                       }
+
+               // build probe, broadcast
+               csProbe = (CSTraceProbe) msg;
+
+               p2pProbe=new P2PTraceProbe();
+               p2pProbe.clientId = idx;
+               p2pProbe.hopsToGo = csProbe.hops;
+               p2pProbe.timestamp = (int) Scheduler.toSeconds(Scheduler.now());
+               p2pProbe.priority = csProbe.priority;
+               p2pProbe.initiatorId=(HostIdentity) 
PersistentHelper.copy(coreAPI.getIdentity());
+
+               handlep2pProbe(coreAPI.getIdentity(),p2pProbe); // first send 
to myself !
+
+               coreAPI.broadcastToConnected(p2pProbe,csProbe.priority,0);
+
+               stat_p2p_requests.add(coreAPI.forAllConnectedNodes(null,null));
+               return true;
+       }
+
+       protected void onClientExit( CSSession c )
+       {
+               int i;
+
+               synchronized(lock) {
+                       for (i=0;i<clientCount;i++)
+                               if (clients[i] == c)
+                                       clients[i] = null;
+                       }
+       }
+
+       protected boolean onP2PMessage( HostIdentity sender, P2PMessage msg )
+       {
+               if (msg instanceof P2PTraceProbe) {
+                       return handlep2pProbe(sender,msg);
+                       }
+               if (msg instanceof P2PTraceReply) {
+                       return handlep2pReply(sender,msg);
+                       }
+               return false;
+       }
+
+       protected boolean handlep2pProbe( HostIdentity sender, P2PMessage 
message )
+       {
+               P2PTraceReply   reply;
+               P2PTraceProbe   msg;
+               int i;
+               int sel;
+               int hops;
+               int oldest;
+               int count;
+               HostIdentity    hi;
+               int max;
+
+               log(Level.FINEST,"Received probe.");
+
+               stat_p2p_requests.inc();
+
+               msg=(P2PTraceProbe) message;
+               if (msg.timestamp>Scheduler.toSeconds(Scheduler.now())+3600) {
+                       log(Level.INFO,"Probe has timestamp in the future 
("+msg.timestamp+" >> "+Scheduler.toSeconds(Scheduler.now())+"), dropping.");
+                       return false;   // timestamp is more than 1 hour in the 
future. Cheaters !
+                       }
+
+               synchronized(lock) {
+                       // test if already processed
+                       for (i=0; i<MAXROUTE; i++) {
+                               if (routeTable[i].timestamp==msg.timestamp && 
routeTable[i].initiator.equals(msg.initiatorId)) {
+                                       log(Level.FINEST,"Probe 
"+msg.timestamp+" from "+msg.initiatorId.getName()+" received twice (slot 
"+i+"), ignored.");
+                                       return true;
+                                       }
+                               }
+
+                       // no, find and kill oldest entry
+                       oldest = msg.timestamp;
+                       sel = -1;
+                       for (i=0; i<MAXROUTE; i++) {
+                               if (oldest > routeTable[i].timestamp) {
+                                       oldest = routeTable[i].timestamp;
+                                       sel = i;
+                                       }
+                               }
+
+                       if (sel == -1) {
+                               log(Level.INFO,"Request routing table full, 
trace request dropped.");
+                               return true;
+                               }
+
+                       routeTable[sel].timestamp= msg.timestamp;
+                       routeTable[sel].priority= msg.priority;
+                       routeTable[sel].initiator=(HostIdentity) 
PersistentHelper.copy(msg.initiatorId);
+                       routeTable[sel].replyTo=(HostIdentity) 
PersistentHelper.copy(sender);
+                       }
+
+               // check if seen, if not, update routing table entries
+               log(Level.FINEST,"Probe "+msg.timestamp+" from 
"+msg.initiatorId.getName()+" received, processing in slot "+sel+".");
+
+               count = coreAPI.forAllConnectedNodes(null, null);
+//log(Level.SEVERE,">>>>>>>>>>>>>>> count = "+count);
+
+               hops = msg.hopsToGo;
+               if (hops > 0) {
+                       msg.hopsToGo = hops-1;
+                       coreAPI.broadcastToConnected(message,msg.priority,0);
+
+                       stat_p2p_requests.add(count);
+                       }
+
+               final List      _hosts = new ArrayList();
+               coreAPI.forAllConnectedNodes(new PerNodeCallback() {
+                       public void callback( HostIdentity identity, Object 
data )
+                       {
+                               _hosts.add(identity);
+                       }
+                       },null);
+
+//log(Level.SEVERE,">>>>>>>>>>>>>>> 2 ");
+               reply = new P2PTraceReply();
+               reply.initiatorId=(HostIdentity) 
PersistentHelper.copy(msg.initiatorId);
+               reply.responderId=(HostIdentity) 
PersistentHelper.copy(coreAPI.getIdentity());
+               reply.initiatorTimestamp= msg.timestamp;
+               reply.clientId= msg.clientId;
+
+//log(Level.SEVERE,">>>>>>>>>>>>>>> 3 ");
+               while (_hosts.size()>0) {
+                       reply.clearHosts();
+//log(Level.SEVERE,">>>>>>>>>>>>>>> 4 ");
+
+                       
max=Math.min((1024-P2PTraceReply.SIZE)/HostIdentity.SIZE,_hosts.size());
+                       while (max>0) {
+                               hi=(HostIdentity) _hosts.remove(0);
+                               reply.addHost(hi);
+                               max--;
+                               }
+//log(Level.SEVERE,">>>>>>>>>>>>>>> 5 max = "+max);
+
+                       if (coreAPI.getIdentity().equals(sender)) {
+                               handlep2pReply(coreAPI.getIdentity(),reply);
+                               }
+                       else {
+                               coreAPI.sendToNode(sender,reply,msg.priority,0);
+
+                               stat_p2p_replies.inc();
+                               }
+                       }
+//log(Level.SEVERE,">>>>>>>>>>>>>>> fin ");
+               return true;
+       }
+
+       protected boolean handlep2pReply( HostIdentity sender, P2PMessage 
message )
+       {
+               int i;
+               P2PTraceReply   reply;
+
+               stat_p2p_replies.inc();
+
+               log(Level.FINEST,"DEBUG: TRACEKIT: receving reply");
+
+               reply = (P2PTraceReply) message;
+
+               log(Level.FINEST,"DEBUG: sending reply back to initiator 
"+reply.initiatorId.getName());
+               synchronized(lock) {
+                       for (i=0;i<MAXROUTE;i++) {
+                               if ( (routeTable[i].timestamp == 
reply.initiatorTimestamp) &&
+                                               ( 
routeTable[i].initiator.equals(reply.initiatorId) ) ) {
+                                       log(Level.INFO,"Found matching entry in 
routing table.");
+                                       if 
(coreAPI.getIdentity().equals(routeTable[i].replyTo) ) {
+                                               int idx;
+                                               CSTraceReply csReply;
+
+                                               idx = reply.clientId;
+                                               log(Level.FINEST,"I am 
initiator, sending to client "+idx);
+                                               if (idx >= clientCount)
+                                                       continue; /* discard */
+                                               if (clients[idx] == null)
+                                                       continue; /* discard */
+
+                                               csReply = new CSTraceReply();
+                                               
csReply.responderId=(HostIdentity) PersistentHelper.copy(reply.responderId);
+                                               
csReply.setHosts(reply.getHosts());
+
+                                               stat_cs_replies.inc();
+
+                                               clients[idx].send(csReply);
+                                               }
+                                       else {
+                                               log(Level.FINEST,"DEBUG: 
forwarding to next hop "+routeTable[i].replyTo.getName());
+
+                                               stat_p2p_replies.inc();
+
+                                               
coreAPI.sendToNode(routeTable[i].replyTo,message,routeTable[i].priority,0);
+                                               }
+                                       }
+                               }
+                       }
+               return true;
+       }
+}

Added: freeway/src/org/gnu/freeway/server/BufferEntry.java
===================================================================
--- freeway/src/org/gnu/freeway/server/BufferEntry.java 2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/server/BufferEntry.java 2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,719 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.server;
+
+import org.gnu.freeway.transport.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.io.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+import java.util.*;
+import java.util.logging.*;
+
+/**
+ * Type of the connection table.
+ */
+
+public class BufferEntry extends LoggedObject
+{
+       /** Hard limit on the send buffer size */
+       public static final int MAX_SEND_BUFFER_SIZE    =       256;
+
+       /** Session for the connection */
+       public Session  session;//todo: le MTU uniquement ???
+
+       /** How much do we trust the host?  signed because that makes many 
operations that go negative easier.
+        Of course, negative trust makes no sense. */
+       public int                      trust;
+
+       /** the current session key */
+       public SessionKey       skey;
+
+       /** at which time was the sessionkey created (by whichever party) */
+       public int                      created;
+
+       /** is this host alive? timestamp of the time of the last-active point 
*/
+       public long                     isAlive;
+
+       /**  Status of the connection (STAT_XXX) */
+       public int                      status;
+
+       /** last sequence number received on this connection (highest) */
+       public int                      lastSequenceNumberReceived;
+
+       /** bit map indicating which of the 32 sequence numbers before the last 
were received
+        (good for accepting out-of-order packets and estimating reliability of 
the connection) */
+       public int                      lastPacketsBitmap;
+
+       /** last sequence number transmitted */
+       public int                      lastSequenceNumberSend;
+
+       /** buffer of entries waiting to be transmitted */
+       private SendEntry[]     sendBuffer;
+
+       /** time of the last send-attempt (to avoid solving knapsack's too 
often) */
+       private long            lastSendAttempt;
+
+       /** How frequent may we attempt to solve the knapsack problem and send 
a message out ?
+        Note that setting this value higher reduces the CPU overhead while a 
lower value can improve thoughput.
+        The value is adjusted according to how fast we perceive the CPU to be 
(and is also proportional too
+        how much bandwidth we have)... */
+       private long            maxSendFrequency;
+
+       /** a hash collision overflow chain */
+       public BufferEntry      overflowChain;
+
+       /** byte-per-minute limit for this connection (outbound bandwidth 
limit) */
+       private long    maxBytesPerMinute;
+
+       /** current bps (actually bytes per minute) for this connection 
(incremented every minute by maxBytesPerMinute,
+        bounded by maxBytesPerMinute * secondsInactive/2; may get negative if 
we have VERY high priority content) (outbound bandwidth limit) */
+       private long    available_send_window;
+
+       /** time of the last increment of available_send_window (outbound 
bandwidth limit) */
+       private long    last_bps_update;
+
+       /** how much traffic (bytes) did we receive on this connection since 
the last update-round ? (inbound bandwidth accounting) */
+       public long                     recently_received;
+
+       /** How valueable were the messages of this peer recently ? (inbound 
bandwidth accounting) */
+       public double           current_connection_value;
+
+       /** what is the limit that we communicated last? (byte per minute) 
(inbound bandwidth accounting) */
+       public int                      transmitted_limit;
+
+       /** the highest bandwidth limit that a well-behaved peer must have 
received by now */
+       public int                      max_transmitted_limit;
+
+       /** what is the limit that we are currently shooting for? (byte per 
minute) (inbound bandwidth accounting) */
+       public int                      idealized_limit;
+
+       /** To whom are we connected with this session ? */
+       private HostIdentity    sender;
+       /** */
+
+       private MappedFile      debugFile;
+
+
+       public BufferEntry( int cpuLoad, MappedFile dbg )
+       {
+               super(true);
+               session=null;
+               trust=0;
+               skey=null;
+               created=0;
+               isAlive=0;
+               status=ConnectionService.STAT_DOWN;
+               lastSequenceNumberReceived=0;
+               lastPacketsBitmap=0;
+               lastSequenceNumberSend=0;
+               sendBuffer=new SendEntry[] {};
+               lastSendAttempt=0;                      // never
+               maxSendFrequency=0;
+               overflowChain=null;
+
+               maxBytesPerMinute=ConnectionService.START_TRANSMIT_LIMIT; /* 
about 1 MTU for most transports */
+               available_send_window=maxBytesPerMinute;
+               last_bps_update=Scheduler.now();                // now
+
+               recently_received=0;
+               current_connection_value=0;
+               transmitted_limit  = ConnectionService.START_TRANSMIT_LIMIT;
+               max_transmitted_limit  = ConnectionService.START_TRANSMIT_LIMIT 
* 10; /* FIXME: "* 10" ONLY for 0.6.2c version
+                                       to make the transition more
+                                       easier; remove factor in next
+                                       release! */
+               idealized_limit=0;
+
+               maxSendFrequency=Scheduler.MILLIS_50*cpuLoad;
+
+               debugFile=dbg;
+       }
+
+       public String toString()
+       {
+               return "Buffer entry";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public HostIdentity getSender()
+       {
+               return sender;
+       }
+
+       public void setSender( HostIdentity h )
+       {
+               sender=(HostIdentity) PersistentHelper.copy(h);
+       }
+
+       public long getMaxBytesPerMinutes()
+       {
+               return maxBytesPerMinute;
+       }
+
+       public void setMaxBytesPerMinutes( long v )
+       {
+               maxBytesPerMinute=v;
+
+               if (available_send_window >= maxBytesPerMinute) {
+                       available_send_window = maxBytesPerMinute;
+                       last_bps_update=Scheduler.now();
+                       }
+       }
+
+       public void hasSent( int bytes )
+       {
+               long    b=bytes;
+               if (available_send_window > b)
+                       available_send_window -= b;
+               else
+                       available_send_window = 0; /* if we overrode limits, 
reset to 0 at least... */
+       }
+
+       /**
+        * Update available_send_window.  Call only when already synchronized.
+        */
+
+       protected void updateCurBPS()
+       {
+               long now;
+               long delta;
+
+               now=Scheduler.now();
+               if (now>last_bps_update) {
+                       delta = now - last_bps_update;
+
+//                     if (max_bpm * delta < 60 000 ms )
+//                             return;
+
+                       // check that delta is greater that the minimum elasped 
time to emit 1 byte...
+                       if (Scheduler.toMillis(delta)*maxBytesPerMinute >= 
60000) {
+//debug("+++ enough : delta = "+Scheduler.toMillis(delta)+"ms");
+                               
available_send_window+=Scheduler.toMinutes(maxBytesPerMinute * delta);
+
+                               if (available_send_window > maxBytesPerMinute * 
ConnectionService.MAX_BUF_FACT)
+                                       available_send_window = 
maxBytesPerMinute * ConnectionService.MAX_BUF_FACT;
+
+                               last_bps_update = now;
+
+//debug("+++ new = "+available_send_window);
+                               }
+                       }
+       }
+
+       public String infos()
+       {
+               String  hostName,skey2;
+               int             ttype;
+
+               hostName=sender.getName();
+               skey2=Utils.toBin(PersistentHelper.toBytes(skey));
+
+               ttype = 0;
+               if (session != null) {
+                       ttype = session.getTransport().getProtocol();
+                       }
+
+               return status+"-"+
+                       ttype+"-"+
+                       (trust & ConnectionService.TRUST_ACTUAL_MASK)+"-"+
+                       Scheduler.toSeconds(Scheduler.now()-isAlive)+"s "+
+                       "(of "+ConnectionService.SECONDS_INACTIVE_DROP+"s) "+
+                       "BPM "+recently_received+"r "+
+                       transmitted_limit+"t "+
+                       idealized_limit+"i-"+
+                       sendBuffer.length+": "+hostName+"-"+skey2;
+       }
+
+       public boolean hasEntries()
+       {
+               return sendBuffer.length>0;
+       }
+
+       public boolean acceptEntry()
+       {
+               return (sendBuffer.length<MAX_SEND_BUFFER_SIZE);
+       }
+
+       public void addEntry( SendEntry se, Stat queuedMessages )
+       {
+               SendEntry[]     ne;
+               double          apri;
+               int                     i;
+
+               assert(acceptEntry());
+
+               /* grow send buffer, insertion sort! */
+               ne = new SendEntry[sendBuffer.length+1];
+               apri=se.getPriorityRatio();
+
+               for (i=0; i<sendBuffer.length && 
sendBuffer[i].getPriorityRatio()>=apri; i++) {
+                       ne[i]=sendBuffer[i];
+                       }
+               ne[i++] = se;
+               while (i < sendBuffer.length+1) {
+                       ne[i] = sendBuffer[i-1];
+                       i++;
+                       }
+               sendBuffer = ne;
+
+               queuedMessages.inc();
+       }
+
+       public void discardEntries( Stat queuedMessages )
+       {
+               queuedMessages.sub(sendBuffer.length);
+               sendBuffer=new SendEntry[] {};
+       }
+
+       public ByteBuffer createMessage( int cpuLoad, int[] priority, 
PolicyService policy, Stat queuedMessages, Stat expiredMessages )
+       {
+               int[]           perm;
+               byte[]          plaintextMsg;
+               boolean[]       knapsackSolution;
+               P2PSequence     seqMsg;
+               long            expired;
+               int                     
headpos,tailpos,remainingBufferSize,targetSBLEN,i,j,p;
+
+               if (status == ConnectionService.STAT_DOWN) {
+                       return null;    // status is down, nothing to send! 
(should at least be wait-for-ping/pong or up)
+                       }
+
+               if (sendBuffer.length == 0) {
+                       debug(session.getRemote().getName()+" Message queue 
empty, nothing transmitted.");
+                       return null; /* nothing to send */
+                       }
+
+               /* recompute max send frequency */
+               if (maxBytesPerMinute<=0) {
+                       maxBytesPerMinute=1;
+                       }
+
+               maxSendFrequency = /* ms per message */
+                       session.getTransport().getMTU()  /* byte per message */
+                       / 
Scheduler.toMillis(Scheduler.minutes(maxBytesPerMinute )) /* bytes per ms */
+                       / 2; /* some head-room */
+
+               /* Also: allow at least MINIMUM_SAMPLE_COUNT knapsack
+                solutions for any MIN_SAMPLE_TIME! */
+               if (maxSendFrequency > ConnectionService.MIN_SAMPLE_TIME / 
ConnectionService.MINIMUM_SAMPLE_COUNT)
+                       maxSendFrequency = ConnectionService.MIN_SAMPLE_TIME / 
ConnectionService.MINIMUM_SAMPLE_COUNT;
+
+               // check if not too repetitive...
+               if (Scheduler.now()<lastSendAttempt+maxSendFrequency && 
sendBuffer.length<MAX_SEND_BUFFER_SIZE/4) {
+                       debug("Send frequency too high (CPU load), send 
deferred.");
+                       return null; /* frequency too high, wait */
+                       }
+
+               knapsackSolution=solveKnapsack(priority,cpuLoad);
+               if (knapsackSolution==null) {
+                       return null;
+                       }
+
+               /* test if receiver has enough bandwidth available!  */
+               updateCurBPS();
+               debug(session.getRemote().getName()+" Receiver window 
available: "+IOUtils.newSizeFormatter().format(new 
Long(available_send_window/60))+"/s (MTU: 
"+session.getTransport().getMTU()+").");
+
+               if (available_send_window < session.getTransport().getMTU()) {
+                       /* if we have a very high priority, we may
+                        want to ignore bandwidth availability (e.g. for HANGUP,
+                        which  has EXTREME_PRIORITY) */
+                       if (priority[0] < ConnectionService.EXTREME_PRIORITY) {
+                               debug(session.getRemote().getName()+" Bandwidth 
limits prevent sending (send window "+available_send_window+" too small).");
+                               return null; /* can not send, BPS available is 
too small */
+                               }
+                       }
+
+               expired = Scheduler.now() - 
Scheduler.seconds(ConnectionService.SECONDS_PINGATTEMPT);
+               /* if it's more than one connection "lifetime" old, always kill 
it! */
+
+               /* check if we (sender) have enough bandwidth available */
+               if (!policy.outgoingCheck(priority[0])) {
+                       int msgCap;
+
+                       lastSendAttempt=Scheduler.now();
+                       debug(session.getRemote().getName()+" Policy prevents 
sending message (priority too low: "+priority[0]+").");
+
+                       /* cleanup queue */
+                       if (cpuLoad > 50)
+                               msgCap = 4;
+                       else
+                               msgCap = 54 - cpuLoad;
+                       if (maxBytesPerMinute > 2)
+                               msgCap += 2 * (int) Math.log(maxBytesPerMinute);
+                       /* allow at least msgCap msgs in buffer */
+                       for (i=0;i<sendBuffer.length;i++) {
+                               SendEntry  entry = sendBuffer[i];
+                               if (sendBuffer.length <= msgCap)
+                                       break;
+                               if ( entry.isExpired(expired)) {
+                                       debug(session.getRemote().getName()+" 
Expiring message, expired "+entry.getSecondsExpired(Scheduler.now())+"s ago, 
queue size is "+sendBuffer.length+" (bandwidth stressed)");
+                                       expiredMessages.inc();
+
+                                       entry=null;
+                                       sendBuffer[i] = 
sendBuffer[sendBuffer.length-1];
+
+                                       SendEntry[]     tmp=sendBuffer;
+                                       sendBuffer=new 
SendEntry[sendBuffer.length-1];
+                                       
System.arraycopy(tmp,0,sendBuffer,0,sendBuffer.length-1);
+
+                                       queuedMessages.dec();
+                                       i--; /* go again for this slot */
+                                       }
+                               }
+                       return null; /* defer further */
+                       }
+
+               /* build message (start with sequence number) */
+               seqMsg = new P2PSequence(lastSequenceNumberSend);
+
+               plaintextMsg = new byte[session.getTransport().getMTU()];
+               PersistentHelper.write(seqMsg,ByteBuffer.wrap(plaintextMsg));
+
+               p = P2PSequence.SIZE;
+               perm = Crypto.permute(sendBuffer.length);
+
+               /* change permutation such that SE_FLAGS are obeyed */
+
+               headpos = 0;
+               tailpos = sendBuffer.length-1;
+               remainingBufferSize = sendBuffer.length;
+               for (i=0;i<sendBuffer.length;i++) {
+                       if (knapsackSolution[perm[i]]) {
+                               remainingBufferSize--;
+                               switch (sendBuffer[perm[i]].getFlags()) {
+                                       case SendEntry.SE_FLAG_NONE:
+                                               break;
+
+                                       case SendEntry.SE_FLAG_PLACE_HEAD:
+                                               /* swap slot with whoever is 
head now */
+                                               j = perm[headpos];
+                                               perm[headpos++] = perm[i];
+                                               perm[i] = j;
+                                               break;
+
+                                       case SendEntry.SE_FLAG_PLACE_TAIL:
+                                               /* swap slot with whoever is 
tail now */
+                                               j = perm[tailpos];
+                                               perm[tailpos--] = perm[i];
+                                               perm[i] = j;
+                                               }
+                               }
+                       }
+
+               targetSBLEN = 0; /* how many entries in sendBuffer afterwards? 
*/
+               for (i=0;i<sendBuffer.length;i++) {
+                       SendEntry entry = sendBuffer[perm[i]];
+                       if (knapsackSolution[perm[i]]) {
+                               entry.transferContentTo(plaintextMsg,p);
+
+                               p += entry.getContentSize();
+                               entry=null;
+                               sendBuffer[perm[i]] = null;
+                               }
+                       else {
+                               int msgCap;
+                               int l = cpuLoad;
+                               if (l >= 50) {
+                                       msgCap = 
session.getTransport().getMTU() / HashCode160.SIZE;
+                                       }
+                               else {
+                                       if (l <= 0)
+                                               l = 1;
+                                       msgCap = 
session.getTransport().getMTU() / HashCode160.SIZE+ (MAX_SEND_BUFFER_SIZE - 
session.getTransport().getMTU() / HashCode160.SIZE) / l;
+                                       }
+                               if (maxBytesPerMinute > 2) {
+                                       msgCap += 2 * (int) 
Math.log(maxBytesPerMinute);
+                                       if (msgCap >= MAX_SEND_BUFFER_SIZE-1)
+                                               msgCap = 
MAX_SEND_BUFFER_SIZE-2; /* try to make sure that there is always room... */
+                                       }
+                               if ( (remainingBufferSize > msgCap) && 
entry.isExpired(expired) ) {
+                                       debug(session.getRemote().getName()+" 
Expiring message, expired "+entry.getSecondsExpired(Scheduler.now())+"s ago, 
queue size is "+remainingBufferSize+" (other messages went through)");
+                                       expiredMessages.inc();
+                                       entry=null;
+                                       sendBuffer[perm[i]] = null;
+                                       remainingBufferSize--;
+                               }
+                               else
+                                       targetSBLEN++;
+                       }
+               }
+
+               /* cleanup/compact sendBuffer */
+               j = 0;
+               for (i=0;i<sendBuffer.length;i++)
+                       if (sendBuffer[i] != null)
+                               sendBuffer[j++] = sendBuffer[i];
+
+               queuedMessages.add(targetSBLEN - sendBuffer.length);
+
+               SendEntry[]     tmp2=sendBuffer;
+               sendBuffer=new SendEntry[targetSBLEN];
+               
System.arraycopy(tmp2,0,sendBuffer,0,Math.min(sendBuffer.length,targetSBLEN));
+
+
+               //temporaire : session.getTransport().getMTU() alloué, [0,p[ 
rempli
+               ByteBuffer      
ret=ByteBuffer.allocateDirect(session.getTransport().getMTU());
+               ret.put(plaintextMsg);
+               ret.position(p);
+               return ret;
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       protected boolean[] solveKnapsack( int[] pri, int cpuLoad )
+       {
+               boolean[]       knapsackSolution;
+               int                     approxProb,priority,i,j;
+
+               /* solve knapsack problem, compute accumulated priority */
+               knapsackSolution = new boolean[sendBuffer.length];
+
+               approxProb = cpuLoad;
+               if (approxProb > 50) {
+                       if (approxProb > 100)
+                               approxProb = 100;
+                       approxProb = 100 - approxProb; /* now value between 0 
and 50 */
+                       approxProb *= 2; /* now value between 0 [always approx] 
and 100 [never approx] */
+                       /* control CPU load probabilistically! */
+                       if (Crypto.nextInt(1+approxProb) == 0) {
+                               priority = 
approximateKnapsack(session.getTransport().getMTU() - 
P2PSequence.SIZE,knapsackSolution);
+                               logKnapsack("0 "+priority);
+                               }
+                       else {
+                               priority = 
solveKnapsack(session.getTransport().getMTU() - 
P2PSequence.SIZE,knapsackSolution);
+                               logKnapsack("1 "+priority);
+                               }
+                       }
+               else { /* never approximate < 50% CPU load */
+                       priority = 
solveKnapsack(session.getTransport().getMTU() - 
P2PSequence.SIZE,knapsackSolution);
+                       logKnapsack("2 "+priority);
+                       }
+               j = 0;
+               for (i=0;i<sendBuffer.length;i++) {
+                       if (knapsackSolution[i]) {
+                               j++;
+                               }
+                       }
+
+               if (j == 0) {
+                       log(Level.SEVERE,"ERROR: solveKnapsack selected "+j+" 
out of "+sendBuffer.length+" messages (MTU: "+(session.getTransport().getMTU() 
- P2PSequence.SIZE)+")");
+                       for (j=0; j<sendBuffer.length; j++) {
+                               log(Level.SEVERE,"ERROR: "+j+": length 
"+sendBuffer[j].getContentSize()+", priority: "+sendBuffer[j].getPriority());
+                               }
+                       return null;
+                       }
+               pri[0]=priority;
+               return knapsackSolution;
+       }
+
+       protected void logKnapsack( String str )
+       {
+               if (debugFile!=null) {
+                       debugFile.writeString(Scheduler.now()+" "+str+"\n");
+                       }
+       }
+
+       /**
+        * Approximate a solution to the 0-1 knapsack problem
+        * using a greedy heuristic.  This function assumes that
+        * the entries in the sendBuffer are ALREADY sorted
+        * (by priority/len).
+        *
+        * The code falls back to this function if the CPU is
+        * too busy.  As long as the CPU is idle, solveKnapsack
+        * is used.
+        *
+        * @param available what is the maximum length available?
+        * @param solution int[count] to store the solution as "YES" and "NO" 
values
+        * @return the overall priority that was achieved
+        */
+
+       protected int approximateKnapsack( int available, boolean[] solution )
+       {
+               SendEntry[]     entries;
+               int                     count,i,max,left;
+
+               entries = sendBuffer;
+               count = sendBuffer.length;
+               left = available;
+               max = 0;
+
+               for (i=0; i<count; i++) {
+                       if (entries[i].getContentSize() <= left) {
+                               solution[i] = true;
+                               left -= entries[i].getContentSize();
+                               max += entries[i].getPriority();
+                               }
+                       else {
+                               solution[i] = false;
+                               }
+                       }
+               return max;
+       }
+
+       /**
+        * Solve the 0-1 knapsack problem.  Given "count" "entries" of
+        * different "len" and "pri"ority and the amount of space "available",
+        * compute the "solution", which is the set of entries to transport.
+        *
+        * Solving this problem is NP complete in "count", but given that
+        * available is small, the complexity is actually
+        * "O(count*available)".
+        *
+        * @param available what is the maximum length available?
+        * @param solution boolean[count] to store the solution as "true" and 
"false" values
+        * @return the overall priority that was achieved
+        */
+
+       protected int solveKnapsack( int available, boolean[] solution )
+       {
+               long[]  v;
+               int[]   efflen;
+               long    startTime,endTime;
+               int             max,i,j;
+
+               if (available<0) {
+                       log(Level.SEVERE,"ERROR: available < 0 in solveKnapsack 
!");
+                       return -1;
+                       }
+
+               startTime=Scheduler.now();
+
+               // fast test: schedule everything ?
+               for (i=max=0; i<sendBuffer.length; i++) {
+                       max+=sendBuffer[i].getContentSize();
+                       }
+
+               if (max<=available) {
+                       // short cut: take everything !
+                       Arrays.fill(solution,true);
+
+                       for (i=max=0; i<sendBuffer.length; i++) {
+                               max+=sendBuffer[i].getPriority();
+                               }
+                       return max;
+                       }
+
+               // division of sizes & available by gcd(sizes,available) to 
reduce cost to O(count*available/gcd)
+               // in terms of CPU and memory. Since gcd is almost always at 
least 4, this is probably a good idea
+
+               max=available;
+               for (i=0; i<sendBuffer.length; i++) {
+                       max=gcd(max,sendBuffer[i].getContentSize());
+                       }
+               available/=max;
+
+               efflen=new int[sendBuffer.length];
+               for (i=0;i<sendBuffer.length;i++) {
+                       efflen[i] = sendBuffer[i].getContentSize()/max;
+                       }
+
+               // dynamic programming:  VARR(i,j) stores the maximum value of 
any subset of objects {1, ... i}
+               // that can fit into a knapsack of weight j.
+               v=new long[(sendBuffer.length+1) * (available+1)];
+               Arrays.fill(v,0);
+
+               for (j=1; j<=available; j++) {
+                       VARR(v,sendBuffer.length,0,j,-1);// = -1;
+                       }
+
+               for (i=1; i<=sendBuffer.length; i++) {
+                       for (j=0;j<=available;j++) {
+                               long take_val;
+                               long leave_val;
+
+                               take_val = -1;
+                               leave_val = VARR(v,sendBuffer.length,i-1,j);
+                               if (j >= efflen[i-1]) {
+                                       take_val = 
sendBuffer[i-1].getPriority() + VARR(v,sendBuffer.length,i-1, j-efflen[i-1]);
+                                       if (leave_val > take_val)
+                                               
VARR(v,sendBuffer.length,i,j,leave_val);// = leave_val;
+                                       else
+                                               
VARR(v,sendBuffer.length,i,j,take_val);// = take_val;
+                               } else
+                                       
VARR(v,sendBuffer.length,i,j,leave_val);// = leave_val;
+                               /*
+                                printf("i: %d j: %d (of %d) efflen: %d take: 
%d "
+                                "leave %d e[i-1].pri %d VAR(i-1,j-eff) %lld 
VAR(i,j) %lld",
+                                i,
+                                j,
+                                available,
+                                efflen[i-1],
+                                take_val,
+                                leave_val,
+                                entries[i-1].pri,
+                                VARR(i-1,j-efflen[i-1]),
+                                VARR(i,j));
+                                */
+                       }
+               }
+
+               // find slot with max value, prefer long messages !
+               max = 0;
+               j = -1;
+               for (i=0;i<=available;i++) {
+                       if (VARR(v,sendBuffer.length,sendBuffer.length, i) >= 
max) {
+                               j = i;
+                               max = (int) 
VARR(v,sendBuffer.length,sendBuffer.length, i);//todo: perte de precision ???
+                               }
+                       }
+
+               // reconstruct selection
+               Arrays.fill(solution,false);
+
+               for (i=sendBuffer.length; i>0; i--) {
+                       if (j>=efflen[i-1]) {
+                               if (VARR(v,sendBuffer.length,i-1, 
j-efflen[i-1]) + sendBuffer[i-1].getPriority() == 
VARR(v,sendBuffer.length,i,j)) {
+                                       j -= efflen[i-1];
+                                       solution[i-1] = true;
+                                       }
+                               }
+                       }
+
+               if (j!=0) {
+                       trace("Knapsack error !!!");
+                       }
+
+               endTime=Scheduler.now();
+
+               debug("Elapsed time (Knapsack) : 
"+Scheduler.toMillis(endTime-startTime)+"ms");
+
+               return max;
+       }
+
+       protected long VARR( long[] v, int count, int i, int j )
+       {
+               return v[i+j*(count+1)];
+       }
+
+       protected void VARR( long[] v, int count, int i, int j, long result )
+       {
+               v[i+j*(count+1)]=result;
+       }
+
+       /**
+        * Compute the greatest common denominator (Euklid).
+        *
+        * @param a
+        * @param b
+        * @return gcd(a,b)
+        */
+
+       protected int gcd( int a, int b )
+       {
+               while (a != 0) {
+                       int t = a;
+                       a = b % a;
+                       b = t;
+                       }
+               return b;
+       }
+}

Added: freeway/src/org/gnu/freeway/server/BufferEntryCallback.java
===================================================================
--- freeway/src/org/gnu/freeway/server/BufferEntryCallback.java 2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/server/BufferEntryCallback.java 2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,19 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.server;
+
+/**
+ * Type of a callback method on every buffer.
+ */
+
+public interface BufferEntryCallback
+{
+       /**
+        * @param be the buffer entry
+        * @param data context for callee
+        */
+
+       public void callback( BufferEntry be, Object data );
+}

Added: freeway/src/org/gnu/freeway/server/BufferFillCallback.java
===================================================================
--- freeway/src/org/gnu/freeway/server/BufferFillCallback.java  2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/server/BufferFillCallback.java  2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,26 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.server;
+
+import org.gnu.freeway.util.crypto.*;
+
+import java.nio.*;
+
+/**
+ * Type of a send callback to fill up buffers.
+ */
+
+public interface BufferFillCallback
+{
+       /**
+        * Fill up the buffer where GNUNet is building messages with specific 
content.
+        *
+        * @param receiver      the receiver of the message
+        * @param buf           the buffer to fill up
+        * @return                      true if succeeded, false otherwise
+        */
+
+       public boolean fillBuffer( HostIdentity receiver, ByteBuffer buf );
+}

Added: freeway/src/org/gnu/freeway/server/CSGetCSMessageSupported.java
===================================================================
--- freeway/src/org/gnu/freeway/server/CSGetCSMessageSupported.java     
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/server/CSGetCSMessageSupported.java     
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,74 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.server;
+
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * Query protocol supported message. Contains the type of the message we are 
requesting the status of.
+ */
+
+public class CSGetCSMessageSupported extends CSMessage
+{
+       public static final int SIZE    =       10;
+
+       /** The type of the CS message we want to know the status of. */
+       private int     csType;
+
+
+       public CSGetCSMessageSupported()
+       {
+               super(IS_CS_MESSAGE_SUPPORTED);
+               csType=0;
+       }
+
+       public CSGetCSMessageSupported( int type )
+       {
+               this();
+               csType=type;
+       }
+
+       public String toString()
+       {
+               return "CS message supported [csType="+csType+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int getCSType()
+       {
+               return csType;
+       }
+
+       public int getByteSize()
+       {
+               return SIZE;
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               int     reserved,size,type;
+
+               size=buf.getShort() & 0x0000ffff;
+               type=buf.getShort() & 0x0000ffff;
+               reserved=buf.getInt();
+               err.reportIf(reserved!=0,"Reserved slot should be 0'ed.");
+               csType=buf.getShort() & 0x0000ffff;
+
+               err.reportIf(size!=SIZE,"Bad size !");
+               err.reportIf(type!=IS_CS_MESSAGE_SUPPORTED,"Bad type !");
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               buf.putShort((short) SIZE);
+               buf.putShort((short) IS_CS_MESSAGE_SUPPORTED);
+               buf.putInt(0);
+               buf.putShort((short) csType);
+       }
+}

Added: freeway/src/org/gnu/freeway/server/CSGetClientCount.java
===================================================================
--- freeway/src/org/gnu/freeway/server/CSGetClientCount.java    2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/server/CSGetClientCount.java    2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,54 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.server;
+
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ *
+ */
+
+public class CSGetClientCount extends CSMessage
+{
+       public static final int SIZE    =       4;
+
+
+       public CSGetClientCount()
+       {
+               super(IS_CLIENT_COUNT);
+       }
+
+       public String toString()
+       {
+               return "Get client count c/s message";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int getByteSize()
+       {
+               return SIZE;
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               int     size,type;
+
+               size=buf.getShort() & 0x0000ffff;
+               err.reportIf(size!=SIZE,"bad size !");
+
+               type=buf.getShort() & 0x0000ffff;
+               err.reportIf(type!=IS_CLIENT_COUNT,"bad type !");
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               buf.putShort((short) SIZE);
+               buf.putShort((short) IS_CLIENT_COUNT);
+       }
+}

Added: freeway/src/org/gnu/freeway/server/CSGetHostInfo.java
===================================================================
--- freeway/src/org/gnu/freeway/server/CSGetHostInfo.java       2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/server/CSGetHostInfo.java       2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,83 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.server;
+
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ *
+ */
+
+public class CSGetHostInfo extends CSMessage
+{
+       public static final int SIZE    =       HostIdentity.SIZE+4;
+
+       private HostIdentity    host;
+
+
+       public CSGetHostInfo()
+       {
+               super(IS_GET_HOST_INFO);
+               host=null;
+       }
+
+       public CSGetHostInfo( HostIdentity h )
+       {
+               this();
+               host=(HostIdentity) PersistentHelper.copy(h);
+       }
+
+       public String toString()
+       {
+               return "Get host information [host="+host+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public HostIdentity getHost()
+       {
+               return host;
+       }
+
+       public int getByteSize()
+       {
+               return SIZE;
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               int     size,type;
+
+               size=buf.getShort() & 0x0000ffff;
+               err.reportIf(size!=SIZE,"bad size !");
+
+               type=buf.getShort() & 0x0000ffff;
+               err.reportIf(type!=IS_GET_HOST_INFO,"bad type !");
+
+               host=new HostIdentity();
+               host.readBytes(buf,err);
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               int     i;
+
+               buf.putShort((short) SIZE);
+               buf.putShort((short) IS_GET_HOST_INFO);
+
+               if (host!=null) {
+                       host.writeBytes(buf);
+                       }
+               else {
+                       for (i=0; i<HostIdentity.SIZE; i++) {
+                               buf.put((byte) 0);
+                               }
+                       }
+       }
+}

Added: freeway/src/org/gnu/freeway/server/CSGetOptionReply.java
===================================================================
--- freeway/src/org/gnu/freeway/server/CSGetOptionReply.java    2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/server/CSGetOptionReply.java    2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,82 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.server;
+
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * Reply with option value.
+ */
+
+//fixme: encoding pbs
+
+public class CSGetOptionReply extends CSMessage
+{
+       public static final int SIZE            =       4;
+
+       /** */
+       private String  value;
+
+
+       public CSGetOptionReply()
+       {
+               super(IS_GET_OPTION_REPLY);
+               value="";
+       }
+
+       public CSGetOptionReply( String str )
+       {
+               this();
+               value=str;
+       }
+
+       public String toString()
+       {
+               return "Get option reply [value="+value+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public String getValue()
+       {
+               return value;
+       }
+
+       public int getByteSize()
+       {
+               return SIZE+value.getBytes().length+1;
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               byte[]  b;
+               int             size,type;
+               byte            zero;
+
+               size=buf.getShort() & 0x0000ffff;
+               err.reportIf(size<SIZE+1,"bad size !");
+
+               type=buf.getShort() & 0x0000ffff;
+               err.reportIf(type!=IS_GET_OPTION_REPLY,"bad type !");
+
+               b=new byte[size-SIZE-1];
+               buf.get(b);
+               value=new String(b);
+
+               zero=buf.get();
+               err.reportIf(zero!=0,"string is not null-terminated !");
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               buf.putShort((short) getByteSize());
+               buf.putShort((short) IS_GET_OPTION_REPLY);
+               buf.put(value.getBytes());
+               buf.put((byte) 0);
+       }
+}

Added: freeway/src/org/gnu/freeway/server/CSGetOptionRequest.java
===================================================================
--- freeway/src/org/gnu/freeway/server/CSGetOptionRequest.java  2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/server/CSGetOptionRequest.java  2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,136 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.server;
+
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+import java.util.*;
+import java.util.logging.*;
+
+/**
+ * Request for option value.
+ */
+
+public class CSGetOptionRequest extends CSMessage
+{
+       private static final int        CS_GET_OPTION_REQUEST_OPT_LEN           
=       32;
+
+       public static final int SIZE                                            
                =       4+CS_GET_OPTION_REQUEST_OPT_LEN*2;
+
+       /** */
+       private String  section;
+
+       /** */
+       private String  option;
+
+
+       public CSGetOptionRequest()
+       {
+               super(IS_GET_OPTION_REQUEST);
+               section="";
+               option="";
+       }
+
+       public CSGetOptionRequest( String s, String o )
+       {
+               this();
+               setSection(s,o);
+       }
+
+       public String toString()
+       {
+               return "Get option request [section="+section+", 
option="+option+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public String getSection()
+       {
+               return section;
+       }
+
+       public String getOption()
+       {
+               return option;
+       }
+
+       public void setSection( String s, String o )
+       {
+               Logger  logger;
+
+               if (s.getBytes().length>=CS_GET_OPTION_REQUEST_OPT_LEN) {
+                       logger=Logger.getLogger(getClass().getName());
+                       logger.log(Level.WARNING,"Section name '"+s+"' is too 
long...");
+                       }
+               if (o.getBytes().length>=CS_GET_OPTION_REQUEST_OPT_LEN) {
+                       logger=Logger.getLogger(getClass().getName());
+                       logger.log(Level.WARNING,"Option name '"+o+"' is too 
long...");
+                       }
+               section=s;
+               option=o;
+       }
+
+       public int getByteSize()
+       {
+               return SIZE;
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               byte[]  b;
+               int             size,type;
+
+               size=buf.getShort() & 0x0000ffff;
+               err.reportIf(size!=SIZE,"bad size !");
+
+               type=buf.getShort() & 0x0000ffff;
+               err.reportIf(type!=IS_GET_OPTION_REQUEST,"bad type !");
+
+               b=new byte[CS_GET_OPTION_REQUEST_OPT_LEN];
+               buf.get(b);
+               section=padded2String(b);
+               buf.get(b);
+               option=padded2String(b);
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               byte[]  b;
+
+               buf.putShort((short) SIZE);
+               buf.putShort((short) IS_GET_OPTION_REQUEST);
+
+               b=new byte[CS_GET_OPTION_REQUEST_OPT_LEN];
+               string2Padded(section,b);
+               buf.put(b);
+               string2Padded(option,b);
+               buf.put(b);
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       //todo: à supprimer
+       public static String padded2String( byte[] b )
+       {
+               int     i;
+
+               for (i=0; i<b.length && b[i]!=0; i++) {}
+               return new String(b,0,i);
+       }
+
+       //todo: à supprimer
+       public static void string2Padded( String str, byte[] b )
+       {
+               byte[]  tmp;
+
+               tmp=str.getBytes();
+
+               Arrays.fill(b,(byte) 0);
+               System.arraycopy(tmp,0,b,0,Math.min(tmp.length,b.length-1));
+       }
+}

Added: freeway/src/org/gnu/freeway/server/CSGetP2PMessageSupported.java
===================================================================
--- freeway/src/org/gnu/freeway/server/CSGetP2PMessageSupported.java    
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/server/CSGetP2PMessageSupported.java    
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,74 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.server;
+
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * Query protocol supported message. Contains the type of the message we are 
requesting the status of.
+ */
+
+public class CSGetP2PMessageSupported extends CSMessage
+{
+       public static final int SIZE    =       10;
+
+       /** The type of the P2P message we want to know the status of. */
+       private int     p2pType;
+
+
+       public CSGetP2PMessageSupported()
+       {
+               super(IS_P2P_MESSAGE_SUPPORTED);
+               p2pType=0;
+       }
+
+       public CSGetP2PMessageSupported( int type )
+       {
+               this();
+               p2pType=type;
+       }
+
+       public String toString()
+       {
+               return "P2P message supported [p2pType="+p2pType+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int getP2PType()
+       {
+               return p2pType;
+       }
+
+       public int getByteSize()
+       {
+               return SIZE;
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               int     reserved,size,type;
+
+               size=buf.getShort() & 0x0000ffff;
+               type=buf.getShort() & 0x0000ffff;
+               reserved=buf.getInt();
+               err.reportIf(reserved!=0,"Reserved slot should be 0'ed.");
+               p2pType=buf.getShort() & 0x0000ffff;
+
+               err.reportIf(size!=SIZE,"Bad size !");
+               err.reportIf(type!=IS_P2P_MESSAGE_SUPPORTED,"Bad type !");
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               buf.putShort((short) SIZE);
+               buf.putShort((short) IS_P2P_MESSAGE_SUPPORTED);
+               buf.putInt(0);
+               buf.putShort((short) p2pType);
+       }
+}

Added: freeway/src/org/gnu/freeway/server/CSHostInfo.java
===================================================================
--- freeway/src/org/gnu/freeway/server/CSHostInfo.java  2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/server/CSHostInfo.java  2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,119 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.server;
+
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ *
+ */
+
+public class CSHostInfo extends CSMessage
+{
+       public static final int SIZE    =       HostIdentity.SIZE+8;
+
+       private HostIdentity    host;
+       private P2PHello[]              helos;
+       private int                             size;
+
+
+       public CSHostInfo()
+       {
+               super(IS_HOST_INFO);
+               size=SIZE;
+               host=null;
+               helos=new P2PHello[] {};
+       }
+
+       public CSHostInfo( HostIdentity h )
+       {
+               this();
+               host=(HostIdentity) PersistentHelper.copy(h);
+       }
+
+       public String toString()
+       {
+               return "Host information [host="+host+", 
helos.length="+helos.length+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public HostIdentity getHost()
+       {
+               return host;
+       }
+
+       public P2PHello[] getHELOs()
+       {
+               return helos;
+       }
+
+       public void setHELOs( P2PHello[] h )
+       {
+               int     i;
+
+               helos=h;
+
+               size=SIZE;
+               for (i=0; i<helos.length; i++) {
+                       size+=helos[i].getByteSize();
+                       }
+       }
+
+       public int getByteSize()
+       {
+               return size;
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               int     count,type,i,j;
+
+               size=buf.getShort() & 0x0000ffff;
+               err.reportIf(size<SIZE,"bad size !");
+
+               type=buf.getShort() & 0x0000ffff;
+               err.reportIf(type!=IS_HOST_INFO,"bad type !");
+
+               host=new HostIdentity();
+               host.readBytes(buf,err);
+
+               count=buf.getInt();
+
+               helos=new P2PHello[count];
+               for (i=j=0; i<count; i++) {
+                       helos[i]=new P2PHello();
+                       helos[i].readBytes(buf,err);
+                       j+=helos[i].getByteSize();
+                       }
+               err.reportIf(size!=SIZE+j,"bad size !");
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               int     i;
+
+               buf.putShort((short) size);
+               buf.putShort((short) IS_HOST_INFO);
+
+               if (host!=null) {
+                       host.writeBytes(buf);
+                       }
+               else {
+                       for (i=0; i<HostIdentity.SIZE; i++) {
+                               buf.put((byte) 0);
+                               }
+                       }
+
+               buf.putInt(helos.length);
+               for (i=0; i<helos.length; i++) {
+                       helos[i].writeBytes(buf);
+                       }
+       }
+}

Added: freeway/src/org/gnu/freeway/server/CSShutdownRequest.java
===================================================================
--- freeway/src/org/gnu/freeway/server/CSShutdownRequest.java   2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/server/CSShutdownRequest.java   2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,53 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.server;
+
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ */
+
+public class CSShutdownRequest extends CSMessage
+{
+       public static final int SIZE    =       4;
+
+
+       public CSShutdownRequest()
+       {
+               super(IS_SHUTDOWN);
+       }
+
+       public String toString()
+       {
+               return "Shutdown request message";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int getByteSize()
+       {
+               return SIZE;
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               int     size,type;
+
+               size=buf.getShort() & 0x0000ffff;
+               type=buf.getShort() & 0x0000ffff;
+
+               err.reportIf(size!=SIZE,"Invalid size !");
+               err.reportIf(type!=IS_SHUTDOWN,"Invalid type !");
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               buf.putShort((short) SIZE);
+               buf.putShort((short) IS_SHUTDOWN);
+       }
+}

Added: freeway/src/org/gnu/freeway/server/CSStatistics.java
===================================================================
--- freeway/src/org/gnu/freeway/server/CSStatistics.java        2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/server/CSStatistics.java        2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,158 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.server;
+
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+import java.util.*;
+
+/**
+ * Statistics message. Contains the timestamp and an aribtrary
+ * (bounded by the maximum CS message size!) number of statistical
+ * numbers. If needed, several messages are used.
+ * Statistics message. Contains the timestamp and an aribtrary
+ * (bounded by the maximum CS message size!) number of statistical
+ * numbers. If needed, several messages are used.
+ */
+
+public class CSStatistics extends CSMessage
+{
+       public static final int SIZE    =       20;
+
+       /** */
+       private int             size;
+
+       /** Start time of statistics service (in seconds). */
+       private int             startTime;
+
+       /** total number of statistical counters */
+       private int             totalCounters;
+
+       /** statistical counters */
+       private List            stats;
+
+
+       public CSStatistics()
+       {
+               super(IS_STATISTICS);
+               size=SIZE;
+               startTime=(int) Scheduler.toSeconds(Scheduler.now());
+               totalCounters=0;
+               stats=new ArrayList();
+       }
+
+       public CSStatistics( int start )
+       {
+               this();
+               startTime=start;
+       }
+
+       public String toString()
+       {
+               return "Statistics message [counters="+stats.size()+", 
totalCounters="+totalCounters+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int getStartTime()
+       {
+               return startTime;
+       }
+
+       public int getTotalCounters()
+       {
+               return totalCounters;
+       }
+
+       public void setTotalCounters( int total )
+       {
+               totalCounters=total;
+       }
+
+       public void add( Stat stat )
+       {
+               stats.add(stat);
+               size+=stat.getByteSize();
+       }
+
+       public Stat removeLast()
+       {
+               Stat    stat;
+
+               assert(stats.size()>0);
+
+               stat=(Stat) stats.remove(stats.size()-1);
+               size-=stat.getByteSize();
+               return stat;
+       }
+
+       public Stat[] getStatistics()
+       {
+               return (Stat[]) stats.toArray(new Stat[stats.size()]);
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int getByteSize()
+       {
+               return size;
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               Stat    stat;
+               int                     type,i,n,reserved,statCounters;
+
+               size=buf.getShort() & 0x0000ffff;
+               type=buf.getShort() & 0x0000ffff;
+
+               reserved=buf.getInt();
+               err.reportIf(reserved!=0,"'reserved' integer not null !");
+
+               startTime=buf.getInt();
+               totalCounters=buf.getInt();
+
+               statCounters=buf.getInt();
+               err.reportIf(statCounters<0,"Negative numbers of counters");
+
+               n=SIZE;
+               stats.clear();
+               for (i=0; i<statCounters; i++) {
+                       stat=new Stat("");
+                       stat.readBytes(buf,err);
+                       stats.add(stat);
+
+                       n+=stat.getByteSize();
+                       }
+
+               err.reportIf(type!=CSMessage.IS_STATISTICS,"bad type !");
+               err.reportIf(startTime<=0,"Invalid start time !");
+               err.reportIf(totalCounters<0,"Invalid total counter !");
+               err.reportIf(statCounters<0,"Invalid stat counter !");
+               err.reportIf(size!=n,"Bad size: "+size+"!="+n);
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               Stat    stat;
+               int                     i;
+
+               buf.putShort((short) size);
+               buf.putShort((short) IS_STATISTICS);
+               buf.putInt(0);
+               buf.putInt(startTime);
+               buf.putInt(totalCounters);
+               buf.putInt(stats.size());
+
+               for (i=0; i<stats.size(); i++) {
+                       stat=(Stat) stats.get(i);
+                       stat.writeBytes(buf);
+                       }
+       }
+}

Added: freeway/src/org/gnu/freeway/server/CSStatisticsRequest.java
===================================================================
--- freeway/src/org/gnu/freeway/server/CSStatisticsRequest.java 2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/server/CSStatisticsRequest.java 2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,71 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.server;
+
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ */
+
+public class CSStatisticsRequest extends CSMessage
+{
+       public static final int SIZE            =       6;
+
+       private int     minLevel;
+
+
+       public CSStatisticsRequest()
+       {
+               super(IS_GET_STATISTICS);
+               minLevel=Stat.NORMAL;
+       }
+
+       public CSStatisticsRequest( int level )
+       {
+               this();
+               minLevel=level;
+       }
+
+       public String toString()
+       {
+               return "Statistics request [minLevel="+minLevel+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int getMinLevel()
+       {
+               return minLevel;
+       }
+
+       public int getByteSize()
+       {
+               return SIZE;
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               int     size,type;
+
+               size=buf.getShort() & 0x0000ffff;
+               type=buf.getShort() & 0x0000ffff;
+
+               err.reportIf(size!=SIZE,"Invalid size !");
+               err.reportIf(type!=IS_GET_STATISTICS,"Invalid type !");
+
+               minLevel=buf.getShort() & 0x0000ffff;
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               buf.putShort((short) SIZE);
+               buf.putShort((short) IS_GET_STATISTICS);
+               buf.putShort((short) minLevel);
+       }
+}

Added: freeway/src/org/gnu/freeway/server/CSTrafficInfo.java
===================================================================
--- freeway/src/org/gnu/freeway/server/CSTrafficInfo.java       2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/server/CSTrafficInfo.java       2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,58 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.server;
+
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * Format of the reply-message to a CS_TRAFFIC_QUERY.
+ * A message of this format is send back to the client
+ * if it sends a CS_TRAFFIC_QUERY to gnunetd.
+ */
+
+public class CSTrafficInfo extends CSMessage
+{
+       public static final int SIZE    =       8;
+
+       /** The number of different message types we have seen in the last 
time. */
+       public int                                      count;
+
+       /** "count" traffic counters. */
+       public TrafficCounter[] counters;
+
+
+       public CSTrafficInfo( int n )
+       {
+               super(IS_TRAFFIC_INFO);
+               count=n;
+               counters=new TrafficCounter[n];
+       }
+
+       public String toString()
+       {
+               return "xxx";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int getByteSize()
+       {
+               return SIZE+TrafficCounter.SIZE*count;
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               throw new IllegalStateException();
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               throw new IllegalStateException();
+       }
+}

Added: freeway/src/org/gnu/freeway/server/CSTrafficRequest.java
===================================================================
--- freeway/src/org/gnu/freeway/server/CSTrafficRequest.java    2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/server/CSTrafficRequest.java    2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,62 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.server;
+
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * Request for CS_TRAFFIC_INFO.
+ */
+
+public class CSTrafficRequest extends CSMessage
+{
+       public static final int SIZE    =       8;
+
+       /** How many time units back should the statistics returned contain ?
+        Must be smaller or equal to HISTORY_SIZE. */
+
+       public int      timePeriod;
+
+
+       public CSTrafficRequest()
+       {
+               super(IS_TRAFFIC_QUERY);
+       }
+
+       public String toString()
+       {
+               return "xxx";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int getByteSize()
+       {
+               return SIZE;
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               int     size,type;
+
+               size=buf.getShort() & 0x0000ffff;
+               err.reportIf(size!=SIZE,"bad size");
+
+               type=buf.getShort() & 0x0000ffff;
+               err.reportIf(type!=IS_TRAFFIC_QUERY,"bad type !");
+
+               timePeriod=buf.getInt();
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               buf.putShort((short) SIZE);
+               buf.putShort((short) IS_TRAFFIC_QUERY);
+               buf.putInt(timePeriod);
+       }
+}

Added: freeway/src/org/gnu/freeway/server/Capability.java
===================================================================
--- freeway/src/org/gnu/freeway/server/Capability.java  2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/server/Capability.java  2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,24 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.server;
+
+/**
+ * Capability specification.
+ */
+
+public class Capability extends Object
+{
+       public static final int SIZE    =       8;
+
+       /** Limit number of bytes send to this peer per minute to "value". */
+       public static final int CAP_BANDWIDTH_RECV      =       0;
+
+       public int capabilityType;
+
+       public int value;
+
+
+
+}

Added: freeway/src/org/gnu/freeway/server/ClientExitHandler.java
===================================================================
--- freeway/src/org/gnu/freeway/server/ClientExitHandler.java   2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/server/ClientExitHandler.java   2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,16 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.server;
+
+import org.gnu.freeway.util.net.CSSession;
+
+/**
+ *
+ */
+
+public interface ClientExitHandler
+{
+       public void handle( CSSession client );
+}

Added: freeway/src/org/gnu/freeway/server/ClientServer.java
===================================================================
--- freeway/src/org/gnu/freeway/server/ClientServer.java        2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/server/ClientServer.java        2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,402 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.server;
+
+import org.gnu.freeway.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.net.*;
+
+import java.net.*;
+import java.nio.channels.*;
+import java.util.*;
+import java.util.logging.*;
+
+/**
+ * TCP server (gnunetd-client communication).
+ */
+
+public class ClientServer extends LoggedObject implements CSSessionHandler, 
CSHandler
+{
+       private Application             appp;
+       private Statistics              stats;
+       private PolicyService           policy;
+       private Prefs                   prefs;
+
+       /** Message handlers. */
+       private List                            handlers;
+
+       /** Handlers to call if client exits. */
+       private List                            exitHandlers;
+
+       private Stat                            bytesIn;
+
+       private Stat                            bytesOut;
+
+       private TCPServer               server;
+
+       private PersistentDecoder       decoder;
+
+
+       public ClientServer()
+       {
+               super(true);
+               handlers=new ArrayList();
+               exitHandlers=new LinkedList();
+               server=new TCPServer("C/S",this);
+               decoder=null;
+       }
+
+       public String toString()
+       {
+               return "TCP server service";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public void createDecoder()
+       {
+               CoreService     core;
+
+               if (decoder==null) {
+                       core=(CoreService) appp.service(CoreService.class);
+
+                       decoder=new PersistentDecoder();
+                       decoder.merge(core.createCSDecoder());
+                       }
+       }
+       /**
+        * Initialize the TCP port and listen for incoming client connections.
+        * @param app
+        */
+
+       public void start( Application app )
+       {
+               int     port;
+
+               appp=app;
+
+               stats=app.getStatistics();
+               bytesIn=stats.getHandle("# bytes received from 
clients",Stat.VERBOSE);
+               bytesOut=stats.getHandle("# bytes sent to 
clients",Stat.VERBOSE);
+
+               policy=(PolicyService) app.service(PolicyService.class);
+               prefs=app.getPreferences();
+
+               // register default handlers
+               registerCSHandler(CSMessage.IS_GET_STATISTICS,this);
+               registerCSHandler(CSMessage.IS_CS_MESSAGE_SUPPORTED,this);
+               registerCSHandler(CSMessage.IS_P2P_MESSAGE_SUPPORTED,this);
+               registerCSHandler(CSMessage.IS_GET_OPTION_REQUEST,this);
+
+               port=prefs.getInt("NETWORK","PORT",0);
+               if (!server.launch(port)) {
+                       return;
+                       }
+       }
+
+       /**
+        * Shutdown the module.
+        * Stop the server (but do not yet destroy the data structures)
+        */
+
+       public void stop()
+       {
+               int     i;
+
+               if (!server.shutdown()) {
+                       return;
+                       }
+
+               // unregister default handlers
+               unregisterCSHandler(CSMessage.IS_GET_OPTION_REQUEST,this);
+               unregisterCSHandler(CSMessage.IS_P2P_MESSAGE_SUPPORTED,this);
+               unregisterCSHandler(CSMessage.IS_CS_MESSAGE_SUPPORTED,this);
+               unregisterCSHandler(CSMessage.IS_GET_STATISTICS,this);
+
+               synchronized(handlers) {
+                       if (exitHandlers.size()>0) {
+                               log(Level.WARNING,"Remaining exit handlers : 
"+exitHandlers);
+                               exitHandlers.clear();
+                               }
+
+                       if (handlers.size()>0) {
+                               for (i=0; i<handlers.size(); i++) {
+                                       if (handlers.get(i)!=null) {
+                                               log(Level.WARNING,"Remaining 
handler at slot #"+i+" : "+handlers.get(i));
+                                               }
+                                       }
+                               handlers.clear();
+                               }
+                       }
+       }
+
+       public CSSession handleAccept( SocketChannel socket )
+       {
+               CSSession       hd;
+               InetAddress     ip;
+
+               hd=null;
+
+               // verify ip for eligibility
+               ip=socket.socket().getInetAddress();
+               if (!policy.isWhitelisted(ip)) {
+                       log(Level.WARNING,"Rejected unauthorized connection 
from "+ip.getHostAddress()+":"+socket.socket().getPort()+".");
+                       return null;
+                       }
+
+//             debug("Accepted connection from 
"+ip.getHostAddress()+":"+socket.socket().getPort()+".");
+
+               hd=new TCPSession(server);
+               return (hd.connect(socket,true) ? hd : null);
+       }
+
+       /**
+        * Handle data available on the TCP socket descriptor. This method
+        * first aquires a slot to register this socket for the writeBack
+        * method (@see writeBack) and then demultiplexes all TCP traffic
+        * received to the appropriate handlers.
+        * Handle a message (that was decrypted if needed).
+        * Checks the CRC and if that's ok, processes the
+        * message by calling the registered handler for
+        * each message part.
+        *
+        * @param s
+        * @param len
+        * @return
+        */
+
+       public boolean handleRead( CSSession s, int len )
+       {
+               CSMessage       msg;
+               CSHandler       hd;
+               int                     type;
+               boolean         err;
+
+               bytesIn.add(len);
+
+               createDecoder();
+
+               err=false;
+               for (msg=(CSMessage) s.receive(decoder); msg!=null && !err; 
msg=(CSMessage) s.receive(decoder)) {
+                       type=msg.getType();
+                       synchronized(handlers) {
+                               hd=getCSHandler(type);
+                               if (hd==null) {
+                                       log(Level.INFO,"Client-server message 
not understood (type is "+type+") !");
+                                       err=true;
+                                       }
+                               else {
+                                       hd.handle(s,msg);
+                                       }
+                               }
+                       }
+               return !err;
+       }
+
+       public boolean handleWrite( CSSession s, int len )
+       {
+               bytesOut.add(len);
+               return true;
+       }
+
+       public void handleDestroy( CSSession s )
+       {
+               ClientExitHandler       hd;
+               int                                     imax,i;
+
+               synchronized(handlers) {
+                       imax=exitHandlers.size();
+                       for (i=0; i<imax; i++) {
+                               hd=(ClientExitHandler) exitHandlers.get(i);
+                               hd.handle(s);
+                               }
+                       }
+       }
+
+       /**
+        * Return whether or not there is a method handler
+        * registered for a specific Client-Server message type.
+        *
+        * @param type the message type
+        * @return true if there is a handler for the type,
+        *      NO if there isn't
+        */
+
+       public boolean isCSHandlerRegistered( int type )
+       {
+               synchronized(handlers) {
+                       return (type>=0 && type<handlers.size() && 
handlers.get(type)!=null);
+                       }
+       }
+
+       public CSHandler getCSHandler( int type )
+       {
+               assert(type>=0);
+
+               synchronized(handlers) {
+                       return (CSHandler) (type<handlers.size() ? 
handlers.get(type) : null);
+                       }
+       }
+       /**
+        * Register a method as a handler for specific message types.
+        *
+        * @param type the message type
+        * @param hd the method to call if a message of that type is received, 
if the callback returns
+        *        false, processing of the message is discontinued afterwards 
(all other parts are ignored)
+        * @return true on success, false if there is already a handler for 
that type
+        */
+
+       public boolean registerCSHandler( int type, CSHandler hd )
+       {
+               synchronized(handlers) {
+                       if (type<0) {
+                               log(Level.WARNING,"Invalid type : "+type+" 
!!!");
+                               return false;
+                               }
+
+                       if (type<handlers.size() && handlers.get(type)!=null) {
+                               log(Level.WARNING,"Failed to register handler, 
slot "+type+" used.");
+                               return false;
+                               }
+
+                       while (handlers.size()<=type) {
+                               handlers.add(null);
+                               }
+
+                       handlers.set(type,hd);
+                       }
+               return true;
+       }
+
+       /**
+        * Unregister a method as a handler for specific message types.
+        *
+        * @param type the message type
+        * @param hd the method to call if a message of that type is received, 
if the callback returns
+        *        false, processing of the message is discontinued afterwards 
(all other parts are ignored)
+        * @return true on success, false if there is no or another handler for 
that type
+        */
+
+       public boolean unregisterCSHandler( int type, CSHandler hd )
+       {
+               synchronized(handlers) {
+                       if (type<0 || type>=handlers.size()) {
+                               log(Level.WARNING,"Invalid type : "+type+" 
!!!");
+                               return false;
+                               }
+
+                       if (handlers.get(type)!=hd) {
+                               log(Level.WARNING,"Another handler present at 
slot #"+type+".");
+                               return false;
+                               }
+
+                       handlers.set(type,null);
+                       return true;
+                       }
+       }
+
+       public boolean registerClientExitHandler( ClientExitHandler hd )
+       {
+               synchronized(handlers) {
+                       exitHandlers.add(hd);
+                       }
+               return true;
+       }
+
+       public boolean unregisterClientExitHandler( ClientExitHandler hd )
+       {
+               synchronized(handlers) {
+                       if (!exitHandlers.contains(hd)) {
+                               log(Level.WARNING,"Client exit handler not 
found ("+hd+") !");
+                               return false;
+                               }
+                       exitHandlers.remove(hd);
+                       return true;
+                       }
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public boolean handle( CSSession client, CSMessage msg )
+       {
+               if (msg instanceof CSStatisticsRequest) {
+                       return handleStatistics(client,(CSStatisticsRequest) 
msg);
+                       }
+               if (msg instanceof CSGetCSMessageSupported) {
+                       return 
handleCSSupported(client,(CSGetCSMessageSupported) msg);
+                       }
+               if (msg instanceof CSGetP2PMessageSupported) {
+                       return 
handleP2PSupported(client,(CSGetP2PMessageSupported) msg);
+                       }
+               if (msg instanceof CSGetOptionRequest) {
+                       return handleGetOption(client,(CSGetOptionRequest) msg);
+                       }
+               return false;
+       }
+
+       protected boolean handleStatistics( CSSession client, 
CSStatisticsRequest msg )
+       {
+               CSStatistics[]  resp;
+               int                             i;
+
+               resp=stats.createMessages(msg.getMinLevel());
+               for (i=0; i<resp.length && client.send(resp[i]); i++) {}
+               return (i==resp.length);
+       }
+
+       /**
+        * Handle a request to see if a particular client server message is 
supported.
+        * @param client
+        * @param msg
+        * @return
+        */
+
+       protected boolean handleCSSupported( CSSession client, 
CSGetCSMessageSupported msg )
+       {
+               int     type;
+
+               type=msg.getCSType();
+               return client.send(new CSResult(isCSHandlerRegistered(type)));
+       }
+
+       /**
+        * Handle a request to see if a particular p2p message is supported.
+        * @param client
+        * @param msg
+        * @return
+        */
+
+       protected boolean handleP2PSupported( CSSession client, 
CSGetP2PMessageSupported msg )
+       {
+               MessagesDispatcher      dispatcher;
+               int     type;
+
+               type=msg.getP2PType();
+
+               dispatcher=((Server) appp).getDispatcher();
+               return client.send(new 
CSResult(dispatcher.isP2PHandlerRegistered(type)));
+       }
+
+       /**
+        * @param sock
+        * @param req
+        * @return
+        */
+
+       protected boolean handleGetOption( CSSession sock, CSGetOptionRequest 
req )
+       {
+               String  val;
+               int             ival;
+
+               val=prefs.getString(req.getSection(),req.getOption(),null);
+               if (val==null) {
+                       ival=prefs.getInt(req.getSection(),req.getOption(),0);
+                       val=String.valueOf(ival);
+                       }
+               return sock.send(new CSGetOptionReply(val));
+       }
+}

Added: freeway/src/org/gnu/freeway/server/ConnectionService.java
===================================================================
--- freeway/src/org/gnu/freeway/server/ConnectionService.java   2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/server/ConnectionService.java   2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,2463 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.server;
+
+import org.gnu.freeway.*;
+import org.gnu.freeway.transport.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.io.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+import java.text.*;
+import java.util.*;
+import java.util.logging.*;
+
+/**
+ * module responsible for the peer-to-peer connections
+ *
+ * This file contains the connection table which lists all the current
+ * connections of the node with other hosts and buffers outgoing
+ * packets to these hosts.  The connection table also contains state
+ * information such as sessionkeys, credibility and the last time we
+ * had host activity.<p>
+ *
+ * This code is responsible for exchanging a sessionkey with another
+ * peer, grouping several messages into a larger packet, padding with
+ * noise, encryption and deferred sending of these messages.<p>
+ */
+
+public class ConnectionService extends AbstractService implements P2PHandler, 
CSHandler
+{
+       /** output knapsack priorities into a file? */
+       private static final boolean    DEBUG_PRIORITIES        =       true;
+
+       /** Priority for special administrative messages that for example 
overrules drop-rules. */
+       public static final int EXTREME_PRIORITY        =       0xFFFFFF;
+
+       /** If an attempt to establish a connection is not answered within 
150s, drop. */
+       public static final int SECONDS_NOPINGPONG_DROP =       150;
+
+       /** If an established connection is inactive for 5 minutes, drop. */
+       public static final int SECONDS_INACTIVE_DROP   =       300;
+
+       /** After 2 minutes on an inactive connection, probe the other node 
with a ping if we have achieved
+        less than 50% of our connectivity goal. */
+       public static final int SECONDS_PINGATTEMPT     =       120;
+
+       public static final int MAX_SEND_FREQUENCY = (int) Scheduler.MILLIS_50;
+
+       /** High priority message that needs to go through fast, but not if 
policies would be disregarded. */
+       public static final int ADMIN_PRIORITY  =       0xFFFF;
+
+       /** Masks to keep track when the trust has changed and to get the real 
trust value. */
+       public static final int TRUST_REFRESH_MASK      =       0x80000000;
+       public static final int TRUST_ACTUAL_MASK       =       0x7FFFFFFF;
+
+       /** If we under-shoot our bandwidth limitation in one time period, how 
much of that limit are we allowed
+        to 'roll-over' into the next period ? The number given here is a 
factor of the total per-minute bandwidth limit. */
+       public static final int MAX_BUF_FACT    =       2;
+
+       /** Expected MTU for a connection (1500 for Ethernet) */
+       public static final int EXPECTED_MTU    =       1500;
+
+       /** Send limit we announce to peers initially, around 1 MTU for most 
transp. */
+       public static final int START_TRANSMIT_LIMIT    =       1500;
+
+       /** How many MTU size messages to we want to transmit per 
SECONDS_INACTIVE_DROP interval ?
+        (must be >=4 to keep connection alive with reasonable probability). */
+       public static final int TARGET_MSG_SID  =       32;
+
+       /** Minimum number of sample messages (per peer) before we recompute 
traffic assignments ? */
+       public static final int MINIMUM_SAMPLE_COUNT    =       8;
+
+       /** What is the minimum number of bytes per minute that we allocate PER 
peer ?
+        (5 minutes inactivity timeout, 1500 MTU, 128 MSGs => 32 * 1500 / 5 = 
38400 bpm [ 160 bps]) */
+       public static final int MIN_BPM_PER_PEER        =       (TARGET_MSG_SID 
* EXPECTED_MTU * 60 / SECONDS_INACTIVE_DROP);
+
+       /** How often do we expect to re-run the traffic allocation code ?
+        (depends on MINIMUM_SAMPLE_COUNT and MIN_BPM_PER_PEER and MTU size).
+        With MSC 16 and 5 minutes inactivity timeout and TMSID 32 about every 
148s */
+       public static final int MIN_SAMPLE_TIME =       (int) 
((Scheduler.minutes(MINIMUM_SAMPLE_COUNT) * EXPECTED_MTU / MIN_BPM_PER_PEER));
+
+       /* status constants */
+
+       public static final int STAT_DOWN             = 0;
+       public static final int STAT_WAITING_FOR_PING = 1;
+       public static final int STAT_WAITING_FOR_PONG = 2;
+       public static final int STAT_UP               = 3;
+
+
+       private static final String     TRUSTDIR        =       "data/credit/";
+
+       private StatusCallsService      status;
+       private TransportService                transport;
+       private PolicyService                   policy;
+       private KnownHostsService               knownHosts;
+       private Prefs   prefs;
+       private PingPongService         pingPong;
+       private TrafficService          traffic;
+
+       /** The buffer containing all current connections. */
+       private BufferEntry[]                   connectionBuffer;
+
+       /** How many hosts are connected (connections up) ? */
+       private int                                     activeHosts;
+
+       /** Use noise padding for encrypted messages ? */
+       private boolean                         useRandomPadding;
+
+       /** Send callbacks for making better use of noise padding... */
+       private SendCallbackEntry[]     sendCallbacks;
+
+       /** Up connections statistic. */
+       private Stat                    upConnections;
+
+       /** Done connections statistic. */
+       private Stat                    downConnections;
+
+       /** Received session keys statistic. */
+       private Stat                    receivedSessionKeys;
+
+       /** Verified session keys statistic. */
+       private Stat                    verifiedSessionKeys;
+
+       /** Transmitted session keys statistic. */
+       private Stat                    transmittedSessionKeys;
+
+       /** Sent bytes statistic. */
+       private Stat                    sentBytes;
+
+       /** Noise bytes sent statistic. */
+       private Stat                    noiseBytesSent;
+
+       /** Queued messages statistic. */
+       private Stat                    queuedMessages;
+
+       /** Expired messages statistic. */
+       private Stat                    expiredMessages;
+
+       /** Lock for the service. */
+       private Object                          lock;
+
+       /** What is the available downstream bandwidth (in bytes per minute) ? 
*/
+       private long                                    maxBytesPerMinute;
+
+       /** Where do we store trust information ? */
+       private DirLocation                     trustDirectory;
+
+       private MappedFile                      debugFile;
+
+       private ScheduledTask                   countTask;
+       private ScheduledTask                   livenessTask;
+       private ScheduledTask                   flushTask;
+
+
+
+       public ConnectionService()
+       {
+               super("Connection");
+               setDebug(true);
+               connectionBuffer=new BufferEntry[] {};
+               useRandomPadding=true;
+       }
+
+       public String toString()
+       {
+               return "Connection service";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public void attach( ServiceManager mgr )
+       {
+               super.attach(mgr);
+               prefs=mgr.app().getPreferences();
+
+               knownHosts=(KnownHostsService) 
mgr.service(KnownHostsService.class);
+       }
+
+       /**
+        * Initialize this module.
+        */
+
+       public void init()
+       {
+               Statistics      stats;
+               DirLocation     home;
+
+               super.init();
+
+               stats=getManager().app().getStatistics();
+               expiredMessages=stats.getHandle("# messages expired (bandwidth 
stressed too long)");
+               receivedSessionKeys=stats.getHandle("# sessionkeys 
received",Stat.VERBOSE);
+               verifiedSessionKeys=stats.getHandle("# valid sessionkeys 
received",Stat.VERBOSE);
+               transmittedSessionKeys=stats.getHandle("# sessionkeys 
sent",Stat.VERBOSE);
+               downConnections=stats.getHandle("# connections 
shutdown",Stat.VERBOSE);
+               queuedMessages=stats.getHandle("# messages in all queues");
+               upConnections=stats.getHandle("# currently connected nodes");
+               noiseBytesSent=stats.getHandle("# bytes noise sent");
+               sentBytes=stats.getHandle("# encrypted bytes sent");
+               sendCallbacks=new SendCallbackEntry[] {};
+               lock=new Object();
+               connectionConfigChangeCallback();
+               activeHosts= 0;
+
+               home=getManager().app().getHome();
+
+               trustDirectory=home.getDirectory(TRUSTDIR);
+               trustDirectory.create();
+
+               debugFile=null;
+               if (DEBUG_PRIORITIES) {
+                       debugFile=home.getFile("knapsack_prio.txt").openNew();
+                       }
+       }
+
+       public void start()
+       {
+               Scheduler       scheduler;
+               MessagesDispatcher      dispatcher;
+
+               super.start();
+               status=(StatusCallsService) 
getManager().service(StatusCallsService.class);
+               transport=(TransportService) 
getManager().service(TransportService.class);
+               policy=(PolicyService) 
getManager().service(PolicyService.class);
+               pingPong=(PingPongService) 
getManager().service(PingPongService.class);
+               traffic=(TrafficService) 
getManager().service(TrafficService.class);
+
+               dispatcher=((Server) getManager().app()).getDispatcher();
+               
dispatcher.registerP2PHandler(P2PMessage.IS_SEQUENCE,P2PSequence.class,this);
+               
dispatcher.registerP2PHandler(P2PMessage.IS_HANGUP,P2PHangUp.class,this);
+               
dispatcher.registerP2PHandler(P2PMessage.IS_CAPABILITY,P2PCapability.class,this);
+               
dispatcher.registerP2PHandler(P2PMessage.IS_SESSION_KEY,P2PSessionKey.class,this);
+
+               ((Server) 
getManager().app()).registerCSHandler(CSMessage.IS_CLIENT_COUNT,CSGetClientCount.class,this);
+
+               countTask=new ScheduledTask("COUNT-CONNECTIONS",new 
EvalAction(this,"cronCountConnections"),Scheduler.SECS_30);
+               livenessTask=new ScheduledTask("DECREASE-LIVENESS",new 
EvalAction(this,"cronDecreaseLiveness"),Scheduler.MINUTES_1 / 
connectionBuffer.length);
+               flushTask=new ScheduledTask("FLUSH-TRUST-BUFFER",new 
EvalAction(this,"cronFlushTrustBuffer"),Scheduler.MINUTES_5);
+
+               scheduler=getManager().app().getScheduler();
+               scheduler.addJob(countTask,Scheduler.MINUTES_1);
+               scheduler.addJob(livenessTask,Scheduler.SECS_1);
+               scheduler.addJob(flushTask,Scheduler.MINUTES_5);
+
+               prefs.registerConfigurationUpdateCallback(new 
EvalAction(this,"connectionConfigChangeCallback"));
+       }
+
+       public void stop()
+       {
+               Scheduler       scheduler;
+               MessagesDispatcher      dispatcher;
+
+               prefs.unregisterConfigurationUpdateCallback(new 
EvalAction(this,"connectionConfigChangeCallback"));
+
+               scheduler=getManager().app().getScheduler();
+               scheduler.deleteJob(flushTask);
+               scheduler.deleteJob(livenessTask);
+               scheduler.deleteJob(countTask);
+
+               ((Server) 
getManager().app()).unregisterCSHandler(CSMessage.IS_CLIENT_COUNT,this);
+
+               dispatcher=((Server) getManager().app()).getDispatcher();
+               dispatcher.unregisterP2PHandler(P2PMessage.IS_SESSION_KEY,this);
+               dispatcher.unregisterP2PHandler(P2PMessage.IS_CAPABILITY,this);
+               dispatcher.unregisterP2PHandler(P2PMessage.IS_HANGUP,this);
+               dispatcher.unregisterP2PHandler(P2PMessage.IS_SEQUENCE,this);
+
+               super.stop();
+       }
+
+       /**
+        * Shutdown the connection module.
+        */
+
+       public void done()
+       {
+               int i;
+               BufferEntry be;
+
+               super.done();
+
+
+               for (i=0;i<connectionBuffer.length;i++) {
+                       be = connectionBuffer[i];
+                       while (be != null) {
+                               log(Level.FINEST,"Closing connection: 
shutdown");
+                               shutdownConnection(be);
+                               flushHostCredit(be, null);
+
+                               be = be.overflowChain;
+                               }
+                       }
+               lock=null;
+
+               connectionBuffer = null;
+
+               trustDirectory = null;
+               sendCallbacks = null;
+
+               if (DEBUG_PRIORITIES) {
+                       debugFile.close();
+                       }
+       }
+
+       /**
+        * From time to time, do a recount on how many hosts are connected.
+        */
+
+       public void cronCountConnections()
+       {
+               int act;
+               int i;
+               BufferEntry be;
+               BufferEntry tmp;
+
+               act = 0;
+               synchronized(lock) {
+                       for (i=0;i< connectionBuffer.length;i++) {
+                               be = connectionBuffer[i];
+
+                               tmp = be;
+                               while(null != tmp) {
+                                       if (tmp.status == STAT_UP)
+                                               act++;
+                                       tmp = tmp.overflowChain;
+                               }
+
+                       }
+                       activeHosts = act;
+                       upConnections.set(act);
+                       }
+       }
+
+       /**
+        * Write host-infromation to a file - flush the buffer entry!
+        * Assumes synchronized access.
+        * @param be
+        * @param unused
+        */
+
+       protected void flushHostCredit( BufferEntry be, Object unused )
+       {
+               String  fil,fn;
+               FileLocation            f;
+               MappedFile      mm;
+
+               if ((be.trust & TRUST_REFRESH_MASK) == 0)
+                       return; /* unchanged */
+               be.trust = be.trust & TRUST_ACTUAL_MASK;
+
+               fil=be.getSender().getName();
+               fn = trustDirectory.getPath()+"/"+fil;
+
+               if (be.trust == 0) {
+                       f=new FileLocation(fn);
+                       if (!f.delete()) {
+                               log(Level.INFO,"Could not unlink 
\""+f.getLabel()+"\".");
+                               }
+                       }
+               else {
+                       mm=new FileLocation(fn).openNew();
+                       mm.writeInt(be.trust);
+                       mm.close();
+                       }
+       }
+
+       /**
+        * Send a buffer; assumes that access is already synchronized.  This
+        * message solves the knapsack problem, assembles the message
+        * (callback to build parts from knapsack, callbacks for padding,
+        * random noise padding, crc, encryption) and finally hands the
+        * message to the transport service.
+        *
+        * @param be connection of the buffer that is to be transmitted
+        */
+
+       protected void sendBuffer( BufferEntry be )
+       {
+               ByteBuffer      buf;
+               P2PNoise                noise;
+               int[]           priority;
+               int                     plen,ptyp,i,j,pos;
+
+               // fast ways out
+               if (be==null) {
+                       log(Level.WARNING,"Can't send null buffer.");
+                       return;
+                       }
+
+               priority=new int[1];
+               
buf=be.createMessage(status.getCPULoad(),priority,policy,queuedMessages,expiredMessages);
+               if (buf==null) {
+                       return;
+                       }
+
+               // still room left? try callbacks !
+               for (i=0; i<sendCallbacks.length; i++) {
+                       if (sendCallbacks[i].minimumPadding<=buf.remaining()) {
+                               
sendCallbacks[i].callback.fillBuffer(be.getSender(),buf);
+                               }
+                       }
+
+               // finally padd with noise
+               if (buf.remaining()>=4 && useRandomPadding) {//todo: methode 
sur P2PNoise qui cree un bruit (si >=4)
+                       noise=new P2PNoise(buf.remaining()-4);
+                       PersistentHelper.write(noise,buf);
+
+                       noiseBytesSent.add(noise.getByteSize());
+                       }
+
+               // for each built message, update associated traffic counter
+               for (j=0; j<buf.position(); j+=plen) {
+                       //todo: plus portable ! (avec decoder ??)
+                       plen=buf.getShort(j) & 0x0000ffff;
+                       ptyp=buf.getShort(j+2) & 0x0000ffff;
+                       traffic.updateTrafficSendCounter(ptyp, plen);
+                       }
+
+               if (be.session==null) {
+                       return; //todo: peut se produire ???
+                       }
+
+               pos=buf.position();
+               sentBytes.add(pos);
+
+               debug(be.session.getRemote().getName()+" Calling transport 
layer to send "+pos+" bytes to "+be.session.getRemoteAddress()+".");
+
+               if (be.session.send(buf,be.skey)) {
+                       be.hasSent(be.session.getTransport().getMTU());
+                       be.lastSequenceNumberSend++;
+                       }
+               else if (priority[0]>=EXTREME_PRIORITY) {
+                       // priority is *very* high and regular send failed
+                       // could be that nonblocking send fails but "reliable" 
send would succeed => try reliable send !
+
+                       if (be.session.sendReliable(buf,be.skey)) {
+                               be.hasSent(be.session.getTransport().getMTU());
+                               be.lastSequenceNumberSend++;
+                               }
+                       }
+       }
+
+       /**
+        * Append a message to the current buffer. This method
+        * assumes that the access to be is already synchronized.
+        *
+        * @param be on which connection to transmit
+        * @param se what to transmit (with meta-data)
+        */
+
+       protected void appendToBuffer( BufferEntry be, SendEntry se )
+       {
+               if (se == null) {
+                       log(Level.WARNING,"WARNING: appendToBuffer got garbage. 
Ignored.");
+                       return;
+                       }
+               if (se.getContentSize() == 0) {
+                       log(Level.WARNING,"WARNING: appendToBuffer got entry of 
size 0. Ignored.");
+                       return;
+                       }
+
+               debug(be.getSender().getName()+" Adding message of size 
"+se.getContentSize()+" to buffer for host.");
+
+               if (be.hasEntries() && be.status!=STAT_UP) {
+                       /* as long as we do not have a confirmed connection, do 
NOT queue messages! */
+                       debug(be.getSender().getName()+" Not connected to, 
message dropped.");
+                       expiredMessages.inc();
+                       return;
+                       }
+
+               if (!be.acceptEntry()) {
+                       /* first, try to remedy! */
+                       sendBuffer(be);
+                       /* did it work? */
+                       if (!be.acceptEntry()) {
+                               /* we need to enforce some hard limit here, 
otherwise we may take FAR too much memory (200 MB easily) */
+                               debug("Send buffer if full, refusing to queue 
message.");
+                               expiredMessages.inc();
+                               return;
+                               }
+                       }
+               be.addEntry(se,queuedMessages);
+               sendBuffer(be);
+       }
+
+       /**
+        * Look for a host in the table. If the entry is there at the time of
+        * checking, returns the entry.
+        *
+        * @param hostId the ID of the peer for which the connection is returned
+        * @return the connection of the host in the table, null if not 
connected
+        */
+
+       protected BufferEntry lookForHost( HostIdentity hostId )
+       {
+               BufferEntry be;
+
+               be = connectionBuffer[computeIndex(hostId)];
+               while (be != null) {
+                       if (hostId.equals(be.getSender()))
+                               return be;
+                       be = be.overflowChain;
+                       }
+               return null;
+       }
+
+       /**
+        * Read host-information from a file.  The connection lock
+        * must be held.
+        *
+        * @param be connection of the peer for which the trust is to be read
+        */
+
+       protected void initHostTrust( BufferEntry be )
+       {
+               MappedFile      mm;
+               FileLocation    f;
+               String          fil;
+
+               fil=be.getSender().getName();
+
+               f=trustDirectory.getFile(fil);
+               if (!f.exists()) {
+                       be.trust=0;
+                       return;
+                       }
+
+               if (f.getSize()==4) {
+                       mm=f.open();
+                       be.trust=mm.readInt(0);
+                       mm.close();
+                       }
+               else {
+                       be.trust=0;
+                       }
+       }
+
+       /**
+        * Force adding of a host to the buffer. If the node is already in the
+        * table, the table entry is returned.
+        *
+        * @param hostId for which peer should we get/create a connection
+        * @param force if true, drop another host from the table if the slot
+        * is already in use. If NO, return null if the slot is busy.
+        * @return the table entry for the host (no keyexchange performed so 
far)
+        */
+
+       protected BufferEntry addHost( HostIdentity hostId, boolean force )
+       {
+               BufferEntry be;
+               BufferEntry prev;
+
+               be=lookForHost(hostId);
+               debug(Level.INFO,hostId.getName()+" Add host to the connection 
table "+(be!=null ? "(was here)" : "(was not here)")+".");
+
+               if (be!=null) {
+                       return be;
+                       }
+
+               be = connectionBuffer[computeIndex(hostId)];
+               prev = null;
+               while (be!=null) {
+                       /* settle for entry in the linked list that is down */
+                       if (be.status == STAT_DOWN || 
hostId.equals(be.getSender()))
+                               break;
+                       prev = be;
+                       be = be.overflowChain;
+                       }
+
+               if (be == null) {
+                       be = new BufferEntry(status.getCPULoad(),debugFile);
+
+                       if (prev == null)
+                               connectionBuffer[computeIndex(hostId)] = be;
+                       else
+                               prev.overflowChain = be;
+                       }
+
+               be.setSender(hostId);
+               initHostTrust(be);
+               return be;
+       }
+
+       /**
+        * Perform an operation for all connected hosts.  The BufferEntry
+        * structure is passed to the method.  No synchronization or other
+        * checks are performed.
+        *
+        * @param method the method to invoke (null for couting only)
+        * @param arg the second argument to the method
+        * @return the number of connected hosts
+        */
+
+       protected int forAllConnectedHosts( BufferEntryCallback method, Object 
arg )
+       {
+               int i;
+               int count = 0;
+               BufferEntry be;
+
+               for (i=0;i<connectionBuffer.length;i++) {
+                       be = connectionBuffer[i];
+                       while (be != null) {
+                               if (be.status == STAT_UP) {
+                                       if (method != null)
+                                               method.callback(be, arg);
+                                       count++;
+                                       }
+                               be = be.overflowChain;
+                               }
+                       }
+               return count;
+       }
+
+       /**
+        * Here in this scanning for applicable hosts, we also want to take
+        * the protocols into account and prefer "cheap" protocols,
+        * i.e. protocols with a low overhead.
+        *
+        * @param id which peer are we currently looking at
+        * @param proto what transport protocol are we looking at
+        * @param im updated structure used to select the peer
+        */
+
+       protected void scanHelperCount( HostIdentity id, int proto, IndexMatch 
im )
+       {
+               Transport       t;
+               LocalIdentity                   keys;
+
+               keys=((Server) getManager().app()).getKeys();
+               if (keys.whoAmI().equals(id))
+                       return;
+               if (computeIndex(id) != im.index)
+                       return;
+
+               t=transport.getTransport(proto);
+               if (t!=null) {
+                       im.matchCount++;
+                       im.costSelector += t.getCost();
+                       }
+               else {
+                       debug("Transport 
"+TransportService.nameForProtocol(proto)+" is not available.");
+                       }
+       }
+
+       /**
+        * Select the peer and transport that was selected based on transport 
cost.
+        *
+        * @param id the current peer
+        * @param proto the protocol of the current peer
+        * @param im structure responsible for the selection process
+        */
+
+       protected void scanHelperSelect( HostIdentity id, int proto, IndexMatch 
im )
+       {
+               Transport       t;
+               LocalIdentity                   keys;
+
+               keys=((Server) getManager().app()).getKeys();
+               if (keys.whoAmI().equals(id))
+                       return;
+
+               if (computeIndex(id) != im.index)
+                       return;
+
+               t=transport.getTransport(proto);
+               if (t!=null) {
+                       im.costSelector -= t.getCost();
+                       if (im.matchCount==0 || im.costSelector<0) {
+                               im.match=(HostIdentity) 
PersistentHelper.copy(id);
+                               }
+                       im.matchCount--;
+                       }
+               else {
+                       debug("Transport 
"+TransportService.nameForProtocol(proto)+" is not available.");
+                       }
+       }
+
+       /**
+        * Create new session key message for the given host.
+        *
+        * @param host          the identity of the other host
+        * @param skey          the SessionKey to use
+        * @param created       the timestamp to use
+        * @return                      the signed session key on success, null 
on failure
+        */
+
+       protected P2PSessionKey createSessionKeyMessage( HostIdentity host, 
SessionKey skey, int created )
+       {
+               P2PSessionKey   msg;
+               P2PHello                helo;
+               EncryptedData   enc;
+               HostIdentity    me;
+               LocalIdentity                   keys;
+
+               keys=((Server) getManager().app()).getKeys();
+
+               
helo=knownHosts.identity2HELO(host,Transport.ANY_PROTOCOL_NUMBER,true);
+               if (helo==null) {
+                       log(Level.SEVERE,"Could not create session key message, 
no HELO found (host \""+host.getName()+"\") !");
+                       return null;
+                       }
+
+               enc=helo.getPublicKey().encrypt(PersistentHelper.toBytes(skey));
+               if (enc==null) {
+                       log(Level.SEVERE,"Failed to encrypt session key (host 
\""+host.getName()+"\") !");
+                       return null;
+                       }
+
+               msg=new P2PSessionKey();
+               msg.setEncryptedKey(enc);
+               msg.setCreationTime(created);
+               if (!msg.sign(keys.getPrivateKey())) {
+                       log(Level.SEVERE,"Failed to sign session key message 
(host \""+host.getName()+"\") !");
+                       return null;
+                       }
+
+               // verify signature
+               me=keys.whoAmI();
+               if (!verifySessionKeyMessage(me,msg)) {
+                       trace("Failed to verify created session key message 
(host \""+host.getName()+"\") !");
+                       return null;
+                       }
+               return msg;
+       }
+
+       /**
+        * Check if the received session key is properly signed.
+        *
+        * @param host the sender of the key
+        * @param msg the session key message
+        * @return false if invalid, true if valid
+        */
+
+       protected boolean verifySessionKeyMessage( HostIdentity host, 
P2PSessionKey msg )
+       {
+               P2PHello        helo;
+               String          limited;
+
+               // check if we are allowed to accept connections from that peer
+               limited=prefs.getString("GNUNETD","LIMIT-ALLOW",null);
+               if (limited!=null && limited.indexOf(host.getName())<0) {
+                       log(Level.FINE,"Connection from \""+host.getName()+"\" 
rejected.");
+                       return false;
+                       }
+
+               limited=prefs.getString("GNUNETD","LIMIT-DENY",null);
+               if (limited!=null && limited.indexOf(host.getName())>=0) {
+                       log(Level.FINE,"Connection from \""+host.getName()+"\" 
rejected.");
+                       return false;
+                       }
+
+               
helo=knownHosts.identity2HELO(host,Transport.ANY_PROTOCOL_NUMBER,true);
+               if (helo==null) {
+                       log(Level.INFO,host.getName()+" No HELO found.");
+                       return false;
+                       }
+
+               if (!msg.verify(helo.getPublicKey())) {
+                       log(Level.WARNING,host.getName()+" Session key message 
has invalid signature !");
+                       return false;
+                       }
+
+               verifiedSessionKeys.inc();
+               return true;
+       }
+
+       /**
+        * Perform a session key exchange for entry be.  First sends a HELO
+        * and then the new SKEY (in two plaintext packets). When called, the
+        * semaphore of at the given index must already be down
+        *
+        * @param be connection on which the key exchange is performed
+        */
+
+       protected void exchangeKey( BufferEntry be )
+       {
+               Session                 tmp;
+               P2PHello                        helo,targetHelo;
+               P2PSessionKey   skey;
+               int                             targetTransport;
+
+               debug(be.getSender().getName()+" Begin the exchange of keys.");
+
+               if (be.status != STAT_DOWN) {
+                       log(Level.WARNING,be.session.getRemote().getName()+" 
Status must be down for session key exchange !");
+                       return;
+                       }
+
+               be.skey=SessionKey.create();
+               be.created=(int) Scheduler.toSeconds(Scheduler.now());  // set 
creation time for session key !
+
+               skey=createSessionKeyMessage(be.getSender(),be.skey,be.created);
+               if (skey==null) {
+                       return;
+                       }
+
+               be.isAlive = 0; /* wait a bit */
+               be.status = STAT_WAITING_FOR_PING;
+               be.lastSequenceNumberReceived = 0;
+               be.lastPacketsBitmap = -1;
+               /* send tmp to target */
+               
targetHelo=knownHosts.identity2HELO(be.getSender(),Transport.ANY_PROTOCOL_NUMBER,true);
+               if (targetHelo==null) {
+                       return;
+                       }
+               targetTransport = targetHelo.getProtocol();
+
+               debug(be.getSender().getName()+" Found HELO with protocol 
"+TransportService.nameForProtocol(targetTransport)+".");
+
+               
helo=transport.transportCreateHELO(Transport.ANY_PROTOCOL_NUMBER);
+               if (helo==null) {
+                       be.status = STAT_DOWN;
+                       return;
+                       }
+
+               tmp=transport.transportConnect(targetHelo);
+               if (tmp==null) {
+                       be.status = STAT_DOWN;
+                       be.session = null;
+                       return;
+                       }
+               be.session=tmp;
+
+               targetHelo = null; /* ensure that we do not use the helo, 
transportConnect is now the owner! */
+               if (be.hasEntries()) {
+                       trace("FAILURE: expected be.sendBuffer to be null");
+                       }
+
+               be.lastSequenceNumberSend = 1;
+
+               traffic.updateTrafficSendCounter(helo);
+               traffic.updateTrafficSendCounter(skey);
+
+               be.session.send(new Persistent[] { helo, skey });
+
+               transmittedSessionKeys.inc();
+       }
+
+       /**
+        * Look in the list for known hosts; pick a random host of minimal
+        * transport cost for the hosttable at index index. When called, the
+        * mutex of at the given index must not be hold.
+        *
+        * @param index for which entry in the connection table are we looking 
for peers?
+        */
+
+       protected void scanForHosts( int index )
+       {
+               BufferEntry     be;
+               IndexMatch      indexMatch;
+               long            now;
+               LocalIdentity                   keys;
+
+               keys=((Server) getManager().app()).getKeys();
+
+//             debug(Level.FINER,"Scanning for host ("+index+").");
+
+               now=Scheduler.now();
+
+               indexMatch=new IndexMatch();
+               indexMatch.index = index;
+               indexMatch.matchCount = 0;
+               indexMatch.costSelector = 0;
+
+               knownHosts.forEachHost(new HostIterator() {
+                       public void iterate( HostIdentity identity, int 
protocol, Object data )
+                       {
+                               scanHelperCount(identity,protocol,(IndexMatch) 
data);
+                       }
+                       },now,indexMatch);
+               if (indexMatch.matchCount == 0)
+                       return;
+
+               log(Level.FINER,"Scanned for host at #"+index+" : found 
"+indexMatch.matchCount+" matching node identities.");
+
+               if (indexMatch.costSelector > 0)
+                       indexMatch.costSelector= 
Crypto.nextInt(indexMatch.costSelector/4)*4;
+
+               indexMatch.match=(HostIdentity) 
PersistentHelper.copy(keys.whoAmI());
+
+               knownHosts.forEachHost(new HostIterator() {
+                       public void iterate( HostIdentity identity, int 
protocol, Object data )
+                       {
+                               scanHelperSelect(identity,protocol,(IndexMatch) 
data);
+                       }
+                       },now,indexMatch);
+
+               if (keys.whoAmI().equals(indexMatch.match)) {
+                       log(Level.SEVERE,"ERROR: scanHelperSelect did not find 
selected match!?");
+                       return;
+                       }
+               if (computeIndex(indexMatch.match) != index) {
+                       log(Level.SEVERE,"ERROR: index of host to add is 
wrong!");
+                       return;
+                       }
+
+               debug(indexMatch.match.getName()+" Attempting to connect using 
slot "+index+".");
+
+               be = addHost(indexMatch.match,false);   // false or true should 
not matter here since we know the slot is empty
+               if (be != null) {
+                       if (be.status == STAT_DOWN) {
+                               
knownHosts.blacklistHost(be.getSender(),activeHosts,false);
+                               // we're trying now, don't try again too soon
+                               exchangeKey(be);
+                               }
+                       }
+       }
+
+       /**
+        * Check if the buffer is up (we got a PONG), if not, repeat the PING
+        * @param be
+        */
+
+       protected void checkAndPing( BufferEntry be )
+       {
+               final HostIdentity              data;
+               P2PPing pmsg=new P2PPing();
+
+               data=(HostIdentity) PersistentHelper.copy(be.getSender());
+               if (pingPong.pingAction(be.getSender(),new AbstractAction() {
+                       public void perform()
+                       {
+                               notifyPONG(data);
+                       }
+                       },data,pmsg)) {
+
+                       SendEntry       se;
+
+                       se=new 
SendEntry(SendEntry.SE_FLAG_NONE,getConnectPriority());
+                       se.setContent(pmsg);
+                       appendToBuffer(be, se);
+                       }
+               else {
+                       log(Level.WARNING,"WARNING: could not send checking 
ping, ping buffer full");
+                       }
+       }
+
+       /**
+        * Shutdown the connection.  Send a HANGUP message to the other side
+        * and mark the sessionkey as dead.
+        *
+        * @param be the connection to shutdown
+        */
+
+       protected void shutdownConnection( BufferEntry be )
+       {
+               P2PHangUp hangup;
+               LocalIdentity                   keys;
+
+               keys=((Server) getManager().app()).getKeys();
+
+               if (be.status == STAT_DOWN)
+                       return; /* nothing to do */
+
+               if (be.status == STAT_UP) {
+                       SendEntry       se;
+
+                       hangup=new P2PHangUp(keys.whoAmI());
+
+                       se=new 
SendEntry(SendEntry.SE_FLAG_PLACE_TAIL,EXTREME_PRIORITY);
+                       se.setContent(hangup);
+                       appendToBuffer(be,se);
+                       }
+
+               be.created = 0;
+               be.status = STAT_DOWN;
+               be.transmitted_limit = START_TRANSMIT_LIMIT;
+               be.max_transmitted_limit = START_TRANSMIT_LIMIT * 10; /* FIXME: 
remove "*10" post 0.6.2c! */
+               if (be.session != null) {
+                       be.session.unlock();
+                       be.session = null;
+                       }
+
+               downConnections.inc();
+
+               be.discardEntries(queuedMessages);
+       }
+
+       /**
+        * Transmit an update to the bandwidth limit to the other peer.
+        *
+        * @param be the connection to transmit the limit over
+        */
+
+       protected void transmitConnectionLimit( BufferEntry be )
+       {
+               SendEntry       se;
+               Format          f;
+               int                     delta;
+               boolean         update;
+
+               delta=Math.abs(be.idealized_limit-be.transmitted_limit);
+
+               // avoid dividing by 0
+               if (be.transmitted_limit==0) {
+                       be.transmitted_limit=1;
+                       }
+
+               update=((delta*100)/be.transmitted_limit)>=10;
+
+               f=IOUtils.newSizeFormatter();
+               if (isDebug()) {
+                       debug(Level.INFO,be.session.getRemote().getName()+" "+
+                                       "Idealized is "+f.format(new 
Long(be.idealized_limit/60))+"/s, "+
+                                       "previously transmitted is 
"+f.format(new Long(be.transmitted_limit/60))+"/s, "+
+                                       "will update ? "+update+" "+
+                                       "(peer limit is "+f.format(new 
Long(be.getMaxBytesPerMinutes()/60))+"/s).");
+                       }
+
+               if (update) {
+                       // limits changed by more than 10%, send updated 
capability message !
+                       se=new SendEntry(SendEntry.SE_FLAG_NONE,ADMIN_PRIORITY);
+                       se.setContent(new 
P2PCapability(Capability.CAP_BANDWIDTH_RECV,be.idealized_limit));
+                       appendToBuffer(be,se);
+
+                       debug("Transmitted with idealized limit : 
"+f.format(new Long(be.idealized_limit/60))+".");
+
+                       be.transmitted_limit = be.idealized_limit;
+                       if (be.transmitted_limit > be.max_transmitted_limit)
+                               be.max_transmitted_limit = be.transmitted_limit;
+                       else
+                               be.max_transmitted_limit=( 
be.max_transmitted_limit * 3 +be.transmitted_limit ) / 4; /* slowly reduce */
+                       }
+       }
+
+       protected void gatherEntries( BufferEntry be, UTLClosure utl )
+       {
+               utl.e[utl.pos++] = be;
+       }
+
+       protected void resetRecentlyReceived( BufferEntry be, Object unused )
+       {
+               be.recently_received = 0;
+       }
+
+       /**
+        * What is the function used to weigh the value of
+        * the connection for bandwidth allocation?
+        * Ok, with this API we can not implement "max takes all",
+        * but it is possible to use:
+        *
+        * - proportional share: (x) [ bandwidth proportional to contribution ]
+        * - square-root (sqrt(x))  [ contributing a lot more gives a little 
gain ]
+        * - square share: (x*x) [ Bush's tax system: if you're rich, you get 
even more ]
+        *
+        * Pretty much every monotonically increasing, always
+        * positive function can be used.  The main loop normalizes later.
+        * @param be
+        * @return
+        */
+
+       protected int SHARE_DISTRIBUTION_FUNCTION( BufferEntry be )
+       {
+               return (int) be.current_connection_value;
+       }
+
+       /**
+        * What is the minimum number of peers to connect to that is
+        * still acceptable? (By dividing connectionBuffer.length by
+        * two, we specify to maintain at least 50% of the maximum
+        * number of connections).
+        * @return
+        */
+
+       protected int minConnect()
+       {
+               return connectionBuffer.length/2;
+       }
+
+       private static long lastRoundStart = 0;
+       private static long timeDifference;
+
+       /**
+        * Schedule the available inbound bandwidth among the peers.  Note
+        * that this function is called A LOT (dozens of times per minute), so
+        * it should execute reasonably fast.
+        */
+
+       protected void scheduleInboundTraffic()
+       {
+               int activePeerCount;
+               UTLClosure utl=new UTLClosure();
+               long                    now;
+               BufferEntry[]   entries;
+               double[]                shares;
+               double                  shareSum;
+               int u;
+               int minCon;
+               long schedulableBandwidth;
+               long decrementSB;
+               long[] adjustedRR;
+               boolean didAssign;
+
+               synchronized(lock) {
+                       now=Scheduler.now();
+
+                       /* if this is the first round, don't bother... */
+                       if (lastRoundStart == 0) {
+                               /* no allocation the first time this function 
is called! */
+                               lastRoundStart = now;
+                               forAllConnectedHosts(new BufferEntryCallback() {
+                                       public void callback( BufferEntry be, 
Object data )
+                                       {
+                                               resetRecentlyReceived(be,data);
+                                       }
+                                       },null);
+                               return;
+                               }
+
+                       /* if time difference is too small, we don't have enough
+                        sample data and should NOT update the limits */
+                       timeDifference = now - lastRoundStart;
+                       if (timeDifference < MIN_SAMPLE_TIME) {
+                               return; /* don't update too frequently, we need 
at least some
+                               semi-representative sampling! */
+                               }
+
+                       /* build an array containing all BEs */
+                       activePeerCount = forAllConnectedHosts(null, null);
+                       if (activePeerCount == 0) {
+                               return; /* nothing to be done here. */
+                               }
+                       entries = new BufferEntry[activePeerCount];
+                       utl.pos = 0;
+                       utl.e = entries;
+                       forAllConnectedHosts(new BufferEntryCallback() {
+                               public void callback( BufferEntry be, Object 
data )
+                               {
+                                       gatherEntries(be,(UTLClosure) data);
+                               }
+                               },utl);
+
+
+                       /* compute shares */
+                       shares = new double[activePeerCount];
+                       shareSum = 0.0;
+                       for (u=0;u<activePeerCount;u++) {
+                               shares[u] = 
SHARE_DISTRIBUTION_FUNCTION(entries[u]);
+                               if (shares[u] < 0.0)
+                                       shares[u] = 0.0;
+                               shareSum += shares[u];
+                       }
+
+                       /* normalize distribution */
+                       if (shareSum >= 0.00001) { /* avoid numeric glitches... 
*/
+                               for (u=0;u<activePeerCount;u++)
+                                       shares[u] = shares[u] / shareSum;
+                       } else {
+                               for (u=0;u<activePeerCount;u++)
+                                       shares[u] = 1 / activePeerCount;
+                       }
+
+                       /* compute how much bandwidth we can bargain with */
+                       minCon = minConnect();
+                       if (minCon > activePeerCount)
+                               minCon = activePeerCount;
+                       schedulableBandwidth = maxBytesPerMinute - minCon * 
MIN_BPM_PER_PEER;
+
+                       adjustedRR = new long[activePeerCount];
+
+                       /* reset idealized limits; if we want a smoothed-limits
+                        algorithm, we'd need to compute the new limits 
separately
+                        and then merge the values; but for now, let's just go
+                        hardcore and adjust all values rapidly */
+                       for (u=0;u<activePeerCount;u++) {
+                               entries[u].idealized_limit = 0;
+                               adjustedRR[u] = 
Scheduler.minutes(entries[u].recently_received) / timeDifference;
+
+                               if (isDebug()) {
+                                       if (adjustedRR[u] > 
entries[u].transmitted_limit) {
+                                               log(Level.INFO,"INFO: peer 
"+entries[u].getSender().getName()+" transmitted above limit: "+adjustedRR[u]+" 
bpm > "+entries[u].transmitted_limit+" bpm");
+                                               }
+                                       }
+
+                               /* Check for peers grossly exceeding send 
limits. Be a bit
+                                * reasonable and make the check against the 
max value we have
+                                * sent to this peer (assume announcements may 
have got lost).
+                                */
+                               if (adjustedRR[u] > 2 * MAX_BUF_FACT *  
entries[u].max_transmitted_limit) {
+                                       if (isDebug()) {
+                                               log(Level.INFO,"Blacklisting 
"+entries[u].session.getRemote().getName()+
+                                                               ", it sent >"+2 
* MAX_BUF_FACT+
+                                                               "x+MTU above 
mLimit: "+adjustedRR[u]+
+                                                               " bpm > 
"+entries[u].max_transmitted_limit+
+                                                               " bpm (cLimit 
"+entries[u].transmitted_limit+" bpm)");
+                                               }
+
+                                       shutdownConnection(entries[u]);
+                                       
knownHosts.blacklistHost(entries[u].session.getRemote(),activeHosts--,true);
+                                       upConnections.dec();
+                                       activePeerCount--;
+                                       entries[u]=entries[activePeerCount];
+                                       shares[u]=shares[activePeerCount];
+                                       
adjustedRR[u]=adjustedRR[activePeerCount];
+                                       u--;
+                                       }
+
+                               if (adjustedRR[u] < MIN_BPM_PER_PEER/2) {
+                                       adjustedRR[u] = MIN_BPM_PER_PEER/2; /* 
even if we received NO traffic, allow    at least MIN_BPM_PER_PEER */
+                                       }
+                               }
+
+                       debug("Freely schedulable bandwidth is 
"+IOUtils.newSizeFormatter().format(new Long(schedulableBandwidth/60))+"/s.");
+
+                       /* now distribute the schedulableBandwidth according
+                        to the shares.  Note that since we cap peers at twice
+                        of what they transmitted last, we may not be done with
+                        just one pass.
+
+                        We don't wait until schedulableBandwidth hits 0 since 
that may
+                        take forever (due to rounding you can even take that 
literally).
+                        The "100" equates to 100 bytes per peer (per minute!) 
being
+                        potentially under-allocated.  Since there's always some
+                        (unencrypted) traffic that we're not quite accounting 
for anyway,
+                        that's probably not so bad. */
+                       while (schedulableBandwidth > connectionBuffer.length * 
100) {
+                               didAssign = false;
+                               decrementSB = 0;
+                               for (u=0;u<activePeerCount;u++) {
+                                       /* always allow allocating 
MIN_BPM_PER_PEER */
+                                       if (entries[u].idealized_limit < 
adjustedRR[u] * 2) {
+                                               int share;
+
+                                               share = 
entries[u].idealized_limit + (int) (shares[u] * schedulableBandwidth);
+                                               if (share > adjustedRR[u] * 2)
+                                                       share = (int) 
(adjustedRR[u] * 2);
+                                               if (share > 
entries[u].idealized_limit) {
+                                                       decrementSB += share - 
entries[u].idealized_limit;
+                                                       didAssign = true;
+                                               }
+                                               entries[u].idealized_limit = 
share;
+                                       }
+                               }
+                               schedulableBandwidth -= decrementSB;
+                               if (!didAssign) {
+                                       int[]   perm = 
Crypto.permute(activePeerCount);
+         /* assign also to random "worthless" (zero-share) peers */
+                                       for (u=0;u<activePeerCount;u++) {
+                                               int v = perm[u]; /* use perm to 
avoid preference to low-numbered slots */
+                                               if (entries[v].idealized_limit 
/ 2 < adjustedRR[u]) {
+                                                       int share;
+
+                                                       share = 
entries[v].idealized_limit + (int) (schedulableBandwidth);
+                                                       if (share > 
adjustedRR[u] * 2)
+                                                               share = (int) 
(adjustedRR[u] * 2);
+                                                       schedulableBandwidth -= 
share - entries[v].idealized_limit;
+                                                       
entries[v].idealized_limit = share;
+                                               }
+                                       }
+
+                                       if ( (schedulableBandwidth > 0) &&
+                                                       (activePeerCount > 0) ) 
{
+                                               /* assign rest disregarding 
traffic limits */
+                                               perm = 
Crypto.permute(activePeerCount);
+                                               for (u=0;u<activePeerCount;u++) 
{
+                                                       
entries[perm[u]].idealized_limit += (int) 
(schedulableBandwidth/activePeerCount);
+                                                       }
+                                               schedulableBandwidth = 0;
+                                               }
+                                       break;
+                               } /* didAssign == NO? */
+                       } /* while bandwidth to distribute */
+
+
+                       /* randomly add the MIN_BPM_PER_PEER to minCon peers; 
yes, this will
+                        yield some fluctuation, but some amount of fluctuation 
should be
+                        good since it creates opportunities. */
+                       for (u=0;u<minCon;u++)
+                               
entries[Crypto.nextInt(activePeerCount)].idealized_limit += MIN_BPM_PER_PEER;
+
+                       /* prepare for next round */
+                       lastRoundStart = now;
+                       for (u=0;u<activePeerCount;u++) {
+                               log(Level.FINEST,"Inbound limit for peer "+u+
+                                               ": 
"+entries[u].getSender().getName()+
+                                               " set to 
"+IOUtils.newSizeFormatter().format(new 
Long(entries[u].idealized_limit/60))+"/s."
+                               );
+                               transmitConnectionLimit(entries[u]);
+                               entries[u].current_connection_value /= 2.0;
+                               entries[u].recently_received = 0;
+                       }
+               }
+       }
+
+       private static final int        BACKOFF_START_VALUE     =       16;
+
+       private static int lastLivenessHost = 0;
+       private static int activePeerCount2 = 0;
+       private static int BACKOFF = BACKOFF_START_VALUE;
+       private static int delay = 1;
+
+       /**
+        * Call this method periodically to decrease liveness of hosts.
+        */
+
+       public void cronDecreaseLiveness()
+       {
+               BufferEntry be;
+               BufferEntry prev;
+               long now;
+
+               scheduleInboundTraffic();
+               now=Scheduler.now();
+               if (lastLivenessHost == 0) {
+                       /* every 12 seconds / for each pass through all slots */
+                       if ( ( (activePeerCount2 <= connectionBuffer.length/8) 
||
+                                       (activePeerCount2 < 2) ) &&
+                                       (activePeerCount2 < 16) ) {
+
+                               /* (almost) no active peers? well,
+                                then we clearly want to discover some :-).
+                                < 2 is a definitive sign of trouble,
+                                < 1/8-th of goal is bad, but if goal is huge
+                                < 16 is required to avoid mega-peers pounding 
on the hostlist-server. */
+                               /* at most try every BACKOFF time units,
+                                use exponential BACKOFF, maybe the peer
+                                is misconfigured, then we don't want to kill
+                                the hostlist server... */
+                               if ( (delay % BACKOFF) == 0) {
+                                       log(Level.FINEST,"DEBUG: attempting to 
download hostlist from server.");
+                                       ((HELOLoader) 
getManager().service(HELOLoader.class)).downloadRandomHostList();
+                                       if (BACKOFF < 65536)
+                                               BACKOFF = BACKOFF * 2;
+                                       }
+                               else {
+                                       String  url;
+                                       
url=prefs.getString("GNUNETD","HOSTLISTURL",null);
+                                       if (url != null) {
+                                               log(Level.FINEST,"I only have 
"+activePeerCount2+" peers connected (want "+connectionBuffer.length+"), "+
+                                                       "waiting for "+(delay % 
BACKOFF)+" to reach "+BACKOFF+" "+
+                                                       "before trying HTTP 
download of hostlist"+((delay>BACKOFF_START_VALUE) ? " (again)" : "")+".");
+                                               }
+                                       }
+                               delay++;
+                               }
+                       activePeerCount2 = 0;
+                       }
+
+               /* Find the correct host */
+               synchronized(lock) {
+                       if (lastLivenessHost >= connectionBuffer.length) {
+                               /* this happens if the connection buffer size 
shrinks due to reconfiguration and SIGHUP */
+                               activePeerCount2 = 0;
+                               lastLivenessHost = 0;
+                               }
+
+                       be = connectionBuffer[lastLivenessHost];
+                       prev = null;
+                       while (be!=null) {
+                               /* is this the host? */
+                               switch (be.status) {
+                                       case STAT_DOWN:
+                                               if (prev == null)
+                                                       
connectionBuffer[lastLivenessHost] = be.overflowChain;
+                                               else
+                                                       prev.overflowChain = 
be.overflowChain;
+                                               be = be.overflowChain;
+                                               continue;
+
+                                       case STAT_UP:
+                                               if ( (now > be.isAlive) && /* 
concurrency might make this false... */
+                                                               (now - 
be.isAlive > Scheduler.seconds(SECONDS_INACTIVE_DROP)) ) {
+                                                       /* switch state form UP 
to DOWN: too much inactivity */
+                                                       
log(Level.FINEST,"Closing connection with "+be.getSender().getName()+": too 
much inactivity ("+(now - be.isAlive)+" ms)");
+                                                       shutdownConnection(be);
+                                                       
knownHosts.whitelistHost(be.getSender()); /* the host may still be worth trying 
again soon */
+                                                       activeHosts--;
+                                                       upConnections.dec();
+                                                       break;
+                                                       }
+
+                                               activePeerCount2++;
+                                               if ( (activeHosts*4 < 
connectionBuffer.length*3) && (now - be.isAlive > 
Scheduler.seconds(SECONDS_PINGATTEMPT)) ){
+                                                       /* if we have less than 
75% of the number of connections
+                                                        that we would like to 
have, try ping-ing the other side
+                                                        to keep the connection 
open instead of hanging up */
+                                                       P2PPing pmsg=new 
P2PPing();
+                                                       HostIdentity    hi=new 
HostIdentity();
+
+                                                       
log(Level.FINEST,"Sending keepalive-ping to peer "+be.getSender().getName());
+
+                                                       hi=(HostIdentity) 
PersistentHelper.copy(be.getSender());
+                                                       final HostIdentity      
_hi = hi;
+                                                       if 
(pingPong.pingAction(be.getSender(),new AbstractAction() {
+                                                                       public 
void perform()
+                                                                       {
+                                                                               
notifyPING(_hi);
+                                                                       }
+                                                                       
},hi,pmsg)) {
+                                                               SendEntry       
se;
+
+                                                               se=new 
SendEntry(SendEntry.SE_FLAG_NONE,getConnectPriority());
+                                                               
se.setContent(pmsg);
+                                                               
se.setDelay(Scheduler.MILLIS_50);
+                                                               
appendToBuffer(be,se);
+                                                               }
+                                                       }
+                                               break;
+
+                                       case STAT_WAITING_FOR_PING:
+                                               if ( (now > be.isAlive) &&
+                                                               (now - 
be.isAlive > Scheduler.seconds(SECONDS_NOPINGPONG_DROP)) ) {
+                                                       
debug(be.getSender().getName()+" Closing connection to, session key not 
answered by a ping.");
+                                                       shutdownConnection(be);
+                                                       }
+                                               break;
+
+                                       case STAT_WAITING_FOR_PONG:
+                                               if ( (now > be.isAlive) &&
+                                                               (now - 
be.isAlive > Scheduler.seconds(SECONDS_NOPINGPONG_DROP)) ) {
+                                                       
debug(be.getSender().getName()+" Closing connection to, ping not answered by a 
pong.");
+                                                       shutdownConnection(be);
+                                                       }
+                                               else {
+                                                       checkAndPing(be);
+                                                       }
+                                               break;
+
+                                       default:
+                                               log(Level.WARNING,"WARNING: 
unknown type of connection status: "+be.status);
+                                               break;
+                                       }
+                               sendBuffer(be);
+
+                               prev = be;
+                               be = be.overflowChain;
+                               }
+
+                       if (connectionBuffer[lastLivenessHost] == null) {
+                               /*log(Level.EVERYTHING,"EVERYTHING: scanning 
for peer using slot %u",
+                                lastLivenessHost);*/
+                               if 
(!prefs.testString("GNUNETD","DISABLE-AUTOCONNECT","YES")) {
+                                       scanForHosts(lastLivenessHost);
+                                       }
+                               }
+
+                       lastLivenessHost++;
+                       if (lastLivenessHost >= connectionBuffer.length)
+                               lastLivenessHost = 0;
+                       }
+       }
+
+       /**
+        * Call once in a while to synchronize trust values with the disk.
+        */
+
+       public void cronFlushTrustBuffer()
+       {
+               debug(Level.FINER,"Enter cronFlushTrustBuffer.");
+
+               synchronized(lock) {
+                       forAllConnectedHosts(new BufferEntryCallback() {
+                               public void callback( BufferEntry be, Object 
data )
+                               {
+                                       flushHostCredit(be,data);
+                               }
+                               },null);
+                       }
+
+               debug(Level.FINER,"Exit cronFlushTrustBuffer.");
+       }
+
+       /**
+        * Connect to another peer.
+        *
+        * @param hostId the peer to connect with
+        * @return the connection handle
+        */
+
+       protected BufferEntry connectTo( HostIdentity hostId )
+       {
+               BufferEntry be;
+               LocalIdentity                   keys;
+
+               keys=((Server) getManager().app()).getKeys();
+
+               if (keys.whoAmI().equals(hostId)) {
+                       log(Level.SEVERE,"ERROR: attempt to connect to myself! 
(dropped)");
+                       return null;
+                       }
+
+               be = lookForHost(hostId);
+               if (be==null || be.status==STAT_DOWN) {
+                       be = addHost(hostId, true);   /* we *really* want to 
talk to this guy */
+                       if (be.status == STAT_DOWN) {
+                               exchangeKey(be);
+                               // note that it can fail and leave the buffer 
in "down" state,
+                               // e.g. if we don't know any key for the other 
peer
+                               }
+                       }
+               return be;
+       }
+
+       /**
+        * How important is it at the moment to establish more connections?
+        *
+        * @return a measure of the importance to establish connections
+        */
+
+       public int getConnectPriority()
+       {
+               if (connectionBuffer.length > 4*activeHosts)
+                       return EXTREME_PRIORITY;
+               if (connectionBuffer.length > 2*activeHosts)
+                       return (connectionBuffer.length - activeHosts)*256;
+               if (connectionBuffer.length > activeHosts)
+                       return (connectionBuffer.length - activeHosts)*64;
+               return 0;
+       }
+
+       /**
+        * Consider switching the transport mechanism used for contacting
+        * the given node. This function is called when the handler handles
+        * an encrypted connection. For example, if we are sending SMTP
+        * messages to a node behind a NAT box, but that node has established
+        * a TCP connection to us, it might just be better to send replies
+        * on that TCP connection instead of keeping SMTP going.<p>
+        *
+        * We can only successfully takeover if the transport is bidirectional
+        * (can be associated). It also only makes sense if the cost is lower.
+        * This method checks both.
+        * Consider switching the transport mechanism used for contacting the
+        * given node.  This function is called when the handler handles an
+        * encrypted connection.  For example, if we are sending SMTP messages
+        * to a node behind a NAT box, but that node has established a TCP
+        * connection to us, it might just be better to send replies on that
+        * TCP connection instead of keeping SMTP going.
+        *
+        *
+        * @param tsession the transport session that is for grabs
+        * @param sender the identity of the other node
+        */
+
+       public void considerTakeover( Session tsession, HostIdentity sender )
+       {
+               BufferEntry be;
+
+               if (tsession == null) {
+                       return;
+                       }
+
+               synchronized(lock) {
+                       be = lookForHost(sender);
+                       if (be != null) {
+                               if (be.status != STAT_DOWN) {
+                                       int cost = -1;
+                                       if (be.session != null)
+                                               cost = 
be.session.getTransport().getCost();
+                                       /* Question: doesn't this always do 
takeover in tcp/udp
+                                        case, which have the same costs? 
Should it? -IW
+
+                                        Answer: this will always switch to TCP 
in the long run (if
+                                        that is possible) since udpAssociate 
always
+                                        returns false. This is intended since 
for long-running
+                                        sessions, TCP is the better choice. 
UDP is only better for
+                                        sending very few messages (e.g. 
attempting an initial exchange
+                                        to get to know each other). See also 
transport paper and the
+                                        data on throughput. - CG
+                                        */
+                                       if (tsession.getTransport().getCost() 
>= cost) {
+                                               if (tsession.lock()) {
+                                                       be.session.unlock();
+                                                       be.session = tsession;
+                                                       }
+                                               }
+                                       }
+                               }
+                       }
+       }
+
+       /**
+        * Accept a session-key that has been sent by another host.
+        * The other host must be known (public key)
+        *
+        * @param tsession the transport session handle
+        * @param sessionkeySigned message with the session key/the session key 
that was "negotiated"
+        * @return
+        */
+
+       public boolean acceptSessionKey( Session tsession, P2PSessionKey 
sessionkeySigned )
+       {
+               BufferEntry             be;
+               SessionKey              key = new SessionKey();
+               P2PHello                        helo;
+               int                             ttype;
+               LocalIdentity                   keys;
+
+               keys=((Server) getManager().app()).getKeys();
+
+               receivedSessionKeys.inc();
+
+               HostIdentity    sender=tsession.getRemote();
+
+               debug(sender.getName()+" Received session key.");
+
+               if (!verifySessionKeyMessage(sender,sessionkeySigned)) {
+                       log(Level.INFO,sender.getName()+" Session key failed 
verification, ignored !");
+                       return false;  /* rejected */
+                       }
+
+               if (tsession != null)
+                       ttype = tsession.getTransport().getProtocol();
+               else
+                       ttype = -1;
+
+               /* prepare file */
+               if 
(SessionKey.SIZE!=keys.getPrivateKey().decrypt(sessionkeySigned.getEncryptedKey(),PersistentHelper.toBytes(key),SessionKey.SIZE))
 {
+                       log(Level.WARNING,sender.getName()+" Session key 
rejected !");
+                       return false;
+                       }
+
+               synchronized(lock) {
+                       be = lookForHost(sender);
+                       if (be == null) {
+                               be = addHost(sender, false);
+                               }
+
+                       if (be == null) {
+                               log(Level.INFO,sender.getName()+" Session key 
exchange denied, slot busy.");
+                               return false;
+                               }
+
+                       if (be.created>sessionkeySigned.getCreationTime()) {
+                               debug(Level.INFO,sender.getName()+" Key 
dropped, we've sent or received a more recent key !");
+                               return false;
+                               }
+
+                       // if we have another connection established with the 
node, shut it down !
+                       if (be.session != null) {
+                               debug(sender.getName()+" Closing old 
connection, received new session key.");
+                               shutdownConnection(be);
+                               }
+
+                       // try to associate with an existing connection if the 
other side contacted us
+                       // with a bi-directional protocol, if that fails, 
establish our own connection
+                       if (tsession==null || !tsession.lock()) {
+                               tsession = null;
+                               
helo=knownHosts.identity2HELO(sender,Transport.ANY_PROTOCOL_NUMBER,false);
+                               if (helo==null) {
+                                       log(Level.WARNING,sender.getName()+" 
Session key received, but could not find transport to reply ("+ttype+").");
+                                       return false;
+                                       }
+                               tsession=transport.transportConnect(helo);
+                               if (tsession==null) {
+                                       log(Level.WARNING,"WARNING: sessionkey 
received, but transport failed to connect");
+                                       return false;
+                                       }
+                               }
+
+                       // ok, everything set, let's change the state to 
waiting for ping and initialize the buffer entry
+                       be.skey=(SessionKey) PersistentHelper.copy(key);
+                       be.session      = tsession;
+                       be.created= sessionkeySigned.getCreationTime();
+                       be.status= STAT_WAITING_FOR_PONG;
+                       be.lastSequenceNumberReceived= 0;
+                       be.lastPacketsBitmap= -1; /* all bits set */
+                       if (be.hasEntries()) {
+                               trace("FAILURE: expected be.sendBuffer to be 
null");
+                               }
+                       be.lastSequenceNumberSend=1;
+
+                       debug(be.session.getRemote().getName()+" Session key 
exchange : send encrypted ping.");
+
+                       checkAndPing(be);
+                       }
+               return true;
+       }
+
+       public void connectionConfigChangeCallback()
+       {
+               long new_max_bpm;
+               int i;
+
+               synchronized(lock) {
+                       /* maxBytesPerMinute may change... */
+                       new_max_bpm     = 60 * 
prefs.getInt("LOAD","MAXNETDOWNBPSTOTAL",0);
+                       if (new_max_bpm == 0)
+                               new_max_bpm = 50000 * 60; /* assume 50 kbps */
+
+                       if (maxBytesPerMinute != new_max_bpm) {
+                               int newMAXHOSTS = 0;
+
+                               maxBytesPerMinute = new_max_bpm;
+                               /* max-hosts is supposed to allow 
TARGET_MSG_SID MTU-sized messages
+                                per SECONDS_INACTIVE_DROP; 
maxbps=maxBytesPerMinute/60 =>
+                                byte per SID = maxbpm*SID/60; divide by MTU to
+                                get number of messages that can be send per 
SID */
+                               newMAXHOSTS     = (int) (maxBytesPerMinute / 
MIN_BPM_PER_PEER);
+                               /* => for 50000 bps, we get 78 (rounded DOWN to 
64) connections! */
+
+                               if (newMAXHOSTS < 2)
+                                       newMAXHOSTS = 2; /* strict minimum is 2 
*/
+
+                               /* make sure it's a power of 2 */
+                               i = 1;
+                               while (i <= newMAXHOSTS)
+                                       i*=2;
+                               newMAXHOSTS = i/2;
+
+                               if (newMAXHOSTS != connectionBuffer.length) {
+                                       /* change size of connection buffer!!! 
*/
+                                       int olen;
+                                       BufferEntry[] newBuffer;
+
+                                       olen = connectionBuffer.length;
+
+                                       newBuffer = new 
BufferEntry[newMAXHOSTS];
+                                       Arrays.fill(newBuffer,null);
+
+                                       
prefs.setInt("gnunetd","connection-max-hosts",newBuffer.length);
+
+                                       /* rehash! */
+                                       for (i=0; i<olen; i++) {
+                                               BufferEntry be;
+
+                                               be = connectionBuffer[i];
+                                               while (be != null) {
+                                                       BufferEntry next;
+                                                       int j;
+
+                                                       next = be.overflowChain;
+                                                       j = 
computeIndex(be.getSender());
+                                                       be.overflowChain = 
newBuffer[j];
+                                                       newBuffer[j] = be;
+                                                       be = next;
+                                                       }
+                                               }
+                                       connectionBuffer = newBuffer;
+
+                                       log(Level.FINEST,"Connection goal is 
"+connectionBuffer.length+" peers ("+IOUtils.newSizeFormatter().format(new 
Long(maxBytesPerMinute/60))+"/s bandwidth downstream).");
+                                       }
+                               }
+                       
useRandomPadding=!prefs.testString("GNUNETD-EXPERIMENTAL","PADDING","NO");
+                       }
+       }
+
+       /**
+        * Increase the host credit by a value.
+        * Increase the host credit by a value - synchronized
+        *
+        * @param hostId is the identity of the host
+        * @param value is the int value by which the host credit is to be 
increased or
+        *        decreased
+        * @return the actual change in trust (positive or negative)/the new 
credit
+        */
+
+       public int changeHostCredit( HostIdentity hostId, int value )
+       {
+               BufferEntry be;
+
+               if (value==0) {
+                       return 0;
+                       }
+
+               synchronized(lock) {
+                       be = lookForHost(hostId);
+                       if (be == null) {
+                               return 0; /* not connected! */
+                       }
+                       if ( (be.trust & TRUST_ACTUAL_MASK) + value < 0) {
+                               value = - (be.trust & TRUST_ACTUAL_MASK);
+                               be.trust = 0 | TRUST_REFRESH_MASK; /* 0 
remaining */
+                               }
+                       else {
+                               be.trust = ( (be.trust & TRUST_ACTUAL_MASK) + 
value) | TRUST_REFRESH_MASK;
+                               }
+                       }
+               return value;
+       }
+
+       /**
+        * Obtain the trust record of a peer.
+        * Obtain the credit record of the host.
+        *
+        * @param hostId the identity of the peer
+        * @return the amount of trust we currently have in that peer
+        */
+
+       public int getHostCredit( HostIdentity hostId )
+       {
+               BufferEntry be;
+               int trust;
+
+               synchronized(lock) {
+                       be = lookForHost(hostId);
+                       if (be == null) {
+                               return 0;
+                       }
+                       trust = be.trust;
+                       }
+               return trust & TRUST_ACTUAL_MASK;
+       }
+
+       /**
+        * Wrapper around forAllConnectedHosts.  Calls a given
+        * method for each connected host.
+        * Call method for every connected node.
+        *
+        * @param method method to call for each connected peer
+        * @param arg second argument to method
+        * @return number of connected nodes
+        */
+
+       public int forEachConnectedNode( PerNodeCallback method, Object arg )
+       {
+               final PerNodeCallback   _method = method;
+               final Object                    _arg = arg;
+
+               synchronized(lock) {
+                       return forAllConnectedHosts(new BufferEntryCallback() {
+                               public void callback( BufferEntry be, Object 
data )
+                               {
+                                       if (_method!=null) {
+                                               
_method.callback(be.getSender(),_arg);
+                                               }
+                               }
+                               },null);
+                       }
+       }
+
+       /**
+        * Print the contents of the connection buffer (for debugging).
+        * For debugging.
+        */
+
+       public void printConnectionBuffer()
+       {
+               BufferEntry     be;
+               int                     i;
+
+               synchronized(lock) {
+                       for (i=0; i<connectionBuffer.length; i++) {
+                               be=connectionBuffer[i];
+                               while (be!=null) {
+                                       if (be.status != STAT_DOWN) {
+                                               
log(Level.FINE,"CONNECTION-TABLE: "+i+"-"+be.infos());
+                                               }
+                                       be=be.overflowChain;
+                                       }
+                               }
+                       }
+       }
+
+       /**
+        * Register a callback method that should be invoked whenever a
+        * message is about to be send that has more than minimumPadding bytes
+        * left before maxing out the MTU. The callback method can then be
+        * used to add additional content to the message (instead of the
+        * random noise that is added by otherwise). Note that if the MTU is 0
+        * (for streams), the callback method will always be called with
+        * padding set to the maximum number of bytes left in the buffer
+        * allocated for the send.
+        *
+        * Register a callback method that should be invoked whenever a
+        * message is about to be send that has more than minimumPadding bytes
+        * left before maxing out the MTU. The callback method can then be
+        * used to add additional content to the message (instead of the
+        * random noise that is added by otherwise).  Note that if the MTU is
+        * 0 (for streams), the callback method will always be called with
+        * padding set to the maximum number of bytes left in the buffer
+        * allocated for the send.
+        *
+        * @param minimumPadding how large must the padding be in order
+        *   to call this method?
+        * @param callback the method to invoke. The receiver is the
+        *   receiver of the message, position is the reference to the
+        *   first unused position in the buffer where GNUnet is building
+        *   the message, padding is the number of bytes left in that buffer.
+        *   The callback method must return the number of bytes written to
+        *   that buffer (must be a positive number).
+        * @return true if the handler was registered, false on error
+        */
+
+       public boolean registerSendCallback( int minimumPadding, 
BufferFillCallback callback )
+       {
+               SendCallbackEntry[]     old;
+               SendCallbackEntry       entry;
+
+               synchronized(lock) {
+                       entry=new SendCallbackEntry();
+                       entry.minimumPadding=minimumPadding;
+                       entry.callback=callback;
+
+                       old=sendCallbacks;
+
+                       sendCallbacks=new 
SendCallbackEntry[sendCallbacks.length+1];
+                       System.arraycopy(old,0,sendCallbacks,0,old.length);
+                       sendCallbacks[sendCallbacks.length-1]=entry;
+                       }
+               return true;
+       }
+
+       /**
+        * Unregister a handler that was registered with registerSendCallback.
+        * Unregister a handler that was registered with registerSendCallback.
+        *
+        * @param minimumPadding how large must the padding be in order
+        *   to call this method?
+        * @param callback the method to invoke. The receiver is the
+        *   receiver of the message, position is the reference to the
+        *   first unused position in the buffer where GNUnet is building
+        *   the message, padding is the number of bytes left in that buffer.
+        *   The callback method must return the number of bytes written to
+        *   that buffer (must be a positive number).
+        * @return true if the handler was removed, false on error
+        */
+
+       public boolean unregisterSendCallback( int minimumPadding, 
BufferFillCallback callback )
+       {
+               SendCallbackEntry[]     old;
+               int                                     i;
+
+               synchronized(lock) {
+                       for (i=0; i<sendCallbacks.length; i++) {
+                               if (sendCallbacks[i].callback==callback && 
sendCallbacks[i].minimumPadding==minimumPadding) {
+                                       
sendCallbacks[i]=sendCallbacks[sendCallbacks.length-1];
+
+                                       old=sendCallbacks;
+                                       sendCallbacks=new 
SendCallbackEntry[sendCallbacks.length-1];
+                                       
System.arraycopy(old,0,sendCallbacks,0,old.length-1);
+                                       return true;
+                                       }
+                               }
+                       }
+               return false;
+       }
+
+       /**
+        * We received a sign of life from this host.
+        *
+        * @param hostId the peer that gave a sign of live
+        */
+
+       public void notifyPONG( HostIdentity hostId )
+       {
+               BufferEntry be;
+
+               synchronized(lock) {
+                       be = lookForHost(hostId);
+                       if (be != null) {
+                               switch (be.status) {
+                               case STAT_DOWN:
+                                       break;
+                               case STAT_WAITING_FOR_PING:
+                                       /* wrong message, ignore */
+                                       break;
+                               case STAT_WAITING_FOR_PONG:
+                                       be.status = STAT_UP;
+                                       be.transmitted_limit = 
START_TRANSMIT_LIMIT;
+                                       be.idealized_limit = MIN_BPM_PER_PEER;
+                                       transmitConnectionLimit(be);
+                                       activeHosts++;
+                                       be.isAlive=Scheduler.now();
+
+                                       upConnections.inc();
+
+                                       debug(hostId.getName()+" Marking host 
active.");
+                                       break;
+                               case STAT_UP:
+                                       be.isAlive=Scheduler.now();
+                                       break;
+                               default:
+                                       log(Level.WARNING,"WARNING: undefined 
status ("+be.status+") !");
+                                       break;
+                               }
+                       }
+                       }
+       }
+
+       /**
+        * We received a sign of life from this host.
+        *
+        * @param hostId the peer that send a PING.
+        */
+
+       public void notifyPING( HostIdentity hostId )
+       {
+               BufferEntry be;
+
+               synchronized(lock) {
+                       be=lookForHost(hostId);
+
+                       if (be!=null) {
+                               debug(hostId.getName()+" Notify ping.");
+
+                               switch (be.status) {
+                                       case STAT_DOWN:
+                                               break;
+
+                                       case STAT_WAITING_FOR_PONG:
+                                               /* wrong message */
+                                               break;
+
+                                       case STAT_WAITING_FOR_PING:
+                                               be.status = STAT_UP;
+                                               be.transmitted_limit = 
START_TRANSMIT_LIMIT;
+                                               be.idealized_limit = 
MIN_BPM_PER_PEER;
+                                               transmitConnectionLimit(be);
+                                               activeHosts++;
+
+                                               upConnections.inc();
+                                               be.isAlive=Scheduler.now();
+
+                                               debug(hostId.getName()+" 
Marking host active.");
+                                               break;
+
+                                       case STAT_UP:
+                                               be.isAlive=Scheduler.now();
+                                               break;
+
+                                       default:
+                                               log(Level.WARNING,"Unknown 
buffer status : "+be.status);
+                                               break;
+                                       }
+                               }
+                       else {
+                               log(Level.WARNING,hostId.getName()+" Notify 
ping, host not found !");
+                               }
+                       }
+       }
+
+       /**
+        * Send a message to all directly connected nodes.
+        *
+        * @param message the message to send
+        * @param priority how important is the message? The higher, the more 
important
+        * @param maxdelay how long can we wait (max), in CRON-time (ms)
+        */
+
+       public void broadcast( P2PMessage message, int priority, int maxdelay )
+       {
+               int i;
+               BufferEntry tmp;
+
+               synchronized(lock) {
+                       for (i=0; i<connectionBuffer.length; i++) {
+                               tmp = connectionBuffer[i];
+                               while (tmp != null) {
+                                       // we need no sync here as we only 
read, and concurrent rw access does not hurt
+                                       if (tmp.status == STAT_UP) {
+                                               
sendToNode(tmp.getSender(),message,priority,maxdelay);
+                                               }
+                                       tmp = tmp.overflowChain;
+                                       }
+                               }
+                       }
+       }
+
+       /**
+        * Send a message to a specific host (reply, enqueue)
+        * Send a message to a specific host (reply, enqueue).  This method
+        * may only be called by a thread that either holds no locks at all or
+        * at most the lock returned by <tt>getConnectionModuleLock</tt>.
+        *
+        * @param message the message to send (unencrypted!), first 2 bytes 
give size
+        * @param hostId the identity of the receiver
+        * @param priority how important is the message?
+        * @param maxdelay how long can we wait (max), in CRON-time (ms)
+        */
+
+       public void sendToNode( HostIdentity hostId, P2PMessage message, int 
priority, int maxdelay )
+       {
+               BufferEntry     be;
+               SendEntry       se;
+
+               debug(hostId.getName()+" Sending message of type 
"+P2PMessage.nameFor(message.getType())+".");
+
+               synchronized(lock) {
+                       be=connectTo(hostId);
+                       if (be!=null && be.status!=STAT_DOWN) {
+                               se=new 
SendEntry(SendEntry.SE_FLAG_NONE,priority);
+                               se.setContent(message);
+                               se.setDelay(maxdelay);
+                               appendToBuffer(be,se);
+                               }
+                       }
+       }
+
+       public void connectToNode( HostIdentity host )
+       {
+               synchronized(lock) {
+                       connectTo(host);
+                       }
+       }
+
+       /**
+        * Shutdown all connections (send HANGUPs, too).
+        * Shutdown all connections with other peers.
+        */
+
+       public void shutdownConnections()
+       {
+               synchronized(lock) {
+                       log(Level.FINEST,"Shutdown of all connections.");
+                       forAllConnectedHosts(new BufferEntryCallback() {
+                               public void callback( BufferEntry be, Object 
data )
+                               {
+                                       shutdownConnection(be);
+                               }
+                               },null);
+                       }
+       }
+
+       /**
+        * Are we connected to this peer?
+        * Are we connected to this host?
+        *
+        * @param hi the peer in question
+        * @return NO if we are not connected, true if we are
+        */
+
+       public boolean isConnected( HostIdentity hi )
+       {
+               BufferEntry be;
+
+               synchronized(lock) {
+                       be=lookForHost(hi);
+                       }
+               return (be!=null && be.status == STAT_UP);
+       }
+
+       public SessionKey getSessionKeyForHost( HostIdentity host )
+       {
+               BufferEntry     be;
+
+               synchronized(lock) {
+                       be=lookForHost(host);
+                       return (be!=null ? be.skey : null);
+                       }
+       }
+
+       /**
+        * Compute the hashtable index of a host id.
+        *
+        * @param hi the ID of a peer
+        * @return the index for this peer in the connection table
+        */
+
+       public int computeIndex( HostIdentity hi )
+       {
+               // have in mind that connection buffer length is a power of 2
+               return (hi.getHashCodeA() & (connectionBuffer.length-1));
+       }
+
+       /**
+        * Obtain the lock for the connection module
+        * Return a pointer to the lock of the connection module.
+        *
+        * @return the lock
+        */
+
+       public Object getConnectionModuleLock()
+       {
+               return lock;
+       }
+
+       /**
+        * Notification for per-connection bandwidth tracking:
+        * we received size bytes from hostId.  Note that only
+        * encrypted messages are counted as "real" traffic.
+        *
+        * @param hostId the peer that send the message
+        * @param size the size of the message
+        */
+
+       public void trafficReceivedFrom( HostIdentity hostId, int size )
+       {
+               BufferEntry be;
+
+               debug(hostId.getName()+" Received "+size+" bytes from.");
+
+               synchronized(lock) {
+                       be=lookForHost(hostId);
+                       if (be!=null) {
+                               be.recently_received += size;
+                               be.isAlive=Scheduler.now();
+                               }
+                       }
+       }
+
+       /**
+        * How many bpm did we assign this peer (how much traffic
+        * may the given peer send to us per minute?)
+        * @param node
+        * @return
+        */
+
+       public int getBandwidthAssignedTo( HostIdentity node )
+       {
+               BufferEntry be;
+               int ret;
+
+               synchronized(lock) {
+                       be = lookForHost(node);
+                       if (be != null)
+                               ret = be.idealized_limit;
+                       else
+                               ret = 0;
+                       }
+               return ret;
+       }
+
+       /**
+        * Increase the preference for traffic from some other peer.
+        * @param node the identity of the other peer
+        * @param preference how much should the traffic preference be 
increased?
+        */
+
+       public void updateTrafficPreference( HostIdentity node, double 
preference )
+       {
+               BufferEntry be;
+
+               synchronized(lock) {
+                       be = lookForHost(node);
+                       if (be != null)
+                               be.current_connection_value += preference;
+                       }
+       }
+
+       /**
+        * Disconnect a particular peer.  Sends a HANGUP message to the other
+        * side and mark the sessionkey as dead.
+        *
+        * @param node the peer to disconnect
+        */
+
+       public void disconnectFromPeer( HostIdentity node )
+       {
+               BufferEntry     be;
+
+               synchronized(lock) {
+                       be = lookForHost(node);
+                       if (be != null) {
+                               shutdownConnection(be);
+                               }
+                       }
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public boolean handle( Session session, P2PMessage msg, boolean 
encrypted )
+       {
+               if (msg instanceof P2PSequence) {
+                       return handleSequence(session.getRemote(),(P2PSequence) 
msg);
+                       }
+               if (msg instanceof P2PCapability) {
+                       return 
handleCapability(session.getRemote(),(P2PCapability) msg);
+                       }
+               if (msg instanceof P2PHangUp) {
+                       return handleHangUp(session.getRemote(),(P2PHangUp) 
msg);
+                       }
+               if (msg instanceof P2PSessionKey) {
+                       return handleSessionKey(session,(P2PSessionKey) msg);
+                       }
+               return false;
+       }
+
+       /**
+        * Check the sequence number. Updates the sequence number as a 
side-effect.
+        *
+        * @param host  from which peer did we receive the SEQ message
+        * @param msg   the sequence message
+        * @return              true if ok, false if not.
+        */
+
+       protected boolean handleSequence( HostIdentity host, P2PSequence msg )
+       {
+               BufferEntry     be;
+               boolean         res;
+               int                     sequenceNumber;
+
+               debug(host.getName()+" Received sequence.");
+
+               sequenceNumber=msg.getNumber();
+
+               synchronized(lock) {
+                       be=lookForHost(host);
+                       if (be==null) {
+                               log(Level.SEVERE,host.getName()+" Sequence 
message received for a node that is not connected !?");
+                               return false;
+                               }
+
+                       res=true;
+                       if (be.lastSequenceNumberReceived >= sequenceNumber) {
+                               int rotbit = 1;
+                               if 
((be.lastSequenceNumberReceived-sequenceNumber)<=32 && 
be.lastSequenceNumberReceived!=sequenceNumber) {
+                                       rotbit = (rotbit << 
(be.lastSequenceNumberReceived - sequenceNumber - 1));
+                                       if ((be.lastPacketsBitmap & rotbit)==0) 
{
+                                               res = true;
+                                               be.lastPacketsBitmap |= rotbit;
+                                               }
+                                       else {
+                                               res = false;
+                                               }
+                                       }
+                               else {
+                                       res = false;
+                                       }
+
+                               if (!res) {
+                                       log(Level.WARNING,host.getName()+" 
Invalid sequence number "+sequenceNumber+" <= 
"+be.lastSequenceNumberReceived+", dropping rest of packet.");
+                                       }
+                               }
+                       else {
+                               be.lastPacketsBitmap=(be.lastPacketsBitmap << 
(sequenceNumber - be.lastSequenceNumberReceived));
+                               be.lastSequenceNumberReceived = sequenceNumber;
+                               }
+                       }
+
+               if (!res) {
+                       log(Level.INFO,host.getName()+" Message received has 
old sequence number, dropped.");
+                       }
+               return res;
+       }
+
+       /**
+        * Handler for processing CAPABILITY. Resets the bandwidth limit of a 
connection
+        * (or other capabilities that we may define in the future).
+        *
+        * @param host  peer that send the CAP message
+        * @param msg   the CAP message
+        * @return              true on success, false on error
+        */
+
+       protected boolean handleCapability( HostIdentity host, P2PCapability 
msg )
+       {
+               BufferEntry be;
+               Capability      cap;
+
+               debug(host.getName()+" Received capability.");
+
+               synchronized(lock) {
+                       be=lookForHost(host);
+                       if (be==null) {
+                               return false;
+                               }
+
+                       cap=msg.getCapability();
+                       switch (cap.capabilityType) {
+                               case Capability.CAP_BANDWIDTH_RECV:
+                                       debug(host.getName()+" Received 
capability of max. "+IOUtils.newSizeFormatter().format(new 
Long(cap.value/60))+"/s for \""+host.getName()+"\".");
+                                       be.setMaxBytesPerMinutes(cap.value);
+                                       break;
+
+                               default:
+                                       log(Level.INFO,host.getName()+" 
Capability type unknown ("+cap.capabilityType+"), ignored.");
+                                       break;
+                               }
+                       }
+               return true;
+       }
+
+       /**
+        * Handler for processing p2p HANGUP message. Terminates a connection, 
if HANGUP message is valid.
+        *
+        * @param host  the peer sending the HANGUP message
+        * @param msg   the HANGUP message
+        * @return              true on success, false on error
+        */
+
+       protected boolean handleHangUp( HostIdentity host, P2PHangUp msg )
+       {
+               BufferEntry     be;
+
+               if (!host.equals(msg.getSender())) {
+                       return false;
+                       }
+
+               debug(host.getName()+" Received hangup.");
+
+               synchronized(lock) {
+                       be=lookForHost(host);
+                       if (be==null) {
+                               return false;
+                               }
+
+                       log(Level.FINEST,host.getName()+" Received hangup, 
closing connection.");
+                       shutdownConnection(be);
+                       }
+               return true;
+       }
+
+       protected boolean handleSessionKey( Session session, P2PSessionKey msg )
+       {
+               debug(session.getRemote().getName()+" Handle plain session 
key.");
+
+               // establish session if slot not busy, challenge with 
(encrypted) ping !
+               acceptSessionKey(session,msg);
+               return true;
+       }
+
+       public boolean handle( CSSession client, CSMessage msg )
+       {
+               if (msg instanceof CSGetClientCount) {
+                       return handleGetClientCount(client,(CSGetClientCount) 
msg);
+                       }
+               return false;
+       }
+
+       /**
+        * We received a request from a client to provide the number of 
directly connected peers. Sends the response.
+        *
+        * @param client        the socket connecting to the client
+        * @param msg           the request from the client
+        * @return                      true if ok, false if not.
+        */
+
+       protected boolean handleGetClientCount( CSSession client, 
CSGetClientCount msg )
+       {
+               return client.send(new CSResult(activeHosts));
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       /**
+        * Type of the linked list of send callbacks (to
+        * implement a round-robbin invocation chain).
+        */
+
+       private static class SendCallbackEntry extends Object
+       {
+               /** Minimum number of bytes that must be available to call this 
callback. */
+               public int                                      minimumPadding;
+
+               /** The callback method. */
+               public BufferFillCallback       callback;
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       /**
+        * Record for state maintanance.
+        */
+
+       private static class IndexMatch extends Object
+       {
+               public int index;
+               public int matchCount;
+               public int costSelector;
+               public HostIdentity match;
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       /**
+        * inbound bandwidth scheduling
+        */
+
+       private static class UTLClosure extends Object
+       {
+               public BufferEntry[]    e;
+               public int                      pos;
+       }
+}

Added: freeway/src/org/gnu/freeway/server/Core.java
===================================================================
--- freeway/src/org/gnu/freeway/server/Core.java        2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/server/Core.java        2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,53 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.server;
+
+import org.gnu.freeway.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+
+/**
+ *
+ */
+
+public interface Core
+{
+       /**
+        * Just the version number of GNUnet-core API.
+        * Encoded as
+        * 0.6.1d  => 0x00060100
+        * 4.5.2   => 0x04050200
+        *
+        * Note that this version number is only changed if
+        * something changes in the core API.  It follows
+        * roughly the main GNUnet version scheme, but is
+        * more a compatibility ID.
+        */
+
+       public static final int GNUNET_CORE_VERSION     =       0x00060105;
+
+       /**
+        * The identity of the local node.
+        * @return
+        */
+
+       public HostIdentity getIdentity();
+
+       /**
+        * The version of the CORE API. For now, always "0".
+        * @return
+        */
+
+       public int getVersion();
+
+       /**
+        * @param c
+        * @return
+        *
+        */
+
+       public Service service( Class c );
+       public Application getApplication();
+}

Added: freeway/src/org/gnu/freeway/server/CoreForProtocol.java
===================================================================
--- freeway/src/org/gnu/freeway/server/CoreForProtocol.java     2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/server/CoreForProtocol.java     2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,214 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.server;
+
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+/**
+ * GNUnet CORE API for applications that are implemented on top of the GNUnet 
core.
+ */
+
+public interface CoreForProtocol extends Core
+{
+       /**
+        * Increase the preference for traffic from some other peer.
+        * @param node the identity of the other peer
+        * @param preference how much should the traffic preference be 
increased?
+        */
+
+       public void preferTrafficFrom( HostIdentity node, double preference );
+
+       /**
+        * Query how much bandwidth is availabe FROM the given node to
+        * this node in bpm (at the moment).
+        * @param node
+        * @return
+        */
+
+       public int queryBPMfromPeer( HostIdentity node );
+
+       /**
+        * Change our trust in some other node.
+        * @param node the identity of the node
+        * @param delta by how much to change the trust
+        * @return the actual change in trust (trust can not go negative,
+        *  so if the existing trust was 6 and delta was -10, then
+        *  changeTrust will return -6.
+        */
+
+       public int changeTrust( HostIdentity node, int delta );
+
+       /**
+        * Get the amount of trust that we have in a node.
+        * @param node
+        * @return
+        */
+
+       public int getTrust( HostIdentity node);
+
+       /**
+        * Send an encrypted message to another node.
+        * @param receiver the target node
+        * @param msg the message to send
+        * @param importance how important is the message?
+        * @param maxdelay how long can the message be delayed?
+        */
+
+       public void sendToNode( HostIdentity receiver, P2PMessage msg, int 
importance, int maxdelay );
+
+       /**
+        * Perform an operation for all connected hosts.
+        * The BufferEntry structure is passed to the method.
+        * No synchronization or other checks are performed.
+        *
+        * @param method the method to invoke (null for couting only)
+        * @param arg the second argument to the method
+        * @return the number of connected hosts
+        */
+
+       public int forAllConnectedNodes( PerNodeCallback method, Object arg );
+
+       /**
+        * Send a message to all connected nodes. Note that this is
+        * not a network-wide broadcast!
+        * @param msg the message to send
+        * @param importance how important is the message?
+        * @param maxdelay how long can we wait (max), in seconds
+        */
+
+       public void broadcastToConnected( P2PMessage msg, int importance, int 
maxdelay );
+
+       /**
+        * Register a callback method that should be invoked whenever a message
+        * is about to be send that has more than minimumPadding bytes left
+        * before maxing out the MTU.
+        * The callback method can then be used to add additional content
+        * to the message (instead of the random noise that is added by
+        * otherwise). Note that if the MTU is 0 (for streams), the
+        * callback method will always be called with padding set to the
+        * maximum number of bytes left in the buffer allocated for the
+        * send.
+        * @param minimumPadding how large must the padding be in order
+        *   to call this method?
+        * @param callback the method to invoke. The receiver is the
+        *   receiver of the message, position is the reference to the
+        *   first unused position in the buffer where GNUnet is building
+        *   the message, padding is the number of bytes left in that buffer.
+        *   The callback method must return the number of bytes written to
+        *   that buffer (must be a positive number).
+        * @return true if the handler was registered, false on error
+        */
+
+       public boolean registerSendCallback( int minimumPadding, 
BufferFillCallback callback );
+
+       /**
+        * Unregister a handler that was registered with registerSendCallback.
+        * @param minimumPadding
+        * @param callback
+        * @return true if the handler was removed, false on error
+        */
+
+       public boolean unregisterSendCallback( int minimumPadding, 
BufferFillCallback callback );
+
+       /**
+        * Return the estimated size of the network in
+        * the number of nodes running at the moment.
+        * @return
+        */
+
+       public int estimateNetworkSize();
+
+       /**
+        * Compute the index (small, positive, pseudo-unique identification
+        * number) of a hostId.
+        * @param hostId
+        * @return
+        */
+
+       public int computeIndex( HostIdentity hostId );
+
+       /**
+        * The the lock of the connection module. A module that registers
+        * callbacks may need this.
+        * @return
+        */
+
+       public Object getConnectionModuleLock();
+
+       /**
+        * Get statistics over the number of messages that
+        * were received or send of a given type.
+        *
+        * @param messageType the type of the message
+        * @param sendReceive TC_SEND for sending, TC_RECEIVE for receiving
+        * @param timePeriod how many seconds to take
+        *        into consideration (limited by HISTORY_SIZE)
+        * @return
+        */
+/*
+        * @param avgMessageSize average size of the messages (set)
+        * @param messageCount number of messages (set)
+        * @param peerCount number of peers engaged (set)
+        * @param timeDistribution bit-vector giving times of interactions,
+        *        highest bit is current time-unit, bit 1 is 32 time-units ago 
(set)
+        * @return true on success, false on error
+*/
+       public int[] getTrafficStats( int messageType, int sendReceive, int 
timePeriod );
+
+       /**
+        * Obtain the public key and address of a known host. If no specific
+        * protocol is specified (ANY_PROTOCOL_NUMBER), HELOs for cheaper
+        * protocols are returned with preference (randomness!).
+        *
+        * @param hostId the host id
+        * @param protocol the protocol that we need,
+        *        ANY_PROTOCOL_NUMBER if we do not care which protocol
+        * @param tryTemporaryList is it ok to check the unverified HELOs?
+        * @return false on failure, true on success
+        */
+
+       public P2PHello identity2HELO( HostIdentity hostId, int protocol, 
boolean tryTemporaryList );
+
+       /**
+        * Bind a host addres (helo) to a hostId.
+        * @param msg the verified (!) HELO message
+        */
+
+       public void bindAddress( P2PHello msg );
+
+       /**
+        * Disconnect a particular peer. Send a HANGUP message to the other side
+        * and mark the sessionkey as dead.
+        *
+        * @param peer  the peer to disconnect
+        */
+
+       public void disconnectFromPeer( HostIdentity peer );
+
+       /**
+        * Disconnect all current connected peers. Send HANGUP messages to the 
other peers
+        * and mark the sessionkeys as dead.
+        *
+        */
+
+       public void disconnectPeers();
+
+       /**
+        * Load an application module.
+        * @param name
+        * @return true on success, false on error
+        */
+
+       public boolean loadProtocol( String name );
+
+       /**
+        * Unload an application module.
+        * @param name
+        * @return true on success, false on error
+        */
+
+       public boolean unloadProtocols( String name );
+}

Added: freeway/src/org/gnu/freeway/server/CoreForTransport.java
===================================================================
--- freeway/src/org/gnu/freeway/server/CoreForTransport.java    2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/server/CoreForTransport.java    2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,27 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.server;
+
+import org.gnu.freeway.transport.*;
+
+/**
+ * This header file contains a draft for the gnunetd
+ * core API. This API is used by the transport layer
+ * for communication with the GNUnet core.
+ *
+ * A pointer to an instance of this struct is passed
+ * to the init method of each Transport API.
+ */
+
+public interface CoreForTransport extends Core
+{
+       /**
+        * Data was received (potentially encrypted), make the core process it.
+        *
+        * @param mp the messages
+        */
+
+       public void receive( MessagePack mp );
+}

Added: freeway/src/org/gnu/freeway/server/CoreService.java
===================================================================
--- freeway/src/org/gnu/freeway/server/CoreService.java 2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/server/CoreService.java 2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,484 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.server;
+
+import org.gnu.freeway.*;
+import org.gnu.freeway.protocol.*;
+import org.gnu.freeway.transport.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import EDU.oswego.cs.dl.util.concurrent.*;
+
+import java.util.*;
+import java.util.logging.*;
+
+/**
+ * implementation of the GNUnet core API for applications
+ * Implementation of the APIs to the GNUnet core
+ * The APIs to the GNUnet core.
+ */
+
+public class CoreService extends AbstractService implements Core, 
CoreForTransport, CoreForProtocol
+{
+       /** How many incoming packages do we have in the buffer ? Must be >= 
THREAD_COUNT to make sense. */
+       private static final int        QUEUE_LENGTH    =       16;
+
+       /** How many threads do we start ? */
+       private static final int        THREAD_COUNT    =       4;
+
+       /** Protocols indexed by name. */
+       private Map                                     protocols;
+
+       /** Queue of incoming messages. */
+       private MessagePack[]                   bufferQueue;
+
+       /** Count of incoming messages in queue. */
+       private int                                     bufferCount;
+
+       /** Index of the first message to be processed. */
+       private int                                     firstFull;
+
+       /** Index+1 of the last inserted message. */
+       private int                                     firstFree;
+
+       /** Sync mechanisms for message handling threads. */
+       private Semaphore                       queueRead;
+
+       /** Sync mechanisms for message handling threads. */
+       private Semaphore                       queueWrite;
+
+       /** Message handling threads. */
+       private Task[]                          threads;
+
+       private Semaphore                       shutdownSemaphore;
+       private KnownHostsService               knownHosts;
+       private Prefs                           prefs;
+       private ConnectionService               connection;
+       private TrafficService          traffic;
+
+
+       public CoreService()
+       {
+               super("Core");
+               protocols=new HashMap();
+
+               bufferQueue=new MessagePack[QUEUE_LENGTH];
+               Arrays.fill(bufferQueue,null);
+               bufferCount=0;
+               firstFull=0;
+               firstFree=0;
+
+               queueRead=new Semaphore(0);
+               queueWrite=new Semaphore(QUEUE_LENGTH);
+
+               threads=new Task[THREAD_COUNT];
+               Arrays.fill(threads,null);
+
+               shutdownSemaphore=null;
+
+               setDebug(true);
+       }
+
+       public String toString()
+       {
+               return "Core service";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public Application getApplication()
+       {
+               return getManager().app();
+       }
+
+       public void init()
+       {
+               super.init();
+               prefs=getManager().app().getPreferences();
+       }
+
+       public void start()
+       {
+               int     i;
+
+               super.start();
+               knownHosts=(KnownHostsService) 
getManager().service(KnownHostsService.class);
+               connection=(ConnectionService) 
getManager().service(ConnectionService.class);
+               traffic=(TrafficService) 
getManager().service(TrafficService.class);
+
+               log(Level.FINE,"Start #"+threads.length+" message handling 
threads.");
+               for (i=0; i<threads.length; i++) {
+                       threads[i]=new Task("MSG-HANDLING-"+i,new 
AbstractAction() {
+                               public void perform()
+                               {
+                                       process();
+                               }
+                               });
+                       threads[i].launch();
+                       }
+       }
+
+       public void stop()
+       {
+               int i;
+
+               // send HANGUPs for connected hosts
+               connection.shutdownConnections();
+
+               /* stop receiving messages, note that "send" may still be 
called! */
+//             transport.stop();//todo: pourquoi stopper à la main ce service ?
+
+               /* shutdown processing of inbound  messages... */
+               try {
+                       shutdownSemaphore=new Semaphore(0);
+
+                       for (i=0; i<threads.length; i++) {
+                               queueRead.release();
+                               shutdownSemaphore.acquire();
+                               }
+
+                       for (i=0; i<threads.length; i++) {
+                               threads[i].join();
+                               }
+                       }
+               catch( InterruptedException x ) {
+                       err("Interrupted !",x);
+                       }
+
+               super.stop();
+       }
+
+       public void done()
+       {
+               unloadProtocols();
+
+               queueRead=null;
+               queueWrite=null;
+               bufferQueue=null;
+               shutdownSemaphore=null;
+
+               super.done();
+       }
+
+       /**
+        * Processing of a message from the transport layer (receive 
implementation).
+        * @param mp
+        */
+
+       public void receive( MessagePack mp )
+       {
+               try {
+                       if (!queueWrite.attempt(0)) {
+                               
log(Level.INFO,mp.getSession().getRemote().getName()+" Discard message 
("+mp.getMessagesInfo()+"), buffer is full !");
+                               return;
+                               }
+
+                       mp.getSession().lock();
+
+                       synchronized(bufferQueue) {
+                               if (firstFree==bufferQueue.length) {
+                                       firstFree=0;
+                                       }
+                               bufferQueue[firstFree++]=mp;
+                               bufferCount++;
+                               debug(Level.FINE,"Enqueued message pack 
"+Utils.gauge(bufferCount,bufferQueue.length)+".");
+                               }
+
+                       queueRead.release();
+                       }
+               catch( InterruptedException x ) {
+                       err("Interrupted !",x);
+                       }
+       }
+
+       /**
+        * This is the main loop of each thread.  It loops *forever* waiting
+        * for incomming packets in the packet queue. Then it calls "handle"
+        * (defined in handler.c) on the packet.
+        */
+
+       protected void process()
+       {
+               MessagePack     mp;
+               MessagesDispatcher      dispatcher;
+
+               dispatcher=((Server) getManager().app()).getDispatcher();
+
+               while (shutdownSemaphore==null) {
+                       try {
+                               queueRead.acquire();
+                               }
+                       catch( InterruptedException x ) {
+                               err("Failed to acquire read semaphore !",x);
+                               }
+
+                       if (shutdownSemaphore==null) {
+                               synchronized(bufferQueue) {
+                                       mp=bufferQueue[firstFull];
+                                       bufferQueue[firstFull++]=null;
+                                       if (firstFull==bufferQueue.length) {
+                                               firstFull=0;
+                                               }
+                                       bufferCount--;
+                                       debug(Level.FINE,"Dequeued message pack 
"+Utils.gauge(bufferCount,bufferQueue.length)+".");
+                                       }
+
+                               queueWrite.release();
+
+                               // handle buffer, out of sync
+                               dispatcher.dispatch(mp);
+
+                               mp.getSession().unlock();
+                               }
+                       }
+
+               shutdownSemaphore.release();
+       }
+
+       public int getVersion()
+       {
+               return 0;
+       }
+
+       public HostIdentity getIdentity()
+       {
+               LocalIdentity           keys;
+
+               keys=((Server) getManager().app()).getKeys();
+               return keys.whoAmI();
+       }
+
+       public org.gnu.freeway.util.Service service( Class c )
+       {
+               return getManager().service(c);
+       }
+
+       public void preferTrafficFrom( HostIdentity node, double preference )
+       {
+               connection.updateTrafficPreference(node,preference);
+       }
+
+       public int queryBPMfromPeer( HostIdentity node )
+       {
+               return connection.getBandwidthAssignedTo(node);
+       }
+
+       public int changeTrust( HostIdentity node, int delta )
+       {
+               return connection.changeHostCredit(node,delta);
+       }
+
+       public int getTrust( HostIdentity node)
+       {
+               return connection.getHostCredit(node);
+       }
+
+       public void sendToNode( HostIdentity receiver, P2PMessage msg, int 
importance, int maxdelay )
+       {
+               connection.sendToNode(receiver,msg,importance,maxdelay);
+       }
+
+       public int forAllConnectedNodes( PerNodeCallback method, Object arg )
+       {
+               return connection.forEachConnectedNode(method,arg);
+       }
+
+       public void broadcastToConnected( P2PMessage msg, int importance, int 
maxdelay )
+       {
+               connection.broadcast(msg,importance,maxdelay);
+       }
+
+       public boolean registerSendCallback( int minimumPadding, 
BufferFillCallback callback )
+       {
+               return connection.registerSendCallback(minimumPadding,callback);
+       }
+
+       public boolean unregisterSendCallback( int minimumPadding, 
BufferFillCallback callback )
+       {
+               return 
connection.unregisterSendCallback(minimumPadding,callback);
+       }
+
+       public int estimateNetworkSize()
+       {
+               return knownHosts.estimateNetworkSize();
+       }
+
+       public int computeIndex( HostIdentity hostId )
+       {
+               return connection.computeIndex(hostId);
+       }
+
+       public Object getConnectionModuleLock()
+       {
+               return connection.getConnectionModuleLock();
+       }
+
+       public int[] getTrafficStats( int messageType, int sendReceive, int 
timePeriod )
+       {
+               return 
traffic.getTrafficStats(messageType,sendReceive,timePeriod);
+       }
+
+       public P2PHello identity2HELO( HostIdentity hostId, int protocol, 
boolean tryTemporaryList )
+       {
+               return 
knownHosts.identity2HELO(hostId,protocol,tryTemporaryList);
+       }
+
+       public void bindAddress( P2PHello msg )
+       {
+               knownHosts.bindAddress(msg);
+       }
+
+       public void disconnectFromPeer( HostIdentity peer )
+       {
+               connection.disconnectFromPeer(peer);
+       }
+
+       public void disconnectPeers()
+       {
+               connection.shutdownConnections();
+       }
+
+       public void loadProtocols()
+       {
+               String[]        all;
+               int                     i;
+
+               all=prefs.getArray("GNUNETD","APPLICATIONS");
+               if (all.length>0) {
+                       for (i=0; i<all.length; i++) {
+                               log(Level.FINEST,"Loading protocol 
\""+all[i]+"\"...");
+
+                               if (!loadProtocol(all[i])) {
+                                       log(Level.SEVERE,"Could not initialize 
protocol \""+all[i]+"\".");
+                                       }
+                               }
+                       }
+               else {
+                       log(Level.WARNING,"No protocol defined in configuration 
(item APPLICATIONS under GNUNETD) !");
+                       }
+       }
+
+       /**
+        * Load the application module named <code>str</code>.
+        * @param str
+        * @return true on success, false on error
+        */
+
+       public boolean loadProtocol( String str )
+       {
+               ProtocolEntry   entry;
+               Protocol                p;
+
+               synchronized(protocols) {
+                       if (protocols.get(str)!=null) {
+                               log(Level.WARNING,"Protocol '"+str+"' already 
loaded.");
+                               return false;
+                               }
+
+                       p=(Protocol) new 
DynamicLibrary("freeway-"+str+".jar").load(Protocol.class);
+                       if (p==null) {
+                               log(Level.WARNING,"Could not load protocol 
'"+str+"' !");
+                               return false;
+                               }
+                       if (!p.init(this)) {
+                               log(Level.WARNING,"Could not init protocol 
'"+str+"' !");
+                               return false;
+                               }
+
+                       entry=new ProtocolEntry();
+                       entry.libraryName=str;
+                       entry.protocol=p;
+                       protocols.put(str,entry);
+                       }
+               return true;
+       }
+
+       public boolean unloadProtocols()
+       {
+               Iterator        iter;
+               String          str;
+               boolean         res;
+
+               synchronized(protocols) {
+                       res=true;
+
+                       iter=new HashSet(protocols.keySet()).iterator();
+                       while (iter.hasNext()) {
+                               str=(String) iter.next();
+                               if (!unloadProtocols(str)) {
+                                       log(Level.SEVERE,"Could not properly 
unload application module "+str+".");
+                                       res=false;
+                                       }
+                               }
+                       return res;
+                       }
+       }
+
+       public boolean unloadProtocols( String str )
+       {
+               ProtocolEntry   entry;
+
+               synchronized(protocols) {
+                       entry=(ProtocolEntry) protocols.get(str);
+                       if (entry==null) {
+                               log(Level.WARNING,"Could not unload protocol 
'"+str+"' (protocol not loaded) !");
+                               return false;
+                               }
+
+                       protocols.remove(str);
+                       entry.protocol.done();
+                       }
+               return true;
+       }
+
+       public PersistentDecoder createCSDecoder()
+       {
+               PersistentDecoder       dec;
+
+               dec=new PersistentDecoder();
+               
dec.add(CSMessage.IS_CS_MESSAGE_SUPPORTED,CSGetCSMessageSupported.class);
+               dec.add(CSMessage.IS_RESULT,CSResult.class);
+               dec.add(CSMessage.IS_STATISTICS,CSStatistics.class);
+               dec.add(CSMessage.IS_TRAFFIC_INFO,CSTrafficInfo.class);
+               dec.add(CSMessage.IS_TRAFFIC_QUERY,CSTrafficRequest.class);
+               dec.add(CSMessage.IS_GET_STATISTICS,CSStatisticsRequest.class);
+               
dec.add(CSMessage.IS_P2P_MESSAGE_SUPPORTED,CSGetP2PMessageSupported.class);
+               dec.add(CSMessage.IS_SHUTDOWN,CSShutdownRequest.class);
+               dec.add(CSMessage.IS_CLIENT_COUNT,CSGetClientCount.class);
+               dec.add(CSMessage.IS_GET_HOST_INFO,CSGetHostInfo.class);
+
+               dec.merge(createCSDecoderForProtocols());
+               return dec;
+       }
+
+       public PersistentDecoder createCSDecoderForProtocols()
+       {
+               PersistentDecoder       dec;
+               ProtocolEntry           entry;
+               Iterator                        iter;
+
+               dec=new PersistentDecoder();
+               synchronized(protocols) {
+                       iter=protocols.values().iterator();
+                       while (iter.hasNext()) {
+                               entry=(ProtocolEntry) iter.next();
+                               dec.merge(entry.protocol.createCSDecoder());
+                               }
+                       }
+               return dec;
+       }
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////////////////
+
+class ProtocolEntry extends Object
+{
+       public String   libraryName;
+       public Protocol protocol;
+}

Added: freeway/src/org/gnu/freeway/server/DirectedTrafficCounter.java
===================================================================
--- freeway/src/org/gnu/freeway/server/DirectedTrafficCounter.java      
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/server/DirectedTrafficCounter.java      
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,257 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.server;
+
+import org.gnu.freeway.util.*;
+
+import java.util.*;
+
+/**
+ * Numbers for one receive/send/self-send type.
+ */
+
+public class DirectedTrafficCounter extends Object
+{
+       /** Of how many peers do we keep track per message type about "recent" 
interactions ?
+        The memory impact of this value n is 8 * 3 * MAX_MESSAGE_ID * n. The 
current number of messages
+        is roughly a dozen, so the memory impact is about 200 bytes * n, or 
for the default of n=15 it is 3kb. */
+       private static final int        MAX_PEER_IDS            =       15;
+
+       /** How many time-units back do we keep the history of?  (must really 
be <=32 since we use the 32 bit in an integer).
+        The memory impact of this value n is 4 * 3 * MAX_MESSAGE_ID * n, which 
is for the default of n=32
+        with the current MAX_MESSAGE_ID being roughly a dozen less than 2k. */
+       private static final int        HISTORY_SIZE            =       32;
+
+       /** When was this record last updated ? */
+       private long                    lastUpdate;
+
+       /** Time slots for processing (shifted bitvector) */
+       private int                     slots;
+
+       /** "peerCount" identities of the peers that we interacted with most 
recently (abreviated identities plus timestamps) */
+       private PeerDate[]      peers;
+
+       /** How many messages were processed (rotating buffer) ? */
+       private int[]           count;
+
+       /** Average sizes (rotating buffer) */
+       private double[]                avgSize;
+
+
+       public DirectedTrafficCounter()
+       {
+               super();
+               init();
+       }
+
+       protected void init()
+       {
+               int     i;
+
+               peers=new PeerDate[MAX_PEER_IDS];
+               for (i=0; i<peers.length; i++) {
+                       peers[i]=new PeerDate();
+                       }
+
+               count=new int[HISTORY_SIZE];
+               Arrays.fill(count,0);
+
+               avgSize=new double[HISTORY_SIZE];
+               Arrays.fill(avgSize,0);
+       }
+
+       public boolean filled()
+       {
+               return slots!=0;
+       }
+
+       /**
+        * Update the use table. A message of the given size was processed 
interacting with a peer with the given peerId.
+        *
+        * @param size
+        * @param peerId
+        * @param expireOnly
+        */
+
+       public void updateUse( int size, int peerId, boolean expireOnly )
+       {
+               long            now,delta;
+               int             unitNow,deltaUnits,minPeerId,minPeerTime,i,slot;
+
+               now=Scheduler.now();
+               unitNow = (int) Scheduler.toSeconds(now);
+               delta = now - lastUpdate;
+               lastUpdate = now;
+               deltaUnits = (int) Scheduler.toSeconds(delta);
+
+               if (!expireOnly) {
+                       /* update peer identities */
+                       minPeerTime = 0;
+                       minPeerId = 0;
+                       for (i=0;i<MAX_PEER_IDS;i++) {
+                               if (peers[i].time < minPeerTime) {
+                                       minPeerId = i;
+                                       }
+                               if (peers[i].peerIdentity_a == peerId) {
+                                       minPeerId = i;
+                                       break; /* if the peer is already 
listed, re-use
+                                       that slot & update the time! */
+                                       }
+                               }
+                       peers[minPeerId].time = unitNow;
+                       peers[minPeerId].peerIdentity_a = peerId;
+                       }
+
+               /* update expired slots: set appropriate slots to 0 */
+               if (deltaUnits > HISTORY_SIZE) {
+                       deltaUnits = HISTORY_SIZE;
+                       }
+
+               for (i=0;i<deltaUnits;i++) {
+                       count[HS_SLOT(unitNow - HISTORY_SIZE - i)] = 0;
+                       avgSize[HS_SLOT(unitNow - HISTORY_SIZE - i)] = 0.0;
+                       }
+
+               if (!expireOnly) {
+                       int devideBy;
+
+                       /* update slots */
+                       slots = 0x80000000 | (slots >> deltaUnits);
+
+                       /* recompute average, increment count */
+                       slot = HS_SLOT(unitNow);
+                       count[slot]++;
+                       devideBy = count[slot];
+                       if (devideBy <= 0)
+                               avgSize[slot] = 0; /* how can this happen? */
+                       else
+                               avgSize[slot]= ((avgSize[slot] * 
(count[slot]-1)) + size) / devideBy;
+                       }
+       }
+
+       /**
+        * Build the traffic counter summary to send it over the network.
+        *
+        * @param res where to write the summary to
+        * @param tcType the type of the counter (for the flags)
+        * @param countTimeUnits for how long ago should we take the history 
into consideration (max is HISTORY_SIZE).
+        * @param msgType what is the requestType of the message that the dtc 
is for?
+        */
+
+       public void buildSummary( TrafficCounter res, int tcType, int 
countTimeUnits, int msgType )
+       {
+               long            now;
+               int             i,peerCount,unitNow,msgCount,totalMsgSize;
+
+               updateUse(0,0,true); /* expire old entries */
+               now=Scheduler.now();
+               unitNow = (int) Scheduler.toSeconds(now);
+
+               /* count number of peers that we interacted with in
+                the last countTimeUnits */
+               peerCount = 0;
+               for (i=0;i<MAX_PEER_IDS;i++)
+                       if (peers[i].time > now - countTimeUnits)
+                               peerCount++;
+               res.flags = tcType|peerCount;
+
+               /* determine number of messages and average size */
+               msgCount = 0;
+               totalMsgSize = 0;
+               for (i=0;i<countTimeUnits;i++) {
+                       int slot = HS_SLOT(unitNow - i);
+                       totalMsgSize += count[slot] * avgSize[slot];
+                       msgCount += count[slot];
+                       }
+
+               res.count = msgCount;
+               res.type = msgType;
+               if (msgCount > 0)
+                       res.avrg_size = totalMsgSize / msgCount;
+               else
+                       res.avrg_size = 0;
+               res.time_slots = slots;
+       }
+
+       /**
+        * Macro to access the slot at time "t" in the history.
+        * @param a
+        * @return
+        */
+
+       public int HS_SLOT( int a )
+       {
+               return (a % HISTORY_SIZE);
+       }
+
+       /**
+        * Get statistics over the messages.
+        *
+        * @param timePeriod    How many seconds to take into consideration 
(limited by HISTORY_SIZE).
+        * @return                      A vector of results, whose components 
are the average size of the messages, the number of messages,
+        *                                      the number of peers engaged and 
a bit-vector giving times of interactions
+        *                                      (highest bit is current 
time-unit, bit 1 is 32 time-units ago).
+        */
+
+       public int[] getTrafficStats( int timePeriod )
+       {
+               int[]   results;
+               double  totSize;
+               int             nowUnit,slot,i;
+
+               updateUse(0,0,true);
+
+               nowUnit = (int) Scheduler.toSeconds(Scheduler.now());
+
+               results=new int[4];
+               results[0]=0;
+               results[1]=0;
+               results[2]=0;
+               results[3]=0;
+
+               for (i=0; i<MAX_PEER_IDS; i++) {
+                       if (peers[i].time>nowUnit-timePeriod) {
+                               results[2]++;
+                               }
+                       }
+
+               totSize=0;
+               for (i=0; i<timePeriod; i++) {
+                       slot=HS_SLOT(nowUnit-i);
+                       results[1]+=count[slot];
+                       totSize+=count[slot]*avgSize[slot];
+                       }
+
+               if (results[1]>0) {
+                       results[0]=(int) (totSize/results[1]);
+                       }
+
+               results[3]=slots;
+               return results;
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       /**
+        * Information about when a peer was last involved
+        * in a message of the given type.
+        */
+
+       private static class PeerDate extends Object
+       {
+               /** The ".a" member of the Host identity of the peer. */
+               public int      peerIdentity_a;
+
+               /** The time of the interaction. */
+               public int      time;
+
+
+               public PeerDate()
+               {
+                       super();
+               }
+       }
+}

Added: freeway/src/org/gnu/freeway/server/HELOCallback.java
===================================================================
--- freeway/src/org/gnu/freeway/server/HELOCallback.java        2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/server/HELOCallback.java        2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,14 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.server;
+
+/**
+ *
+ */
+
+public interface HELOCallback
+{
+       public void onHELO( P2PHello helo, Object arg );
+}

Added: freeway/src/org/gnu/freeway/server/HELOLoader.java
===================================================================
--- freeway/src/org/gnu/freeway/server/HELOLoader.java  2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/server/HELOLoader.java  2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,356 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.server;
+
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import EDU.oswego.cs.dl.util.concurrent.*;
+
+import java.io.*;
+import java.net.*;
+import java.nio.*;
+import java.nio.channels.*;
+import java.util.*;
+import java.util.logging.*;
+
+/**
+ * HOSTLISTURL support
+ */
+
+public class HELOLoader extends AbstractService
+{
+       private Prefs   prefs;
+       private StatusCallsService      status;
+       private HelloExchangeService    heloExchange;
+       private Scheduler                       scheduler;
+
+       private Stat    heloReceivedViaHTTP;
+
+       /** is the HELO processing still ongoing from previous 
downloadHostlist()? */
+       private Semaphore       downloading;
+
+       /** The HTTP proxy (optional) */
+       private InetAddress     theProxy;
+       private int                     theProxyPort;
+
+
+       public HELOLoader()
+       {
+               super("Helo exchange");
+               setDebug(true);
+       }
+
+       public String toString()
+       {
+               return "HELO loader";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public void init()
+       {
+               Statistics      stats;
+               String  ip,port;
+
+               super.init();
+               scheduler=getManager().app().getScheduler();
+               stats=getManager().app().getStatistics();
+               prefs=getManager().app().getPreferences();
+               status=(StatusCallsService) 
getManager().service(StatusCallsService.class);
+               heloExchange=(HelloExchangeService) 
getManager().service(HelloExchangeService.class);
+
+               heloReceivedViaHTTP=stats.getHandle("# HELO messages received 
from http server",Stat.VERBOSE);
+
+               ip=prefs.getString("GNUNETD", "HTTP-PROXY",null);
+               if (ip!=null) {
+                       try {
+                               theProxy=InetAddress.getByName(ip);
+
+                               
port=prefs.getString("GNUNETD","HTTP-PROXY-PORT",null);
+                               if (port==null) {
+                                       theProxyPort=8080;
+                                       }
+                               else {
+                                       theProxyPort=Integer.parseInt(port);
+                                       }
+                               }
+                       catch( UnknownHostException x ) {
+                               err("Couldn't resolve name of HTTP proxy 
"+ip,x);
+                               theProxy=null;
+                               }
+                       }
+               else {
+                       theProxy=null;
+                       }
+
+               downloading = new Semaphore(1);
+       }
+
+       public void done()
+       {
+               /* FIXME: to be ultimately clean, we would here have to go 
through
+                the cron jobs, find the one matching receiveHeloDeferred (if
+                exists) and free the hcq.helos array.  Anyway, this would
+                require extending the cron API first...  For now, we have the
+                following possible one-shot memory leak
+                */
+               super.done();
+               downloading=null;
+       }
+
+       /**
+        * Download hostlist from the web. This method is invoked
+        * when gnunetd starts and if we suddenly know no more hosts.
+        */
+
+       public void downloadRandomHostList()
+       {
+               String[]                urls;
+               List                    list;
+               int                     cnt;
+
+               urls = prefs.getArray("GNUNETD","HOSTLISTURL","[\\s;]+");
+               if (urls.length==0) {
+                       debug("CRON: exit downloadHostlist (error: URL not 
specified)");
+                       return;
+                       }
+
+               list=new ArrayList();
+
+               try {
+                       if (!downloading.attempt(0)) {
+                               log(Level.INFO,"Won't download hostlist until 
last time HELOs are all processed.");
+                               return;
+                               }
+                       }
+               catch( InterruptedException x ) {
+                       err("",x);
+                       }
+
+               cnt = Crypto.nextInt(urls.length); /* pick random hostlist of 
the pack */
+
+               if (downloadHostList(urls[cnt],new HELOCallback() {
+                       public void onHELO( P2PHello helo, Object arg )
+                       {
+                               ((List) arg).add(helo);
+                       }
+                       },list)) {
+                       debug("Received "+list.size()+" HELOs from 
\""+urls[cnt]+"\".");
+                       }
+
+               if (list.size() > 0) {
+                       final List      _helos = list;
+
+                       scheduler.addJob(new 
ScheduledTask("RECEIVED-HELO-DEFERRED",new AbstractAction() {
+                               public void perform()
+                               {
+                                       receiveHELODeferred(_helos);
+                               }
+                               }),Scheduler.SECS_2);
+                       }
+               else {
+                       debug("postProcessHelos has no HELOs to process");
+                       downloading.release();
+                       }
+       }
+
+       protected void receiveHELODeferred( List helos )
+       {
+               P2PHello                helo;
+               int                     rndidx;
+
+               assert(helos.size()>0);
+
+               // select HELO by random
+               rndidx = Crypto.nextInt(helos.size());
+               debug("Choosed HELO "+rndidx+" out of "+helos.size()+" to 
process...");
+
+               helo=(P2PHello) helos.remove(rndidx);
+               heloExchange.receivedHELO(helo);
+
+               if (helos.size()>0) {
+                       final List      _helos = helos;
+                       // schedule next helo
+                       int load;
+                       int nload;
+                       load = status.getCPULoad();
+                       nload = status.getNetworkLoadUp();
+                       if (nload > load)
+                               load = nload;
+                       nload = status.getNetworkLoadDown();
+                       if (nload > load)
+                               load = nload;
+
+                       scheduler.addJob(new 
ScheduledTask("RECEIVED-HELO-DEFERRED-2",new AbstractAction() {
+                               public void perform()
+                               {
+                                       receiveHELODeferred(_helos);
+                               }
+                               }),50 + Crypto.nextInt((load+1)*(load+1)));
+                       }
+               else {
+                       // all HELOs processed, let's continue
+                       debug("Processed all HELOs.");
+                       downloading.release();
+                       }
+       }
+
+       /**
+        * Download hostlist from the web and call method on each HELO.
+        * @param url
+        * @param callback
+        * @param arg
+        * @return
+        */
+
+       public boolean downloadHostList( String url, HELOCallback callback, 
Object arg )
+       {
+               ByteBuffer      one;
+               Socket          sock;
+               InetAddress     ip;
+               URI                     uri;
+               String          hostname,filename,command;
+               long                    start,stop;
+               int                     curpos,ret,port,len;
+               PersistentReader        pQueue;
+               P2PHello                helo;
+
+               debug("Trying to download a hostlist from \""+url+"\".");
+
+               // syntax
+               uri=null;
+               try {
+                       uri=new URI(url);
+                       }
+               catch( URISyntaxException x ) {
+                       err("Malformed URL : \""+url+"\" !",x);
+                       return false;
+                       }
+               if (!uri.getScheme().equals("http")) {
+                       log(Level.SEVERE,"Invalid URL : \""+url+"\" (must begin 
with http://).");
+                       return false;
+                       }
+
+               hostname=uri.getHost();
+               filename=uri.getPath();
+               command="GET http://"+hostname+filename+"; HTTP/1.0\r\n\r\n";
+
+               // do we need to connect through a proxy ?
+               if (theProxy==null) {
+                       try {
+                               ip=InetAddress.getByName(hostname);
+                               }
+                       catch( UnknownHostException x ) {
+                               err("Could not download hostlist, host 
\""+hostname+"\" is unknown !",x);
+                               return false;
+                               }
+                       port=80;
+                       }
+               else {
+                       ip=theProxy;
+                       port=theProxyPort;
+                       }
+
+               sock=null;
+               try {
+                       sock=SocketChannel.open().socket();
+                       sock.getChannel().configureBlocking(true);
+                       sock.getChannel().connect(new 
InetSocketAddress(ip,port));
+                       
sock.getChannel().write(ByteBuffer.wrap(command.getBytes("US-ASCII")));
+                       sock.getChannel().configureBlocking(false);     // pour 
les lectures suivantes
+                       }
+               catch( IOException x ) {
+                       err("Failed to send HTTP request ("+sock+", "+ip+", 
"+port+") !",x);
+                       if (sock!=null) {
+                               try {
+                                       sock.getChannel().close();
+                                       }
+                               catch( IOException xx ) {
+                                       }
+                               }
+                       return false;
+                       }
+
+               start=Scheduler.now();
+               stop=start+Scheduler.MINUTES_5;
+
+               // we first have to read out the http_response
+               // it ends with four line delimiters: "\r\n\r\n"
+
+               one=ByteBuffer.allocateDirect(1);
+               curpos = 0;
+               while (curpos < 4) {
+                       if (stop< Scheduler.now()) {
+                               break;  // exit after 5mn
+                               }
+
+                       try {
+                               one.clear();
+                               ret=sock.getChannel().read(one);
+                               }
+                       catch( IOException x ) {
+                               err("Got exception while reading on socket 
"+sock+" !",x);
+                               break;  // end of transmission or error
+                               }
+                       if (ret<1) {
+                               Utils.sleep(Scheduler.MILLIS_100);
+                               continue;
+                               }
+                       if (one.get(0)=='\r' || one.get(0)=='\n') {
+                               curpos++;
+                               }
+                       else {
+                               curpos=0;
+                               }
+                       }
+
+               if (curpos < 4) {       // we have not found it
+                       log(Level.WARNING,"WARNING: exit downloadHostlist 
(error: no http response read)");
+                       try {
+                               sock.getChannel().close();
+                               }
+                       catch( IOException xx ) {
+                               err("Failed to close socket !",xx);
+                               }
+                       return false;
+                       }
+
+               pQueue=new PersistentReader(100);
+               while (Scheduler.now()<stop) {
+                       while (!pQueue.canConsume() && Scheduler.now()<stop) {
+                               len=pQueue.feed(sock.getChannel());
+                               if (len==-1) {
+                                       // end of file or error
+                                       break;
+                                       }
+                               if (len==0) {
+                                       Scheduler.sleep(Scheduler.SECS_1);
+                                       }
+                               }
+
+                       helo=(P2PHello) pQueue.consume(P2PHello.class);
+                       if (helo==null) {
+                               break;
+                               }
+
+                       heloReceivedViaHTTP.inc();
+
+                       callback.onHELO(helo,arg);
+                       }
+
+               try {
+                       sock.getChannel().close();
+                       }
+               catch( IOException xx ) {
+                       err("Failed to close socket !",xx);
+                       }
+
+               debug("Done download 
"+Scheduler.toSeconds(stop-Scheduler.now())+" seconds before timeout.");
+               return true;
+       }
+}

Added: freeway/src/org/gnu/freeway/server/HelloExchangeService.java
===================================================================
--- freeway/src/org/gnu/freeway/server/HelloExchangeService.java        
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/server/HelloExchangeService.java        
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,607 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.server;
+
+import org.gnu.freeway.*;
+import org.gnu.freeway.transport.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.util.*;
+import java.util.logging.*;
+
+/**
+ * Cron-jobs that exchange HELOs to ensure that the network is connected 
(nodes know of each other).
+ * Cron-jobs exchanging routing information (HELOs messages)
+ */
+
+public class HelloExchangeService extends AbstractService implements P2PHandler
+{
+       /** How long may a HELO be valid (in seconds). We use 10 days, do not 
change
+        (would break compatibility with peers that have a different limit). */
+       public static final int MAX_HELO_EXPIRES                        =       
(60 * 60 * 24 * 10);
+
+       /** */
+       public static final long        HELO_BROADCAST_FREQUENCY        =       
Scheduler.MINUTES_2;
+
+       /** */
+       public static final long        HELO_FORWARD_FREQUENCY  =       
Scheduler.MINUTES_4;
+
+       /** */
+       public static final int HELO_HELPER_TABLE_START_SIZE    =       128;
+
+       private ConnectionService               connection;
+       private Prefs                           prefs;
+       private KnownHostsService               knownHosts;
+       private TransportService                transport;
+       private PingPongService         pingPong;
+       private TrafficService          traffic;
+       private PolicyService                   policy;
+
+       /* handles for stats */
+       private Stat                                    stat_helo_received;
+       private Stat                                    
stat_helo_valid_received;
+       private Stat                                    stat_helo_forwarded;
+       private Stat                                    stat_helo_initiated;
+
+       /** Meanings of the bits in activeCronJobs (ACJ). */
+       public static final int         ACJ_NONE                =       0;
+       public static final int         ACJ_ANNOUNCE    =       1;
+       public static final int         ACJ_FORWARD             =       2;
+       public static final int         ACJ_ALL                 =       
(ACJ_ANNOUNCE | ACJ_FORWARD);
+
+       /** Which types of cron-jobs are currently scheduled with cron ? */
+       private int                                     activeCronJobs  =       
ACJ_NONE;
+
+       private long                                    lastHELOMsg     =       
0;
+
+       private Scheduler                       scheduler;
+       private ScheduledTask                   broadcastTask;
+       private ScheduledTask                   forwardTask;
+
+
+       public HelloExchangeService()
+       {
+               super("Helo exchange");
+               setDebug(true);
+
+               broadcastTask=new ScheduledTask("BROADCAST-HELOS",new 
EvalAction(this,"broadcastHELO"),HELO_BROADCAST_FREQUENCY);
+               forwardTask=new ScheduledTask("FORWARD-HELOS",new 
EvalAction(this,"forwardHELO"),HELO_FORWARD_FREQUENCY);
+       }
+
+       public String toString()
+       {
+               return "Helo exchange service";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public void attach( ServiceManager mgr )
+       {
+               super.attach(mgr);
+               connection=(ConnectionService) 
mgr.service(ConnectionService.class);
+               knownHosts=(KnownHostsService) 
mgr.service(KnownHostsService.class);
+               transport=(TransportService) 
mgr.service(TransportService.class);
+               pingPong=(PingPongService) mgr.service(PingPongService.class);
+               traffic=(TrafficService) mgr.service(TrafficService.class);
+               policy=(PolicyService) mgr.service(PolicyService.class);
+
+               prefs=mgr.app().getPreferences();
+       }
+
+       /**
+        * Initialize a few cron jobs. Must be called after
+        * initcron (!).
+        */
+
+       public void init()
+       {
+               Statistics                      stats;
+               MessagesDispatcher      dispatcher;
+
+               super.init();
+               stats=getManager().app().getStatistics();
+               stat_helo_received= stats.getHandle("# HELO messages received 
overall",Stat.VERBOSE);
+               stat_helo_valid_received= stats.getHandle("# valid HELO 
messages received",Stat.VERBOSE);
+               stat_helo_forwarded= stats.getHandle("# HELO messages forwarded 
from other peers",Stat.VERBOSE);
+               stat_helo_initiated= stats.getHandle("# HELO messages 
originated",Stat.VERBOSE);
+
+               dispatcher=((Server) getManager().app()).getDispatcher();
+               
dispatcher.registerP2PHandler(P2PMessage.IS_HELO,P2PHello.class,this);
+
+               prefs.registerConfigurationUpdateCallback(new 
EvalAction(this,"configurationUpdateCallback"));
+
+               scheduler=getManager().app().getScheduler();
+               if 
(!prefs.testString("NETWORK","DISABLE-ADVERTISEMENTS","YES")) {
+                       scheduler.addJob(broadcastTask,Scheduler.MINUTES_1);
+                       activeCronJobs += ACJ_ANNOUNCE;
+                       }
+               else {
+                       log(Level.WARNING,"WARNING: network advertisements 
disabled by configuration !");
+                       }
+
+               if (prefs.testString("NETWORK","HELOEXCHANGE","YES")) {
+                       scheduler.addJob(forwardTask,Scheduler.MINUTES_4 /* see 
connection.c: SECONDS_INACTIVE_DROP */);
+                       activeCronJobs += ACJ_FORWARD;
+                       }
+               else {
+                       debug("HELO forwarding disabled !");
+                       }
+       }
+
+       /**
+        * Stops a few cron jobs that exchange HELOs.
+        */
+
+       public void done()
+       {
+               MessagesDispatcher      dispatcher;
+
+               super.done();
+               if (ACJ_ANNOUNCE == (activeCronJobs & ACJ_ANNOUNCE)) {
+                       scheduler.deleteJob(broadcastTask);
+                       activeCronJobs -= ACJ_ANNOUNCE;
+                       }
+               if (ACJ_FORWARD == (activeCronJobs & ACJ_FORWARD)) {
+                       scheduler.deleteJob(forwardTask);
+                       activeCronJobs -= ACJ_FORWARD;
+                       }
+               prefs.unregisterConfigurationUpdateCallback(new 
EvalAction(this,"configurationUpdateCallback"));
+
+               dispatcher=((Server) getManager().app()).getDispatcher();
+               dispatcher.unregisterP2PHandler(P2PMessage.IS_HELO,this);
+       }
+
+       public boolean handle( Session session, P2PMessage msg, boolean 
encrypted )
+       {
+               if (!encrypted) {
+                       debug(session.getRemote().getName()+" Handle plain 
HELO.");
+
+                       receivedHELO((P2PHello) msg);
+                       return true;
+                       }
+               return eHELOHandler(session.getRemote(),(P2PHello) msg);
+       }
+
+       /**
+        * Type for a HELO send via an encrypted channel.
+        * @param sender
+        * @param message
+        * @return
+        */
+
+       protected boolean eHELOHandler( HostIdentity sender, P2PHello message )
+       {
+               double  preference;
+
+               if (receivedHELO(message)) {
+                       /* if the HELO was ok, update traffic preference
+                        for the peer (depending on how much we like
+                        to learn about other peers) */
+
+                       /* we should'nt give lots of bandwidth for HELOs
+                        if we're less than 2 peers away from the connection
+                        goal */
+                       preference = (double) connection.getConnectPriority() / 
4;
+                       /* see also afs/policy.h: give some decent, but 
compared to
+                        (migrated) content competitive amount of bandwidth to 
peers
+                        sending (valid) HELOs */
+                       if (preference < 0.4)
+                               preference = 0.4;
+                       connection.updateTrafficPreference(sender,preference);
+                       }
+               return true; /* even if we had errors processing the HELO, keep 
going */
+       }
+
+       /**
+        * The configuration has changed, update set of
+        * running cron jobs.  Does not have to suspend
+        * cron since this guaranteed to be a cron job!
+        */
+
+       public void configurationUpdateCallback()
+       {
+               if (ACJ_ANNOUNCE == (activeCronJobs & ACJ_ANNOUNCE)) {
+                       if 
(prefs.testString("NETWORK","DISABLE-ADVERTISEMENTS","YES"))
+                               scheduler.deleteJob(broadcastTask);
+                       activeCronJobs -= ACJ_ANNOUNCE;
+                       }
+               else {
+                       if (prefs.testString("NETWORK","HELOEXCHANGE","YES"))
+                               
scheduler.addJob(broadcastTask,Scheduler.MINUTES_1);
+                       activeCronJobs += ACJ_ANNOUNCE;
+                       }
+
+               if (ACJ_FORWARD == (activeCronJobs & ACJ_FORWARD)) {
+                       if (!prefs.testString("NETWORK","HELOEXCHANGE","YES"))
+                               scheduler.deleteJob(forwardTask);
+                       activeCronJobs -= ACJ_FORWARD;
+                       }
+               else {
+                       if 
(!prefs.testString("NETWORK","DISABLE-ADVERTISEMENTS","YES"))
+                               
scheduler.addJob(broadcastTask,Scheduler.MINUTES_1);
+                       activeCronJobs += ACJ_FORWARD;
+                       }
+       }
+
+       /**
+        * We have received a HELO. Verify (signature, integrity, ping-pong) 
and store identity
+        * if it's okay and its protocol is supported.
+        *
+        * @param helo  the HELO message
+        * @return              false on error, true on success
+        */
+
+       public boolean receivedHELO( P2PHello helo )
+       {
+               byte[]          b;
+               Session         tsession;
+               Transport       t;
+               P2PHello                copy;
+               P2PPing         ping;
+               HostIdentity    foreignId;
+               List                    buffer;
+               long                    now;
+               int                     len,mtu;
+               boolean         res;
+
+               stat_helo_received.inc();
+
+               // first verify that it is actually a valid HELO
+               foreignId=new HostIdentity(helo.getPublicKey());
+               if (!helo.getSenderIdentity().equals(foreignId)) {
+                       return false;   // public key and host hash do not match
+                       }
+
+               if (!helo.verify()) {
+                       log(Level.WARNING,"HELO message received from 
"+helo.getSenderIdentity().getName()+" is invalid (signature invalid). 
Dropping.");
+                       return false;   // message invalid
+                       }
+
+               if 
(helo.getExpirationTime()>Scheduler.toSeconds(Scheduler.now())+MAX_HELO_EXPIRES)
 {
+                       log(Level.WARNING,"HELO message received from 
"+helo.getSenderIdentity().getName()+" is invalid (expiration time over limit). 
Dropping.");
+                       return false;
+                       }
+
+               t=transport.getTransport(helo);
+               if (t==null || !t.verifyHELO(helo)) {
+                       log(Level.FINEST,"Could not verify HELO !");
+                       return true;    // not good, but do process rest of 
message
+                       }
+
+               stat_helo_valid_received.inc();
+
+               debug(Level.INFO,helo.getSenderIdentity().getName()+" HELO 
advertisement for protocol 
"+TransportService.nameForProtocol(helo.getProtocol())+" received.");
+
+               if (helo.getProtocol()==Transport.NAT_PROTOCOL_NUMBER) {
+                       // we *can* not verify NAT. Ever. So all we can do is 
just accept it. The best thing that we may do
+                       // is check that it was not forwarded by another peer 
(forwarding NAT advertisements is illegal),
+                       // but even that check can not be done securely (since 
we have to accept HELOs in plaintext).
+                       // Thus we take NAT advertisements at face value (which 
is OK since we never attempt to connect to a NAT).
+                       knownHosts.bindAddress(helo);
+                       return true;
+                       }
+
+               // then check if we have seen this HELO before, if it is 
identical except for the TTL,
+               // we trust it and do not play PING-PONG
+               
copy=knownHosts.identity2HELO(foreignId,helo.getProtocol(),false);
+               if (copy!=null) {
+                       if (helo.sameProtocolMTUAndAddress(copy)) {
+                               // okay, we've seen this one exactly like this 
before (at most the TTL has changed)
+                               // thus we can trust it without playing 
ping-pong
+                               knownHosts.bindAddress(helo);
+                               return true;
+                               }
+
+                       debug("advertised HELO differs from prior knowledge, 
requireing ping-pong confirmation.");
+                       //todo: mettre plus d'infos...
+                       debug("HELO-diff: "+helo.getProtocol()+" -- 
"+copy.getProtocol()+", "+helo.getMTU()+" -- "+copy.getMTU());
+                       }
+
+               if (prefs.testString("GNUNETD","PRIVATE-NETWORK","YES")) {
+                       /* the option 'PRIVATE-NETWORK' can be used
+                        to limit the connections of this peer to
+                        peers of which the hostkey has been copied
+                        by hand to data/hosts;  if this option is
+                        given, GNUnet will not accept advertisements
+                        of peers that the local node does not already
+                        know about.  Note that in order for this
+                        option to work, HOSTLISTURL should either
+                        not be set at all or be set to a trusted
+                        peer that only advertises the private network.
+                        Also, the option does NOT work at the moment
+                        if the NAT transport is loaded; for that,
+                        a couple of lines above would need some minor
+                        editing :-). */
+                       return false;
+                       }
+
+               now=Scheduler.now();
+               if ( Scheduler.toSeconds(now - lastHELOMsg) * 
prefs.getInt("LOAD","MAXNETDOWNBPSTOTAL",0)  / 100< helo.getByteSize() ) {
+                       /* do not use more than about 1% of the
+                        available bandwidth to VERIFY HELOs (by sending
+                        our own with a PING).  This does not affect
+                        the HELO advertising.  Sure, we should not
+                        advertise much more than what other peers
+                        can verify, but the problem is that buggy/
+                        malicious peers can spam us with HELOs, and
+                        we don't want to follow that up with massive
+                        HELO-ing by ourselves. */
+                       return false;
+                       }
+               lastHELOMsg = now;
+
+               // Ok, must play PING-PONG. Add the HELO to the temporary 
(in-memory only) buffer
+               // to make it available for a short time in order to play 
PING-PONG
+               copy = (P2PHello) PersistentHelper.copy(helo);
+               knownHosts.addTemporaryHost(copy);
+
+               // Establish session as advertised in the HELO
+               copy = (P2PHello) PersistentHelper.copy(helo);
+               tsession=transport.transportConnect(copy);
+               if (tsession==null) {
+                       return false; /* could not connect */
+                       }
+
+               // build message to send, ping must contain return-information, 
such as a selection of our HELOs...
+               buffer=new ArrayList();
+
+               mtu=tsession.getTransport().getMTU();
+
+               len=transport.getAdvertisedHELOs(mtu-P2PPing.SIZE,buffer);
+               if (len==-1) {
+                       log(Level.WARNING,"Could not obtain advertised HELOs, 
no PINGing.");
+                       tsession.unlock();
+                       return false;
+                       }
+
+               ping=new P2PPing();
+               buffer.add(ping);
+
+               copy = (P2PHello) PersistentHelper.copy(helo);
+               final P2PHello  _h = copy;
+
+               res = true;
+               if (!pingPong.pingAction(helo.getSenderIdentity(),new 
AbstractAction() {
+                       public void perform()
+                       {
+                               knownHosts.bindAddress(_h);
+                       }
+                       },_h,ping)) {
+                       res = false;
+                       log(Level.INFO,"Could not send HELOs+PING, ping buffer 
full !");
+                       }
+
+               // ok, finally we can send !
+               if (res) {
+                       b=PersistentHelper.toBytes(buffer);
+
+                       if (tsession.send((Persistent[]) buffer.toArray(new 
Persistent[0]))) {
+                               
traffic.updateTrafficSendCounter(P2PMessage.IS_HELO,b.length-P2PPing.SIZE);
+                               
traffic.updateTrafficSendCounter(P2PMessage.IS_PING,P2PPing.SIZE);
+                               }
+                       else {
+                               res = false;
+                               }
+                       }
+
+               if (!tsession.unlock()) {
+                       res = false;
+                       }
+               return res;
+       }
+
+       /**
+        * Tell a couple of random hosts on the current known host list that we 
exist.
+        */
+
+       public void broadcastHELO()
+       {
+               transport.forEachTransport(new TransportCallback() {
+                       public void callback( Transport t, Object data )
+                       {
+                               broadcastHELOTransport(t);
+                       }
+                       },null);
+       }
+
+       /**
+        * Tell a couple of random hosts on the current known host list that we 
exist (called for each transport).
+        *
+        * @param t     The transport that we advertise.
+        */
+
+       protected void broadcastHELOTransport( Transport t )
+       {
+               SendData                sd;
+               long                    now;
+
+               debug(Level.FINE,"About to advertise myself for transport 
"+TransportService.nameForProtocol(t.getProtocol())+".");
+
+               now=Scheduler.now();
+
+               sd=new SendData();
+               sd.n=knownHosts.forEachHost(null,now,null);     // just count
+               if (sd.n==0) {
+                       log(Level.WARNING,"Could not advertise : no hosts are 
known.");
+                       return;
+                       }
+
+               sd.helo=transport.transportCreateHELO(t.getProtocol());
+               if (sd.helo==null) {
+                       log(Level.WARNING,"Could not advertise : unable to 
create HELO.");
+                       return;
+                       }
+
+               knownHosts.bindAddress(sd.helo);
+               knownHosts.forEachHost(new HostIterator() {
+                       public void iterate( HostIdentity identity, int 
protocol, Object data )
+                       {
+                               broadcastHelper(identity,protocol,(SendData) 
data);
+                       }
+                       },now,sd);
+
+               debug(Level.FINE,"Succeedeed to advertise myself.");
+       }
+
+       protected void broadcastHelper( HostIdentity hi, int proto, SendData sd 
)
+       {
+               Session session;
+               P2PHello        helo;
+               LocalIdentity           keys;
+               int             prio;
+
+               if (proto==Transport.NAT_PROTOCOL_NUMBER) {     // don't 
advertise NAT addresses via broadcast
+                       return;
+                       }
+               if (Crypto.nextInt(sd.n)!=0) {
+                       return;
+                       }
+
+               keys=((Server) getManager().app()).getKeys();
+               if (keys.whoAmI().equals(hi)) { // never advertise to myself...
+                       return;
+                       }
+
+               debug("Send advertisement to "+hi.getName()+".");
+
+               prio=connection.getConnectPriority();
+               if (prio>=ConnectionService.EXTREME_PRIORITY) {
+                       prio=ConnectionService.EXTREME_PRIORITY/4;
+                       }
+
+               if (connection.isConnected(hi)) {
+                       connection.sendToNode(hi,sd.helo,prio,(int) 
HELO_BROADCAST_FREQUENCY);
+                       stat_helo_initiated.inc();
+                       return;
+                       }
+
+               // with even lower probability (with n peers trying to contact 
with a probability of 1/n^2,
+               // we get a probability of 1/n for this, which is what we want 
: fewer attempts to contact fresh peers
+               // as the network grows)
+               if (Crypto.nextInt(sd.n)!=0) {
+                       debug("Could not send advertisement, randomly 
discarded.");
+                       return;
+                       }
+
+               if (!policy.outgoingCheck(prio)) {      // peer too busy
+                       debug("Could not send advertisement, peer is too 
busy.");
+                       return;
+                       }
+
+               // establish short-lived connection, send, tear down
+               helo=knownHosts.identity2HELO(hi,proto,false);
+               if (helo==null) {
+                       debug("Could not send advertisement, unable to find 
HELO for \""+hi.getName()+"\".");
+                       return;
+                       }
+
+               session=transport.transportConnect(helo);
+               if (session==null) {
+                       debug("Could not send advertisement, connect error.");
+                       return;
+                       }
+
+               if (session.send(sd.helo)) {
+                       
traffic.updateTrafficSendCounter(P2PMessage.IS_HELO,sd.helo.getByteSize());
+                       }
+
+               stat_helo_initiated.inc();
+               session.unlock();
+       }
+
+       /**
+        * Forward HELOs from all known hosts to all connected hosts.
+        * @param identity
+        * @param protocol
+        * @param probability
+        */
+
+       protected void forwardHELOHelper( HostIdentity identity, int protocol, 
int probability )
+       {
+               P2PHello        helo;
+               int                     now,count;
+
+               if (protocol == Transport.NAT_PROTOCOL_NUMBER) {
+                       return; /* don't forward NAT addresses */
+                       }
+
+               if (Crypto.nextInt(probability+1) != 0) {
+                       return; /* only forward with a certain chance, (on 
average: 1 peer per run!) */
+                       }
+
+               debug(Level.FINE,"Forwarding HELOs.");
+
+               helo=knownHosts.identity2HELO(identity,protocol,false);
+               if (helo==null) {
+                       return; /* this should not happen */
+                       }
+
+               // do not forward expired HELOs, remove them
+               now=(int) Scheduler.toSeconds(Scheduler.now());
+               if (helo.getExpirationTime()<now) {
+                       log(Level.INFO,"Removing expired HELO from 
"+identity.getName()+" (expired "+(now-helo.getExpirationTime())+"s ago).");
+                       knownHosts.delHostFromKnown(identity, protocol);
+                       return;
+                       }
+
+               count = connection.forEachConnectedNode(null,null);
+               stat_helo_forwarded.add(count);
+
+               if (count == 0) {
+                       count = 1; /* avoid division by 0 */
+                       }
+               //todo: pas plutôt (probability  * HELO_BROADCAST_FREQUENCY) / 
count ?
+               connection.broadcast(helo,0  /* priority: 0 */,(int) 
((probability / count)  * HELO_BROADCAST_FREQUENCY));      // send before the 
next round...
+       }
+
+       /**
+        * Forward HELOs from all known hosts to all connected hosts.
+        * Forward HELOs from all known hosts to all known hosts.
+        */
+
+       public void forwardHELO()
+       {
+               int count;
+               int conn;
+
+               debug("Enter forwardHELO.");
+               count = knownHosts.forEachHost(null, 0,null);
+               conn = connection.forEachConnectedNode(null,null);
+               // reduce to 1 message on average for each period; yes, we get 
always a bunch at a time, but that's ok
+               count = count * conn;
+               final int       ccc = count;
+
+               knownHosts.forEachHost(new HostIterator() {
+                       public void iterate( HostIdentity identity, int 
protocol, Object data )
+                       {
+                               forwardHELOHelper(identity,protocol,ccc);
+                       }
+                       },0, /* ignore blacklisting */null);
+               debug("Exit forwardHELO.");
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       private static class SendData extends Object
+       {
+               /** The HELO message. */
+               private P2PHello                helo;
+
+               /** Send the HELO in 1 out of n cases. */
+               private int                     n;
+
+
+               private SendData()
+               {
+                       super();
+                       helo=null;
+                       n=0;
+               }
+       }
+}

Added: freeway/src/org/gnu/freeway/server/Host.java
===================================================================
--- freeway/src/org/gnu/freeway/server/Host.java        2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/server/Host.java        2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,62 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.server;
+
+import org.gnu.freeway.util.crypto.*;
+
+/**
+ * A remote host that daemon is talking to.
+ */
+
+public class Host extends Object
+{
+       private HostIdentity    identity;
+
+
+       public Host( HostIdentity id )
+       {
+               super();
+               identity=id;
+       }
+
+       public String toString()
+       {
+               return "Host";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public HostIdentity getIdentity()
+       {
+               return identity;
+       }
+
+       public int getCredit()
+       {
+               return -1;
+       }
+
+       public HostFailure[] getFailureHistory()
+       {
+               return null;
+       }
+
+       public void recordFailure( HostFailure fail )
+       {
+       }
+
+       public void flushOnDisk()
+       {
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public static Host fromDisk( HostIdentity id )
+       {
+               return null;
+       }
+}

Added: freeway/src/org/gnu/freeway/server/HostFailure.java
===================================================================
--- freeway/src/org/gnu/freeway/server/HostFailure.java 2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/server/HostFailure.java 2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,26 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.server;
+
+/**
+ *
+ */
+
+public class HostFailure extends Object
+{
+       public HostFailure()
+       {
+               super();
+       }
+
+       public String toString()
+       {
+               return "Host failure";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+}

Added: freeway/src/org/gnu/freeway/server/HostIterator.java
===================================================================
--- freeway/src/org/gnu/freeway/server/HostIterator.java        2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/server/HostIterator.java        2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,22 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.server;
+
+import org.gnu.freeway.util.crypto.*;
+
+/**
+ * Type of an iterator over all hosts.
+ */
+
+public interface HostIterator
+{
+       /**
+        * @param identity the identity of the host
+        * @param protocol the available protocol
+        * @param data the data-argument passed to forEachHost
+        */
+
+       public void iterate( HostIdentity identity, int protocol, Object data );
+}

Added: freeway/src/org/gnu/freeway/server/KnownHostsService.java
===================================================================
--- freeway/src/org/gnu/freeway/server/KnownHostsService.java   2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/server/KnownHostsService.java   2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,708 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.server;
+
+import org.gnu.freeway.*;
+import org.gnu.freeway.transport.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.io.*;
+import org.gnu.freeway.util.net.*;
+
+import java.util.*;
+import java.util.logging.*;
+
+/**
+ * maintains list of known peers
+ * Code to maintain the list of currently known hosts
+ * (in memory structure of data/hosts) and temporary blacklisting
+ * information.
+ *
+ * Code to maintain the list of currently known hosts (in memory
+ * structure of data/hosts) and (temporary) blacklisting information
+ * and a list of HELOs that are temporary unless confirmed via PONG
+ * (used to give the transport module the required information for the
+ * PING).
+ *
+ * Todo:
+ * - we may want to cache more HELOs in memory
+ */
+
+public class KnownHostsService extends AbstractService implements CSHandler
+{
+       private static final int        MAX_TEMP_HOSTS  =       32;
+
+       private Scheduler                       scheduler;
+       private Prefs                           prefs;
+       private TransportService                transport;
+
+       /** The list of known hosts. */
+       private List                                    hosts_;
+
+       /** Directory where the HELOs are stored in. */
+       private DirLocation                     hostsDirectory;
+
+       /** The ring buffer containing temporarily known hosts. */
+       private P2PHello[]                      tempHosts;
+
+       /** The current index into the ring buffer. */
+       private int                                     tempHostsNextSlot;
+
+       /** Lock used to gain access to hosts. */
+       private Object                          lock_;
+
+       private ScheduledTask                   scanTask;
+
+
+       public KnownHostsService()
+       {
+               super("Known hosts");
+               setDebug(true);
+
+               hosts_=new ArrayList();
+               tempHosts=new P2PHello[MAX_TEMP_HOSTS];
+
+               scanTask=new ScheduledTask("SCAN-HOSTS",new AbstractAction() {
+                       public void perform()
+                       {
+                               cronScanDirectoryDataHosts();
+                       }
+                       },Scheduler.MINUTES_15);
+       }
+
+       public String toString()
+       {
+               return "Known hosts service";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public void attach( ServiceManager mgr )
+       {
+               super.attach(mgr);
+               prefs=mgr.app().getPreferences();
+
+               scheduler=mgr.app().getScheduler();
+       }
+
+       /**
+        * Initialize this service.
+        */
+
+       public void init()
+       {
+               super.init();
+
+               Arrays.fill(tempHosts,null);
+
+               tempHostsNextSlot = 0;
+               lock_=new Object();
+
+               hostsDirectory=prefs.getDirLocation("GNUNETD","HOSTS");
+               if (hostsDirectory==null) {
+                       log(Level.SEVERE,"Configuration file must specify 
directory for network identities in section GNUNETD under HOSTS.");
+                       return;
+                       }
+               hostsDirectory.create();
+               log(Level.INFO,"Hosts directory is 
\""+hostsDirectory.getLabel()+"\".");
+
+               cronScanDirectoryDataHosts();
+               scheduler.addJob(scanTask,Scheduler.MINUTES_15);
+       }
+
+       public void start()
+       {
+               super.start();
+               transport=(TransportService) 
getManager().service(TransportService.class);
+
+               ((Server) 
getManager().app()).registerCSHandler(CSMessage.IS_GET_HOST_INFO,CSGetHostInfo.class,this);
+       }
+
+       public void stop()
+       {
+               super.stop();
+               ((Server) 
getManager().app()).unregisterCSHandler(CSMessage.IS_GET_HOST_INFO,this);
+       }
+
+       /**
+        * Shutdown the module.
+        */
+
+       public void done()
+       {
+               super.done();
+
+               scheduler.deleteJob(scanTask);
+               tempHosts=null;
+               lock_=null;
+               hosts_.clear();
+               hostsDirectory=null;
+       }
+
+       /**
+        * Get the filename under which we would store the HELO_Message
+        * for the given host and protocol.
+        * @param id
+        * @param protocol
+        * @return filename of the form DIRECTORY/HOSTID.PROTOCOL
+        */
+
+       protected String getHostFileName( HostIdentity id, int protocol )
+       {
+               return hostsDirectory.getPath()+"/"+id.getName()+"."+protocol;
+       }
+
+       /**
+        * Add a host to the temporary list.
+        * @param helo
+        */
+
+       public void addTemporaryHost( P2PHello helo )
+       {
+               debug(helo.getSenderIdentity().getName()+" Adding node to 
temporary list.");
+
+               synchronized(lock_) {
+                       tempHosts[tempHostsNextSlot++]=helo;
+                       if (tempHostsNextSlot==tempHosts.length) {
+                               tempHostsNextSlot=0;
+                               }
+                       }
+       }
+
+       /**
+        * Add a host to the list.
+        * @param identity the identity of the host
+        * @param protocol the protocol for the host
+        */
+
+       protected void addHostToKnown( HostIdentity identity, int protocol )
+       {
+               HostEntry       he;
+               int                     i;
+
+               synchronized(lock_) {
+                       for (i=0; i<hosts_.size(); i++) {
+                               he=(HostEntry) hosts_.get(i);
+                               if (identity.equals(he.identity) && 
protocol==he.protocol) {
+                                       return; // already there
+                                       }
+                               }
+
+                       he=new HostEntry();
+                       he.identity=(HostIdentity) 
PersistentHelper.copy(identity);
+                       he.until=0;
+                       he.delta=Scheduler.SECS_30;
+                       he.protocol=protocol;
+                       he.strict=false;
+
+                       hosts_.add(he);
+                       }
+       }
+
+       /**
+        * Delete a host from the list.
+        * @param helo
+        */
+
+       public void delHostFromKnown( P2PHello helo )
+       {
+               delHostFromKnown(helo.getSenderIdentity(),helo.getProtocol());
+       }
+
+       public void delHostFromKnown( HostIdentity identity, int protocol )
+       {
+               HostEntry       he;
+               String          fn;
+               int                     i;
+
+               synchronized(lock_) {
+                       for (i=0; i<hosts_.size(); i++) {
+                               he=(HostEntry) hosts_.get(i);
+                               if (identity.equals(he.identity) && 
protocol==he.protocol) {
+                                       he=(HostEntry) hosts_.remove(i);
+
+                                       // now remove the file
+                                       fn = 
getHostFileName(he.identity,he.protocol);
+                                       new FileLocation(fn).delete();
+                                       return;
+                                       }
+                               }
+                       }
+
+               trace("Host not registered !??? 
("+identity.getName()+","+protocol+")");
+       }
+
+       /**
+        * Bind a host address (helo) to a hostId.
+        * @param helo the verified HELO message
+        */
+
+       public void bindAddress( P2PHello helo )
+       {
+               P2PHello                old;
+               MappedFile      mm;
+               String          str;
+               FileLocation    f;
+
+               if (helo==null) {
+                       log(Level.SEVERE,"Can't bind null HELO !");
+                       return;
+                       }
+
+               debug(helo.getSenderIdentity().getName()+" Bind address for 
protocol "+TransportService.nameForProtocol(helo.getProtocol())+".");
+
+               
str=getHostFileName(helo.getSenderIdentity(),helo.getProtocol());
+
+               f=new FileLocation(str);
+               if (f.exists()) {
+                       mm=f.open();
+                       old=(P2PHello) mm.asPersistent(P2PHello.class);
+                       mm.close();
+
+                       if (old==null) {
+                               log(Level.WARNING,"Corrupted HELO found at 
\""+str+"\", replace it.");
+                               }
+                       else if (old.getExpirationTime() > 
helo.getExpirationTime()) {
+                               return; // have more recent HELO in stock
+                               }
+                       }
+
+               mm=f.openNew();
+               mm.writePersistent(helo);
+               mm.close();
+
+               addHostToKnown(helo.getSenderIdentity(),helo.getProtocol());
+       }
+
+       /**
+        * Obtain the public key and address of a known host. If no specific
+        * protocol is specified (ANY_PROTOCOL_NUMBER), HELOs for cheaper
+        * protocols are returned with preference (randomness!).
+        *
+        * @param host                          the host
+        * @param protocol                      the protocol that we need, 
ANY_PROTOCOL_NUMBER if we do not care which protocol
+        * @param tryTemporaryList      is it ok to check the unverified HELOs?
+        * @return                                      HELO message on 
success, null on failure
+        */
+
+       public P2PHello identity2HELO( HostIdentity host, int protocol, boolean 
tryTemporaryList )
+       {
+               final HeloResult        _res;
+               P2PHello        helo;
+               int                     i;
+
+               helo=loadHELO(host,protocol);
+               if (helo!=null && 
transport.isTransportAvailable(helo.getProtocol())) {
+                       return helo;
+                       }
+
+               if (tryTemporaryList) {
+                       // ok, then try temporary hosts
+                       synchronized(lock_) {
+                               for (i=0; i<tempHosts.length; i++) {
+                                       if (tempHosts[i]!=null && 
match(tempHosts[i],host,protocol)) {
+                                               return (P2PHello) 
PersistentHelper.copy(tempHosts[i]);
+                                               }
+                                       }
+                               }
+                       }
+
+               if (protocol!=Transport.ANY_PROTOCOL_NUMBER) {
+                       return null;    // nothing found
+                       }
+
+               // ok, last chance, scan directory !
+               debug("Scanning directory \""+hostsDirectory.getLabel()+"\" for 
peer identity.");
+
+               _res=new HeloResult();
+               _res.name=host.getName();
+               _res.helo=null;
+
+               TraverserContext        ctx;
+
+               ctx=new TraverserContext();
+               ctx.setFilesOnly(true);
+               ctx.setMaxDepth(1);
+
+               hostsDirectory.traverse(new Traverser() {
+                       public boolean examine( Location loc, int depth )
+                       {
+                               P2PHello                h;
+
+                               if (loc.getName().startsWith(_res.name)) {
+                                       h=loadHELO((FileLocation) loc);
+                                       if (h!=null && 
transport.isTransportAvailable(h.getProtocol()) ) {
+                                               _res.helo=(_res.helo!=null ? 
randomChoice(h,_res.helo) : h);
+                                               }
+                                       }
+                               return true;
+                       }
+                       },ctx);
+               return _res.helo;
+       }
+
+       public P2PHello[] identity2HELOs( HostIdentity host, boolean 
tryTemporaryList )
+       {
+               final List              _list = new ArrayList();
+               final String    _name = host.getName();
+               int                             i;
+
+               if (tryTemporaryList) {
+                       synchronized(lock_) {
+                               for (i=0; i<tempHosts.length; i++) {
+                                       if (tempHosts[i]!=null && 
match(tempHosts[i],host,Transport.ANY_PROTOCOL_NUMBER)) {
+                                               
_list.add(PersistentHelper.copy(tempHosts[i]));
+                                               }
+                                       }
+                               }
+                       }
+
+               TraverserContext        ctx;
+
+               ctx=new TraverserContext();
+               ctx.setFilesOnly(true);
+               ctx.setMaxDepth(1);
+
+               hostsDirectory.traverse(new Traverser() {
+                       public boolean examine( Location node, int depth )
+                       {
+                               P2PHello        helo;
+
+                               if (node.getName().startsWith(_name)) {
+                                       helo=loadHELO((FileLocation) node);
+                                       if (helo!=null && 
transport.isTransportAvailable(helo.getProtocol()) ) {
+                                               _list.add(helo);
+                                               }
+                                       }
+                               return true;
+                       }
+                       },ctx);
+               return (P2PHello[]) _list.toArray(new P2PHello[_list.size()]);
+       }
+
+       protected boolean match( P2PHello helo, HostIdentity host, int protocol 
)
+       {
+               if (!helo.getSenderIdentity().equals(host)) {
+                       return false;
+                       }
+               if (helo.getProtocol()==protocol) {
+                       return true;
+                       }
+               return (protocol==Transport.ANY_PROTOCOL_NUMBER && 
transport.isTransportAvailable(helo.getProtocol()));
+       }
+
+       protected P2PHello randomChoice( P2PHello h1, P2PHello h2 )
+       {
+               Transport       t;
+               int                     c1,c2;
+
+               t=transport.getTransport(h1);
+               c1=(t!=null ? Crypto.nextInt(t.getCost()) : Integer.MAX_VALUE);
+
+               t=transport.getTransport(h2);
+               c2=(t!=null ? Crypto.nextInt(t.getCost()) : Integer.MAX_VALUE);
+
+               return (c1<c2 ? h1 : h2);
+       }
+
+       protected P2PHello loadHELO( HostIdentity host, int protocol )
+       {
+               MappedFile              mm;
+               FileLocation            f;
+               P2PHello                        helo;
+
+               f=hostsDirectory.getFile(host.getName()+"."+protocol);
+               if (!f.exists()) {
+                       return null;
+                       }
+
+               mm=f.open();
+               helo=(P2PHello) mm.asPersistent(P2PHello.class);
+               mm.close();
+
+               if (helo==null) {
+                       log(Level.WARNING,"Remove invalid HELO file 
\""+f.getLabel()+"\".");
+                       f.delete();
+                       }
+
+               return helo;
+       }
+
+       protected P2PHello loadHELO( FileLocation f )
+       {
+               P2PHello                helo;
+               MappedFile      mm;
+
+               mm=f.open();
+               if (mm!=null) {
+                       helo=(P2PHello) mm.asPersistent(P2PHello.class);
+                       mm.close();
+                       }
+               else {
+                       helo=null;
+                       }
+
+               if (helo==null) {
+                       log(Level.WARNING,"Remove invalid HELO file 
\""+f.getLabel()+"\".");
+                       f.delete();
+                       }
+               return helo;
+       }
+
+       /**
+        * Blacklist a host. This method is called if a host
+        * failed to respond to a connection attempt.
+        *
+        * @param identity the ID of the peer to blacklist
+        * @param desperation how desperate are we to connect ? [0,MAXHOSTS]
+        * @param strict should we reject incoming connection attempts as well ?
+        * @return true on success false on error
+        */
+
+       public boolean blacklistHost( HostIdentity identity, int desperation, 
boolean strict )
+       {
+               HostEntry       he;
+               int                     i;
+
+               if (desperation < 0)
+                       desperation = 0;
+
+               synchronized(lock_) {
+                       for (i=0; i<hosts_.size(); i++) {
+                               he=(HostEntry) hosts_.get(i);
+
+                               if (identity.equals(he.identity)) {
+                                       if (strict) {
+                                               /* Presumably runs a broken 
version of GNUnet;
+                                                blacklist for 1 day (we hope 
the other peer
+                                                updates the software 
eventually...) */
+                                               he.delta = Scheduler.DAYS_1;
+                                               }
+                                       else {
+                                               he.delta=he.delta * 2 
+Crypto.nextInt((int) Scheduler.seconds(desperation+1));
+                                               if (he.delta > 
Scheduler.HOURS_4) {
+                                                       he.delta = (int) 
(Scheduler.HOURS_4+Crypto.nextInt(desperation+1));
+                                                       }
+                                               }
+
+                                       he.until=Scheduler.now();
+                                       he.until += he.delta;
+                                       he.strict = strict;
+
+                                       debug(identity.getName()+" Blacklisting 
host for "+Scheduler.toSeconds(he.delta)+" seconds (strict="+strict+").");
+                                       return true;
+                                       }
+                               }
+                       }
+               return false;
+       }
+
+       /**
+        * Is the node currently 'strictly' blacklisted?
+        * @param identity host to check
+        * @return YES if true, else NO
+        */
+
+       public boolean isBlacklistedStrict( HostIdentity identity )
+       {
+               HostEntry       he;
+               int                     i;
+
+               synchronized(lock_) {
+                       for (i=0; i<hosts_.size(); i++) {
+                               he=(HostEntry) hosts_.get(i);
+                               if (identity.equals(he.identity)) {
+                                       if (Scheduler.now()<he.until && 
he.strict) {
+                                               return true;
+                                               }
+                                       return false;
+                                       }
+                               }
+                       }
+               return false;
+       }
+
+       /**
+        * Whitelist a host. This method is called if a host
+        * successfully established a connection. It typically
+        * resets the exponential backoff to the smallest value.
+        * @param identity
+        * @return true on success false on error
+        */
+
+       public boolean whitelistHost( HostIdentity identity )
+       {
+               HostEntry       he;
+               int                     i;
+
+               synchronized(lock_) {
+                       for (i=0; i<hosts_.size(); i++) {
+                               he=(HostEntry) hosts_.get(i);
+                               if (identity.equals(he.identity)) {
+                                       he.delta=Scheduler.SECS_30;
+                                       he.until=0;
+                                       he.strict=false;
+
+                                       debug(identity.getName()+" Whitelisted 
host.");
+                                       return true;
+                                       }
+                               }
+                       }
+               return false;
+       }
+
+       /**
+        * Call a method for each known host.
+        *
+        * @param callback      the method to call for each host, may be null
+        * @param now           the time to use for excluding hosts due to 
blacklisting, use 0 to go through all hosts.
+        * @param data          an argument to pass to the method
+        * @return the number of known hosts matching
+        */
+
+       public int forEachHost( HostIterator callback, long now, Object data )
+       {
+               HostIdentity    hi;
+               HostEntry       he;
+               LocalIdentity                   keys;
+               int                     count,i;
+
+               keys=((Server) getManager().app()).getKeys();
+
+               count=0;
+
+               synchronized(lock_) {
+                       for (i=0; i<hosts_.size(); i++) {
+                               he=(HostEntry) hosts_.get(i);
+
+                               if (he.identity.equals(keys.whoAmI())) {
+                                       continue;
+                                       }
+
+                               if (now==0 || now>=he.until) {
+                                       count++;
+                                       if (callback != null) {
+                                               hi=(HostIdentity) 
PersistentHelper.copy(he.identity);
+
+                                               
callback.iterate(hi,he.protocol,data);
+                                               }
+                                       }
+                               }
+                       }
+               return count;
+       }
+
+       /**
+        * Get an estimate of the network size.
+        * @return the estimated number of nodes, false on error
+        */
+
+       public int estimateNetworkSize()
+       {
+               return hosts_.size();
+       }
+
+       /**
+        * Call this method periodically to scan data/hosts for new hosts.
+        */
+
+       public void cronScanDirectoryDataHosts()
+       {
+               final int[]     _count = new int[] { 0 };
+
+               debug(Level.FINE,"Enter cronScanDirectoryDataHosts.");
+
+               TraverserContext        ctx;
+
+               ctx=new TraverserContext();
+               ctx.setFilesOnly(true);
+               ctx.setMaxDepth(1);
+
+               hostsDirectory.traverse(new Traverser() {
+                       public boolean examine( Location node, int depth )
+                       {
+                               cronHelper(node.getName());
+                               _count[0]++;
+                               return true;
+                       }
+                       },ctx);
+
+               if (_count[0] == 0) {
+                       log(Level.WARNING,"Scan of 
\""+hostsDirectory.getLabel()+"\" returned no known hosts !");
+                       }
+
+               debug(Level.FINE,"Exit cronScanDirectoryDataHosts.");
+       }
+
+       protected void cronHelper( String filename )
+       {
+               HostIdentity    identity;
+               String                  fullname,id;
+               int                             protoNumber;
+
+               if (filename.matches("\\w{40}\\.[\\d]+")) {
+                       id=filename.substring(0,40);
+                       protoNumber=Integer.parseInt(filename.substring(41));
+
+                       identity=new HostIdentity(HashCode160.parse(id));
+
+                       addHostToKnown(identity,protoNumber);
+                       }
+               else {
+                       log(Level.WARNING,"File in "+hostsDirectory.getPath()+" 
does not match naming convention ("+filename+"), remove it.");
+
+                       fullname=hostsDirectory.getPath()+"/"+filename;
+                       new FileLocation(fullname).delete();
+                       }
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public boolean handle( CSSession client, CSMessage msg )
+       {
+               if (msg instanceof CSGetHostInfo) {
+                       return handleGetHostInfo(client,(CSGetHostInfo) msg);
+                       }
+               return false;
+       }
+
+       protected boolean handleGetHostInfo( CSSession client, CSGetHostInfo 
msg )
+       {
+               CSHostInfo              infos;
+               HostIdentity    host;
+
+               host=msg.getHost();
+
+               infos=new CSHostInfo(host);
+               infos.setHELOs(identity2HELOs(host,true));
+               return client.send(infos);
+       }
+}
+
+class HeloResult extends Object
+{
+       public String   name;
+       public P2PHello helo;
+}
+
+class HostEntry extends Object
+{
+       /** */
+       public HostIdentity     identity;
+
+       /** how long is this host blacklisted ? */
+       public long                     until;
+
+       /** what would be the next increment for blacklisting ? (in seconds) */
+       public long                     delta;
+
+       /** for which protocol is this host known ? */
+       public int                      protocol;
+
+       /** should we also reject incoming messages? (YES/NO) */
+       public boolean          strict;
+}

Added: freeway/src/org/gnu/freeway/server/LocalIdentity.java
===================================================================
--- freeway/src/org/gnu/freeway/server/LocalIdentity.java       2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/server/LocalIdentity.java       2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,128 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.server;
+
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.io.*;
+
+import java.util.logging.*;
+
+/**
+ * Encapsulation of the identity of the local host.
+ */
+
+public class LocalIdentity extends LoggedObject
+{
+       private static final String     PRIVATE_KEY_FILE        =       
".private_key";
+
+       /** The <em>secret</em> hostkey. Keep local, never export outside of 
this module ! */
+       private PrivateKey                      privateKey;
+
+       /** The public hostkey */
+       private PublicKey                       publicKey;
+
+       /** The identity of THIS node. our identity. */
+       private HostIdentity                    myIdentity;
+
+
+       public LocalIdentity()
+       {
+               super(true);
+               privateKey=null;
+               publicKey=null;
+               myIdentity=null;
+       }
+
+       public String toString()
+       {
+               return "Keys";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public HostIdentity whoAmI()
+       {
+               return myIdentity;
+       }
+
+       /**
+        * Get the public key of the host.
+        *
+        * @return      The host's public key.
+        */
+
+       public PublicKey getPublicKey()
+       {
+               return publicKey;
+       }
+
+       /**
+        * Get the private key of the host.
+        *
+        * @return      The host's private key.
+        */
+
+       public PrivateKey getPrivateKey()
+       {
+               return privateKey;
+       }
+
+       /**
+        * Load identity from disk. If not found or corrupted one, create a new 
key pair.
+        *
+        * @param home  Home directory for the application.
+        * @return              True if succeeded, false otherwise.
+        */
+
+       public boolean load( DirLocation home )
+       {
+               MappedFile      f;
+               FileLocation    loc;
+               boolean         res;
+
+               privateKey=null;
+               publicKey=null;
+               myIdentity=null;
+
+               loc=home.getFile(PRIVATE_KEY_FILE);
+               if (loc.exists()) {
+                       f=loc.open();
+                       privateKey=(PrivateKey) 
f.asPersistent(PrivateKey.class);
+                       f.close();
+
+                       if (privateKey==null) {
+                               log(Level.WARNING,"Hostkey file failed format 
check, creating new hostkey.");
+                               }
+                       }
+
+               if (privateKey==null) {
+                       log(Level.INFO,"Creating new key pair (this may take a 
while)...");
+
+                       privateKey=PrivateKey.create();
+                       if (privateKey==null) {
+                               log(Level.SEVERE,"Could not create key pair !");
+                               return false;
+                               }
+
+                       f=loc.openNew();
+                       res=f.writePersistent(privateKey);
+                       f.close();
+
+                       if (!res) {
+                               log(Level.SEVERE,"Failed to store hostkey !");
+                               return false;
+                               }
+
+                       log(Level.INFO,"Done creating hostkey.");
+                       }
+
+               publicKey=privateKey.toPublicKey();
+               myIdentity=new HostIdentity(publicKey);
+               log(Level.FINEST,"I am "+myIdentity.getName()+".");
+               return true;
+       }
+}

Added: freeway/src/org/gnu/freeway/server/MessagesDispatcher.java
===================================================================
--- freeway/src/org/gnu/freeway/server/MessagesDispatcher.java  2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/server/MessagesDispatcher.java  2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,375 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.server;
+
+import org.gnu.freeway.*;
+import org.gnu.freeway.transport.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+import java.util.*;
+import java.util.logging.*;
+
+/**
+ * demultiplexer for incoming peer-to-peer packets.
+ * Main handler for incoming packets.
+ */
+
+public class MessagesDispatcher extends LoggedObject
+{
+       /** */
+       private Application                     application;
+
+       /** Array of the message handlers; there may be null pointers in it ! */
+       private P2PHandler[]                    handlers;
+
+       /** Received bytes statistic. */
+       private Stat                                    receivedBytes;
+
+       /** */
+       private Stat                                    decryptedBytesFailed;
+
+       /** What percentage of inbound messages should be randomly dropped (for 
testing unreliability of the network) ? */
+       private int                                     
percentRandomInboundDrop;
+
+       /** */
+       private PersistentDecoder               decoder;
+
+
+       public MessagesDispatcher()
+       {
+               super(true);
+               handlers=new P2PHandler[0];
+               percentRandomInboundDrop=0;
+               decoder=new PersistentDecoder();
+       }
+
+       public String toString()
+       {
+               return "Messages dispatcher";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public void init( Application app )
+       {
+               Statistics      stats;
+
+               application=app;
+
+               stats=app.getStatistics();
+               decryptedBytesFailed=stats.getHandle("# bytes received and 
decryption failed");
+               receivedBytes=stats.getHandle("# bytes decrypted");
+       }
+
+       public void done()
+       {
+               int     i;
+
+               for (i=0; i<handlers.length; i++) {
+                       if (handlers[i]!=null) {
+                               log(Level.WARNING,"Handler still registered for 
type #"+i+" ("+P2PMessage.nameFor(i)+") : "+handlers[i]);
+                               handlers[i]=null;
+                               }
+                       }
+               handlers=null;
+       }
+
+       public int getPercentRandomInboundDrop()
+       {
+               return percentRandomInboundDrop;
+       }
+
+       /**
+        * Which percentage of inbound messages should gnunetd drop at random 
(to simulate network unreliability or congestion) ?
+        *
+        * @param value Percent of messages to drop (>=0 and <=100).
+        */
+
+       public void setPercentRandomInboundDrop( int value )
+       {
+               assert(value>=0 && value<=100);
+
+               percentRandomInboundDrop=value;
+       }
+
+       /**
+        * Message dispatch/handling.
+        * The actual main method of GNUnet: message dispatch/handling.
+        *
+        * @param mp    Message to be handled.
+        */
+
+       public void dispatch( MessagePack mp )
+       {
+               HostIdentity    sender;
+               KnownHostsService               knownhosts;
+
+               sender=mp.getSession().getRemote();
+
+               if (percentRandomInboundDrop>0 && 
percentRandomInboundDrop>Crypto.nextInt(100)) {
+                       log(Level.FINE,sender.getName()+" Randomly dropped 
message pack : "+mp.getMessagesInfo());
+                       return;
+                       }
+
+               knownhosts=(KnownHostsService) 
application.service(KnownHostsService.class);
+               if (knownhosts.isBlacklistedStrict(sender) ) {
+                       debug(sender.getName()+" Strictly blacklisted host has 
sent message, dropping for now.");
+                       return;
+                       }
+
+               if (mp.isEncrypted()) {
+                       
debug(Level.INFO,mp.getSession().getRemote().getName()+" Handle *encrypted* 
message pack.");
+                       handleEncrypted(mp);
+                       }
+               else {
+                       
debug(Level.INFO,mp.getSession().getRemote().getName()+" Handle *plain* message 
pack : "+mp.getMessagesInfo()+".");
+                       handlePlain(mp);
+                       }
+       }
+
+       /**
+        * Handle a pack of messages. Decrypts it, checks the CRC and if that's 
ok, processes the message
+        * by calling the registered handler for each message.
+        *
+        * @param mp    The message pack.
+        */
+
+       protected void handleEncrypted( MessagePack mp )
+       {
+               ConnectionService       connection;
+               TrafficService          traffic;
+               P2PHandler                      callback;
+               P2PMessage                      part;
+               PersistentReader                pQueue;
+               SessionKey                      key;
+               HostIdentity                    sender;
+               ByteBuffer                      buf;
+               int                                     crc;
+               boolean                         stop;
+
+               buf=mp.getMessages();
+
+               sender=mp.getSession().getRemote();
+
+               connection=(ConnectionService) 
application.service(ConnectionService.class);
+
+               receivedBytes.add(buf.position());
+               debug(sender.getName()+" Decrypting message.");
+
+               key=connection.getSessionKeyForHost(sender);
+               if (key==null) {
+                       log(Level.INFO,sender.getName()+" Could not find 
session key !");
+                       // try to establish a connection, that way, we don't 
keep
+                       // getting bogus messages until the other one times out.
+                       connection.connectToNode(sender);
+                       return;
+                       }
+
+               if (!mp.decryptWith(key)) {
+                       decryptedBytesFailed.add(buf.position());
+
+                       log(Level.INFO,sender.getName()+" Could not decrypt 
packet : bad session key ?");
+                       return;
+                       }
+
+               buf=mp.getMessages();
+               if (isDebug()) {
+                       
debug(Level.INFO,mp.getSession().getRemote().getName()+" Handle *encrypted* 
message pack : "+mp.getMessagesInfo());
+                       }
+
+               // we may be able to use this transport-session to reduce our 
cost to send replies to the sender, let's check
+               connection.considerTakeover(mp.getSession(),sender);
+
+               crc=mp.getCRC();
+               if (Crypto.crc32(buf)!=crc) {
+                       log(Level.WARNING,sender.getName()+" CRC check failed, 
message ignored (crc("+buf.position()+" 
bytes)=0x"+Utils.toHex(Crypto.crc32(buf))+" instead of 
0x"+Utils.toHex(crc)+").");
+                       return;
+                       }
+
+               connection=(ConnectionService) 
application.service(ConnectionService.class);
+               connection.trafficReceivedFrom(sender,buf.position());
+
+               pQueue=new PersistentReader(buf);
+
+               stop=false;
+               while (pQueue.canConsume() && !stop) {
+                       part=(P2PMessage) pQueue.consume(decoder);
+                       if (part==null) {
+                               continue;       // skip not recognized messages 
(ad-hoc protocols not loaded)
+                               }
+
+                       traffic=(TrafficService) 
application.service(TrafficService.class);
+                       traffic.updateTrafficReceiveCounter(part);
+
+                       callback=getP2PHandler(part.getType());
+                       if (callback==null) {
+                               log(Level.FINEST,"P2P message not understood: 
"+part.getType()+" (no handler registered).");
+                               continue;
+                               }
+
+                       traffic.trafficReceive(part,sender);
+
+                       if (!callback.handle(mp.getSession(),part,true)) {
+                               log(Level.FINEST,"DEBUG: handler aborted 
message processing at ptyp "+part.getType());
+                               stop=true;      // handler says: do not process 
the rest of the message */
+                               }
+                       }
+
+               if (pQueue.getNotConsumed()>0) {
+                       trace("Not all bytes have been consumed, data 
corruption ?");
+                       }
+
+               pQueue.clear();
+       }
+
+       /**
+        * Handle a pack of messages. Checks the CRC and if that's ok, 
processes the message
+        * by calling the registered handler for each message part.
+        *
+        * @param mp    The message pack.
+        */
+
+       protected void handlePlain( MessagePack mp )
+       {
+               PersistentReader        queue;
+               P2PMessage              p;
+               P2PHandler      handler;
+               boolean cont;
+               TrafficService  traffic;
+               ByteBuffer              buf;
+               Session tsession;
+               int     crc;
+
+               tsession=mp.getSession();
+               crc=mp.getCRC();
+               buf=mp.getMessages();
+
+               if (Crypto.crc32(buf)!=crc) {
+                       log(Level.WARNING,"CRC check failed ("+crc+" expected, 
got "+Crypto.crc32(buf)+"), message of size "+buf.position()+" ignored.");
+                       return;
+                       }
+
+               cont=true;
+
+               queue=new PersistentReader(buf);
+               while (queue.canConsume() && cont) {
+                       p=(P2PMessage) queue.consume(decoder);
+                       if (p==null) {
+                               continue;
+                               }
+
+                       // one call for all types...
+                       traffic=(TrafficService) 
application.service(TrafficService.class);
+                       traffic.updateTrafficReceiveCounter(p);
+
+                       handler=getP2PHandler(p.getType());
+                       if (handler!=null) {
+                               cont=handler.handle(tsession,p,false);
+                               }
+                       else {
+                               log(Level.WARNING,"Unsupported *plain* message 
: "+p);
+                               }
+                       }
+
+               if (queue.getNotConsumed()>0) {
+                       trace("Not all bytes have been consumed, data 
corruption ?");
+                       }
+
+               queue.clear();
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       /**
+        * Return whether or not there is a method handler registered for a 
specific p2p message type.
+        *
+        * @param type  The message type.
+        * @return              True if there is a handler for the type, false 
if there isn't.
+        */
+
+       public boolean isP2PHandlerRegistered( int type )
+       {
+               assert(type>=0);
+
+               return (type<handlers.length && handlers[type]!=null);
+       }
+
+       public P2PHandler getP2PHandler( int type )
+       {
+               assert(type>=0);
+
+               return (type<handlers.length ? handlers[type] : null);
+       }
+
+       /**
+        * Register a method as a handler for specific message types.
+        *
+        * @param type          The message type.
+        * @param c                     The message class.
+        * @param handler       The method to call if a message of that type is 
received, if the callback returns false, processing of the message is 
discontinued afterwards (all other parts are ignored).
+        * @return                      True on success, false if there is 
already a handler for that type.
+        */
+
+       public boolean registerP2PHandler( int type, Class c, P2PHandler 
handler )
+       {
+               P2PHandler[]    tmp;
+
+               assert(type>=0);
+
+               log(Level.FINE,"Register p2p handler for type #"+type+" 
("+P2PMessage.nameFor(type)+").");
+
+               synchronized(handlers) {
+                       if (type>=handlers.length) {
+                               tmp=handlers;
+
+                               handlers=new P2PHandler[type+32];
+                               Arrays.fill(handlers,null);
+                               System.arraycopy(tmp,0,handlers,0,tmp.length);
+                               }
+
+                       if (handlers[type]!=null) {
+                               log(Level.WARNING,"Could not register handler 
for type "+type+", slot is used.");
+                               return false;
+                               }
+
+                       decoder.add(type,c);
+                       handlers[type]=handler;
+                       return true;
+                       }
+       }
+
+       /**
+        * Remove a method as a handler for specific message types.
+        *
+        * @param type          The message type.
+        * @param handler       The method to call if a message of that type is 
received.
+        * @return                      True on success, false if there is a 
different handler for that type.
+        */
+
+       public boolean unregisterP2PHandler( int type, P2PHandler handler )
+       {
+               assert(type>=0);
+
+               log(Level.FINE,"Unregister p2p handler for type #"+type+" 
("+P2PMessage.nameFor(type)+").");
+
+               synchronized(handlers) {
+                       if (type<handlers.length) {
+                               if (handlers[type]!=handler) {
+                                       return false;
+                                       }
+
+                               handlers[type]=null;
+                               decoder.remove(type);
+                               return true;
+                               }
+                       return false;
+                       }
+       }
+}

Added: freeway/src/org/gnu/freeway/server/P2PCapability.java
===================================================================
--- freeway/src/org/gnu/freeway/server/P2PCapability.java       2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/server/P2PCapability.java       2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,86 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.server;
+
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * This message is used to advertise node capabilities.  Each
+ * capability has a capability type and a value.  The primary
+ * motivation for the introduction of capabilities is
+ * CAP_BANDWIDTH_RECV which can be used by a peer to specify a maximum
+ * amount of data that it is currently (!) willing to receive and
+ * process from another peer.  After receiving a capability message
+ * the other peer is expected to only send requests to the sender that
+ * match the capabilities.<p>
+ *
+ * If a peer does not understand a given capability type, the message
+ * is to be ignored.  Future capabilities that are currently planned
+ * include an advertisment that specifies the set of application
+ * services that are (not) supported.
+ */
+
+public class P2PCapability extends P2PMessage
+{
+       public static final int SIZE    =       4+Capability.SIZE;
+
+       private Capability      cap;
+
+
+       public P2PCapability()
+       {
+               super(IS_CAPABILITY);
+               cap=new Capability();
+       }
+
+       public P2PCapability( int type, int value )
+       {
+               this();
+               cap.capabilityType=type;
+               cap.value=value;
+       }
+
+       public String toString()
+       {
+               return "Capability message [cap="+cap+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public Capability getCapability()
+       {
+               return cap;
+       }
+
+       public int getByteSize()
+       {
+               return SIZE;
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               int     size,type;
+
+               size=buf.getShort() & 0x0000ffff;
+               err.reportIf(size!=SIZE,"bad size");
+
+               type=buf.getShort() & 0x0000ffff;
+               err.reportIf(type!=IS_CAPABILITY,"bad type");
+
+               cap.capabilityType=buf.getInt();
+               cap.value=buf.getInt();
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               buf.putShort((short) SIZE);
+               buf.putShort((short) IS_CAPABILITY);
+               buf.putInt(cap.capabilityType);
+               buf.putInt(cap.value);
+       }
+}

Added: freeway/src/org/gnu/freeway/server/P2PHangUp.java
===================================================================
--- freeway/src/org/gnu/freeway/server/P2PHangUp.java   2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/server/P2PHangUp.java   2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,98 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.server;
+
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * The other side has decided to terminate the connection.  This
+ * message MAY be send if the other node decides to be nice.  It is
+ * not required.  Mind that the message contains for which host the
+ * termination is, such that we don't hang up the wrong connection...
+ * A node can also choose to ignore the HANGUP message, though this is
+ * probably not going to help that node.  This message is used to
+ * prevent sending data to connections that were closed on the other
+ * side (can happen anyway, so this is just an optimization between
+ * well-behaved, non-malicious nodes that like each other).
+ *
+ *
+ * <table border='1' cellpadding='2' cellspacing='0'>
+ *   <tr>
+ * <td>SIZE</td>
+ *</tr>
+ * </table>
+ */
+
+public class P2PHangUp extends P2PMessage
+{
+       public static final int SIZE    =       4+HostIdentity.SIZE;
+
+       /** */
+       private HostIdentity    sender;
+
+
+       public P2PHangUp()
+       {
+               super(IS_HANGUP);
+               sender=null;
+       }
+
+       public P2PHangUp( HostIdentity who )
+       {
+               this();
+               sender=who;
+       }
+
+       public String toString()
+       {
+               return "Hang up message [sender="+sender+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public HostIdentity getSender()
+       {
+               return sender;
+       }
+
+       public int getByteSize()
+       {
+               return SIZE;
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               int     size,type;
+
+               size=buf.getShort() & 0x0000ffff;
+               err.reportIf(size!=SIZE,"bad size");
+
+               type=buf.getShort() & 0x0000ffff;
+               err.reportIf(type!=IS_HANGUP,"bad type");
+
+               sender=new HostIdentity();
+               sender.readBytes(buf,err);
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               int     i;
+
+               buf.putShort((short) SIZE);
+               buf.putShort((short) IS_HANGUP);
+               if (sender!=null) {
+                       sender.writeBytes(buf);
+                       }
+               else {
+                       for (i=0; i<HostIdentity.SIZE; i++) {
+                               buf.put((byte) 0);
+                               }
+                       }
+       }
+}

Added: freeway/src/org/gnu/freeway/server/P2PHello.java
===================================================================
--- freeway/src/org/gnu/freeway/server/P2PHello.java    2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/server/P2PHello.java    2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,236 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.server;
+
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+import java.util.*;
+
+/**
+ * A HELO message which contains the current HostAddress, the host identity 
(hash), the validity of the
+ * HostAddress, a signature signing the information above and the public key 
of the host.
+ * The hash of the public key must match the host identity.
+ * <p>
+ * The signature goes over the message starting at the HostIdentity and 
includes the senderAddress.
+ * Since the senderAddress may be long, what is actually signed is the hash of 
these bytes.
+ */
+
+public class P2PHello extends P2PMessage implements Persistent
+{
+       public static final int         SIZE            =       
4+Signature.SIZE+PublicKey.SIZE+HostIdentity.SIZE+12;
+
+       private static final int        SIGN_OFFSET     =       
4+Signature.SIZE+PublicKey.SIZE;
+
+       /** The signature. */
+       private Signature               signature;
+
+       /** The public key. */
+       private PublicKey               publicKey;
+
+       /** Whose identity follows ? No, this is NOT a duplicate as a node may 
send us the identity of ANOTHER node ! */
+       private HostIdentity            senderIdentity;
+
+       /** Time this address expires. */
+       private int                             expirationTime;
+
+       /** Size of the sender address. */
+       private int                             senderAddressSize;
+
+       /** Protocol supported by the node (only one protocol can be advertised 
by the same HELO). Examples are UDP, TCP, etc. */
+       private int                             protocol;
+
+       /** Advertised MTU for sending (replies can have a different MTU !). */
+       private int                             mtu;
+
+       /** Address of the node in a protocol specific format. */
+       private byte[]                  senderAddress;
+
+
+       public P2PHello()
+       {
+               super(IS_HELO);
+               signature=null;
+               publicKey=null;
+               senderIdentity=null;
+               expirationTime=0;
+               senderAddressSize=0;
+               protocol=0;
+               mtu=0;
+               senderAddress=new byte[] {};
+       }
+
+       public P2PHello( int proto, int m )
+       {
+               this();
+               protocol=proto;
+               mtu=m;
+       }
+
+       public String toString()
+       {
+               return "Hello message [publicKey="+publicKey+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int getProtocol()
+       {
+               return protocol;
+       }
+
+       public int getMTU()
+       {
+               return mtu;
+       }
+
+       public int getExpirationTime()
+       {
+               return expirationTime;
+       }
+
+       public void setExpirationTime( int t )
+       {
+               expirationTime=t;
+       }
+
+       public boolean verify()
+       {
+               byte[]  b;
+
+               if (publicKey==null) {
+                       return false;
+                       }
+               b=PersistentHelper.toBytes(this);
+               return 
publicKey.verify(signature,b,SIGN_OFFSET,b.length-SIGN_OFFSET);
+       }
+
+       public boolean sign( PrivateKey priv )
+       {
+               byte[]  b;
+
+               publicKey=priv.toPublicKey();
+               senderIdentity=new HostIdentity(publicKey);
+
+               b=PersistentHelper.toBytes(this);
+               signature=priv.sign(b,SIGN_OFFSET,b.length-SIGN_OFFSET);
+               return (signature!=null);
+       }
+
+       public PublicKey getPublicKey()
+       {
+               return publicKey;
+       }
+
+       public HostIdentity getSenderIdentity()
+       {
+               return senderIdentity;
+       }
+
+       public Persistent decodeSenderAddress( Class c )
+       {
+               return 
PersistentHelper.readFully(c,ByteBuffer.wrap(senderAddress));
+       }
+
+       public void encodeSenderAddress( Persistent p )
+       {
+               senderAddress=new byte[p.getByteSize()];
+               PersistentHelper.writeFully(p,ByteBuffer.wrap(senderAddress));
+               senderAddressSize=p.getByteSize();
+       }
+
+       public boolean sameProtocolMTUAndAddress( P2PHello h )
+       {
+               if (h.senderAddressSize!=senderAddressSize) {
+                       return false;
+                       }
+               if (h.protocol!=protocol) {
+                       return false;
+                       }
+               if (h.mtu!=mtu) {
+                       return false;
+                       }
+               return Arrays.equals(h.senderAddress,senderAddress);
+       }
+
+       public int getByteSize()
+       {
+               return SIZE+senderAddressSize;
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               int     size,type;
+
+               size=buf.getShort() & 0x0000ffff;
+               type=buf.getShort() & 0x0000ffff;
+               err.reportIf(type!=IS_HELO,"Bad type ("+type+"!="+IS_HELO+") 
!");
+
+               signature=new Signature();
+               signature.readBytes(buf,err);
+
+               publicKey=new PublicKey();
+               publicKey.readBytes(buf,err);
+
+               senderIdentity=new HostIdentity();
+               senderIdentity.readBytes(buf,err);
+
+               expirationTime=buf.getInt();
+
+               senderAddressSize=buf.getShort() & 0x0000ffff;
+               protocol=buf.getShort() & 0x0000ffff;
+
+               mtu=buf.getInt();
+               err.reportIf(mtu<0,"Invalid MTU !");
+
+               senderAddress=new byte[senderAddressSize];
+               buf.get(senderAddress);
+
+               err.reportIf(size!=SIZE+senderAddressSize,"Bad size 
(size="+size+",SIZE="+SIZE+",senderAddressSize="+senderAddressSize+") !");
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               int     i;
+
+               buf.putShort((short) (SIZE+senderAddressSize));
+               buf.putShort((short) IS_HELO);
+
+               if (signature!=null) {
+                       signature.writeBytes(buf);
+                       }
+               else {
+                       for (i=0; i<256; i++) {
+                               buf.put((byte) 0);
+                               }
+                       }
+
+               if (publicKey!=null) {
+                       publicKey.writeBytes(buf);
+                       }
+               else {
+                       for (i=0; i<264; i++) {
+                               buf.put((byte) 0);
+                               }
+                       }
+
+               if (senderIdentity!=null) {
+                       senderIdentity.writeBytes(buf);
+                       }
+               else {
+                       for (i=0; i<20; i++) {
+                               buf.put((byte) 0);
+                               }
+                       }
+
+               buf.putInt(expirationTime);
+               buf.putShort((short) senderAddressSize);
+               buf.putShort((short) protocol);
+               buf.putInt(mtu);
+               buf.put(senderAddress);
+       }
+}

Added: freeway/src/org/gnu/freeway/server/P2PNoise.java
===================================================================
--- freeway/src/org/gnu/freeway/server/P2PNoise.java    2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/server/P2PNoise.java    2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,84 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.server;
+
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * The other side has decided to terminate the connection.  This
+ * message MAY be send if the other node decides to be nice.  It is
+ * not required.  Mind that the message contains for which host the
+ * termination is, such that we don't hang up the wrong connection...
+ * A node can also choose to ignore the HANGUP message, though this is
+ * probably not going to help that node.  This message is used to
+ * prevent sending data to connections that were closed on the other
+ * side (can happen anyway, so this is just an optimization between
+ * well-behaved, non-malicious nodes that like each other).
+ *
+ *
+ * <table border='1' cellpadding='2' cellspacing='0'>
+ *   <tr>
+ * <td>SIZE</td>
+ *</tr>
+ * </table>
+ */
+
+public class P2PNoise extends P2PMessage
+{
+       public static final int SIZE    =       4;
+
+       private byte[]  noise;
+
+
+       public P2PNoise()
+       {
+               super(IS_NOISE);
+               noise=new byte[] {};
+       }
+
+       public P2PNoise( int len )
+       {
+               this();
+               noise=new byte[len];
+               Crypto.nextBytes(noise,0,noise.length);
+       }
+
+       public String toString()
+       {
+               return "Noise message [length="+noise.length+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int getByteSize()
+       {
+               return SIZE+noise.length;
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               int     size,type;
+
+               size=buf.getShort() & 0x0000ffff;
+               err.reportIf(size<SIZE,"bad size");
+
+               type=buf.getShort() & 0x0000ffff;
+               err.reportIf(type!=IS_NOISE,"bad type");
+
+               noise=new byte[size-SIZE];
+               buf.get(noise);
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               buf.putShort((short) (SIZE+noise.length));
+               buf.putShort((short) IS_NOISE);
+               buf.put(noise);
+       }
+}

Added: freeway/src/org/gnu/freeway/server/P2PPing.java
===================================================================
--- freeway/src/org/gnu/freeway/server/P2PPing.java     2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/server/P2PPing.java     2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,115 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.server;
+
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * Ping message (test if address actually corresponds to
+ * the advertised GNUnet host. The receiver responds with
+ * exactly the same message, except that it is now a pong.
+ * This message can be send in plaintext and without padding
+ * and typically does make little sense (except keepalive)
+ * for an encrypted (authenticated) tunnel.
+ * <br>
+ * There is also no proof that the other side actually
+ * has the acclaimed identity, the only thing that is
+ * proved is that the other side can be reached via
+ * the underlying protocol and that it is a GNUnet node.
+ * <br>
+ * The challenge prevents an inept adversary from sending
+ * us a HELO and then an arbitrary PONG reply (adversary
+ * must at least be able to sniff our outbound traffic).
+ */
+
+public class P2PPing extends P2PMessage
+{
+       public static final int SIZE    =       4+HostIdentity.SIZE+4;
+
+       /** Which peer is the target of the ping? This is important since for 
plaintext-pings,
+        we need to catch faulty advertisements that advertise a correct 
address but with the wrong public key. */
+       private HostIdentity    receiver;
+
+       /** The challenge is a (pseudo) random number that an adversary that 
wants to fake a pong message would have to guess.
+        Since even if the number is guessed, the security impact is at most 
some wasted resources, 32 bit are more than enough. */
+       private int                     challenge;
+
+
+       public P2PPing()
+       {
+               super(IS_PING);
+               receiver=null;
+               challenge=0;
+       }
+
+       public String toString()
+       {
+               return "Ping message [receiver="+receiver+", 
challenge="+challenge+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public HostIdentity getReceiver()
+       {
+               return receiver;
+       }
+
+       public void setReceiver( HostIdentity hi )
+       {
+               receiver=(HostIdentity) PersistentHelper.copy(hi);
+       }
+
+       public int getChallenge()
+       {
+               return challenge;
+       }
+
+       public void setChallenge( int n )
+       {
+               challenge=n;
+       }
+
+       public int getByteSize()
+       {
+               return SIZE;
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               int     size,type;
+
+               size=buf.getShort() & 0x0000ffff;
+               err.reportIf(size!=SIZE,"bad size");
+
+               type=buf.getShort() & 0x0000ffff;
+               err.reportIf(type!=IS_PING,"bad type");
+
+               receiver=new HostIdentity();
+               receiver.readBytes(buf,err);
+
+               challenge=buf.getInt();
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               int     i;
+
+               buf.putShort((short) SIZE);
+               buf.putShort((short) IS_PING);
+               if (receiver!=null) {
+                       receiver.writeBytes(buf);
+                       }
+               else {
+                       for (i=0; i<20; i++) {
+                               buf.put((byte) 0);
+                               }
+                       }
+               buf.putInt(challenge);
+       }
+}

Added: freeway/src/org/gnu/freeway/server/P2PPong.java
===================================================================
--- freeway/src/org/gnu/freeway/server/P2PPong.java     2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/server/P2PPong.java     2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,117 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.server;
+
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * Ping message (test if address actually corresponds to
+ * the advertised GNUnet host. The receiver responds with
+ * exactly the same message, except that it is now a pong.
+ * This message can be send in plaintext and without padding
+ * and typically does make little sense (except keepalive)
+ * for an encrypted (authenticated) tunnel.
+ * <br>
+ * There is also no proof that the other side actually
+ * has the acclaimed identity, the only thing that is
+ * proved is that the other side can be reached via
+ * the underlying protocol and that it is a GNUnet node.
+ * <br>
+ * The challenge prevents an inept adversary from sending
+ * us a HELO and then an arbitrary PONG reply (adversary
+ * must at least be able to sniff our outbound traffic).
+ */
+
+public class P2PPong extends P2PMessage
+{
+       public static final int SIZE    =       4+HostIdentity.SIZE+4;
+
+       /** Which peer is the target of the ping? This is important since for 
plaintext-pings,
+        we need to catch faulty advertisements that advertise a correct 
address but with the wrong public key. */
+       private HostIdentity    receiver;
+
+       /** The challenge is a (pseudo) random number that an adversary that 
wants to fake a pong message would have to guess.
+        Since even if the number is guessed, the security impact is at most 
some wasted resources, 32 bit are more than enough. */
+       private int                             challenge;
+
+
+       public P2PPong()
+       {
+               super(IS_PONG);
+               receiver=null;
+               challenge=0;
+       }
+
+       public P2PPong( P2PPing ping )
+       {
+               this();
+               receiver=ping.getReceiver();
+               challenge=ping.getChallenge();
+       }
+
+       public String toString()
+       {
+               return "Pong message [receiver="+receiver+", 
challenge="+challenge+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public boolean isCompatible( P2PPing ping )
+       {
+               return (receiver.equals(ping.getReceiver()) && 
challenge==ping.getChallenge());
+       }
+
+       public HostIdentity getReceiver()
+       {
+               return receiver;
+       }
+
+       public int getChallenge()
+       {
+               return challenge;
+       }
+
+       public int getByteSize()
+       {
+               return SIZE;
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               int     size,type;
+
+               size=buf.getShort() & 0x0000ffff;
+               err.reportIf(size!=SIZE,"bad size");
+
+               type=buf.getShort() & 0x0000ffff;
+               err.reportIf(type!=IS_PONG,"bad type");
+
+               receiver=new HostIdentity();
+               receiver.readBytes(buf,err);
+
+               challenge=buf.getInt();
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               int     i;
+
+               buf.putShort((short) SIZE);
+               buf.putShort((short) IS_PONG);
+               if (receiver!=null) {
+                       receiver.writeBytes(buf);
+                       }
+               else {
+                       for (i=0; i<20; i++) {
+                               buf.put((byte) 0);
+                               }
+                       }
+               buf.putInt(challenge);
+       }
+}

Added: freeway/src/org/gnu/freeway/server/P2PSequence.java
===================================================================
--- freeway/src/org/gnu/freeway/server/P2PSequence.java 2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/server/P2PSequence.java 2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,76 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.server;
+
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * Sequence number.  If the sequence number is lower than a previous
+ * number, the rest of the packet should be ignored (replay).  This
+ * will of course break if UDP packets arrive out-of-order, but this
+ * is rare and we're best-effort. This is used to defend against
+ * replay-attacks.
+ */
+
+public class P2PSequence extends P2PMessage
+{
+       public static final int SIZE    =       8;
+
+       /** sequence number */
+       private int     number;
+
+
+       public P2PSequence()
+       {
+               super(IS_SEQUENCE);
+               number=0;
+       }
+
+       public P2PSequence( int num )
+       {
+               this();
+               number=num;
+       }
+
+       public String toString()
+       {
+               return "Sequence message [number="+number+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int getNumber()
+       {
+               return number;
+       }
+
+       public int getByteSize()
+       {
+               return SIZE;
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               int     size,type;
+
+               size=buf.getShort() & 0x0000ffff;
+               err.reportIf(size!=SIZE,"bad size");
+
+               type=buf.getShort() & 0x0000ffff;
+               err.reportIf(type!=IS_SEQUENCE,"bad type");
+
+               number=buf.getInt();
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               buf.putShort((short) SIZE);
+               buf.putShort((short) IS_SEQUENCE);
+               buf.putInt(number);
+       }
+}

Added: freeway/src/org/gnu/freeway/server/P2PSessionKey.java
===================================================================
--- freeway/src/org/gnu/freeway/server/P2PSessionKey.java       2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/server/P2PSessionKey.java       2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,149 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.server;
+
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * Session key exchange. The header is followed by an inlined SKS.
+ */
+
+public class P2PSessionKey extends P2PMessage
+{
+       public static final int SIZE    =       
8+EncryptedData.SIZE+Signature.SIZE;
+
+       /** Time when this key was created (in seconds since epoch). */
+       private int                             creationTime;
+
+       /** The encrypted session key. */
+       private EncryptedData   encryptedKey;
+
+       /** Signature of the stuff above. */
+       private Signature               signature;
+
+
+       public P2PSessionKey()
+       {
+               super(IS_SESSION_KEY);
+       }
+
+       public String toString()
+       {
+               return "Session key message [creationTime="+creationTime+", 
signature="+signature+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int getCreationTime()
+       {
+               return creationTime;
+       }
+
+       public void setCreationTime( int t )
+       {
+               creationTime=t;
+       }
+
+       public EncryptedData getEncryptedKey()
+       {
+               return encryptedKey;
+       }
+
+       public void setEncryptedKey( EncryptedData enc )
+       {
+               encryptedKey=enc;
+       }
+
+       public Signature getSignature()
+       {
+               return signature;
+       }
+
+       /**
+        * Compute hash and sign hash.
+        * @param priv
+        * @return
+        */
+
+       public boolean sign( PrivateKey priv )
+       {
+               HashCode160     h;
+
+               h=hash();
+               signature=priv.sign(PersistentHelper.toBytes(h));
+               return (signature!=null);
+       }
+
+       public boolean verify( PublicKey pub )
+       {
+               HashCode160     h;
+
+               h=hash();
+               return pub.verify(signature,PersistentHelper.toBytes(h));
+       }
+
+       protected HashCode160 hash()
+       {
+               byte[]  b;
+
+               b=PersistentHelper.toBytes(this);
+               return HashCode160.create(b,4,4+EncryptedData.SIZE);    // 
creation time + encrypted session key
+       }
+
+       public int getByteSize()
+       {
+               return SIZE;
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               int     size,type;
+
+               size=buf.getShort() & 0x0000ffff;
+               err.reportIf(size!=SIZE,"bad size");
+
+               type=buf.getShort() & 0x0000ffff;
+               err.reportIf(type!=IS_SESSION_KEY,"bad type");
+
+               creationTime=buf.getInt();
+
+               encryptedKey=new EncryptedData();
+               encryptedKey.readBytes(buf,err);
+
+               signature=new Signature();
+               signature.readBytes(buf,err);
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               int     i;
+
+               buf.putShort((short) SIZE);
+               buf.putShort((short) IS_SESSION_KEY);
+               buf.putInt(creationTime);
+
+               if (encryptedKey!=null) {
+                       encryptedKey.writeBytes(buf);
+                       }
+               else {
+                       for (i=0; i<EncryptedData.SIZE; i++) {
+                               buf.put((byte) 0);
+                               }
+                       }
+
+               if (signature!=null) {
+                       signature.writeBytes(buf);
+                       }
+               else {
+                       for (i=0; i<Signature.SIZE; i++) {
+                               buf.put((byte) 0);
+                               }
+                       }
+       }
+}

Added: freeway/src/org/gnu/freeway/server/PerNodeCallback.java
===================================================================
--- freeway/src/org/gnu/freeway/server/PerNodeCallback.java     2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/server/PerNodeCallback.java     2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,21 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.server;
+
+import org.gnu.freeway.util.crypto.*;
+
+/**
+ * Type of a handler for some message type.
+ */
+
+public interface PerNodeCallback
+{
+       /**
+        * @param identity the id of the node
+        * @param data
+        */
+
+       public void callback( HostIdentity identity, Object data );
+}

Added: freeway/src/org/gnu/freeway/server/PingPongService.java
===================================================================
--- freeway/src/org/gnu/freeway/server/PingPongService.java     2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/server/PingPongService.java     2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,364 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.server;
+
+import org.gnu.freeway.*;
+import org.gnu.freeway.transport.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.util.logging.*;
+
+/**
+ * Pings a host and triggers an action if a reply is received.
+ */
+
+public class PingPongService extends AbstractService implements P2PHandler
+{
+       private static final int        MAX_PING_PONG   =       64;
+
+       private ConnectionService               connection;
+       private KnownHostsService               knownHosts;
+       private TransportService                transport;
+       private TrafficService          traffic;
+
+       private PingPongEntry[]         pingPongs;
+       private Object                          lock;
+       private Stat                                    pingSent;
+       private Stat                                    pingReceived;
+       private Stat                                    pongSent;
+       private Stat                                    pongReceived;
+
+
+       public PingPongService()
+       {
+               super("Ping pong");
+               setDebug(true);
+       }
+
+       public String toString()
+       {
+               return "Ping pong service";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public void init()
+       {
+               Statistics      stats;
+               int                     i;
+
+               super.init();
+
+               pingPongs=new PingPongEntry[MAX_PING_PONG];
+               for (i=0; i<pingPongs.length; i++) {
+                       pingPongs[i]=new PingPongEntry();
+                       }
+
+               stats=getManager().app().getStatistics();
+               pingSent=stats.getHandle("# ping messages sent",Stat.VERBOSE);
+               pingReceived=stats.getHandle("# ping messages 
received",Stat.VERBOSE);
+               pongSent=stats.getHandle("# pong messages sent",Stat.VERBOSE);
+               pongReceived=stats.getHandle("# pong messages 
received",Stat.VERBOSE);
+       }
+
+       /**
+        * Initialize the pingpong module.
+        */
+
+       public void start()
+       {
+               MessagesDispatcher      dispatcher;
+
+               super.start();
+
+               connection=(ConnectionService) 
getManager().service(ConnectionService.class);
+               knownHosts=(KnownHostsService) 
getManager().service(KnownHostsService.class);
+               transport=(TransportService) 
getManager().service(TransportService.class);
+               traffic=(TrafficService) 
getManager().service(TrafficService.class);
+
+               lock = connection.getConnectionModuleLock();
+
+               dispatcher=((Server) getManager().app()).getDispatcher();
+               
dispatcher.registerP2PHandler(P2PMessage.IS_PING,P2PPing.class,this);
+               
dispatcher.registerP2PHandler(P2PMessage.IS_PONG,P2PPong.class,this);
+       }
+
+       /**
+        * Shutdown the pingpong module.
+        */
+
+       public void stop()
+       {
+               MessagesDispatcher      dispatcher;
+
+               dispatcher=((Server) getManager().app()).getDispatcher();
+               dispatcher.unregisterP2PHandler(P2PMessage.IS_PONG,this);
+               dispatcher.unregisterP2PHandler(P2PMessage.IS_PING,this);
+
+               super.stop();
+               pingPongs=null;
+       }
+
+       /**
+        * We received a PING message, send the PONG reply and notify the
+        * connection module that the session is still life.
+        * @param sender
+        * @param ping
+        * @return
+        */
+
+       private boolean pingReceived( HostIdentity sender, P2PPing ping )
+       {
+               P2PPong pong;
+               LocalIdentity           keys;
+
+               if (isStopped()) {
+                       return false;
+                       }
+               debug(sender.getName()+" Received encrypted ping.");
+
+               pingReceived.inc();
+
+               keys=((Server) getManager().app()).getKeys();
+               if (!keys.whoAmI().equals(ping.getReceiver())) {
+                       log(Level.INFO,"Received PING not destined for us !");
+                       return false; /* not for us */
+                       }
+
+               pong=new P2PPong(ping);
+
+               pongSent.inc();
+
+               connection.notifyPING(sender); /* special! we want to know 
about all pings! */
+               
connection.sendToNode(sender,pong,connection.getConnectPriority(),0); /* send 
now! */
+               return true;
+       }
+
+       /**
+        * Handler for a pong.
+        * @param sender
+        * @param pong
+        * @return
+        */
+
+       private boolean pongReceived( HostIdentity sender, P2PPong pong )
+       {
+               PingPongEntry   entry;
+               int                             i;
+               boolean                 success;
+
+               if (isStopped()) {
+                       return false;
+                       }
+               success=false;
+
+               if (!pong.getReceiver().equals(sender))
+                       return false; /* bad pong */
+
+               pongReceived.inc();
+
+               synchronized(lock) {
+                       for (i=0; i<pingPongs.length; i++) {
+                               entry = pingPongs[i];
+                               if (pong.getChallenge()==entry.challenge && 
entry.receiverIdentity.equals(sender)) {
+                                       debug(sender.getName()+" Received pong, 
triggering action.");
+
+                                       success = true;
+                                       
SafeAction.safe(entry.method).perform();//entry.data);  //todo: avec entry.data 
!!!
+                                       /* entry was valid for one time only */
+                                       entry=null;
+                                       }
+                               }
+
+                       if (!success) {
+                               debug(sender.getName()+" No handler found for 
pong.");
+                               }
+                       }
+               return true;
+       }
+
+       /**
+        * We received a PING message, send the PONG reply and notify the 
connection module that the session is still alive.
+        *
+        * @param tsession
+        * @param ping
+        * @return
+        */
+
+       private boolean plainPingReceived( Session tsession, P2PPing ping )
+       {
+               Session         mytsession;
+               P2PHello                helo;
+               P2PPong         pong;
+               HostIdentity    sender;
+               LocalIdentity                   keys;
+
+               if (isStopped()) {
+                       return false;
+                       }
+
+               sender=tsession.getRemote();
+               debug(sender.getName()+" Received plaintext ping.");
+
+               pingReceived.inc();
+
+               keys=((Server) getManager().app()).getKeys();
+               if (!keys.whoAmI().equals(ping.getReceiver())) {
+                       log(Level.INFO,"Received PING not destined for us !");
+                       return false; /* not for us */
+                       }
+
+               pong=new P2PPong(ping);
+
+               pongSent.inc();
+
+               // allow using a different transport for sending the reply, the 
transport may have been uni-directional !
+               if (!tsession.send(pong)) {
+                       // ok, try fresh connect
+
+                       
helo=knownHosts.identity2HELO(sender,Transport.ANY_PROTOCOL_NUMBER,true);
+                       if (helo==null) {
+                               debug(sender.getName()+" Received ping, but can 
not send pong (no transport known for peer).");
+                               return false;
+                               }
+
+                       mytsession=transport.transportConnect(helo);
+                       if (mytsession==null) {
+                               return false;
+                               }
+
+                       if (!mytsession.send(pong)) {
+                               mytsession.unlock();
+                               return false;
+                               }
+
+                       
traffic.updateTrafficSendCounter(P2PMessage.IS_PONG,P2PPong.SIZE);
+
+                       mytsession.unlock();
+                       }
+               else {
+                       
traffic.updateTrafficSendCounter(P2PMessage.IS_PONG,P2PPong.SIZE);
+                       }
+               return true;
+       }
+
+       /**
+        * Handler for a pong.
+        * @param tsession
+        * @param pong
+        * @return
+        */
+
+       private boolean plainPongReceived( Session tsession, P2PPong pong )
+       {
+               return pongReceived(tsession.getRemote(),pong);
+       }
+
+       /**
+        * Ping a host and call a method if a reply comes back.
+        *
+        * @param receiver      the identity to fill into the ping
+        * @param method                the method to call if a PONG comes back
+        * @param data          an argument to pass to the method.
+        * @param ping          the ping-message, pingAction just fills it in, 
the caller is responsible for sending it !
+        * @return                      true on success, false on error
+        */
+
+       public boolean pingAction( HostIdentity receiver, Action method, Object 
data, P2PPing ping )
+       {
+               PingPongEntry   entry;
+               long                    min,now;
+               int                             i,j;
+
+               if (isStopped()) {
+                       return false;
+                       }
+               synchronized(lock) {
+                       now=min=Scheduler.toSeconds(Scheduler.now());
+
+                       // find the eldest entry (may not have been sent ?)
+                       j=-1;
+                       for (i=0; i<pingPongs.length; i++) {
+                               if (min>pingPongs[i].sendTime) {
+                                       min=pingPongs[i].sendTime;
+                                       j=i;
+                                       }
+                               }
+                       if (j==-1) {    // all send this second !?
+                               return false;
+                               }
+
+                       entry = pingPongs[j];
+                       entry.sendTime = now;
+                       entry.method = method;
+                       entry.data = data;
+                       entry.receiverIdentity=(HostIdentity) 
PersistentHelper.copy(receiver);
+                       entry.challenge = Crypto.nextInt();
+
+                       ping.setReceiver(receiver);
+                       ping.setChallenge(entry.challenge);
+
+                       pingSent.inc();
+                       }
+               return true;
+       }
+
+       public boolean handle( Session session, P2PMessage msg, boolean 
encrypted )
+       {
+               if (msg instanceof P2PPing) {
+                       return handlePing(session,(P2PPing) msg,encrypted);
+                       }
+               if (msg instanceof P2PPong) {
+                       return handlePong(session,(P2PPong) msg,encrypted);
+                       }
+               return false;
+       }
+
+       protected boolean handlePing( Session session, P2PPing msg, boolean 
encrypted )
+       {
+               if (!encrypted) {
+                       debug(session.getRemote().getName()+" Handle plain 
ping.");
+
+                       // challenge: send back reply - NOW !
+                       plainPingReceived(session,msg);
+                       return true;
+                       }
+               return pingReceived(session.getRemote(),msg);
+       }
+
+       protected boolean handlePong( Session session, P2PPong msg, boolean 
encrypted )
+       {
+               if (!encrypted) {
+                       debug(session.getRemote().getName()+" Handle plain 
pong.");
+
+                       // this confirms a PING => add a HELO to knownhosts.
+                       plainPongReceived(session,msg);
+                       return true;
+                       }
+               return pongReceived(session.getRemote(),msg);
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       private static class PingPongEntry extends Object
+       {
+               private HostIdentity    receiverIdentity;
+               private int                     challenge;
+               private long                    sendTime;
+               private Action          method;
+               public Object           data;   //todo: bug car n'est jamais 
utilisé
+
+
+               private PingPongEntry()
+               {
+                       super();
+                       sendTime=0;
+               }
+       }
+}

Added: freeway/src/org/gnu/freeway/server/PolicyService.java
===================================================================
--- freeway/src/org/gnu/freeway/server/PolicyService.java       2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/server/PolicyService.java       2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,137 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.server;
+
+import org.gnu.freeway.util.*;
+
+import java.net.*;
+import java.util.logging.*;
+
+/**
+ * bandwidth allocation code for outbound messages
+ */
+
+public class PolicyService extends AbstractService
+{
+       /** Outgoing messages sent statistic. */
+       private Stat                    outgoingOK;
+
+       /** Outgoing messages deferred statistic. */
+       private Stat                    outgoingDropped;
+
+       /** Trusted networks. */
+       private CIDRNetworks    trustedNetworks;
+
+
+       public PolicyService()
+       {
+               super("Policy");
+               setDebug(true);
+       }
+
+       public String toString()
+       {
+               return "Policy service";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       /**
+        * Initialize the policy module.
+        */
+
+       public void start()
+       {
+               Statistics      stats;
+               Prefs           prefs;
+               String          str;
+
+               super.start();
+
+               prefs=getManager().app().getPreferences();
+               str=prefs.getString("NETWORK","TRUSTED","127.0.0.0/8;");        
// by default, trust localhost only
+               trustedNetworks=CIDRNetworks.parse(str);
+
+               log(Level.INFO,"Trusted networks : 
"+trustedNetworks.getDescription()+".");
+
+               stats=getManager().app().getStatistics();
+               outgoingOK=stats.getHandle("# times outgoing msg sent 
(bandwidth ok)");
+               outgoingDropped=stats.getHandle("# times outgoing msg deferred 
(bandwidth stressed)");
+               outgoingOK.reset();
+               outgoingDropped.reset();
+       }
+
+       /**
+        * A new packet is supposed to be send out. Should it be
+        * dropped because the load is too high?
+        * <p>
+        * @param priority the highest priority of contents in the packet
+        * @return true if the packet should be handled, false if the packet 
should be dropped.
+        */
+
+       public boolean outgoingCheck( int priority )
+       {
+               StatusCallsService      statusCalls;
+               int                                     load,delta;
+
+               statusCalls=(StatusCallsService) 
getManager().service(StatusCallsService.class);
+
+               // how much free bandwidth do we have ?
+               load=statusCalls.getNetworkLoadUp();
+
+               if (load>=150) {        // always drop
+                       outgoingDropped.inc();
+                       return false;
+                       }
+
+               if (load>100) { // allow administrative msgs, but nothing else
+                       if (priority >= ConnectionService.EXTREME_PRIORITY) {
+                               outgoingOK.inc();
+                               return true;
+                               }
+                       outgoingDropped.inc();
+                       return false;
+                       }
+
+               if (load<=50) { // everything goes, allow
+                       outgoingOK.inc();
+                       return true;
+                       }
+
+               /* Now load in [51, 100].  Between 51% and 100% load:
+                at 51% require priority >= 1 = (load-50)^3
+                at 52% require priority >= 8 = (load-50)^3
+                at 75% require priority >= 15626 = (load-50)^3
+                at 100% require priority >= 125000 = (load-50)^3
+                (cubic function)
+                */
+
+               delta = load - 50; /* now delta is in [1,50] with 50 == 100% 
load */
+               if (delta * delta * delta > priority ) {
+                       debug("Network load too high ("+load+"%, priority is 
"+priority+", require "+(delta * delta * delta)+"), dropping outgoing.");
+
+                       outgoingDropped.inc();
+                       return false; /* drop */
+                       }
+
+               debug("Network load ok ("+load+"%, priority is "+priority+" >= 
"+(delta * delta * delta)+"), sending outgoing.");
+
+               outgoingOK.inc();
+               return true; /* allow */
+       }
+
+       /**
+        * Is this IP labeled as trusted for CS connections ?
+        *
+        * @param ip
+        * @return
+        */
+
+       public boolean isWhitelisted( InetAddress ip )
+       {
+               return trustedNetworks.check(ip);
+       }
+}

Added: freeway/src/org/gnu/freeway/server/SendEntry.java
===================================================================
--- freeway/src/org/gnu/freeway/server/SendEntry.java   2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/server/SendEntry.java   2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,104 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.server;
+
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.net.*;
+
+/**
+ * Entry in the send buffer. Contains the size of the message, the
+ * priority, when the message was passed to unicast, a callback to
+ * fill in the actual message and a content.
+ */
+
+public class SendEntry extends Object
+{
+       /** Do not place entry at a specific location. */
+       public static final int SE_FLAG_NONE            =       0;
+
+       /** Place entry at the head of the message. */
+       public static final int SE_FLAG_PLACE_HEAD      =       1;
+
+       /** Place entry at the tail of the message. */
+       public static final int SE_FLAG_PLACE_TAIL      =       2;
+
+       /** Placement mask. */
+       public static final int SE_PLACEMENT_MASK       =       3;
+
+       /** Flags. */
+       private int             flags;
+
+       /** How important is this entry ? */
+       private int             priority;
+
+       /** When did we intend to transmit ? */
+       private long    transmissionTime;
+
+       /** Content of the entry. */
+       private byte[]  content;
+
+
+       public SendEntry( int f, int p )
+       {
+               super();
+               flags=f;
+               priority=p;
+               transmissionTime=Scheduler.now();
+               content=null;
+       }
+
+       public String toString()
+       {
+               return "Send entry [flags="+flags+", priority="+priority+", 
transmissionTime="+transmissionTime+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int getFlags()
+       {
+               return (flags & SE_PLACEMENT_MASK);
+       }
+
+       public int getPriority()
+       {
+               return priority;
+       }
+
+       public double getPriorityRatio()
+       {
+               return (double) priority/(double) content.length;
+       }
+
+       public void setDelay( long delta )
+       {
+               transmissionTime=Scheduler.now()+delta;
+       }
+
+       public boolean isExpired( long time )
+       {
+               return transmissionTime<time;
+       }
+
+       public int getSecondsExpired( long time )
+       {
+               return (int) Scheduler.toSeconds(time-transmissionTime);
+       }
+
+       public int getContentSize()
+       {
+               return content.length;
+       }
+
+       public void setContent( Persistent p )
+       {
+               content=PersistentHelper.toBytes(p);
+       }
+
+       public void transferContentTo( byte[] dest, int offset )
+       {
+               System.arraycopy(content,0,dest,offset,content.length);
+       }
+}

Added: freeway/src/org/gnu/freeway/server/TrafficService.java
===================================================================
--- freeway/src/org/gnu/freeway/server/TrafficService.java      2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/server/TrafficService.java      2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,300 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.server;
+
+import org.gnu.freeway.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.util.*;
+
+/**
+ * tracks current traffic patterns
+ *
+ * Module to keep track of recent amounts of p2p traffic on the local GNUnet 
node.
+ * Uses roughly 6 kb of memory given the current settings.
+ *
+ * The current settings allow the minimal anonymity requirements that can be 
confirmed to reach 15 peers
+ * in the last 32 minutes (for any given message type).
+ *
+ * If significantly higher levels are required, the current code would need to 
be recompiled with different values.
+ *
+ * I currently do not believe we should make better traffic tracking even an 
option.
+ */
+
+public class TrafficService extends AbstractService implements CSHandler
+{
+       /** The actual counters. */
+       private Counter[]       counters;
+
+       /** Traffic received by type. */
+       private Stat[]          receivedTrafficByType;
+
+       /** Traffic transmitted by type. */
+       private Stat[]          transmittedTrafficByType;
+
+
+       public TrafficService()
+       {
+               super("Traffic");
+               setDebug(true);
+
+               counters=new Counter[0];
+
+               receivedTrafficByType=new Stat[P2PMessage.IS_MAX];
+               Arrays.fill(receivedTrafficByType,null);
+
+               transmittedTrafficByType=new Stat[P2PMessage.IS_MAX];
+               Arrays.fill(transmittedTrafficByType,null);
+       }
+
+       public String toString()
+       {
+               return "Traffic service";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       /**
+        * Initialize the traffic module.
+        */
+
+       public void start()
+       {
+               super.start();
+
+               ((Server) 
getManager().app()).registerCSHandler(CSMessage.IS_TRAFFIC_QUERY,CSTrafficRequest.class,this);
+       }
+
+       /**
+        * Shutdown the traffic module.
+        */
+
+       public void stop()
+       {
+               super.stop();
+
+               counters=null;
+
+               ((Server) 
getManager().app()).unregisterCSHandler(CSMessage.IS_TRAFFIC_QUERY,this);
+       }
+
+       public void updateTrafficReceiveCounter( P2PMessage msg )
+       {
+               Statistics      stats;
+               String          str;
+               int                     type;
+
+               type=msg.getType();
+               if (type<P2PMessage.IS_MAX) {
+                       if (receivedTrafficByType[type]==null) {
+                               str=P2PMessage.nameFor(type);
+                               if (str==null) {
+                                       str=String.valueOf(type);
+                                       }
+
+                               stats=getManager().app().getStatistics();
+                               receivedTrafficByType[type]=stats.getHandle("# 
bytes received of type "+str);
+                               }
+                       receivedTrafficByType[type].add(msg.getByteSize());
+                       }
+       }
+
+       /**
+        * A message was received. Update traffic stats.
+        *
+        * @param msg   the message
+        * @param sender the identity of the sender
+        */
+
+       public void trafficReceive( P2PMessage msg, HostIdentity sender )
+       {
+               int     type;
+
+               type=msg.getType();
+               synchronized(counters) {
+                       checkPort(type);
+                       
counters[type].receive.updateUse(msg.getByteSize(),sender.getHashCodeA(),false);
+                       }
+       }
+
+       public void updateTrafficSendCounter( P2PMessage msg )
+       {
+               updateTrafficSendCounter(msg.getType(),msg.getByteSize());
+       }
+
+       /**
+        * @param ptyp
+        * @param plen
+        */
+
+       public void updateTrafficSendCounter( int ptyp, int plen )
+       {
+               Statistics      stats;
+               String          str;
+
+               if (ptyp >= P2PMessage.IS_MAX)
+                       return; /* not tracked */
+
+               if (transmittedTrafficByType[ptyp]==null) {
+                       str=P2PMessage.nameFor(ptyp);
+
+                       stats=getManager().app().getStatistics();
+                       transmittedTrafficByType[ptyp]=stats.getHandle("# bytes 
transmitted of type "+(str!=null ? str : String.valueOf(ptyp)));
+                       }
+               transmittedTrafficByType[ptyp].add(plen);
+       }
+
+       /**
+        * A message is send. Update traffic stats.
+        *
+        * @param header the header of the message
+        * @param receiver the identity of the receiver
+        */
+
+       public void trafficSend( P2PMessage header, HostIdentity receiver )
+       {
+               int port;
+
+               port = header.getType();
+               synchronized(counters) {
+                       checkPort(port);
+                       
counters[port].send.updateUse(header.getByteSize(),receiver.getHashCodeA(),false);
+                       }
+       }
+
+       /**
+        * Ensure that the counters array has the appropriate size
+        * and a valid traffic counter allocated for the given port.
+        *
+        * @param port
+        */
+
+       protected void checkPort( int port )
+       {
+               Counter[]       tmp;
+
+               if (port>=counters.length) {
+                       tmp=counters;
+
+                       counters=new Counter[port+1];
+                       Arrays.fill(counters,null);
+                       System.arraycopy(tmp,0,counters,0,tmp.length);
+                       }
+
+               if (counters[port]==null) {
+                       counters[port]=new Counter();
+                       }
+       }
+
+       /**
+        * Get statistics over the number of messages that were received or 
send of a given type.
+        *
+        * @param type                  The type of the message.
+        * @param sendReceive           TC_SENT for sending, TC_RECEIVED for 
receiving
+        * @param timePeriod            how many seconds to take into 
consideration (limited by HISTORY_SIZE)
+        * @return                              A vector of results on success, 
null on error.
+        *                                              The vector is composed 
of the average size of the messages, the number of messages,
+        *                                              the number of peers 
engaged and a bit-vector giving times of interactions
+        *                                              (highest bit is current 
time-unit, bit 1 is 32 time-units ago).
+        */
+
+       public int[] getTrafficStats( int type, int sendReceive, int timePeriod 
)
+       {
+               DirectedTrafficCounter  dtc;
+
+               assert(type>=0);
+
+               if (counters==null) {
+                       return null;
+                       }
+
+               synchronized(counters) {
+                       if (type>=counters.length || counters[type]==null) {
+                               return new int[] { 0, 0, 0, 0 };
+                               }
+
+                       dtc=(sendReceive==TrafficCounter.TC_SENT ? 
counters[type].send : counters[type].receive);
+                       return dtc.getTrafficStats(timePeriod);
+                       }
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public boolean handle( CSSession client, CSMessage message )
+       {
+               return trafficQueryHandler(client,message);
+       }
+
+       protected boolean trafficQueryHandler( CSSession sock, CSMessage 
message )
+       {
+               CSTrafficRequest        msg;
+               CSTrafficInfo   reply;
+
+               msg = (CSTrafficRequest) message;
+               reply = buildReply(msg.timePeriod);
+               return sock.send(reply);
+       }
+
+       /**
+        * Build a reply message for the client.
+        * @param countTimeUnits
+        * @return
+        */
+
+       protected CSTrafficInfo buildReply( int countTimeUnits )
+       {
+               CSTrafficInfo   reply;
+               int                             count,i;
+
+               synchronized(counters) {
+                       count = 0;
+                       for (i=0;i<counters.length;i++)
+                               if (counters[i] != null) {
+                                       if (counters[i].send.filled())
+                                               count++;
+                                       if (counters[i].receive.filled())
+                                               count++;
+                               }
+
+                       reply = new CSTrafficInfo(count);
+
+                       count = 0;
+                       for (i=0;i<counters.length;i++)
+                               if (counters[i] != null) {
+                                       if (counters[i].send.filled()) {
+                                               
counters[i].send.buildSummary(reply.counters[count++],TrafficCounter.TC_SENT,countTimeUnits,i);
+                                               }
+                                       if (counters[i].receive.filled()) {
+                                               
counters[i].receive.buildSummary(reply.counters[count++],TrafficCounter.TC_RECEIVED,countTimeUnits,i);
+                                               }
+                                       }
+                       }
+               return reply;
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       private static class Counter extends Object
+       {
+               /** Statistics for sending */
+               public DirectedTrafficCounter   send;
+
+               /** Statistics for receiving */
+               public DirectedTrafficCounter   receive;
+
+
+               public Counter()
+               {
+                       super();
+                       send=new DirectedTrafficCounter();
+                       receive=new DirectedTrafficCounter();
+               }
+       }
+}

Added: freeway/src/org/gnu/freeway/server/TransportCallback.java
===================================================================
--- freeway/src/org/gnu/freeway/server/TransportCallback.java   2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/server/TransportCallback.java   2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,16 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.server;
+
+import org.gnu.freeway.transport.*;
+
+/**
+ * Type of the per-transport callback method.
+ */
+
+public interface TransportCallback
+{
+       public void callback( Transport tapi, Object data );
+}

Added: freeway/src/org/gnu/freeway/server/TransportService.java
===================================================================
--- freeway/src/org/gnu/freeway/server/TransportService.java    2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/server/TransportService.java    2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,588 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.server;
+
+import org.gnu.freeway.*;
+import org.gnu.freeway.transport.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.io.*;
+import org.gnu.freeway.util.net.*;
+
+import java.util.*;
+import java.util.logging.*;
+
+/**
+ * Methods to access the transport layer.
+ * The APIs for GNUnet transport layer implementations.
+ */
+
+public class TransportService extends AbstractService
+{
+       private static final long               MIN_SECONDS_BEFORE_RECONNECTING 
=       60*60;          // one hour
+       private static final long               MAX_UNCONNECTABLE_SECONDS       
                =       60*60*24;       // one day
+
+       //todo: beurk, a faire autrement qu'en static
+       private static CoreForTransport coreFacade;
+
+       static {
+               coreFacade=null;
+               }
+
+       /** */
+       private Scheduler       scheduler;
+
+       /** */
+       private Transport[]     transports;
+
+       /** An array of cached HELO for each transport. HELOs must be signed 
with RSA, so caching the result
+        for a while is a good idea. Updated by a cron job periodically. */
+       private P2PHello[]      cachedHelos;
+
+       /** */
+       private Object          lock;
+
+       /** */
+       private int                     helo_live;
+
+
+       public TransportService()
+       {
+               super("Transport");
+               setDebug(true);
+
+               transports=new Transport[] {};
+               cachedHelos=new P2PHello[] {};
+       }
+
+       public String toString()
+       {
+               return "Transport service";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       /**
+        * Initialize the transport layer.
+        */
+
+       public void init()
+       {
+               String[]                p;
+               Transport       t;
+               Prefs           prefs;
+               int                     i;
+
+               super.init();
+
+               prefs=getManager().app().getPreferences();
+               scheduler=getManager().app().getScheduler();
+
+               helo_live = prefs.getInt("GNUNETD","HELOEXPIRES",0) * 60;       
// minutes to seconds
+               if (helo_live > HelloExchangeService.MAX_HELO_EXPIRES) {
+                       helo_live = HelloExchangeService.MAX_HELO_EXPIRES;
+                       }
+
+               if (helo_live <= 0) {
+                       log(Level.WARNING,"HELOEXPIRES not set in configuration 
under GNUNETD, setting to 1 hour.");
+                       helo_live = 60 * 60;
+                       }
+
+               lock=new Object();
+
+               // now load transports
+               p=prefs.getArray("GNUNETD","TRANSPORTS");
+               if (p.length>0) {
+                       for (i=0; i<p.length; i++) {
+                               t=(Transport) new 
DynamicLibrary("freeway-"+p[i]+".jar").load(Transport.class);
+                               if (t!=null) {
+                                       register(t);
+                                       }
+                               else {
+                                       log(Level.SEVERE,"Failed to load 
transport '"+p[i]+"', please correct configuration and restart application 
later.");
+                                       }
+                               }
+                       }
+               else {
+                       log(Level.WARNING,"No transport defined ! You should 
specify at least one under section GNUNETD, item TRANSPORTS.");
+                       }
+       }
+
+       /**
+        * Actually start the transport services and begin
+        * receiving messages.
+        */
+
+       public void start()
+       {
+               KnownHostsService       knownHosts;
+               P2PHello                                helo;
+               int                                     i;
+
+               super.start();
+
+               for (i=0; i<transports.length; i++) {
+                       if (transports[i]!=null) {
+                               createSignedHELO(transports[i]);
+
+                               
helo=transportCreateHELO(transports[i].getProtocol());
+                               if (helo!=null) {
+                                       knownHosts=(KnownHostsService) 
getManager().service(KnownHostsService.class);
+                                       knownHosts.bindAddress(helo);
+                                       }
+                               }
+                       }
+
+               for (i=0; i<transports.length; i++) {
+                       if (transports[i]!=null) {
+                               transports[i].launch();
+                               }
+                       }
+       }
+
+       /**
+        * Stop the transport services, stop receiving messages.
+        */
+
+       public void stop()
+       {
+               int i;
+
+               super.stop();
+               for (i=0; i<transports.length; i++) {
+                       if (transports[i] != null) {
+                               transports[i].shutdown();
+                               }
+                       }
+       }
+
+       /**
+        * Shutdown the transport layer.
+        */
+
+       public void done()
+       {
+               int     i;
+
+               super.done();
+
+               for (i=0; i<transports.length; i++) {
+                       if (transports[i] != null) {
+                               unregister(transports[i]);
+                               transports[i]=null;
+                               }
+                       }
+
+               lock=null;
+               transports=new Transport[] {};
+               cachedHelos=new P2PHello[] {};
+       }
+
+       /**
+        * Add an implementation of a transport protocol.
+        * @param _t
+        */
+
+       protected void register( final Transport _t )
+       {
+               CoreForTransport        c;
+               int                             ttype;
+
+               assert(_t!=null && _t.getProtocol()>=0);
+
+               ttype=_t.getProtocol();
+               if (ttype>=transports.length) {
+                       resizeTo(ttype+1);
+                       }
+
+               c=coreFacade;
+               if (c==null) {
+                       c=(CoreForTransport) 
getManager().service(CoreService.class);
+                       }
+
+               transports[ttype]=_t;
+               transports[ttype].init(c);
+               if (isStarted()) {
+                       transports[ttype].launch();
+                       }
+
+               scheduler.addJob(new 
ScheduledTask("SIGNED-HELO-"+_t.getName(),new AbstractAction() {
+                       public void perform()
+                       {
+                               createSignedHELO(_t);
+                       }
+                       
},Scheduler.seconds(helo_live)/10),Scheduler.seconds(helo_live)/10);
+       }
+
+       protected void unregister( final Transport _t )
+       {
+               assert(_t!=null);
+
+               scheduler.deleteJob(new 
ScheduledTask("SIGNED-HELO-"+_t.getName(),new AbstractAction() {
+                       public void perform()
+                       {
+                               createSignedHELO(_t);
+                       }
+                       },Scheduler.seconds(helo_live)/10));
+               _t.done();
+       }
+
+       /**
+        * Is this transport mechanism available (for sending) ?
+        *
+        * @param ttype
+        * @return true or false
+        */
+
+       public boolean isTransportAvailable( int ttype )
+       {
+               return (ttype>=0 && ttype<transports.length && 
transports[ttype]!=null);
+       }
+
+       /**
+        * Iterate over all available transport mechanisms.
+        * @param callback the method to call on each transport API 
implementation
+        * @param data second argument to callback
+        */
+
+       public void forEachTransport( TransportCallback callback, Object data )
+       {
+               int i;
+
+               for (i=0; i<transports.length; i++) {
+                       if (transports[i]!=null) {
+                               callback.callback(transports[i], data);
+                               }
+                       }
+       }
+
+       /**
+        * @param size
+        */
+
+       protected void resizeTo( int size )
+       {
+               Transport[]     oldTransports;
+               P2PHello[]                      oldHelos;
+
+               oldTransports=transports;
+
+               transports=new Transport[size];
+               Arrays.fill(transports,null);
+               
System.arraycopy(oldTransports,0,transports,0,oldTransports.length);
+
+               oldHelos=cachedHelos;
+
+               cachedHelos=new P2PHello[size];
+               Arrays.fill(cachedHelos,null);
+               System.arraycopy(oldHelos,0,cachedHelos,0,oldHelos.length);
+       }
+
+       /**
+        * Create signed HELO for this transport and put it into the cache 
tapi.helo.
+        * @param t
+        */
+
+       public void createSignedHELO( Transport t )
+       {
+               LocalIdentity           keys;
+               int             type;
+
+               keys=((Server) getManager().app()).getKeys();
+
+               synchronized(lock) {
+                       type=t.getProtocol();
+                       
cachedHelos[type]=t.createSignedHELO(keys.getPrivateKey());
+                       if (cachedHelos[type]!=null) {
+                               cachedHelos[type].setExpirationTime((int) 
(Scheduler.toSeconds(Scheduler.now())+helo_live));    // seconds
+                               }
+                       }
+       }
+
+       /**
+        * Connect to a remote host using the advertised transport layer. This 
may fail if the appropriate
+        * transport mechanism is not available.
+        *
+        * @param helo  The HELO of the target node.
+        * @return              The transport session on success, null on error.
+        */
+
+       public Session transportConnect( P2PHello helo )
+       {
+               Session tsession;
+               long    delta,t;
+               int             protocol;
+
+               assert(helo!=null) : "HELO can't be null !";
+
+               protocol=helo.getProtocol();
+               if (protocol<0 || protocol>=transports.length || 
transports[protocol]==null) {
+                       log(Level.INFO,"Failed to connect, protocol 
"+protocol+" isn't supported.");
+                       return null;
+                       }
+
+               t=getLastNotConnect(helo);
+               delta=Scheduler.toSeconds(Scheduler.now()-t);
+               if (t!=0 && delta<MIN_SECONDS_BEFORE_RECONNECTING) {
+                       log(Level.INFO,helo.getSenderIdentity().getName()+" 
Last connect attempt for prococol "+nameForProtocol(helo.getProtocol())+" was 
"+delta+"s ago, has not waited enough...");
+                       return null;
+                       }
+
+//todo: avec le channel en non-blocking, va toujours revenir : prévoir un 
callback transport.notifyNotConnectable( P2PHello )
+               tsession=transports[protocol].connect(helo);
+               if (tsession==null) {
+                       markNotConnectable(helo);
+                       return null;
+                       }
+
+               debug("Core connected to "+tsession+".");
+               return tsession;
+       }
+
+       /**
+        * IP has changed(dynamic IP, ISPs), host removed from network, server 
failure...
+        * @param helo
+        */
+
+       protected void markNotConnectable( P2PHello helo )
+       {
+               String  id;
+               long    delta,t;
+
+               id=helo.getSenderIdentity().getName();
+               log(Level.INFO,id+" Mark not connectable for prococol 
"+nameForProtocol(helo.getProtocol())+".");
+
+               t=getLastNotConnect(helo);
+               if (t!=0) {
+                       delta=Scheduler.toSeconds(Scheduler.now()-t);
+                       if (delta>MAX_UNCONNECTABLE_SECONDS) {
+                               log(Level.INFO,id+" Last time it has not been 
connected is "+delta+"s ago, have waited too long, remove host entry !");
+                               ((KnownHostsService) 
getManager().service(KnownHostsService.class)).delHostFromKnown(helo);
+                               }
+                       else {
+                               log(Level.INFO,id+" Last time it has not been 
connected to : "+delta+"s ago.");
+                               }
+                       }
+
+               setLastNotConnect(helo);
+       }
+
+       public long getLastNotConnect( P2PHello helo )
+       {
+               DirLocation     dir;
+               MappedFile      f;
+               FileLocation    loc;
+               String          id;
+               long                    t;
+
+               dir=getManager().app().getHome();
+               
dir=dir.getDirectory("data").getDirectory("hosts").getDirectory("last-attempts");
+               dir.create();
+
+               id=helo.getSenderIdentity().getName();
+
+               loc=dir.getFile(id+"."+helo.getProtocol());
+               if (!loc.exists()) {
+                       return 0;
+                       }
+
+               f=loc.open();
+               t=f.readLong(0);
+               f.close();
+
+               if (t==0) {
+                       log(Level.WARNING,id+" Corrupted entry, remove it.");
+                       loc.delete();
+                       }
+               return t;
+       }
+
+       public void setLastNotConnect( P2PHello helo )
+       {
+               DirLocation     dir;
+               MappedFile      f;
+               String          id;
+
+               id=helo.getSenderIdentity().getName();
+
+               dir=getManager().app().getHome();
+               
dir=dir.getDirectory("data").getDirectory("hosts").getDirectory("last-attempts");
+               dir.create();
+
+               f=dir.getFile(id+"."+helo.getProtocol()).openNew();
+               f.writeLong(Scheduler.now());
+               f.close();
+       }
+
+       /**
+        * Return transport mechanism that supports this HELO.
+        *
+        * @param helo
+        * @return the transport on success, null if none found
+        */
+
+       public Transport getTransport( P2PHello helo )
+       {
+               return getTransport(helo.getProtocol());
+       }
+
+       public Transport getTransport( int protocol )
+       {
+               assert(protocol>=0);
+
+               if (protocol>=transports.length || transports[protocol]==null) {
+                       log(Level.FINEST,"No transport found for protocol 
"+protocol+" ("+nameForProtocol(protocol)+").");
+                       return null;
+                       }
+               return transports[protocol];
+       }
+
+       /**
+        * Create a HELO advertisement for the given transport type for this 
node.
+        *
+        * @param ttype
+        * @return
+        */
+
+       public P2PHello transportCreateHELO( int ttype )
+       {
+               Transport       tapi;
+               P2PHello                helo;
+               int[]                   perm;
+
+               helo=null;
+               synchronized(lock) {
+                       if (ttype==Transport.ANY_PROTOCOL_NUMBER) {
+                               perm=Crypto.permute(transports.length);
+
+                               ttype=transports.length-1;
+                               while (ttype>=0 && 
(transports[perm[ttype]]==null || cachedHelos[perm[ttype]]==null)) {
+                                       ttype--;
+                                       }
+                               if (ttype<0) {
+                                       return null;
+                                       }
+                               ttype=perm[ttype];
+                               }
+
+                       if (ttype<0 || ttype>=transports.length) {
+                               log(Level.WARNING,"Transport type "+ttype+" > 
"+transports.length+" (maximum transport ID).");
+                               return null;
+                               }
+
+                       tapi = transports[ttype];
+                       if (tapi == null) {
+                               log(Level.WARNING,"No transport of type 
"+ttype+" known.");
+                               return null;
+                               }
+
+                       if (cachedHelos[ttype] == null) {
+                               debug("Transport of type "+ttype+" configured 
for sending only.");
+                               return null;
+                               }
+
+                       helo=(P2PHello) 
PersistentHelper.copy(cachedHelos[ttype]);
+                       }
+               return helo;
+       }
+
+       /**
+        * Get a message consisting of (if possible) all addresses that this
+        * node is currently advertising.  This method is used to send out
+        * possible ways to contact this node when sending a (plaintext) PING
+        * during node discovery. Note that if we have many transport
+        * implementations, it may not be possible to advertise all of our
+        * addresses in one message, thus the caller can bound the size of the
+        * advertisements.
+        *
+        * @param maxLen the maximum size of the HELO message collection in 
bytes
+        * @param buff where to write the HELO messages
+        * @return the number of bytes written to buff, -1 on error
+        */
+
+       public int getAdvertisedHELOs( int maxLen, List buff )
+       {
+               P2PHello[]      helos;
+               P2PHello        tmp;
+               int                     used,i,j,tcount;
+
+               tcount=0;
+               for (i=0; i<transports.length; i++) {
+                       if (transports[i]!=null) {
+                               tcount++;
+                               }
+                       }
+
+               helos=new P2PHello[tcount];
+
+               tcount=0;
+               for (i=0; i<transports.length; i++) {
+                       if (transports[i]!=null) {
+                               tmp=transportCreateHELO(i);
+                               if (tmp!=null) {
+                                       helos[tcount++]=tmp;
+                                       }
+                               }
+                       }
+
+               if (tcount==0) {
+                       return -1;
+                       }
+
+               j=0;
+               used=0;
+               while (j < 10) {
+                       j++;
+                       i=Crypto.nextInt(tcount);       // select a HELO at 
random
+                       if (helos[i]!=null && 
used+helos[i].getByteSize()<=maxLen) {
+                               buff.add(helos[i]);
+
+                               used+=helos[i].getByteSize();
+                               helos[i]=null;
+                               j=0;    // try until 10 attempts fail, restart 
after every success !
+                               }
+                       }
+               return used;
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////
+
+       /**
+        * Return the core used to dialog with.
+        * @return
+        */
+
+       public static CoreForTransport getUsedCoreFacade()
+       {
+               return coreFacade;
+       }
+
+       /**
+        * Give an opportunity to use this service with a home-made core (may 
be used for debugging purpose...).
+        * @param c
+        */
+
+       public static void useCoreFacade( CoreForTransport c )
+       {
+               coreFacade=c;
+       }
+
+       public static String nameForProtocol( int protocol )
+       {
+               switch (protocol) {
+                       case Transport.ANY_PROTOCOL_NUMBER: return "ANY";
+                       case Transport.NAT_PROTOCOL_NUMBER: return "NAT";
+                       case Transport.TCP_PROTOCOL_NUMBER: return "TCP";
+                       case Transport.HTTP_PROTOCOL_NUMBER: return "HTTP";
+                       case Transport.TCP6_PROTOCOL_NUMBER: return "TCP6";
+                       case Transport.UDP_PROTOCOL_NUMBER: return "UDP";
+                       case Transport.UDP6_PROTOCOL_NUMBER: return "UDP6";
+                       case Transport.SMTP_PROTOCOL_NUMBER: return "SMTP";
+                       }
+               return String.valueOf(protocol);
+       }
+}

Added: freeway/src/org/gnu/freeway/test/AbstractTest.java
===================================================================
--- freeway/src/org/gnu/freeway/test/AbstractTest.java  2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/test/AbstractTest.java  2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,47 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.test;
+
+import org.gnu.freeway.*;
+
+/**
+ *
+ */
+
+public abstract class AbstractTest extends Object
+{
+       protected GNUNetTests           suite;
+
+
+       protected AbstractTest()
+       {
+               super();
+               suite=null;
+       }
+
+       public boolean test( GNUNetTests ts )
+       {
+               int     ret;
+
+               suite=ts;
+
+               try {
+                       ret=test();
+                       if (ret!=0) {
+                               suite.err("Failed with error #"+ret+" !");
+                               }
+                       else {
+                               suite.out("Succeeded.");
+                               return true;
+                               }
+                       }
+               catch( Throwable x ) {
+                       suite.err("Got exception !",x);
+                       }
+               return false;
+       }
+
+       public abstract int test() throws Throwable;
+}

Added: freeway/src/org/gnu/freeway/test/BloomTest.java
===================================================================
--- freeway/src/org/gnu/freeway/test/BloomTest.java     2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/test/BloomTest.java     2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,112 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.test;
+
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.io.*;
+
+import java.util.*;
+
+/**
+ * Testcase for the bloomfilter.
+ */
+
+public class BloomTest extends AbstractTest
+{
+       private static final int        K               =       4;
+       private static final int        SIZE    =       65536;
+
+       private static final int        LOOP    =       200;
+
+       public BloomTest()
+       {
+               super();
+       }
+
+       public int test()
+       {
+               HashCode160[]           hashcodes;
+               Statistics      stats;
+               BloomFilter                     bf;
+               List                                    all;
+               int                                     i,ok,falseok;
+
+               stats=suite.getStatistics();
+
+               new FileLocation("/tmp/bloomtest.dat").delete();
+
+               all=new ArrayList();
+               for (i=0; i<LOOP; i++) {
+                       all.add(HashCode160.random());
+                       }
+               hashcodes=(HashCode160[]) all.toArray(new 
HashCode160[all.size()]);
+
+               bf=BloomFilter.load(stats,"/tmp/bloomtest.dat", SIZE, K);
+
+               for (i=0; i<LOOP; i++) {
+                       bf.add(hashcodes[i]);
+                       }
+
+               ok=0;
+               for (i=0; i<LOOP; i++) {
+                       if (bf.test(hashcodes[i])) {
+                               ok++;
+                               }
+                       }
+
+               if (ok!=LOOP) {
+                       suite.err("Got "+ok+" elements out of "+LOOP+" expected 
after insertion.");
+                       return -1;
+                       }
+
+               bf.free();
+
+               bf=BloomFilter.load(stats,"/tmp/bloomtest.dat", SIZE, K);
+
+               ok=0;
+               for (i=0; i<LOOP; i++) {
+                       if (bf.test(hashcodes[i])) {
+                               ok++;
+                               }
+                       }
+
+               if (ok != LOOP) {
+                       suite.out("ERROR: Got "+ok+" elements out of "+LOOP+" 
expected after reloading.");
+                       return -1;
+                       }
+
+               for(i=0; i<LOOP/2; i++) {
+                       bf.delete(hashcodes[i]);
+                       }
+
+               ok=0;
+               for (i=0; i<LOOP; i++) {
+                       if (bf.test(hashcodes[i])) {
+                               ok++;
+                               }
+                       }
+
+               if (ok != LOOP/2) {
+                       suite.out("ERROR: Expected "+(LOOP/2)+" elements in 
filter after adding "+LOOP+" and deleting "+(LOOP/2)+", got "+ok);
+                       return -1;
+                       }
+
+               falseok=0;
+               for(i=0; i<1000; i++) {
+                       if (bf.test(HashCode160.random())) {
+                               falseok++;
+                               }
+                       }
+               if (falseok!=0) {
+                       suite.err("Got "+falseok+" collision(s), something went 
wrong ?!");
+                       }
+
+               bf.free();
+
+               new FileLocation("/tmp/bloomtest.dat").delete();
+               return 0;
+       }
+}

Added: freeway/src/org/gnu/freeway/test/CRCTest.java
===================================================================
--- freeway/src/org/gnu/freeway/test/CRCTest.java       2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/test/CRCTest.java       2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,103 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.test;
+
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+
+/**
+ * Testcase for crc computing.
+ */
+
+public class CRCTest extends AbstractTest
+{
+       private static  int[]   expected = {
+               0xB70B4C26, 0x376B976D, 0xC1881125, 0x6AC1BB87, 0xE69F53E7, 
0x7F75CA2E, 0x04EC251F, 0x739C2E52, 0x6BAC7044, 0xA64B7844, 0x6FF43E04, 
0xC48D97D3, 0x0BFEA8B1, 0x66B6D746, 0xBDFD9E32, 0xEB76647F,
+               0xBE4D0A29, 0x759A2101, 0x2AA14A19, 0x8F8CFB6B, 0x78794933, 
0xAD4EFA9E, 0x2358C1F2, 0xDDF5E354, 0x543239E0, 0x189121CE, 0x9F13833E, 
0x5CC63A0F, 0xEEBA0E50, 0xCD4FDA59, 0x7B2F8FDB, 0xC9A67467,
+               0xAB008C8A, 0x1DC8949B, 0xCE348069, 0x6C4F0ADE, 0xB72CDF8A, 
0x5A6B1380, 0xC21510A3, 0x81E2390D, 0x1FBF40A4, 0x76481CA7, 0xE8604EB1, 
0xAFCF56CC, 0xDCC99D67, 0x9498D487, 0x6873EDD4, 0xE8F06DFA,
+               0x1B0E0F8D, 0x5C8AA279, 0xCCCDD56A, 0x452AFECB, 0xD3E1A051, 
0xE2FEEF70, 0x41B742F5, 0xE140CFBC, 0x9FC9000E, 0x5DB54A9F, 0x383D910B, 
0xE4251ACA, 0x907E675F, 0x58095010, 0xD0BB55A8, 0xCB033179,
+               0xD6754B8C, 0x323AAC65, 0x61E4E233, 0xCFF71A2A, 0x4C957069, 
0x5D228CEC, 0x7CABAF21, 0x6E697349, 0x843B003B, 0x161B87DB, 0x46B760BF, 
0x736650EB, 0x2DDB7A7F, 0x0AC0B3A2, 0x792734DC, 0xF9B6C829,
+               0xBD3F2410, 0x24CA7C27, 0x45B22C98, 0x69927288, 0x86411854, 
0x26F01F4B, 0xC93F1CE7, 0x140832FF, 0xC98A4486, 0x74F50170, 0xE1E01D52, 
0xCE03A247, 0xBBF5CE8A, 0x353C4A1E, 0xB0B548DD, 0x0747653B,
+               0x424120D0, 0x290E6FC8, 0x3C929B99, 0x9481B677, 0x156F851B, 
0x3DADC76A, 0xDD59294A, 0x5ED3EADF, 0xE8C11E16, 0x786B9CFC, 0x65166927, 
0xE456DBA1, 0xCC9522A9, 0xAC413773, 0xD207BCD7, 0x2C006FE6,
+               0xC24A257A, 0xC8551F7E, 0x4ACD420E, 0x630E0796, 0xCB4D4702, 
0x6B50D8C1, 0x308F5FE7, 0xB7A2CA7A, 0xDFBF285B, 0x712B32AD, 0xF1E25446, 
0x59C1B21B, 0x4AA70E34, 0xC5870360, 0x59882C57, 0x80C3EAA8,
+               0x1CCD6125, 0x4C14607C, 0x204109EA, 0xB6713871, 0x14E8006C, 
0x0F5FDE4E, 0x3D6B8702, 0x19ED675A, 0xBE8BC878, 0xC45CB7A0, 0xDD99B9AA, 
0x266EF696, 0xD10F2667, 0xB3356519, 0x3B7D7BAC, 0x4B10374F,
+               0x38F7EAEF, 0xC6E05206, 0x681D629C, 0x1AEA8F0A, 0x720C4EDB, 
0xC7413B45, 0xA340C68A, 0xCE82EF37, 0x31C12FE1, 0x90087EDC, 0x29B92AC2, 
0xC2C57916, 0x531041F0, 0xB4DFAEC5, 0xCDD8EFAC, 0x352CDDBD,
+               0x5917C5F2, 0x9DB37BBA, 0x6269C0EE, 0xC1E747D5, 0x8DC1D977, 
0xB10D5675, 0x0961D2CB, 0xD8E977FF, 0x8C27B35B, 0xFB3F2832, 0x143FBD97, 
0xF7C11525, 0x424648CB, 0xE60D7FDE, 0xE0369388, 0xF4A976C0,
+               0x99941EE2, 0x9EC44EFA, 0x65B38A9B, 0x2C6FF1CB, 0x5094FC6E, 
0x6B77BE3A, 0xB763DDAA, 0x577C1A96, 0x05075C31, 0xD59B2858, 0x654B262C, 
0xD22D2774, 0x3276B320, 0x7D5D518B, 0x831C70FA, 0xE5BD69F0,
+               0xEA6A1D1B, 0x786DE8EB, 0x14FCB5EB, 0xCD621164, 0xB425C7D3, 
0x67EACA53, 0x7870F1D5, 0x9DA71D88, 0x0A4AEC67, 0x30ADEF54, 0x895FDA35, 
0x35CDF44D, 0xEE351914, 0x09C99B8A, 0x0B58F0E5, 0x0E74BCD7,
+               0x06C21764, 0x122B0B19, 0xBF8FBB93, 0x30D4528E, 0xBE605572, 
0xCCFB5F89, 0x410C0D57, 0xC84BED64, 0x1F06553D, 0xADA3F543, 0xE2B2E1B9, 
0x803CE5C7, 0x29C6CDBD, 0x85DE35C8, 0xF6683822, 0x883C27C7,
+               0x57469B3E, 0x5E754E1B, 0xD22F613C, 0xC4CBB576, 0x4B732A6B, 
0x904FC368, 0x22508D35, 0xC53069B5, 0x4BBD2AA0, 0x0547BCD9, 0x993DF9AF, 
0xE790FDCE, 0x33A8557A, 0xE329A005, 0xF52A92EB, 0xC691C4C9,
+               0x9B7856F2, 0x9CD99D14, 0xBCBD3911, 0xAE67E727, 0x074A6D33, 
0x2F6FE571, 0x1F816E83, 0xFECFEFCE, 0x519DDA16, 0x4CA1B36A, 0xD3F44B7D, 
0x4F2C211F, 0x20E77B24, 0xAAAD212E, 0xB828F51E, 0x028C97A9,
+               0xB0C0DF2A, 0xE53A194E, 0x384327E5, 0x3AD4AD11, 0xC7AA5512, 
0xEFE72314, 0x625E98A2, 0xE9FDA738, 0x22B11382, 0xB6BB5B52, 0x08D0CBCB, 
0xE1276A96, 0xA00D45D8, 0xBF3829DC, 0x52F835ED, 0xED491D28,
+               0xDB706132, 0x33010A75, 0x57F57DE5, 0xBDB69E42, 0x791DE178, 
0x5D53DED9, 0x790DA6B1, 0x19F58B75, 0x6CB0D3E3, 0xD6CA3773, 0xCEB11C60, 
0x3F03287C, 0xED4680B1, 0xDA716FE1, 0xAFAE8F48, 0x1C88A148,
+               0xA496394C, 0xAB0A8CB5, 0x2D6B02C4, 0x5CFF6E69, 0x8C0403B9, 
0x47DE6CF9, 0xC6D19359, 0xDD9F9C2E, 0xF6D03BA1, 0xBAF8A998, 0x468F2E24, 
0x50B76C6F, 0xA48DD917, 0xCEE2E323, 0xF5D5D9C2, 0xC99BFC76,
+               0x6EC6AAA5, 0x1BD6F4B9, 0x7CA2BD9D, 0x96CBD336, 0x9D178A55, 
0xBA07DBA1, 0x8DFDB077, 0xFAF5980B, 0x77DA1858, 0xC90BDA14, 0x384C0DC0, 
0xC90AC6DF, 0x3EE3943A, 0x3CB7F567, 0xB94375A1, 0x880F4B20,
+               0x4E2E4111, 0x108CBDCE, 0x78F945F0, 0x432D9806, 0x3D5C3362, 
0x11071643, 0xF4C3657D, 0xD9CA855C, 0x26F8C080, 0xCFF6B14C, 0x9DF56B4C, 
0x50A33115, 0x293982BC, 0xA7F50A24, 0xD363DBDF, 0x3D6F3193,
+               0x88526B7F, 0xB8AC60A8, 0x4E520188, 0x0A92080C, 0xDE39341E, 
0xBD533DFA, 0x6534B550, 0x148B44FC, 0xA733B6B4, 0xCAFB2FDF, 0x4B13D68E, 
0xD54A0D80, 0x7E266B56, 0x1355788F, 0x6AA79220, 0x5CEB640A,
+               0x743E74ED, 0xD12ED449, 0x4604B792, 0xE65DFFB8, 0xCCC542B3, 
0xDD643AB4, 0x2439446D, 0xBCC1CB34, 0x5C0CFDF3, 0x570A6120, 0x966DB847, 
0x48762A8A, 0x108638C0, 0xE734076C, 0xE70788FF, 0x99310AD0,
+               0x181FB5FD, 0x3F1AF20C, 0xA61826A7, 0x0DB3BCAD, 0xDDE33D1D, 
0x703F3131, 0x37824F41, 0x62E6EC03, 0xFA621121, 0xF19AA112, 0x20AA20EA, 
0xEDF9A2BB, 0x2291E42B, 0x04610925, 0xC54ABD52, 0x5CA5FDA5,
+               0x7C43A344, 0xA92AF412, 0xBE20196F, 0x25847F47, 0xFF071197, 
0x82AD1853, 0x6FAE03C6, 0x09993378, 0x593AEA87, 0x769F4A9B, 0xE23BAE5A, 
0x121EF81A, 0x027FE32F, 0xCFF59C92, 0xDDEC8FBC, 0x68376D41,
+               0x7CF214CF, 0xF9107D81, 0x9078DBFF, 0xBFEF5B6A, 0xA31596F0, 
0x0DCA2E05, 0x7E8830A0, 0x82B115F7, 0xEF3939A7, 0xEB881536, 0xD7DEBE35, 
0x69DC800F, 0x5E44B53E, 0x7AC2B4A3, 0x2DD3B2F8, 0x504C7E64,
+               0x9A920769, 0x0E9EB4D7, 0xDE6D36D2, 0xDCAE472F, 0x631635D6, 
0x52DFECA5, 0xA540B6B4, 0xD5E479F1, 0x7B5A5001, 0xEE020B8D, 0xD19B606D, 
0xA9544C72, 0x4A738168, 0x9710CC87, 0x0FA0C0A8, 0x25987F95,
+               0xA6D5C7CC, 0xFE36885C, 0x8968B53D, 0x27482490, 0xEC5C93F4, 
0xC42FB222, 0xB12B80E4, 0x155FE6AC, 0x85A83C65, 0x6E267535, 0x6CE52CFA, 
0xC419B282, 0x962DFED3, 0x7F34F878, 0x9855B177, 0x70D052F6,
+               0x373B3F2B, 0xC1D8B996, 0x7867E97C, 0x24247C92, 0xB519A9E7, 
0x07021631, 0xDD056564, 0x0B680C5D, 0x628EF22B, 0xB0C4F43B, 0xE9988A29, 
0xBF096CA2, 0x47E9937A, 0x1FD79C34, 0xC1D8DA2A, 0xB9C44FAF,
+               0xE570C38B, 0xA67E266F, 0xA0BE5DF9, 0xE41FA49F, 0x2CC00E17, 
0x6A29218D, 0x67143F82, 0x0101F768, 0x3363A5D4, 0x868A5C95, 0x56BDB89A, 
0xA71DC975, 0x3F14BDF8, 0x6603E23E, 0x50C49DDE, 0x0D918320,
+               0x17A325DB, 0xF5A764A6, 0xE79F8647, 0xC4C69667, 0xA8561CB6, 
0x161E2E27, 0x0C7DB046, 0x7820A697, 0xD4E0DF47, 0xAB207C2C, 0x8372904A, 
0x150E53C9, 0x7C3B5995, 0xD0C0B6B1, 0x29CE60B6, 0xAA214CBC,
+               0x582CA5B3, 0x71B71120, 0x4976D3E1, 0xF0B2E19F, 0x17E7F549, 
0x251009C5, 0x33C0A6BD, 0x4A342047, 0x7875F726, 0x0700A983, 0x4FF3C8E8, 
0x8C8BE892, 0xE576C75D, 0xCDB55E7D, 0xE60DFBC9, 0x3E1703FF,
+               0x1C613576, 0xB6DF6091, 0xAA67AA65, 0xEE4B6A82, 0x67B0E1D4, 
0x742682E9, 0xD531FC8A, 0x44114AEA, 0x63C1E188, 0xF43E4A42, 0x5FA22D82, 
0x54E194DD, 0xE30DED77, 0x65D8D87E, 0xCD633463, 0x5DCDF2B7,
+               0x1F9CAAA7, 0x69F69323, 0x5212B780, 0xBF20E0C0, 0x6CD91BC9, 
0x2C453E58, 0xAD6016B5, 0x894ED1E4, 0xE5875539, 0xE59782EE, 0xA169CFF2, 
0x982FF8C5, 0x72C9A6B2, 0x81B3E528, 0x2FEA66E8, 0xA7ADA17E,
+               0x3B24F999, 0x4DA4595A, 0x1A7ECAA6, 0x5309B3C2, 0x87434B2E, 
0x7162CD59, 0x9BA68FFB, 0x2FB90335, 0x3415E5D4, 0xDB11F232, 0x88AAD476, 
0x874DEC3F, 0x75F4F08A, 0x77ED0DC8, 0x28476A28, 0x173650DF,
+               0xCC4F4F7C, 0x0DC7F8CF, 0xEDE296B7, 0x562B47A9, 0xAD6394FF, 
0x4E1C48CA, 0x2296452D, 0xF66D6F76, 0xB0AC23FE, 0x1D08B761, 0x4304C01E, 
0x0D9178A8, 0x92EFB8F6, 0x3C7CE50A, 0xB050F8E5, 0xC1E14C22,
+               0xE412DDED, 0xB94AFF1D, 0xE3D0F98D, 0x4874DA40, 0x856A0977, 
0xCBF3C485, 0x09584997, 0x8FBFF221, 0xCDC7B7FB, 0xD5390194, 0x2D54CAB6, 
0x400FF467, 0x2EDD7D79, 0x308521B9, 0xDE8BEC51, 0xF96CEBD1,
+               0x927BDBB0, 0x3AFD26C4, 0xFB26B02A, 0x208BE3CA, 0x0D492EAA, 
0x4D694DB5, 0x6AC9E2F5, 0x5D519F3D, 0x5A5CB16E, 0x19228057, 0x6EE252A3, 
0x8A60EC42, 0x6E87CC63, 0xAC774990, 0xF68C8809, 0x59FFB127,
+               0xF89B885E, 0xE468226F, 0x0F7D9E1B, 0xA5B1155B, 0xEEBE68E3, 
0xC9897458, 0x299BD756, 0x9EC56A3A, 0xE1C39878, 0x711D358F, 0xBD59BB82, 
0x9B71E9C9, 0x03A8F974, 0x8DF0AE4D, 0xE3C9E120, 0xF08E569E,
+               0xDD9C861F, 0x9B4E7B0A, 0x1333B8FF, 0x207D6A90, 0x07985A98, 
0x93CE111C, 0x849AB614, 0xBA1C9F5C, 0x23B7F99E, 0xC9B4AAE3, 0x6B21D90B, 
0xC80D58AD, 0x0885E020, 0x1E12F80F, 0x480DDA1B, 0xCFDB7CD1,
+               0xDB4842E9, 0xF0CDFF0F, 0xFC97F10B, 0xBEB3766F, 0x771CDD1E, 
0x69A59640, 0x2A83A86F, 0xB89985E6, 0x30368F0F, 0xEDA24197, 0xB1A7019F, 
0x10F267EC, 0xD3B4ECC7, 0x9F4CAFB0, 0x9C9B6ED0, 0x07136D06,
+               0x3ECCE4F3, 0x84726C09, 0xF5610CF0, 0xCEA0D8C1, 0x7A0764E0, 
0xB1D513F6, 0x8A89EAF5, 0xD36AD9FC, 0x052C8E8F, 0xE601D19B, 0x574CB7D3, 
0x2BDC0A83, 0x01F17192, 0xF4A59B2E, 0xD4260E70, 0x1CC80B5B,
+               0x1C5A0838, 0xD5761743, 0x54E1B788, 0xF704FABF, 0xBC37BCD4, 
0x9BB314BD, 0xFD2308BF, 0x442F713A, 0xA416B767, 0x10FB8A33, 0xA15DB01E, 
0xDABBA5B9, 0xD3108013, 0xE6220C36, 0xB0BCEC3B, 0x9E71BFE2,
+               0x648A45DE, 0x01A8CAAA, 0x6839FFB2, 0xCB9376C0, 0x7AA779BA, 
0x2E1E40FE, 0x06B11963, 0x881A50BE, 0x5A77BFB1, 0x8C2D8D2E, 0x0FFBE3DF, 
0xC2307A13, 0x8C051308, 0x9C525D30, 0x12412B16, 0xD6B2234C,
+               0xD62CD4FB, 0xE467787C, 0x37FAE258, 0x20FA8840, 0x6AB4F888, 
0x7CCCBF69, 0xD8C6686B, 0xF4E90683, 0x8104298E, 0xC39B4117, 0x46FB3C49, 
0x813EDBAD, 0xA6047B42, 0xB2FF3E28, 0xAAE49E4B, 0x12733086,
+               0x17253E45, 0x0F7A17D5, 0x8F0B2F2E, 0x0690648C, 0xF5DB53A9, 
0xE70000B0, 0xD6804F9C, 0xCE66143C, 0xEAEF0F58, 0xD0746F32, 0x9E00798A, 
0x2AA08688, 0xB2297B23, 0xF74F79A1, 0xFA106F3C, 0xD68C44B2,
+               0xCC109A82, 0xD4C1CF04, 0x38CB8C6D, 0xD6F73581, 0x98E35F0C, 
0xF9D12C84, 0x3F1FE3C0, 0x21E85271, 0xC56DB7F9, 0xF8549BF1, 0x7D2294D0, 
0x59E5E7E8, 0x83750D7D, 0xE8B2CB51, 0x2798835A, 0xA031910A,
+               0xF6450D2E, 0xBD2A64DC, 0xF859241D, 0xDB4D3FB7, 0x840EFFCB, 
0x2D54F355, 0x2865D239, 0xD34A1C27, 0xC0C9ABE8, 0x7EDD5F58, 0x1F4EB15C, 
0x76793DD4, 0x7925F47D, 0x881ECAC0, 0x73CC65DF, 0x643A9CA4,
+               0x29058C73, 0xD0161F87, 0x28CFD06C, 0x4E059625, 0x56C0DCE5, 
0x2F20821B, 0x5CDCB790, 0xE928C8B3, 0xEA73C7B6, 0xD3BD312C, 0x214F2DC9, 
0xB794EA70, 0x4AB371F9, 0xF8E05E57, 0xC4C14E9F, 0xFE01C3D1,
+               0xF56C48FD, 0x46BF15A9, 0x84686251, 0x46D781AF, 0x710A8487, 
0x47888200, 0x73127253, 0x8DE884FD, 0x5B78FE20, 0xB8E46B7B, 0x91D59120, 
0xF2D61EB9, 0x3BB65AAD, 0x4D6A1350, 0x5B6035CC, 0x8E044672,
+               0x65A924C7, 0x5D77388B, 0xACB1B24D, 0x30267C20, 0x918D846F, 
0xA4D21E7F, 0xA215BAF7, 0xB3B0A0B0, 0xA28FF421, 0x910595E6, 0x4C197C13, 
0x45589427, 0x255D6D59, 0x12E426B6, 0x9078B1DA, 0x4FEEA455,
+               0x5F1F9F25, 0x74488C5C, 0x616FD034, 0x3FDD6D57, 0xCFC23BCE, 
0xBAC41A43, 0x83ECF932, 0x83B0E949, 0x012769E0, 0x1333661C, 0xD65DF661, 
0x4B8F00F6, 0xF20F1BEE, 0xF877E19B, 0xAAFD098E, 0x41353C71,
+               0x1DD0238F, 0xEF19669E, 0x9BECAFB1, 0x740CDC87, 0x58C69B21, 
0x7C8C53BE, 0x554842F5, 0x74EF6CEE, 0xFA1251F9, 0xDF0C6F10, 0x45A8F4F5, 
0x95AC1E3B, 0x7C869B71, 0x412368E9, 0x3681FD61, 0x04C8BAB3,
+               0x8063BD66, 0xD4B43F9B, 0xC30EAD93, 0x7DFF5DBB, 0x588F105E, 
0xF944F06B, 0x76F0535A, 0x2E2BAADE, 0x384555AB, 0x5AB7803F, 0x113B1DC7, 
0xE8F00EC9, 0x8B24F169, 0x155DAA75, 0xF1C5BFE4, 0x8DF0298D,
+               0xC4604100, 0x67387CD8, 0xB4352232, 0x8DB5BC29, 0x7F34BD8D, 
0xCEA43C02, 0xBF12B729, 0xA5B56FE1, 0x644F7F3A, 0xE340939F, 0x7DC7C401, 
0x06E62042, 0x868165B6, 0x03ED63A4, 0xDFD8B630, 0x99E28B78,
+               0x922F41CA, 0x9D15580C, 0xC9C93B75, 0x3345133A, 0x4F328D50, 
0x3E3A48D0, 0x829A7381, 0xBFFC3538, 0xB1C08257, 0xCB4043A8, 0xD5F5B385, 
0xB4B1E96C, 0x86DEA6AC, 0x98B6E5B1, 0x93E331AC, 0x572CFD87,
+               0x4649F09D, 0x6742A88C, 0x2406DAD5, 0xFA7FDA50, 0xB8F0FB8C, 
0x1228A80F, 0x771F7BE2, 0x94909E79, 0x948CEB39, 0x9A624C72, 0xC5CF6334, 
0x3BF3E93E, 0x7DB2678A, 0x514B9ABD, 0x323FC842, 0xC20893E7,
+               0x4C97A77A, 0x0575DB91, 0x18F0ADD9, 0x286FE756, 0xC7A6F0C7, 
0xE5C6026F, 0xC13B19E3, 0x195FF7E1, 0xAE2D0F4C, 0x2347DCD9, 0x4942690D, 
0xFC9F638A, 0x7F91A508, 0x317CB78D, 0x2D8867AD, 0x0A779A05,
+               0x827E3D65, 0xC1F640DA, 0x4600941E, 0x143929E1, 0x14A8FA4B, 
0xF53DB9DC, 0xAEBF5D0D, 0x0C8DCE72, 0x3D53AED9, 0x222F5E72, 0x29A1A7D5, 
0x082A71E5, 0x3C892850, 0xAC720515, 0xEE3D05E4, 0x9376BD43,
+               0xB29168BF, 0x4D72AE59, 0xEA274FD1, 0x05749A1B, 0x425E2542, 
0xC7011894, 0x0152D15B, 0xACE389CD, 0x8DFF05A9, 0xA958ED94, 0x602AA88F, 
0xF5E57D41, 0x507F4DF5, 0x203B5C7D, 0x839FBF20, 0x41466163,
+               0xCF8DD739, 0x6EDF0132, 0xBD291E4F, 0x9C9FBEF9, 0xE306F0F2, 
0x09ECE3D0, 0x5FF4FDA5, 0x2B67BB25, 0x115973C2, 0x2683BF3E, 0x067700C5, 
0x9FA3B26A, 0x3C981728, 0x7D2AD328, 0x01AA4010, 0x0AF4E38C,
+               0x00D07150, 0xCFFBAD29, 0x37CB467A, 0xE5D4D012, 0x54516D78, 
0x0CDD8A6D, 0xAF6578AB, 0x93AC41BA, 0xB166113F, 0xF96E5F21, 0x0D9F012F, 
0x659C5E5E, 0x3B80C093, 0xA5572082, 0xD9BC854D, 0x83700ED9,
+               0x7CD611D4, 0xA9C9F9F7, 0xD887D604, 0x638C8958, 0x00930FC9, 
0x1D70B487, 0x275785D6, 0x4760F74F, 0x65172BDE, 0xF3C808A1, 0xBE30133F, 
0xCD32781F, 0x8C955806, 0xCE8B873E, 0xB52E534F, 0x4B5751E7,
+               0x61E8443C, 0xD91B5263, 0x2E53D9BC, 0x78EE7EF3, 0xAB1BCB43, 
0xFCC269AB, 0x8C5B7B1F, 0x948AFB19, 0xE5369E8D, 0x31F69165, 0xECBE90B2, 
0x2F57FC73, 0xF7D5B53E, 0xE5601A2F, 0xE6E43141, 0xFF000000
+               };
+
+
+       public int test()
+       {
+               byte[]  b;
+               int             res,i;
+
+               b=new byte[1024];
+               for (i=0; i<1024; i++) {
+                       b[i]=(byte) i;
+                       }
+
+               for (i=0; i<1024; i++) {
+                       res=Crypto.crc32(b,i,1024-i);
+                       if (res!=expected[i]) {
+                               suite.err("Error : expected 
0x"+Utils.toHex(expected[i])+" at iteration "+i+", got 0x"+Utils.toHex(res));
+                               return 1;
+                               }
+                       }
+               return 0;
+       }
+}

Added: freeway/src/org/gnu/freeway/test/CharsetTest.java
===================================================================
--- freeway/src/org/gnu/freeway/test/CharsetTest.java   2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/test/CharsetTest.java   2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,89 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.test;
+
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.io.*;
+
+import java.nio.charset.*;
+
+/**
+ *
+ */
+
+public class CharsetTest extends AbstractTest
+{
+       private static final String[]           REF     =       {
+               "line 1",
+               "àéèû",
+               "ßõöçëœ",
+               "end."
+               };
+
+       public int test()
+       {
+               char[]          line;
+               MappedFile      mm;
+               LineDecoder     dec;
+               FileLocation    f;
+               int                     len,i;
+
+               f=new FileLocation("../res/test-charset");
+               if (!f.exists()) {
+                       return -2;
+                       }
+
+               mm=f.open();
+               if (mm==null) {
+                       return -1;
+                       }
+
+               try {
+                       line=new char[512];
+
+                       // advance in file
+                       dec=new 
LineDecoder(mm.asText(Charset.forName("UTF-8")),line);
+                       dec.decode();
+
+                       // should reset position to 0
+                       mm.seekStart();
+                       dec=new 
LineDecoder(mm.asText(Charset.forName("UTF-8")),line);
+
+                       len=dec.decode();
+                       for (i=0; i<4 && len>=0; i++) {
+                               System.out.println("Have read : 
'"+Utils.toHex(new String(line,0,len),true)+"' / 
'"+Utils.toHex(REF[i],true)+"'");
+                               if (!REF[i].equals(new String(line,0,len))) {
+                                       return -(i+2);
+                                       }
+                               len=dec.decode();
+                               }
+
+                       if (i!=4) {
+                               return -6;
+                               }
+
+                       if (len!=-1) {
+                               return -7;
+                               }
+
+                       dec=new 
LineDecoder(mm.asText(Charset.forName("ISO-8859-1")),line);
+                       // first line must be the same
+                       len=dec.decode();
+                       if (len!=REF[0].length()) {
+                               return -8;
+                               }
+
+                       // not encoded the same way for the second
+                       len=dec.decode();
+                       if (len<0 || REF[1].equals(new String(line,0,len))) {
+                               return -9;
+                               }
+                       }
+               finally {
+                       mm.close();
+                       }
+               return 0;
+       }
+}

Added: freeway/src/org/gnu/freeway/test/ConfigTest.java
===================================================================
--- freeway/src/org/gnu/freeway/test/ConfigTest.java    2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/test/ConfigTest.java    2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,63 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.test;
+
+import org.gnu.freeway.util.*;
+
+/**
+ * Test that the configuration module works.
+ */
+
+public class ConfigTest extends AbstractTest
+{
+       private Prefs   prefs;
+
+
+       public ConfigTest()
+       {
+               super();
+       }
+
+       public int test()
+       {
+               prefs=suite.getPreferences();
+               prefs.readConfiguration("tests/test-config");
+
+               if (!prefs.testString("test","a","a")) {
+                       System.out.println("[test]-a not mapped to a");
+                       return 1;
+                       }
+               if (!"b".equals(prefs.getString("test","b",null))) {
+                       System.out.println("[test]-b not mapped to b");
+                       return 1;
+                       }
+               if (5 != prefs.getInt("test","five",0)) {
+                       System.out.println("[test]-five not mapped to 5");
+                       return 1;
+                       }
+               prefs.setString("more","c","d");
+               if (!prefs.testString("more","c","d")) {
+                       System.out.println("[more]-c not re-mapped to d");
+                       return 1;
+                       }
+               if (42 != prefs.getInt("more","five",0)) {
+                       System.out.println("[more]-five not mapped to 42");
+                       return 1;
+                       }
+               if (!prefs.testString("last","test","hello/world")) {
+                       System.out.println("string substitution did not work: 
>>"+prefs.getString("last","test",null)+"<<");
+                       return 1;
+                       }
+               if (!prefs.testString("last","boom","1 2 3 testing")) {
+                       System.out.println("string enclosing with \"'s did not 
work: >>"+prefs.getString("last","boom",null)+"<<");
+                       return 1;
+                       }
+               if (!prefs.testString("last","trailing","YES")) {
+                       System.out.println("confused with trailing spaces: 
>>"+prefs.getString("last","trailing",null)+"<<");
+                       return 1;
+                       }
+               return 0;
+       }
+}

Added: freeway/src/org/gnu/freeway/test/CronTest.java
===================================================================
--- freeway/src/org/gnu/freeway/test/CronTest.java      2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/test/CronTest.java      2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,133 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.test;
+
+import org.gnu.freeway.util.*;
+
+/**
+ * Testcase for cron.
+ */
+
+public class CronTest extends AbstractTest
+{
+       private Scheduler               scheduler;
+       private ScheduledTask           task1;
+       private ScheduledTask           task2;
+       private ScheduledTask           task3;
+       private ScheduledTask           task4;
+       private ScheduledTask           task5;
+       private int                             global1;
+       private int                             global2;
+       private int                             global3;
+
+
+       public CronTest()
+       {
+               super();
+               scheduler=null;
+               task1=new ScheduledTask("JOB1",new 
EvalAction(this,"cronJob1"),Scheduler.SECS_1);
+               task2=new ScheduledTask("JOB2",new 
EvalAction(this,"cronJob2"),Scheduler.SECS_4);
+               task3=new ScheduledTask("JOB3",new 
EvalAction(this,"cronJob3"),Scheduler.SECS_16);
+               task4=new ScheduledTask("JOB1",new 
EvalAction(this,"cronJob1"),42);
+               task5=new ScheduledTask("DELJOB",new EvalAction(this,"delJob"));
+               global1=0;
+               global2=0;
+               global3=0;
+       }
+
+       public int test()
+       {
+               int     count;
+
+               scheduler=suite.getScheduler();
+
+               count=test1();
+               count+=test2();
+               return count;
+       }
+
+       protected int test1()
+       {
+               int i;
+
+               global1 = -1;
+               global2 = -1;
+               global3 = -1;
+               scheduler.addJob(task1,Scheduler.SECS_1);
+               scheduler.addJob(task2,Scheduler.SECS_4);
+               scheduler.addJob(task3,Scheduler.SECS_16);
+               for (i=0;i<10;i++) {
+                       try {
+                               /*    fprintf(stderr,"."); */
+                               Thread.sleep(1000);
+                       }
+                       catch (InterruptedException e) {
+                               e.printStackTrace();
+                       }
+                       if (((global1-i) * (global1-i)) > 9) {
+                               suite.err(" *** 1: Expected "+i+" got 
"+global1);
+                               return 1;
+                               }
+                       if (((global2-(i>>2)) * (global2-(i>>2))) > 9) {
+                               suite.err(" *** 2: Expected "+(i>>2)+" got 
"+global2);
+                               return 1;
+                               }
+                       if (((global3-(i>>4)) * (global3-(i>>4))) > 9) {
+                               suite.err(" *** 3: Expected "+(i>>4)+" got 
"+global3);
+                               return 1;
+                               }
+                       }
+
+               scheduler.deleteJob(task1);
+               scheduler.deleteJob(task2);
+               scheduler.deleteJob(task3);
+               return 0;
+       }
+
+       protected int test2()
+       {
+               global1 = 0;
+
+               scheduler.addJob(task4,Scheduler.SECS_1);
+               scheduler.addJob(task5,Scheduler.MILLIS_500);
+
+               try {
+                       Thread.sleep(1000);
+                       }
+               catch( InterruptedException e ) {
+                       e.printStackTrace();
+                       }
+
+               if (global1 != 0) {
+                       suite.err(" *** Cron job was supposed to be deleted, 
but ran anyway !");
+                       return 1;
+                       }
+               return 0;
+       }
+
+       public void cronJob1()
+       {
+               suite.out("in job1");
+               global1++;
+       }
+
+       public void cronJob2()
+       {
+               suite.out("in job2");
+               global2++;
+       }
+
+       public void cronJob3()
+       {
+               suite.out("in job3");
+               global3++;
+       }
+
+       public void delJob()
+       {
+               suite.out("in deljob");
+               scheduler.deleteJob(task4);
+       }
+}

Added: freeway/src/org/gnu/freeway/test/FilterTest.java
===================================================================
--- freeway/src/org/gnu/freeway/test/FilterTest.java    2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/test/FilterTest.java    2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,45 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.test;
+
+import org.gnu.freeway.util.*;
+
+import java.util.*;
+
+/**
+ *
+ */
+
+public class FilterTest extends AbstractTest
+{
+       public FilterTest()
+       {
+               super();
+       }
+
+       public int test()
+       {
+               OutputFilter    f;
+               Iterator                iter;
+               String                  str;
+
+               f=new 
OutputFilter("^\\S+\\s+\\S+\\s+\\S+\\s+\\S+\\s+(\\d+)\\s+\\S+\\s+(\\d+)\\s+\\S+\\s+\\S+\\s?$",false);
+
+               iter=f.filter("netstat -n -f inet -i");
+               while (iter.hasNext()) {
+                       str=(String) iter.next();
+                       suite.out("NETSTAT: "+str);
+                       }
+
+               f=new OutputFilter("CPU usage:\\s+([0-9]+(\\.[0-9]+)?)% 
user",false);
+
+               iter=f.filter("top -c e -l 1");
+               while (iter.hasNext()) {
+                       str=(String) iter.next();
+                       suite.out("TOP: "+str);
+                       }
+               return 0;
+       }
+}

Added: freeway/src/org/gnu/freeway/test/HashTest.java
===================================================================
--- freeway/src/org/gnu/freeway/test/HashTest.java      2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/test/HashTest.java      2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,57 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.test;
+
+import org.gnu.freeway.util.crypto.*;
+
+import java.util.*;
+
+/**
+ * Testcase for hashcodes.
+ */
+
+public class HashTest extends AbstractTest
+{
+       public HashTest()
+       {
+               super();
+       }
+
+       public int test()
+       {
+               HashCode160     hc;
+               String          hex;
+
+               suite.out("       Available signatures : "+new 
TreeSet(java.security.Security.getAlgorithms("Signature")));
+               suite.out("  Available message digests : "+new 
TreeSet(java.security.Security.getAlgorithms("MessageDigest")));
+               suite.out("          Available ciphers : "+new 
TreeSet(java.security.Security.getAlgorithms("Cipher")));
+               suite.out("             Available macs : "+new 
TreeSet(java.security.Security.getAlgorithms("Mac")));
+               suite.out("        Available keystores : "+new 
TreeSet(java.security.Security.getAlgorithms("KeyStore")));
+
+               hc=HashCode160.create("TEST");
+               if (hc.getA()!=830102737 || hc.getB()!=-2066785626 || 
hc.getC()!=-326698784 || hc.getD()!=-183450437 || hc.getE()!=1019905624) {
+                       System.out.println("Wrong hash for \"TEST\" 
("+hc.getA()+", "+hc.getB()+", "+hc.getC()+", "+hc.getD()+", "+hc.getE()+").");
+                       return -1;
+                       }
+
+               hex=hc.toHex();
+               if (!hex.equals("13A7C51D48FCA56ACE688F0E5F014CBBC3AC6885")) {
+                       System.out.println("Bad hex representation : "+hex);
+                       return -1;
+                       }
+
+               if 
(!hc.equals(HashCode160.parse("13A7C51D48FCA56ACE688F0E5F014CBBC3AC6885"))) {
+                       System.out.println("Failed to correctly parse hex 
string.");
+                       return -1;
+                       }
+
+               hc=HashCode160.create("");
+               if (hc.getA()!=-1676573275 || hc.getB()!=-974521260 || 
hc.getC()!=1630013591 || hc.getD()!=2129196360 || hc.getE()!=-1306161871) {
+                       System.out.println("Wrong hash of nothing 
("+hc.getA()+", "+hc.getB()+", "+hc.getC()+", "+hc.getD()+", "+hc.getE()+").");
+                       return -1;
+                       }
+               return 0;
+       }
+}

Added: freeway/src/org/gnu/freeway/test/LayoutTest.java
===================================================================
--- freeway/src/org/gnu/freeway/test/LayoutTest.java    2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/test/LayoutTest.java    2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,64 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.test;
+
+import org.gnu.freeway.util.ui.*;
+
+import EDU.oswego.cs.dl.util.concurrent.*;
+
+import java.awt.event.*;
+import javax.swing.*;
+
+/**
+ *
+ */
+
+public class LayoutTest extends AbstractTest
+{
+       private Semaphore       sem;
+
+
+       public LayoutTest()
+       {
+               super();
+               sem=new Semaphore(0);
+       }
+
+       public int test() throws InterruptedException
+       {
+               JFrame  frame;
+               JPanel  panel;
+
+               panel=new JPanel(new TileLayout(7,0));
+               panel.add(new JButton("--- 1 ---"),TileLayout.WEST);
+               panel.add(new JButton("2"),TileLayout.EAST);
+               panel.add(new JButton("3"),TileLayout.EAST);
+               panel.add(new JButton("4"),TileLayout.NORTH);
+               panel.add(new JButton("5"),TileLayout.WEST);
+               panel.add(new JButton("*6*"),TileLayout.NORTH);
+               panel.add(new JButton("7"),TileLayout.SOUTH);
+               panel.add(new JButton("8"),TileLayout.SOUTH);
+               panel.add(new JButton("9"),TileLayout.WEST);
+               panel.add(new JButton("10"));
+
+               frame=new JFrame("Test");
+               
frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
+               frame.addWindowListener(new WindowAdapter() {
+                       public void windowClosing( WindowEvent evt )
+                       {
+                               sem.release();
+                       }
+                       });
+               frame.setContentPane(panel);
+               frame.pack();
+               frame.setVisible(true);
+
+               sem.acquire();
+
+               frame.setVisible(false);
+               frame.dispose();
+               return 0;
+       }
+}

Added: freeway/src/org/gnu/freeway/test/MySQLTest.java
===================================================================
--- freeway/src/org/gnu/freeway/test/MySQLTest.java     2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/test/MySQLTest.java     2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,231 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.test;
+
+import org.gnu.freeway.protocol.afs.*;
+import org.gnu.freeway.protocol.afs.esed2.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+
+import java.util.*;
+
+/**
+ *
+ */
+
+public class MySQLTest extends AbstractTest
+{
+
+       private static final byte[]     TEST_ESC        =       new byte[] {
+               (byte) 0x85, (byte) 0x36, (byte) 0x28, (byte) 0x47, (byte) 
0x67, (byte) 0x9f, (byte) 0xa4, (byte) 0x6e,
+               (byte) 0x8a, (byte) 0x70, (byte) 0x48, (byte) 0xa8, (byte) 
0x9b, (byte) 0xba, (byte) 0x34, (byte) 0x3a,
+               (byte) 0x13, (byte) 0x5d, (byte) 0x25, (byte) 0x87, (byte) 
0xa4, (byte) 0x6d, (byte) 0x48, (byte) 0x7f,
+               (byte) 0xfd, (byte) 0xa1, (byte) 0xbb, (byte) 0xb3, (byte) 
0x74, (byte) 0xd9, (byte) 0x98, (byte) 0x3c,
+               (byte) 0xb2, (byte) 0x55, (byte) 0xbd, (byte) 0x64, (byte) 
0x90, (byte) 0xd4, (byte) 0x3d, (byte) 0xaf,
+               (byte) 0xa3, (byte) 0xaa, (byte) 0x50, (byte) 0x7c, (byte) 
0xb4, (byte) 0x63, (byte) 0x14, (byte) 0x97,
+               (byte) 0x88, (byte) 0xf4, (byte) 0x9b, (byte) 0xa5, (byte) 
0x67, (byte) 0x1f, (byte) 0x33, (byte) 0x2f,
+               (byte) 0xae, (byte) 0x99, (byte) 0xb2, (byte) 0x88, (byte) 
0x90, (byte) 0xf6, (byte) 0x40, (byte) 0x4a,
+               (byte) 0x77, (byte) 0x22, (byte) 0xe5, (byte) 0xe6, (byte) 
0xb3, (byte) 0x12, (byte) 0xce, (byte) 0x19,
+               (byte) 0xcf, (byte) 0x68, (byte) 0xc9, (byte) 0x84, (byte) 
0x83, (byte) 0x08, (byte) 0x00, (byte) 0x76,
+               (byte) 0x0c, (byte) 0xcf, (byte) 0x27, (byte) 0x32, (byte) 
0xaf, (byte) 0xf9, (byte) 0x40, (byte) 0xe6,
+               (byte) 0x8f, (byte) 0xd7, (byte) 0xd5, (byte) 0x75, (byte) 
0x1a, (byte) 0xe2, (byte) 0x5c, (byte) 0xc5,
+               (byte) 0xd8, (byte) 0x49, (byte) 0x56, (byte) 0x83, (byte) 
0x16, (byte) 0x8c, (byte) 0xfa, (byte) 0x57,
+               (byte) 0x7a, (byte) 0xa3, (byte) 0xf9, (byte) 0xfe, (byte) 
0xbf, (byte) 0x96, (byte) 0x6b, (byte) 0x18,
+               (byte) 0xb1, (byte) 0x52, (byte) 0xe8, (byte) 0xfe, (byte) 
0xc9, (byte) 0xe6, (byte) 0x1e, (byte) 0xf4,
+               (byte) 0x4c, (byte) 0x99, (byte) 0x9f, (byte) 0x6b, (byte) 
0x10, (byte) 0xba, (byte) 0x95, (byte) 0x0d,
+               (byte) 0xe7, (byte) 0x05, (byte) 0x6e, (byte) 0x31, (byte) 
0x3b, (byte) 0x7d, (byte) 0xcc, (byte) 0xe4,
+               (byte) 0x62, (byte) 0x38, (byte) 0x4b, (byte) 0x21, (byte) 
0xbb, (byte) 0x43, (byte) 0xab, (byte) 0x23,
+               (byte) 0xed, (byte) 0x2e, (byte) 0xd5, (byte) 0x4a, (byte) 
0x90, (byte) 0xfd, (byte) 0xac, (byte) 0x31,
+               (byte) 0x31, (byte) 0x89, (byte) 0xb0, (byte) 0x8d, (byte) 
0x50, (byte) 0x3c, (byte) 0xd9, (byte) 0x59,
+               (byte) 0x69, (byte) 0xd9, (byte) 0x44, (byte) 0xae, (byte) 
0x24, (byte) 0x97, (byte) 0xb3, (byte) 0x15,
+               (byte) 0xdd, (byte) 0xbe, (byte) 0xcd, (byte) 0x22, (byte) 
0xc5, (byte) 0xab, (byte) 0x8b, (byte) 0xd8,
+               (byte) 0xc7, (byte) 0x39, (byte) 0x90, (byte) 0x8d, (byte) 
0x2e, (byte) 0x64, (byte) 0xa5, (byte) 0x1b,
+               (byte) 0x47, (byte) 0x54, (byte) 0x31, (byte) 0x36, (byte) 
0x26, (byte) 0x10, (byte) 0x82, (byte) 0x54,
+               (byte) 0xd8, (byte) 0x86, (byte) 0xab, (byte) 0xa6, (byte) 
0x61, (byte) 0x4d, (byte) 0x77, (byte) 0x45,
+               (byte) 0x51, (byte) 0xe1, (byte) 0xb1, (byte) 0x76, (byte) 
0x8a, (byte) 0xd8, (byte) 0x28, (byte) 0x5f,
+               (byte) 0x90, (byte) 0x6c, (byte) 0x79, (byte) 0x60, (byte) 
0x6f, (byte) 0x90, (byte) 0x24, (byte) 0xfd,
+               (byte) 0xed, (byte) 0x1c, (byte) 0x6c, (byte) 0x8c, (byte) 
0xdb, (byte) 0xdd, (byte) 0x98, (byte) 0xf5,
+               (byte) 0x3b, (byte) 0x60, (byte) 0x09, (byte) 0x98, (byte) 
0x3c, (byte) 0x2e, (byte) 0x6b, (byte) 0x76,
+               (byte) 0x3d, (byte) 0xfc, (byte) 0x7f, (byte) 0x8b, (byte) 
0xa5, (byte) 0x86, (byte) 0x70, (byte) 0x82,
+               (byte) 0x80, (byte) 0x69, (byte) 0xb8, (byte) 0xfb, (byte) 
0x38, (byte) 0xcf, (byte) 0x64, (byte) 0xaa,
+               (byte) 0xfb, (byte) 0xca, (byte) 0xc3, (byte) 0xaf, (byte) 
0x11, (byte) 0x71, (byte) 0x3c, (byte) 0x6f,
+               (byte) 0x30, (byte) 0xf3, (byte) 0x93, (byte) 0xa8, (byte) 
0x98, (byte) 0x21, (byte) 0x53, (byte) 0x0c,
+               (byte) 0x76, (byte) 0x0b, (byte) 0x17, (byte) 0x77, (byte) 
0x66, (byte) 0xf8, (byte) 0xe7, (byte) 0xee,
+               (byte) 0x25, (byte) 0x59, (byte) 0x95, (byte) 0xa1, (byte) 
0x8e, (byte) 0x8f, (byte) 0x05, (byte) 0xc7,
+               (byte) 0xaf, (byte) 0x29, (byte) 0x36, (byte) 0x21, (byte) 
0xaa, (byte) 0xcb, (byte) 0xc7, (byte) 0x15,
+               (byte) 0xef, (byte) 0xaa, (byte) 0x18, (byte) 0xfc, (byte) 
0x0c, (byte) 0xf5, (byte) 0x36, (byte) 0xbb,
+               (byte) 0x8b, (byte) 0x7f, (byte) 0x9a, (byte) 0xbc, (byte) 
0x21, (byte) 0x57, (byte) 0xa6, (byte) 0xec,
+               (byte) 0x31, (byte) 0x58, (byte) 0x4c, (byte) 0xb2, (byte) 
0x00, (byte) 0x7f, (byte) 0xf4, (byte) 0x7d,
+               (byte) 0xa1, (byte) 0xa6, (byte) 0xcd, (byte) 0xea, (byte) 
0x0c, (byte) 0xc6, (byte) 0xf8, (byte) 0x1e,
+               (byte) 0x95, (byte) 0x53, (byte) 0xf2, (byte) 0x17, (byte) 
0xea, (byte) 0x5e, (byte) 0xd1, (byte) 0x38,
+               (byte) 0xc8, (byte) 0x3e, (byte) 0x40, (byte) 0xac, (byte) 
0x9a, (byte) 0xf5, (byte) 0x99, (byte) 0x5c,
+               (byte) 0x2e, (byte) 0x3a, (byte) 0x29, (byte) 0x7e, (byte) 
0x81, (byte) 0xf4, (byte) 0x98, (byte) 0xa9,
+               (byte) 0xcb, (byte) 0xde, (byte) 0xd7, (byte) 0x9f, (byte) 
0xc8, (byte) 0x73, (byte) 0x0d, (byte) 0xec,
+               (byte) 0xb6, (byte) 0xb2, (byte) 0x90, (byte) 0x79, (byte) 
0x6a, (byte) 0x7a, (byte) 0x04, (byte) 0x05,
+               (byte) 0x19, (byte) 0xa8, (byte) 0x78, (byte) 0xdc, (byte) 
0xb5, (byte) 0xb8, (byte) 0x39, (byte) 0x87,
+               (byte) 0xf8, (byte) 0x6e, (byte) 0x4c, (byte) 0x92, (byte) 
0x95, (byte) 0xbb, (byte) 0x6e, (byte) 0x39,
+               (byte) 0xc3, (byte) 0xd0, (byte) 0x7a, (byte) 0xaf, (byte) 
0x66, (byte) 0xb4, (byte) 0x7e, (byte) 0xac,
+               (byte) 0x15, (byte) 0x48, (byte) 0xe8, (byte) 0xb6, (byte) 
0xcc, (byte) 0x0f, (byte) 0x90, (byte) 0xec,
+               (byte) 0x0f, (byte) 0xce, (byte) 0x5e, (byte) 0x83, (byte) 
0xef, (byte) 0x2d, (byte) 0x5f, (byte) 0xe0,
+               (byte) 0x34, (byte) 0xe0, (byte) 0x55, (byte) 0x7b, (byte) 
0xc1, (byte) 0x2a, (byte) 0xd5, (byte) 0x44,
+               (byte) 0xa1, (byte) 0xe7, (byte) 0x25, (byte) 0x20, (byte) 
0xfa, (byte) 0x1b, (byte) 0x7a, (byte) 0x50,
+               (byte) 0x17, (byte) 0x3d, (byte) 0x74, (byte) 0xbd, (byte) 
0xa2, (byte) 0xa2, (byte) 0xa3, (byte) 0x09,
+               (byte) 0x1e, (byte) 0x76, (byte) 0x5c, (byte) 0x27, (byte) 
0x56, (byte) 0xdb, (byte) 0xcc, (byte) 0x99,
+               (byte) 0x9f, (byte) 0x7f, (byte) 0x7d, (byte) 0x41, (byte) 
0x56, (byte) 0xc6, (byte) 0xe7, (byte) 0x9b,
+               (byte) 0x50, (byte) 0x0f, (byte) 0xdb, (byte) 0xc2, (byte) 
0xbd, (byte) 0x00, (byte) 0x32, (byte) 0x2e,
+               (byte) 0x98, (byte) 0xbc, (byte) 0x0b, (byte) 0x71, (byte) 
0x62, (byte) 0x8b, (byte) 0xa4, (byte) 0xef,
+               (byte) 0x97, (byte) 0x2d, (byte) 0x01, (byte) 0x05, (byte) 
0x05, (byte) 0xdc, (byte) 0x61, (byte) 0x18,
+               (byte) 0x20, (byte) 0xc1, (byte) 0x9f, (byte) 0xaa, (byte) 
0x11, (byte) 0xdc, (byte) 0xed, (byte) 0xba,
+               (byte) 0x49, (byte) 0x15, (byte) 0xb3, (byte) 0x41, (byte) 
0xee, (byte) 0xd9, (byte) 0x9b, (byte) 0x80,
+               (byte) 0xbf, (byte) 0x85, (byte) 0x54, (byte) 0x65, (byte) 
0xca, (byte) 0xa4, (byte) 0x2c, (byte) 0x18,
+               (byte) 0x4f, (byte) 0x62, (byte) 0x42, (byte) 0xe4, (byte) 
0xc9, (byte) 0xb6, (byte) 0xcc, (byte) 0xf0,
+               (byte) 0x28, (byte) 0x4c, (byte) 0xc7, (byte) 0xc6, (byte) 
0xb4, (byte) 0x34, (byte) 0x55, (byte) 0x0e,
+               (byte) 0x81, (byte) 0xa6, (byte) 0x0e, (byte) 0x65, (byte) 
0xc5, (byte) 0x3d, (byte) 0x91, (byte) 0xa9,
+               (byte) 0x5e, (byte) 0x38, (byte) 0x63, (byte) 0x14, (byte) 
0x67, (byte) 0x19, (byte) 0xa0, (byte) 0xcc,
+               (byte) 0x2f, (byte) 0xa4, (byte) 0xc6, (byte) 0xa2, (byte) 
0xdf, (byte) 0xbd, (byte) 0x0b, (byte) 0x6e,
+               (byte) 0xc7, (byte) 0xae, (byte) 0x79, (byte) 0x84, (byte) 
0x04, (byte) 0x7b, (byte) 0x84, (byte) 0xf0,
+               (byte) 0xf9, (byte) 0x6a, (byte) 0x08, (byte) 0xd4, (byte) 
0x14, (byte) 0x5c, (byte) 0x8e, (byte) 0x36,
+               (byte) 0x96, (byte) 0x62, (byte) 0x0c, (byte) 0xb4, (byte) 
0xbc, (byte) 0x7d, (byte) 0xd4, (byte) 0xbd,
+               (byte) 0xe8, (byte) 0x67, (byte) 0x7e, (byte) 0xac, (byte) 
0x28, (byte) 0x30, (byte) 0xc9, (byte) 0x30,
+               (byte) 0x00, (byte) 0x42, (byte) 0x9d, (byte) 0x9f, (byte) 
0x39, (byte) 0x1e, (byte) 0x34, (byte) 0x26,
+               (byte) 0xf5, (byte) 0x11, (byte) 0xbc, (byte) 0x2e, (byte) 
0x80, (byte) 0x86, (byte) 0x83, (byte) 0x18,
+               (byte) 0xe8, (byte) 0xce, (byte) 0x9e, (byte) 0x8c, (byte) 
0x3a, (byte) 0x3b, (byte) 0xab, (byte) 0xa7,
+               (byte) 0x56, (byte) 0x8c, (byte) 0x13, (byte) 0xc5, (byte) 
0xed, (byte) 0xd4, (byte) 0xe1, (byte) 0xa1,
+               (byte) 0x31, (byte) 0x9f, (byte) 0x8a, (byte) 0xde, (byte) 
0x0d, (byte) 0xfd, (byte) 0x48, (byte) 0xe6,
+               (byte) 0xad, (byte) 0x38, (byte) 0x59, (byte) 0xad, (byte) 
0x99, (byte) 0x35, (byte) 0x41, (byte) 0x24,
+               (byte) 0x58, (byte) 0x8b, (byte) 0xd0, (byte) 0x9e, (byte) 
0x55, (byte) 0x8e, (byte) 0xe5, (byte) 0xd5,
+               (byte) 0xc6, (byte) 0xdf, (byte) 0xf3, (byte) 0xf9, (byte) 
0xf4, (byte) 0x81, (byte) 0x16, (byte) 0x1b,
+               (byte) 0x97, (byte) 0x87, (byte) 0x9a, (byte) 0xda, (byte) 
0xf4, (byte) 0xba, (byte) 0xb3, (byte) 0xf5,
+               (byte) 0x1f, (byte) 0x93, (byte) 0x10, (byte) 0xce, (byte) 
0xa7, (byte) 0x5f, (byte) 0x0e, (byte) 0x8a,
+               (byte) 0x1b, (byte) 0x3f, (byte) 0x42, (byte) 0x61, (byte) 
0xcc, (byte) 0xfc, (byte) 0x54, (byte) 0x83,
+               (byte) 0x28, (byte) 0x05, (byte) 0x25, (byte) 0x20, (byte) 
0x38, (byte) 0x0c, (byte) 0x6c, (byte) 0xcf,
+               (byte) 0x80, (byte) 0x3d, (byte) 0x1b, (byte) 0x5c, (byte) 
0x6c, (byte) 0xc7, (byte) 0x19, (byte) 0xf4,
+               (byte) 0xcb, (byte) 0x26, (byte) 0x46, (byte) 0xb1, (byte) 
0x2a, (byte) 0x34, (byte) 0x3b, (byte) 0xc4,
+               (byte) 0xa4, (byte) 0x86, (byte) 0x07, (byte) 0x78, (byte) 
0xf8, (byte) 0x32, (byte) 0x2d, (byte) 0x29,
+               (byte) 0xdb, (byte) 0x0b, (byte) 0xd4, (byte) 0xbe, (byte) 
0x4c, (byte) 0x84, (byte) 0x01, (byte) 0xd1,
+               (byte) 0xe7, (byte) 0x53, (byte) 0x72, (byte) 0xb1, (byte) 
0x9c, (byte) 0xd8, (byte) 0xee, (byte) 0x83,
+               (byte) 0xb9, (byte) 0x08, (byte) 0x37, (byte) 0xb4, (byte) 
0xd4, (byte) 0x17, (byte) 0xd9, (byte) 0x55,
+               (byte) 0x45, (byte) 0xce, (byte) 0xa4, (byte) 0x73, (byte) 
0x9e, (byte) 0xdc, (byte) 0x8c, (byte) 0x67,
+               (byte) 0xfd, (byte) 0xd4, (byte) 0x76, (byte) 0xd1, (byte) 
0x5a, (byte) 0xff, (byte) 0x06, (byte) 0xa4,
+               (byte) 0x60, (byte) 0x46, (byte) 0x86, (byte) 0xbb, (byte) 
0x26, (byte) 0xa9, (byte) 0x1f, (byte) 0xa2,
+               (byte) 0x01, (byte) 0x74, (byte) 0x39, (byte) 0xab, (byte) 
0x1e, (byte) 0xde, (byte) 0xbd, (byte) 0x06,
+               (byte) 0x6c, (byte) 0x60, (byte) 0xf2, (byte) 0xa8, (byte) 
0x78, (byte) 0xf2, (byte) 0xd2, (byte) 0x1d,
+               (byte) 0x95, (byte) 0xb3, (byte) 0x8c, (byte) 0xf9, (byte) 
0x58, (byte) 0x96, (byte) 0xc3, (byte) 0x23,
+               (byte) 0x55, (byte) 0x46, (byte) 0xcb, (byte) 0x0b, (byte) 
0xc9, (byte) 0xdc, (byte) 0x04, (byte) 0x5e,
+               (byte) 0xb3, (byte) 0xd6, (byte) 0xe7, (byte) 0x82, (byte) 
0xaa, (byte) 0x1d, (byte) 0x03, (byte) 0x9f,
+               (byte) 0x09, (byte) 0x53, (byte) 0x72, (byte) 0xc9, (byte) 
0x53, (byte) 0x52, (byte) 0x1d, (byte) 0x0d,
+               (byte) 0x7c, (byte) 0xb8, (byte) 0xe2, (byte) 0x7a, (byte) 
0x0b, (byte) 0xbb, (byte) 0x5a, (byte) 0xa2,
+               (byte) 0x81, (byte) 0xa9, (byte) 0xd5, (byte) 0x3b, (byte) 
0x80, (byte) 0x9a, (byte) 0xbc, (byte) 0x47,
+               (byte) 0x56, (byte) 0xa0, (byte) 0x01, (byte) 0xab, (byte) 
0xb7, (byte) 0xa6, (byte) 0x45, (byte) 0xe0,
+               (byte) 0x9b, (byte) 0xae, (byte) 0x34, (byte) 0x47, (byte) 
0x01, (byte) 0xe7, (byte) 0xa8, (byte) 0x0f,
+               (byte) 0x84, (byte) 0xee, (byte) 0x59, (byte) 0xfb, (byte) 
0xa7, (byte) 0x78, (byte) 0x0f, (byte) 0x75,
+               (byte) 0x3e, (byte) 0xfb, (byte) 0x82, (byte) 0x4f, (byte) 
0x3b, (byte) 0x03, (byte) 0x3c, (byte) 0x14,
+               (byte) 0x2c, (byte) 0x1a, (byte) 0x98, (byte) 0x78, (byte) 
0xe0, (byte) 0x9d, (byte) 0x94, (byte) 0x12,
+               (byte) 0x44, (byte) 0x07, (byte) 0x33, (byte) 0xc8, (byte) 
0xb9, (byte) 0x67, (byte) 0x23, (byte) 0x76,
+               (byte) 0x02, (byte) 0x36, (byte) 0x67, (byte) 0xae, (byte) 
0x00, (byte) 0x68, (byte) 0x75, (byte) 0xb3,
+               (byte) 0x0d, (byte) 0x6a, (byte) 0x8d, (byte) 0xc3, (byte) 
0x22, (byte) 0x6d, (byte) 0xad, (byte) 0x98,
+               (byte) 0xd1, (byte) 0x43, (byte) 0x67, (byte) 0xc5, (byte) 
0x73, (byte) 0x1b, (byte) 0x9e, (byte) 0x51,
+               (byte) 0xe3, (byte) 0xbe, (byte) 0x83, (byte) 0xb6, (byte) 
0xc8, (byte) 0x1f, (byte) 0x62, (byte) 0xb9,
+               (byte) 0xe2, (byte) 0xe1, (byte) 0x32, (byte) 0xce, (byte) 
0x92, (byte) 0x19, (byte) 0xb8, (byte) 0x9d,
+               (byte) 0xb2, (byte) 0x41, (byte) 0xd5, (byte) 0x5c, (byte) 
0x0f, (byte) 0x09, (byte) 0x06, (byte) 0x2f,
+               (byte) 0x83, (byte) 0x71, (byte) 0xbf, (byte) 0x95, (byte) 
0xed, (byte) 0xce, (byte) 0x45, (byte) 0xca,
+               (byte) 0xdc, (byte) 0x42, (byte) 0xf3, (byte) 0xbf, (byte) 
0x4a, (byte) 0x70, (byte) 0x41, (byte) 0xe2,
+               (byte) 0x01, (byte) 0x53, (byte) 0x61, (byte) 0xba, (byte) 
0x9f, (byte) 0xfe, (byte) 0xf0, (byte) 0x53,
+               (byte) 0x19, (byte) 0x6a, (byte) 0x66, (byte) 0xb6, (byte) 
0xa6, (byte) 0xae, (byte) 0xc8, (byte) 0x2c,
+               (byte) 0x75, (byte) 0xc5, (byte) 0xe1, (byte) 0x20, (byte) 
0x69, (byte) 0xc8, (byte) 0x64, (byte) 0x49,
+               (byte) 0x99, (byte) 0x5d, (byte) 0x00, (byte) 0xe5, (byte) 
0x48, (byte) 0x72, (byte) 0xee, (byte) 0x15,
+               (byte) 0x2f, (byte) 0x91, (byte) 0xe2, (byte) 0x6e, (byte) 
0x40, (byte) 0x3f, (byte) 0x82, (byte) 0x5b,
+               (byte) 0xef, (byte) 0x1c, (byte) 0x15, (byte) 0x32, (byte) 
0xeb, (byte) 0xda, (byte) 0x93, (byte) 0x8f,
+               (byte) 0x95, (byte) 0x2c, (byte) 0x94, (byte) 0x07, (byte) 
0xcf, (byte) 0xc1, (byte) 0x5d, (byte) 0x58,
+               (byte) 0x69, (byte) 0xa2, (byte) 0x41, (byte) 0x69, (byte) 
0xd0, (byte) 0x8e, (byte) 0x03, (byte) 0x34,
+               (byte) 0xcb, (byte) 0x6b, (byte) 0xc8, (byte) 0xed, (byte) 
0x8c, (byte) 0x88, (byte) 0x30, (byte) 0x14,
+               (byte) 0x7b, (byte) 0x2b, (byte) 0x37, (byte) 0xd8, (byte) 
0x4e, (byte) 0x17, (byte) 0x42, (byte) 0x90,
+               (byte) 0x48, (byte) 0x97, (byte) 0x14, (byte) 0xbc, (byte) 
0xdd, (byte) 0x53, (byte) 0xe3, (byte) 0xc5,
+               (byte) 0xf3, (byte) 0x16, (byte) 0xaa, (byte) 0x91, (byte) 
0x8e, (byte) 0x4b, (byte) 0x95, (byte) 0x6c,
+               (byte) 0x7b, (byte) 0x6d, (byte) 0xfd, (byte) 0x41, (byte) 
0xba, (byte) 0x4d, (byte) 0x21, (byte) 0x83,
+               (byte) 0x41, (byte) 0x86, (byte) 0xcd, (byte) 0x5b, (byte) 
0xdf, (byte) 0xc0, (byte) 0x16, (byte) 0x44,
+               (byte) 0xcc, (byte) 0x33, (byte) 0xaf, (byte) 0x9a, (byte) 
0xed, (byte) 0x7b, (byte) 0x9a, (byte) 0xc9
+               };
+
+       private MySQLHandle     handle;
+
+
+       public MySQLTest()
+       {
+               super();
+       }
+
+       public int test()
+       {
+               byte[]  b;
+               Prefs   prefs;
+               int             ret,i;
+
+               prefs=suite.getPreferences();
+               
prefs.setString("AFS","MYSQL_DRIVER",prefs.getString("TEST","MYSQL_DRIVER",null));
+               
prefs.setString("AFS","MYSQL_HOST",prefs.getString("TEST","MYSQL_HOST",null));
+               
prefs.setString("AFS","MYSQL_USER",prefs.getString("TEST","MYSQL_USER",null));
+               
prefs.setString("AFS","MYSQL_PASSWORD",prefs.getString("TEST","MYSQL_PASSWORD",null));
+               prefs.setString("AFS","MYSQL_DELAYED","NO");
+
+               handle=new MySQLHandle();
+               if (!handle.open(prefs,"AAA","BBB")) {
+                       handle.close();
+                       return 1;
+                       }
+
+               b=new byte[4096];
+               for (i=0; i<b.length; i++) {
+                       b[i]=(byte) i;
+                       }
+
+               suite.out("Sequence #1");
+               ret=testSeq(b);
+               if (ret!=0) {
+                       return ret;
+                       }
+
+               suite.out("Sequence #2");
+               ret=testSeq(TEST_ESC);
+               if (ret!=0) {
+                       return ret;
+                       }
+
+//             handle.drop();
+               return 0;
+       }
+
+       protected int testSeq( byte[] b )
+       {
+               byte[]                  bb;
+               ContentIndex    ce,cee;
+
+               ce=new ContentIndex();
+               ce.hash=HashCode160.random();
+               ce.importance=0;
+               ce.type=ContentIndex.LOOKUP_TYPE_FREE;
+               ce.fileNameIndex=0;
+               ce.fileOffset=0;
+
+               if (!handle.writeContent(ce,b,0,b.length)) {
+                       return 2;
+                       }
+
+               cee=new ContentIndex();
+               bb=handle.readContent(ce.hash,cee,0);
+               if (!cee.equals(ce)) {
+                       return 3;
+                       }
+
+               if (bb==null) {
+                       return 4;
+                       }
+
+               if (!Arrays.equals(bb,b)) {
+                       return 5;
+                       }
+               return 0;
+       }
+}

Added: freeway/src/org/gnu/freeway/test/RSATest.java
===================================================================
--- freeway/src/org/gnu/freeway/test/RSATest.java       2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/test/RSATest.java       2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,112 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.test;
+
+import org.gnu.freeway.util.crypto.*;
+
+/**
+ * Testcase for RSA public key cryptography.
+ */
+
+public class RSATest extends AbstractTest
+{
+       private static final String     TESTSTRING      =       "Hello, world 
!";
+       private static final int        MAX_TESTVAL     =       20;
+       private static final int        ITER            =       100;
+
+
+       public RSATest()
+       {
+               super();
+       }
+
+       public int test() throws Throwable
+       {
+               int     failureCount;
+
+               failureCount=0;
+               failureCount+=testEncryptDecrypt();
+               failureCount+=testSignVerify();
+               return failureCount;
+       }
+
+       protected int testEncryptDecrypt()
+       {
+               PrivateKey              priv;
+               PublicKey               pub;
+               EncryptedData   target=new EncryptedData();
+               byte[]                          result=new byte[MAX_TESTVAL];
+               int                                     i;
+               long                            start;
+               int                                     ok;
+
+               System.err.println("Create key pair");
+               priv=PrivateKey.create();
+               pub=priv.toPublicKey();
+
+               ok = 0;
+
+               start=System.currentTimeMillis();
+               for (i=0; i<ITER; i++) {
+                       System.err.println(" "+(i+1)+"/"+ITER);
+
+                       target=pub.encrypt(TESTSTRING);
+                       if (target==null) {
+                               System.err.println("Failed to encrypt string");
+                               ok++;
+                               continue;
+                               }
+
+                       if (-1 == priv.decrypt(target,result,MAX_TESTVAL)) {
+                               System.err.print("Failed to decrypt data");
+                               ok++;
+                               continue;
+                               }
+
+                       if (!new 
String(result,0,TESTSTRING.length()).equals(TESTSTRING)) {
+                               System.out.println(TESTSTRING+" != "+new 
String(result,0,MAX_TESTVAL)+" - testEncryptDecrypt failed !");
+                               ok++;
+                               continue;
+                               }
+                       }
+               System.out.println(ITER+" RSA encrypt/decrypt operations 
"+(System.currentTimeMillis()-start)+"ms ("+ok+" failures)");
+               return ok;
+       }
+
+       protected int testSignVerify()
+       {
+               PrivateKey priv;
+               PublicKey       pub;
+               org.gnu.freeway.util.crypto.Signature sig=new 
org.gnu.freeway.util.crypto.Signature();
+               int i;
+               long start;
+               int     ok;
+
+               System.err.println("Create key pair");
+               priv = PrivateKey.create();
+               pub=priv.toPublicKey();
+
+               ok=0;
+               start=System.currentTimeMillis();
+               for (i=0; i<ITER; i++) {
+                       System.err.println(" "+(i+1)+"/"+ITER);
+
+                       sig=priv.sign(TESTSTRING);
+                       if (sig==null) {
+                               System.err.println("Failed to sign string");
+                               ok++;
+                               continue;
+                               }
+                       if (!pub.verify(sig,TESTSTRING)) {
+                               System.out.println("Failed to verify generated 
signature");
+                               ok++;
+                               continue;
+                               }
+                       }
+               System.out.println(ITER+" RSA sign/verify operations 
"+(System.currentTimeMillis()-start)+"ms ("+ok+" failures)");
+               return ok;
+       }
+}
+

Added: freeway/src/org/gnu/freeway/test/SemaphoreTest.java
===================================================================
--- freeway/src/org/gnu/freeway/test/SemaphoreTest.java 2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/test/SemaphoreTest.java 2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,216 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.test;
+
+import org.gnu.freeway.util.*;
+
+import EDU.oswego.cs.dl.util.concurrent.*;
+
+/**
+ * testcase for util/semaphore.c
+ */
+
+public class SemaphoreTest extends AbstractTest
+{
+       private Sync            lock;
+       private Semaphore       sem;
+
+       private volatile int    sv;
+       private volatile int    tv;
+
+
+       public int test() throws Throwable
+       {
+               int ret = 0;
+
+               ret += testPTHREAD_CREATE();
+               ret += testMutex();
+               ret += testRecursiveMutex();
+               ret += testSemaphore();
+               return ret;
+       }
+
+       protected void lockIt() throws InterruptedException
+       {
+               sv = 0;
+
+               suite.out(".");
+
+               while (sv == 0) {
+                       Scheduler.sleep(Scheduler.MILLIS_50);   // busy waiting 
may not always work
+                       }
+               lock.acquire();
+               sv = 1;
+               lock.release();
+               sv = 2;
+               tv = 2;
+       }
+
+       protected int testPTHREAD_CREATE()
+       {
+               Task            pt;
+
+               sv = -1;
+               tv = 0;
+
+               suite.out(".");
+
+               lock=new Mutex();
+
+               pt=new Task("THREAD-TEST",new AbstractAction() {
+                       public void perform() throws InterruptedException
+                       {
+                               lockIt();
+                       }
+                       });
+               pt.launch();
+
+               while (tv != 2) {
+                       sv = 1;
+                       Scheduler.sleep(Scheduler.MILLIS_50);   // busy waiting 
may not always work
+                       }
+
+               pt.join();
+               return 0;
+       }
+
+       protected int testMutex() throws InterruptedException
+       {
+               Task            pt;
+
+               lock=new Mutex();
+
+               sv = 1;
+               tv = 0;
+
+               pt=new Task("MUTEX-TEST",new AbstractAction() {
+                       public void perform() throws InterruptedException
+                       {
+                               lockIt();
+                       }
+                       });
+               pt.launch();
+
+               while (sv == 1) {
+                       Scheduler.sleep(Scheduler.MILLIS_50);   // busy waiting 
may not always work
+                       }
+
+               lock.acquire();
+               sv = 5; /* release lockIt from while sv==0 loop, blocks it on 
lock */
+               suite.out(".");
+
+               if (sv != 5) {
+                       lock.release();
+
+                       while (tv != 2) {
+                               Scheduler.sleep(Scheduler.MILLIS_50);   // busy 
waiting may not always work
+                               }
+
+                       suite.trace("MUTEX test failed");
+                       return 1; /* error */
+                       }
+
+               lock.release();
+               while (tv != 2) {
+                       Scheduler.sleep(Scheduler.MILLIS_50);   // busy waiting 
may not always work
+                       }
+               pt.join();
+               return 0; /* ok */
+       }
+
+       protected int testRecursiveMutex() throws InterruptedException
+       {
+               int i;
+
+               suite.out(".");
+
+               lock=new ReentrantLock();
+               for (i=0; i<50; i++) {
+                       lock.acquire();
+                       }
+               for (i=0; i<50; i++) {
+                       lock.release();
+                       }
+               return 0; /* ok -- fails by hanging!*/
+       }
+
+       protected void semUpDown() throws InterruptedException
+       {
+               int i;
+
+               suite.out(".");
+
+               for (i=0; i<42; i++) {
+                       sem.acquire();  // fails by blocking
+                       }
+
+               if (sem.attempt(0)) {
+                       suite.trace("SEMAPHORE_DOWN_NONBLOCKING failed, 
Testcase deadlocked.");
+                       return; /* will halt testcase! */
+                       }
+
+               for (i=0; i<42; i++) {
+                       sem.release();
+                       }
+       }
+
+       protected int testSemaphore() throws InterruptedException
+       {
+               Task            pt;
+               int             i;
+
+               sem = new Semaphore(42);
+
+               suite.out(".");
+
+               for (i=0; i<42; i++) {
+                       sem.acquire();  // fails by blocking
+                       }
+
+               if (sem.attempt(0)) {
+                       suite.trace("SEMAPHORE_DOWN_NONBLOCKING failed");
+                       return 1;
+                       }
+
+               for (i=0; i<42; i++) {
+                       sem.release();
+                       }
+
+               for (i=0; i<42; i++) {
+                       if (!sem.attempt(0)) {
+                               suite.trace("SEMAPHORE_DOWN_NONBLOCKING 
failed");
+                               return 1;
+                               }
+                       }
+
+               if (sem.attempt(0)) {
+                       suite.trace("SEMAPHORE_DOWN_NONBLOCKING failed");
+                       return 1;
+                       }
+
+               suite.out(".");
+
+               pt=new Task("SEMAPHORE-TEST",new AbstractAction() {
+                       public void perform() throws InterruptedException
+                       {
+                               semUpDown();
+                       }
+                       });
+               pt.launch();
+
+               for (i=0; i<42; i++) {
+                       sem.release();
+                       }
+               pt.join();
+               for (i=0; i<42; i++) {
+                       sem.acquire();
+                       }
+               if (sem.attempt(0)) {
+                       suite.trace("SEMAPHORE_DOWN_NONBLOCKING failed");
+                       return 1;
+                       }
+               return 0;
+       }
+}

Added: freeway/src/org/gnu/freeway/test/ShutdownTest.java
===================================================================
--- freeway/src/org/gnu/freeway/test/ShutdownTest.java  2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/test/ShutdownTest.java  2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,39 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.test;
+
+import org.gnu.freeway.util.*;
+
+/**
+ * Testcase for shutdown mechanism.
+ */
+
+public class ShutdownTest extends AbstractTest
+{
+       public ShutdownTest()
+       {
+               super();
+       }
+
+       public int test()
+       {
+               SignalManager   mgr;
+
+               if (suite.isShutdowning()) {
+                       return 1;
+                       }
+
+               // test SIGINT (simulated)
+               mgr=SignalManager.getInstance();
+               mgr.signal(mgr.getPID(),Signals.SIGINT);
+
+               if (!suite.isShutdowning()) {
+                       return 2;
+                       }
+
+               suite.waitForShutdown();
+               return 0;
+       }
+}

Added: freeway/src/org/gnu/freeway/test/SignalsTest.java
===================================================================
--- freeway/src/org/gnu/freeway/test/SignalsTest.java   2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/test/SignalsTest.java   2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,50 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.test;
+
+import org.gnu.freeway.util.*;
+
+/**
+ *
+ */
+
+public class SignalsTest extends AbstractTest implements Signals
+{
+       public int test()
+       {
+               SignalListener  listener;
+               SignalManager   mgr;
+
+               listener=new SignalListener() {
+                       public void signalReceived( SignalEvent evt )
+                       {
+                               suite.out("*** "+evt);
+                       }
+                       };
+
+
+               suite.out("*** TEST");
+
+               mgr=SignalManager.getInstance();
+               mgr.addSignalListener(SIGEMT,listener);
+               mgr.addSignalListener(SIGBUS,listener);
+               mgr.addSignalListener(SIGSYS,listener);
+               mgr.addSignalListener(SIGCHLD,listener);
+
+               Scheduler.sleep(Scheduler.SECS_3);
+
+               mgr.signal(SIGEMT);
+               Scheduler.sleep(Scheduler.MILLIS_500);
+
+               mgr.signal(SIGBUS);
+               Scheduler.sleep(Scheduler.MILLIS_500);
+
+               mgr.signal(SIGSYS);
+               Scheduler.sleep(Scheduler.SECS_3);
+
+               suite.out("*** FIN TEST");
+               return 0;
+       }
+}

Added: freeway/src/org/gnu/freeway/test/StateTest.java
===================================================================
--- freeway/src/org/gnu/freeway/test/StateTest.java     2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/test/StateTest.java     2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,76 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.test;
+
+import org.gnu.freeway.util.*;
+
+import java.nio.*;
+import java.util.*;
+
+/**
+ * testcase for the state module
+ */
+
+public class StateTest extends AbstractTest
+{
+       private static final String     TH              =       "TestHandle";
+       private static final byte[]     TEST            =       "Hello 
World".getBytes();
+
+       private Prefs   prefs;
+
+
+       public StateTest()
+       {
+               super();
+               prefs=suite.getPreferences();
+       }
+
+       public int test()
+       {
+               byte[]          b;
+               ByteBuffer      buf;
+               String          str;
+
+               // go to defined state
+               prefs.unlink(TH);
+
+               if (!prefs.putContent(TH,TEST,0,5)) {
+                       return 1;
+                       }
+
+               if (!prefs.appendContent(TH,TEST,5,TEST.length-5)) {
+                       return 2;
+                       }
+
+               buf=prefs.getContent(TH);
+               if (buf==null || buf.capacity()!=TEST.length) {
+                       return 3;
+                       }
+
+               b=new byte[TEST.length];
+               buf.get(b);
+               if (!Arrays.equals(TEST,b)) {
+                       return 4;
+                       }
+
+               if (!prefs.putContent(TH,new String(TEST))) {
+                       return 5;
+                       }
+
+               str=prefs.getContentAsString(TH);
+               if (str==null) {
+                       return 6;
+                       }
+
+               if (!str.equals(new String(TEST))) {
+                       return 7;
+                       }
+
+               if (!prefs.unlink(TH)) {
+                       return 8;
+                       }
+               return 0;
+       }
+}

Added: freeway/src/org/gnu/freeway/test/StatusCallsTest.java
===================================================================
--- freeway/src/org/gnu/freeway/test/StatusCallsTest.java       2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/test/StatusCallsTest.java       2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,77 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.test;
+
+import org.gnu.freeway.util.*;
+
+/**
+ * testcase for util/statuscalls.c
+ */
+
+public class StatusCallsTest extends AbstractTest
+{
+       private StatusCallsService      status;
+
+
+       public StatusCallsTest()
+       {
+               super();
+       }
+
+       public int test()
+       {
+               long    start;
+               int             ret,i;
+
+               status=(StatusCallsService) 
suite.service(StatusCallsService.class);
+
+               for (i=0; i<3; i++) {
+                       if (status.cpuUsage() == -1) {
+                               System.out.println("cpuUsage == -1");
+                               return -1;
+                               }
+                       if (status.networkUsageUp() == -1) {
+                               System.out.println("networkUsageUp == -1");
+                               return -1;
+                               }
+                       if (status.networkUsageDown() == -1) {
+                               System.out.println("networkUsageDown == -1");
+                               return -1;
+                               }
+                       Scheduler.sleep(Scheduler.SECS_1);
+                       }
+
+               // need to run each phase for more than 10s since statuscalls 
only refreshes that often...
+               start=Scheduler.now();
+               while (start + Scheduler.SECS_12 > Scheduler.now()) {
+                       Scheduler.sleep(Scheduler.SECS_1);
+                       }
+
+               start=Scheduler.now();
+
+               ret = status.cpuUsage();
+               while (start + Scheduler.SECS_12 > Scheduler.now()) {
+                       Math.sqrt(245.2523);    // do some processing to drive 
load up
+                       }
+
+               if (ret > status.cpuUsage()) {
+                       System.out.println("WARNING: busy loop decreased CPU 
load: "+ret+" < "+status.cpuUsage()+".");
+                       }
+
+               // make sure we don't leak open files...
+               for (i=0;i<10000;i++) {
+                       if (status.cpuUsage() == -1) {
+                               return -1;
+                               }
+                       if (status.networkUsageUp() == -1) {
+                               return -1;
+                               }
+                       if (status.networkUsageDown() == -1) {
+                               return -1;
+                               }
+                       }
+               return 0;
+       }
+}

Added: freeway/src/org/gnu/freeway/test/StorageTest.java
===================================================================
--- freeway/src/org/gnu/freeway/test/StorageTest.java   2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/test/StorageTest.java   2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,216 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.test;
+
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.io.*;
+
+/**
+ * testcase for the storage module
+ */
+
+public class StorageTest extends AbstractTest
+{
+       public static final String      TESTSTRING      =       "Hello, world 
!";
+
+
+       public StorageTest()
+       {
+               super();
+       }
+
+       protected int testLinkToFile()
+       {
+               FileLocation    file;
+               LinkLocation    link;
+
+               // check file creation
+               file=FileLocation.temporary();
+               if (file==null) {
+                       return 1;
+                       }
+               if (!file.exists()) {
+                       return 2;
+                       }
+
+               // check link creation
+               link=new LinkLocation("lnk1");
+               if (!link.setTarget(file)) {
+                       return 3;
+                       }
+               if (!link.create()) {
+                       return 4;
+                       }
+               if (!link.exists()) {
+                       return 5;
+                       }
+               if (link.getTarget()==null) {
+                       return 6;
+                       }
+
+               // check whether it's not detected as a regular file/directory
+               if (new FileLocation(link.getPath()).exists()) {
+                       return 7;
+                       }
+               if (new DirLocation(link.getPath()).exists()) {
+                       return 8;
+                       }
+
+               // target check
+               link=new LinkLocation("lnk1");
+               if (!link.exists()) {
+                       return 9;
+                       }
+               if (!file.equals(link.getTarget())) {
+                       return 10;
+                       }
+
+               // check link deletion
+               if (!link.delete()) {
+                       return 11;
+                       }
+               if (link.exists()) {
+                       return 12;
+                       }
+
+               // check file deletion
+               if (!file.delete()) {
+                       return 13;
+                       }
+               if (file.exists()) {
+                       return 14;
+                       }
+               return 0;
+       }
+
+       protected int testLinkToDir()
+       {
+               DirLocation     dir;
+               LinkLocation    link;
+
+               // check dir creation
+               dir=new DirLocation("dir_referenced");
+               if (!dir.create()) {
+                       return 101;
+                       }
+               if (!dir.exists()) {
+                       return 102;
+                       }
+
+               // check link creation
+               link=new LinkLocation("lnk2");
+               if (!link.setTarget(dir)) {
+                       return 103;
+                       }
+               if (!link.create()) {
+                       return 104;
+                       }
+               if (!link.exists()) {
+                       return 105;
+                       }
+               if (link.getTarget()==null) {
+                       return 106;
+                       }
+
+               // check whether it's not detected as a regular file/directory
+               if (new FileLocation(link.getPath()).exists()) {
+                       return 107;
+                       }
+               if (new DirLocation(link.getPath()).exists()) {
+                       return 108;
+                       }
+
+               // target check
+               link=new LinkLocation("lnk2");
+               if (!link.exists()) {
+                       return 109;
+                       }
+               if (!dir.equals(link.getTarget())) {
+                       return 110;
+                       }
+
+               // check link deletion
+               if (!link.delete()) {
+                       return 111;
+                       }
+               if (link.exists()) {
+                       return 112;
+                       }
+
+               // check dir deletion
+               if (!dir.delete()) {
+                       return 113;
+                       }
+               if (dir.exists()) {
+                       return 114;
+                       }
+               return 0;
+       }
+
+       protected int testFiles()
+       {
+               FileLocation            file;
+               MappedFile              f;
+               HashCode160             h;
+               String                  str;
+
+               h=HashCode160.create(TESTSTRING);
+               file=new FileLocation(h.toHex());
+
+               f=file.openNew();
+               if (f==null) {
+                       return 200;
+                       }
+               if (!f.writeString(TESTSTRING)) {
+                       return 201;
+                       }
+               if (!f.close()) {
+                       return 202;
+                       }
+
+               f=file.open();
+               if (f==null) {
+                       return 203;
+                       }
+               str=f.asString("");
+               if (!f.close()) {
+                       return 204;
+                       }
+
+               if (!str.equals(TESTSTRING)) {
+                       suite.err("Read error : \""+str+"\" != 
\""+TESTSTRING+"\" for file \""+file.getLabel()+"\" !");
+                       return 205;
+                       }
+
+               if (!file.delete()) {
+                       return 206;
+                       }
+               if (file.exists()) {
+                       return 207;
+                       }
+               return 0;
+       }
+
+       public int test()
+       {
+               int     ret;
+
+               ret=testLinkToFile();
+               if (ret!=0) {
+                       return ret;
+                       }
+
+               ret=testLinkToDir();
+               if (ret!=0) {
+                       return ret;
+                       }
+
+               ret=testFiles();
+               if (ret!=0) {
+                       return ret;
+                       }
+               return 0;
+       }
+}

Added: freeway/src/org/gnu/freeway/test/SymCipherTest.java
===================================================================
--- freeway/src/org/gnu/freeway/test/SymCipherTest.java 2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/test/SymCipherTest.java 2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,115 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.test;
+
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+
+import java.nio.*;
+
+/**
+ * SymCipher testcode.
+ */
+
+public class SymCipherTest extends AbstractTest
+{
+       private static final String     TESTSTRING      =       "Hello World!";
+
+
+       public int test()
+       {
+               int     failureCount;
+
+               failureCount=0;
+               failureCount+=testSymcipher();
+//             failureCount+=testSymcipher2();
+               failureCount+=testSymcipher3();
+               if (failureCount==0) {
+                       return 0;
+                       }
+               System.out.println(failureCount+" TESTS FAILED !");
+               return -1;
+       }
+
+       public int testSymcipher()
+       {
+               byte[]          result,r2;
+               SessionKey      key;
+
+               key=SessionKey.create();
+
+               result = key.encrypt(TESTSTRING,SessionKey.INITVALUE);
+               if (result == null) {
+                       System.out.println("symciphertest failed: encryptBlock 
returned null");
+                       return 1;
+                       }
+               
System.out.println("encryption("+TESTSTRING.getBytes().length+") -> 
"+result.length+" bytes ("+Utils.toHex(result,true)+")");
+
+               r2=key.decrypt(result,0,result.length,SessionKey.INITVALUE);
+               if (!TESTSTRING.equals(new String(r2))) {
+                       System.out.println("symciphertest failed: "+new 
String(r2)+" != "+TESTSTRING);
+                       return 1;
+                       }
+               System.out.println("Test succedeed !");
+               return 0;
+       }
+
+       public int testSymcipher2()
+       {
+               byte[]          test,res;
+               SessionKey      key;
+               int                     i;
+
+               key=SessionKey.create();
+
+               test=new byte[64];
+               for (i=0; i<test.length; i++) {
+                       test[i]=(byte) i;
+                       }
+
+               for (i=0; i<=test.length; i++) {
+                       res=key.encrypt(test,0,i,SessionKey.INITVALUE);
+                       System.out.println("encryption("+i+") -> "+res.length+" 
bytes ("+Utils.toHex(res,true)+")");
+                       }
+               return 0;
+       }
+
+       public int testSymcipher3()
+       {
+               byte[]          b,res;
+               SessionKey      key;
+               ByteBuffer      buf;
+               boolean         eq;
+
+               b=TESTSTRING.getBytes();
+               buf=ByteBuffer.allocate(b.length);
+               buf.put(b);
+               
System.out.println(Utils.toString(buf,0,buf.position(),"BUFFER",16));
+
+               key=SessionKey.create();
+
+               buf=key.encrypt(buf);
+               if (buf==null) {
+                       System.out.println("Failed to encrypt.");
+                       return 1;
+                       }
+               
System.out.println(Utils.toString(buf,0,buf.position(),"BUFFER",16));
+               res=key.encrypt(b,SessionKey.INITVALUE);
+               
System.out.println(Utils.toString(res,0,res.length,"BUFFER+",16));
+
+               if (!key.decrypt(buf,SessionKey.INITVALUE)) {
+                       System.out.println("Failed to decrypt.");
+                       return 2;
+                       }
+               
System.out.println(Utils.toString(buf,0,buf.position(),"BUFFER",16));
+
+               buf.clear();
+               buf.get(b);
+
+               eq=TESTSTRING.equals(new String(b));
+               System.out.println("Equals ? "+eq);
+               return (eq ? 0 : 3);
+       }
+}

Added: freeway/src/org/gnu/freeway/test/TCPTest.java
===================================================================
--- freeway/src/org/gnu/freeway/test/TCPTest.java       2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/test/TCPTest.java       2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,231 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.test;
+
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.net.*;
+
+import java.io.*;
+import java.net.*;
+import java.nio.channels.*;
+
+/**
+ * testcase for util/tcpiotest.c
+ */
+
+public class TCPTest extends AbstractTest
+{
+       private static final int        MAX_SIZE        =       1024;
+//     private static final int        MAX_SIZE        =       8;
+
+
+       public TCPTest()
+       {
+               super();
+       }
+
+       public int test()
+       {
+               int imax,i;
+               int ret;
+               ServerSocket            serverSocket;
+               CSSession       clientSocket;
+               CSSession       acceptSocket;
+
+               ret = 0;
+
+               serverSocket = openServerSocket();
+
+               clientSocket = suite.connect();
+
+               if (serverSocket == null) {
+                       return 1;
+                       }
+
+               imax=4;
+               for (i=0; i<imax; i++) {
+                       acceptSocket=new TCPSession();
+
+                       if (acceptSocket.connect(doAccept(serverSocket),true)) {
+                               acceptSocket.setBlocking(true);
+
+                               suite.out(">>> test transmission "+(i+1)+" out 
of "+imax);
+                               ret = ret | testTransmission(clientSocket, 
acceptSocket);
+                               suite.out(">>> test transmission (reverse) 
"+(i+1)+" out of "+imax);
+                               ret = ret | testTransmission(acceptSocket, 
clientSocket);
+                               suite.out(">>> test non blocking "+(i+1)+" out 
of "+imax);
+                               ret = ret | testNonblocking(clientSocket, 
acceptSocket);
+                               suite.out(">>> test non blocking (reverse) 
"+(i+1)+" out of "+imax);
+                               ret = ret | testNonblocking(acceptSocket, 
clientSocket);
+                               }
+                       else {
+                               suite.err("can't connect");
+                               }
+
+                       acceptSocket.disconnect();
+                       suite.out("*");
+                       }
+
+               clientSocket.disconnect();
+
+               if (ret > 0) {
+                       suite.err("Error "+ret);
+                       }
+               return ret;
+       }
+
+       protected ServerSocket openServerSocket()
+       {
+               ServerSocket    listenerFD;
+               int                             listenerPort;
+
+               listenerPort = 
suite.getPreferences().getInt("NETWORK","PORT",0);
+
+               // create the socket
+               listenerFD=null;
+               while (listenerFD==null) {
+                       try {
+                               listenerFD=ServerSocketChannel.open().socket();
+                               }
+                       catch( IOException x ) {
+                               suite.err("ERROR opening socket. No client 
service started. Trying again in 30 seconds.",x);
+                               Scheduler.sleep(Scheduler.SECS_30);
+                               }
+                       }
+
+               // configure and bind the socket
+               try {
+                       listenerFD.getChannel().configureBlocking(false);
+                       listenerFD.setReuseAddress(true);
+                       listenerFD.bind(new InetSocketAddress(listenerPort),5);
+                       }
+               catch( IOException x ) {
+                       suite.err("ERROR configuring/binding the TCP listener 
to port "+listenerPort+". Test failed.",x);
+                       return null;
+                       }
+               return listenerFD;
+       }
+
+       protected SocketChannel doAccept( ServerSocket serverSocket )
+       {
+               SocketChannel   c;
+
+               c = null;
+               while (c ==null) {
+                       try {
+                               c=serverSocket.getChannel().accept();
+                               c.configureBlocking(false);
+                               }
+                       catch( IOException x ) {
+                               suite.err("ERROR accepting new connection.",x);
+                               }
+                       }
+               return c;
+       }
+
+       protected int testTransmission( CSSession a, CSSession b )
+       {
+               CSUnknown       hdr,buf;
+               byte[]          bin;
+               int i;
+               int j;
+
+               hdr=new CSUnknown(0);
+
+               bin=new byte[MAX_SIZE];
+               for (i=0; i<MAX_SIZE-CSUnknown.SIZE; i++) {
+//                     suite.out(".");
+                       for (j=0; j<i; j++) {
+                               bin[j]=(byte) (i+j);
+                               }
+
+                       hdr.setData(bin,0,i);
+
+                       if (!a.send(hdr)) {
+                               return 1;
+                               }
+
+                       buf = (CSUnknown) b.receive(CSUnknown.class);
+                       if (buf==null) {
+                               return 2;
+                               }
+
+                       if (!buf.equals(hdr)) {
+                               return 4;
+                               }
+                       }
+               return 0;
+       }
+
+       protected int testNonblocking( CSSession a, CSSession b )
+       {
+               byte[]          bin;
+               CSUnknown       hdr,buf;
+               int                     len,cnt,i;
+
+               bin=new byte[64];
+               for (i=0; i<64-CSUnknown.SIZE; i++) {
+                       bin[i]=(byte) i;
+                       }
+
+               hdr=new CSUnknown(0);
+               hdr.setData(bin,0,64-CSUnknown.SIZE);
+
+               a.setBlocking(false);
+
+               len=0;
+               for (cnt=0; a.flushAndSend(hdr); cnt++) {
+                       len+=hdr.getByteSize();
+
+                       hdr=new CSUnknown(cnt+1);
+                       hdr.setData(bin,0,64-CSUnknown.SIZE);
+                       }
+
+               a.setBlocking(true);
+
+               suite.out("Wrote "+cnt+" messages ("+len+" bytes in non 
blocking mode), reading them now...");
+
+               if (cnt==0) {
+                       return 8;       // could not write ANY data 
non-blocking !?
+                       }
+
+               for (i=0; i<cnt; i++) {
+                       hdr=new CSUnknown(i);
+                       hdr.setData(bin,0,64-CSUnknown.SIZE);
+
+                       buf=(CSUnknown) b.receive(CSUnknown.class);
+                       if (buf==null) {
+                               return 16;
+                               }
+
+                       if (!buf.equals(hdr)) {
+                               suite.err("Failure in message "+i+".  Headers: 
"+buf.getType()+" ? "+hdr.getType());
+                               return 32;
+                               }
+
+                       if (i==cnt-2) {
+                               suite.out("Blocking write to flush last 
non-blocking message.");
+
+                               hdr=new CSUnknown(cnt);
+                               hdr.setData(bin,0,64-CSUnknown.SIZE);
+                               if (!a.send(hdr)) {
+                                       return 64;
+                                       }
+                               }
+                       }
+
+               hdr=new CSUnknown(cnt);
+               hdr.setData(bin,0,64-CSUnknown.SIZE);
+
+               buf=(CSUnknown) b.receive(CSUnknown.class);
+               if (buf==null) {
+                       return 128;
+                       }
+               if (!buf.equals(hdr)) {
+                       return 256;
+                       }
+               return 0;
+       }
+}

Added: freeway/src/org/gnu/freeway/test/TableTest.java
===================================================================
--- freeway/src/org/gnu/freeway/test/TableTest.java     2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/test/TableTest.java     2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,200 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.test;
+
+import org.gnu.freeway.*;
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.ui.*;
+
+import EDU.oswego.cs.dl.util.concurrent.*;
+
+import java.awt.event.*;
+import java.util.*;
+import javax.swing.*;
+
+/**
+ *
+ */
+
+public class TableTest extends AbstractTest implements SwingConstants
+{
+       private Semaphore                       sem;
+       private DefaultListModel        dataModel;
+       private ListSelectionModel      selModel;
+
+
+       public TableTest()
+       {
+               super();
+               sem=new Semaphore(0);
+               dataModel=null;
+               selModel=null;
+       }
+
+       protected Adapter createAdapter()
+       {
+               return new AbstractAdapter(7) {
+                       protected void fillFacets( Object obj, Object[] facets )
+                       {
+                               List    list = (List) obj;
+
+                               facets[0]=list.get(0);
+                               facets[1]=list.get(1);
+                               facets[2]="name";
+                               facets[3]=new Long(0);
+                               facets[4]="";
+                               facets[5]="";
+                               facets[6]="mime/mime";
+                       }
+                       };
+       }
+
+       protected Controller createController()
+       {
+               return new Controller() {
+                       public Application getApplication()
+                       {
+                               return suite;
+                       }
+                       };
+       }
+
+       public int test() throws InterruptedException
+       {
+               GFrame  frame;
+               GTable  table;
+               GBar    bar;
+               JPanel  panel;
+               JButton button;
+
+               dataModel=new DefaultListModel();
+
+               table=new GTable(createAdapter(),dataModel);
+               table.setColumns(new GColumn[] {
+                       new GColumn("Description", 30, LEFT,   true),
+                       new GColumn("Size",        10, RIGHT,  true),
+                       new GColumn("Filename",    20, LEFT,   true),
+                       new GColumn("CRC",         10, LEFT,   false),
+                       new GColumn("HASH1",       10, LEFT,   false),
+                       new GColumn("HASH2",       10, LEFT,   false),
+                       new GColumn("Mimetype",    20, CENTER, true)
+                       });
+
+               selModel=table.getUnsortedSelectionModel();
+
+               onPopulate();
+
+               bar=new GBar();
+               button=new JButton("Populate");
+               button.addActionListener(new ActionListener() {
+                       public void actionPerformed( ActionEvent evt )
+                       {
+                               onPopulate();
+                       }
+                       });
+               bar.add(button);
+
+               button=new JButton("Remove");
+               button.addActionListener(new ActionListener() {
+                       public void actionPerformed( ActionEvent evt )
+                       {
+                               onRemove();
+                       }
+                       });
+               bar.add(button);
+
+               button=new JButton("Add");
+               button.addActionListener(new ActionListener() {
+                       public void actionPerformed( ActionEvent evt )
+                       {
+                               onAdd();
+                       }
+                       });
+               bar.add(button);
+
+               button=new JButton("Replace");
+               button.addActionListener(new ActionListener() {
+                       public void actionPerformed( ActionEvent evt )
+                       {
+                               onReplace();
+                       }
+                       });
+               bar.add(button);
+
+               panel=new JPanel(new TileLayout(0,0));
+               panel.add(bar,TileLayout.SOUTH);
+               panel.add(new JScrollPane(table));
+
+               frame=new GFrame(createController(),"table-test");
+               frame.addWindowListener(new WindowAdapter() {
+                       public void windowClosing( WindowEvent evt )
+                       {
+                               sem.release();
+                       }
+                       });
+               frame.setContent(panel);
+               frame.display();
+
+               sem.acquire();
+
+               frame.close();
+               return 0;
+       }
+
+       protected void add( String str, int num )
+       {
+               dataModel.addElement(Arrays.asList(new Object[] { str, new 
Long(num) }));
+       }
+
+       protected void onPopulate()
+       {
+               String[]        all = { "Returns", "a", "box", "containing", 
"the", "search", "results", "list.", "aa", "a" };
+               int[]           sizes = { 10, 50, 60, 50, 11, 111, 5, 10, 9, 8 
};
+               int                     i;
+
+               for (i=0; i<all.length; i++) {
+                       add(all[i],sizes[i]);
+                       }
+       }
+
+       protected void onRemove()
+       {
+               int     i;
+
+               for (i=selModel.getMaxSelectionIndex(); 
i>=selModel.getMinSelectionIndex(); i--) {
+                       if (selModel.isSelectedIndex(i)) {
+                               System.out.println("data("+i+") = 
"+dataModel.get(i));
+
+                               dataModel.remove(i);
+                               }
+                       }
+       }
+
+       protected void onAdd()
+       {
+               add(Crypto.nextString(3),(Crypto.nextInt() & 0x0000ffff));
+       }
+
+       protected void onReplace()
+       {
+               List    list;
+               String  str;
+               int             row;
+
+               row=Crypto.nextInt(dataModel.size());
+
+               list=(List) dataModel.get(row);
+
+               str=list.get(0).toString();
+               if (!str.startsWith("_")) {
+                       list.set(0,"_"+str);
+                       }
+               else {
+                       list.set(0,str.substring(1));
+                       }
+
+               dataModel.set(row,list);
+       }
+}

Added: freeway/src/org/gnu/freeway/test/TimerTest.java
===================================================================
--- freeway/src/org/gnu/freeway/test/TimerTest.java     2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/test/TimerTest.java     2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,127 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.test;
+
+import org.gnu.freeway.util.*;
+
+import EDU.oswego.cs.dl.util.concurrent.*;
+
+/**
+ * measures how precise the timers are. Expect values between 10 and 20 ms on 
modern machines.
+ */
+
+public class TimerTest extends AbstractTest
+{
+       public static final boolean     VERBOSE =       true;
+
+       public static final int INCR            =       47;
+       public static final int INCR2           =       113;
+       public static final int MAXV            =       1500;
+       public static final int MAXV2           =       1500;
+
+
+       public TimerTest()
+       {
+               super();
+       }
+
+       public int test() throws InterruptedException
+       {
+               int     ret;
+
+               ret=check();
+               if (ret!=0) {
+                       suite.err("ERROR "+ret);
+                       }
+               return 0;
+       }
+
+       protected void semaphore_up( Semaphore sem )
+       {
+               sem.release();
+       }
+
+       protected int check() throws InterruptedException
+       {
+               long now;
+               long last;
+               int i;
+               long cumDelta;
+               final Semaphore sem;
+
+               // test that times are monotonically increasing
+               // measure precision of sleep and report
+               // test that sleep is interrupted by signals
+
+               last=now= Scheduler.now();
+
+               while (now == last)
+                       now = Scheduler.now();
+
+               if (now < last)
+                       return 3;
+
+               cumDelta = 0;
+               for (i=0;i<MAXV;i+=INCR) {
+                       last=Scheduler.now();
+
+                       if (!Scheduler.sleep(Scheduler.millis(i)))
+                               return 5;
+
+                       now=Scheduler.now();
+                       if (VERBOSE) {
+                               System.err.println(Scheduler.toMillis(i)+" ms 
requested, got: "+Scheduler.toMillis(now-last)+" ms");
+                               }
+                       if (last + Scheduler.millis(i) < now)
+                               cumDelta += (now - (last+Scheduler.millis(i)));
+                       else
+                               cumDelta += ((last+Scheduler.millis(i)) - now);
+                       }
+               System.out.print("Sleep precision: 
"+(Scheduler.toMillis(cumDelta)/(MAXV/INCR))+" ms.  ");
+               if (cumDelta <= Scheduler.MILLIS_10 * MAXV / INCR)
+                       System.out.println("Timer precision is excellent.");
+               else if (cumDelta <= Scheduler.MILLIS_50 * MAXV / INCR) /* 50 
ms average deviation */
+                       System.out.println("Timer precision is good.");
+               else if (cumDelta > Scheduler.MILLIS_250 * MAXV / INCR)
+                       System.out.println("Timer precision is awful.");
+               else
+                       System.out.println("Timer precision is acceptable.");
+
+               sem = new Semaphore(0);
+
+               cumDelta = 0;
+
+               for (i=50;i<MAXV2+50;i+=INCR2) {
+                       last=Scheduler.now();
+                       suite.getScheduler().addJob(new 
ScheduledTask("SEMAPHORE-UP-"+i,new AbstractAction() {
+                               public void perform()
+                               {
+                                       semaphore_up(sem);
+                               }
+                               }),Scheduler.millis(i));
+                       sem.acquire();
+                       now=Scheduler.now();
+                       if (now < last + i)
+                               now = last + i - now;
+                       else
+                               now = now - (last + i);
+                       cumDelta += now;
+                       if (VERBOSE) {
+                               System.err.println("Sleep interrupted by signal 
within "+now+" ms of deadline (intended delay: "+i+" ms).");
+                               }
+                       }
+               System.out.print("Sleep interrupt precision is "+(cumDelta / 
(MAXV2/INCR2))+"ms. ");
+               if (cumDelta <= Scheduler.MILLIS_10 * MAXV2 / INCR2)
+                       System.out.println("Timer precision is excellent.");
+               else if (cumDelta <= Scheduler.MILLIS_50 * MAXV2 / INCR2) /* 
50ms average deviation */
+                       System.out.println("Timer precision is good.");
+               else if (cumDelta > Scheduler.MILLIS_250 * MAXV2 / INCR2)
+                       System.out.println("Timer precision is awful.");
+               else
+                       System.out.println("Timer precision is acceptable.");
+
+               return 0;
+       }
+}

Added: freeway/src/org/gnu/freeway/test/high_db_test.c
===================================================================
--- freeway/src/org/gnu/freeway/test/high_db_test.c     2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/test/high_db_test.c     2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,395 @@
+/*
+ * @file test/high_db_test.c
+ * @brief Test for the high-level DB API implementations.
+ * @author Christian Grothoff
+ *
+ * Not tested (but easily testable):
+ * - deleteContent
+ * - unlinkFromDB
+ * - forEachEntryInDatabase (with content in it)
+ * - countContentEntries (with content in it)
+ */
+
+#include "gnunet_util.h"
+#include "high_backend.h"
+#include "platform.h"
+
+#ifndef MINGW
+ #include <netinet/in.h>
+#endif
+
+typedef struct {
+  HighDBHandle (*initContentDatabase)(unsigned int i,
+                                     unsigned int n);
+  void (*doneContentDatabase)(HighDBHandle handle);
+  int (*forEachEntryInDatabase)(HighDBHandle handle,
+                               EntryCallback callback,
+                               void * data);
+  int (*countContentEntries)(HighDBHandle handle);
+  int (*readContent)(HighDBHandle handle,
+                    HashCode160 * query,
+                    ContentIndex * ce,
+                    void ** result,
+                    int prio);
+  int (*writeContent)(HighDBHandle handle,
+                     ContentIndex * ce,
+                     int len,
+                     void * block);
+  int (*unlinkFromDB)(HighDBHandle handle,
+                     HashCode160 * query);
+  int (*getRandomContent)(HighDBHandle handle,
+                         ContentIndex * ce);
+  int (*deleteContent)(HighDBHandle handle,
+                      int count,
+                      EntryCallback callback,
+                      void * closure);
+  unsigned int (*getMinimumPriority)(HighDBHandle handle); 
+  int (*estimateAvailableBlocks)(HighDBHandle handle,
+                                int quota);
+  void (*deleteDatabase)(HighDBHandle handle);
+} HighAPI;
+
+static void doerror(HashCode160 * key,
+                   ContentIndex * ce,
+                   void * data,
+                   unsigned int dataLen,
+                   void * closure) {
+  *(int*)closure = SYSERR;
+}
+
+/**
+ * Add testcode here!
+ **/
+static int testTAPI(HighAPI * a) {
+  HighDBHandle h;
+  int error;
+  void * v1;
+  void * v2;
+  ContentIndex ce1;
+  ContentIndex ce2;
+  HashCode160 hc;
+
+  /* get into well-defined state */
+  h = a->initContentDatabase(0,0); /* 0,0 is an otherwise invalid entry,
+                                     so this is good for testing */
+  if (h == NULL)
+    return SYSERR;
+  fprintf(stderr, ".");
+  a->deleteDatabase(h);
+  /* ok, now for real */
+  h = a->initContentDatabase(0,0); 
+  if (h == NULL)
+    return SYSERR;
+  fprintf(stderr, ".");
+  if (0 != a->countContentEntries(h))
+    return SYSERR;
+  fprintf(stderr, ".");
+  if (0 != a->getMinimumPriority(h))
+    return SYSERR;
+  fprintf(stderr, ".");
+  error = OK;
+  if (0 != a->forEachEntryInDatabase(h, 
+                                    &doerror, &error))
+    return SYSERR;
+  fprintf(stderr, ".");
+  if (error != OK)
+    return SYSERR;
+  fprintf(stderr, ".");
+  memset(&hc, 42, sizeof(hc));
+  memset(&ce1, 44, sizeof(ce1));
+  v1 = MALLOC(92);
+  memset(v1, 46, 92);
+  ce1.type = htons(LOOKUP_TYPE_3HASH);
+  if (SYSERR == a->writeContent(h, &ce1, 92, v1))
+    return SYSERR;
+  fprintf(stderr, ".");
+  v2 = NULL;
+  if (SYSERR != a->readContent(h, &hc, &ce2, &v2, 0))
+    return SYSERR;
+  fprintf(stderr, ".");
+  if (v2 != NULL)
+    return SYSERR;
+  fprintf(stderr, ".");
+  hash(&ce1.hash,
+       sizeof(HashCode160),
+       &hc);
+  if (92 != a->readContent(h, &hc, &ce2, &v2, 0))
+    return SYSERR;
+  fprintf(stderr, ".");
+  if (0 != memcmp(v1, v2, 92))
+    return SYSERR;
+  fprintf(stderr, ".");
+  if (0 != memcmp(&ce1, &ce2, sizeof(ce1)))
+    return SYSERR;
+  fprintf(stderr, ".");
+  FREE(v2); v2 = NULL;
+  if (OK != a->unlinkFromDB(h, &hc))
+    return SYSERR;
+  if (0 != a->countContentEntries(h))
+    return SYSERR;
+  if (SYSERR != a->readContent(h, &hc, &ce2, &v2, 0))
+    return SYSERR;
+  if (v2 != NULL)
+    return SYSERR;
+  if (SYSERR == a->writeContent(h, &ce1, 92, v1))
+    return SYSERR;
+  fprintf(stderr, ".");
+  a->doneContentDatabase(h);
+  h = a->initContentDatabase(0,0); 
+  if (h == NULL)
+    return SYSERR;
+  if (92 != a->readContent(h, &hc, &ce2, &v2, 0))
+    return SYSERR;
+  fprintf(stderr, ".");
+  if (0 != memcmp(v1, v2, 92))
+    return SYSERR;
+  fprintf(stderr, ".");
+  if (0 != memcmp(&ce1, &ce2, sizeof(ce1)))
+    return SYSERR;
+  FREE(v2); v2 = NULL;
+  a->deleteDatabase(h);
+  h = a->initContentDatabase(0,0); 
+  if (SYSERR != a->readContent(h, &hc, &ce2, &v2, 0))
+    return SYSERR;
+  if (v2 != NULL)
+    return SYSERR;
+  FREE(v1); v1 = NULL;
+  a->deleteDatabase(h);
+
+  h = a->initContentDatabase(0,0); 
+  if (h == NULL)
+    return SYSERR;
+  fprintf(stderr, ".");
+  if (0 != a->countContentEntries(h))
+    return SYSERR;
+  fprintf(stderr, ".");
+  if (0 != a->getMinimumPriority(h))
+    return SYSERR;
+  fprintf(stderr, ".");
+  error = OK;
+  if (0 != a->forEachEntryInDatabase(h,
+                                    &doerror, &error))
+    return SYSERR;
+  fprintf(stderr, ".");
+  if (error != OK)
+    return SYSERR;
+  fprintf(stderr, ".");
+  memset(&hc, 42, sizeof(hc));
+  memset(&ce1, 44, sizeof(ce1));
+  v1 = MALLOC(92);
+  memset(v1, 46, 92);
+  ce1.type = htons(LOOKUP_TYPE_CHK);
+  if (SYSERR == a->writeContent(h, &ce1, 92, v1))
+    return SYSERR;
+  fprintf(stderr, ".");
+  v2 = NULL;
+  if (SYSERR != a->readContent(h, &hc, &ce2, &v2, 0))
+    return SYSERR;
+  fprintf(stderr, ".");
+  if (v2 != NULL)
+    return SYSERR;
+  fprintf(stderr, ".");
+  memcpy(&hc,
+        &ce1.hash,
+        sizeof(HashCode160));
+  if (92 != a->readContent(h, &hc, &ce2, &v2, 0))
+    return SYSERR;
+  fprintf(stderr, ".");
+  if (0 != memcmp(v1, v2, 92))
+    return SYSERR;
+  fprintf(stderr, ".");
+  if (0 != memcmp(&ce1, &ce2, sizeof(ce1)))
+    return SYSERR;
+  fprintf(stderr, ".");
+  FREE(v2); v2 = NULL;
+  if (OK != a->unlinkFromDB(h, &hc))
+    return SYSERR;
+  if (0 != a->countContentEntries(h))
+    return SYSERR;
+  if (SYSERR != a->readContent(h, &hc, &ce2, &v2, 0))
+    return SYSERR;
+  if (v2 != NULL)
+    return SYSERR;
+  if (SYSERR == a->writeContent(h, &ce1, 92, v1))
+    return SYSERR;
+  fprintf(stderr, ".");
+  a->deleteDatabase(h);
+  FREE(v1); v1 = NULL;
+
+  fprintf(stderr, ".\n");
+  return OK;
+}
+
+static char * tselect = NULL;
+
+/**
+ * Perform option parsing from the command line. 
+ **/
+static int parser(int argc, 
+                 char * argv[]) {
+  int cont = OK;
+  int c;
+
+  FREENONNULL(setConfigurationString("GNUNETD",
+                                    "_MAGIC_",
+                                    "YES"));
+  FREENONNULL(setConfigurationString("",
+                                    "GNUNETD_HOME",
+                                    "/tmp/gnunet_test/"));
+  FREENONNULL(setConfigurationString("FILES",
+                                    "gnunet.conf",
+                                    "/tmp/gnunet_test/gnunet.conf"));
+
+  while (1) {
+    int option_index = 0;
+    static struct GNoption long_options[] = {
+      { "loglevel",1, 0, 'L' },
+      { "config",  1, 0, 'c' },
+      { "version", 0, 0, 'v' },
+      { "help",    0, 0, 'h' },
+      { "type",    1, 0, 't' },
+      { 0,0,0,0 }
+    };
+    
+    c = GNgetopt_long(argc,
+                     argv, 
+                     "vhc:L:t:", 
+                     long_options, 
+                     &option_index);
+    
+    if (c == -1) 
+      break;  /* No more flags to process */
+    
+    switch(c) {
+    case 'c':
+      FREENONNULL(setConfigurationString("FILES",
+                                        "gnunet.conf",
+                                        GNoptarg));
+      break;
+    case 't':
+      tselect = STRDUP(GNoptarg);
+      break;
+    case 'v': 
+      printf("GNUnet High-level DB API Tester v%s\n",
+            VERSION);
+      cont = SYSERR;
+      break;
+    case 'h': 
+      printf("GNUnet High-level DB API Tester. Options:"
+            " -c config, -L loglevel, -h help,"
+            " -v version, -t DBAPI\n");
+      cont = SYSERR;
+      break;
+    case 'L':
+      FREENONNULL(setConfigurationString("GNUNETD",
+                                        "LOGLEVEL",
+                                        GNoptarg));
+      break;
+    default:
+      LOG(LOG_FAILURE, 
+         "FAILURE: Unknown option %c. Aborting.\n"\
+         "Use --help to get a list of options.\n",
+         c);
+      cont = SYSERR;    
+    } /* end of parsing commandline */
+  }
+  if (GNoptind < argc) {
+    LOG(LOG_WARNING, 
+       "WARNING: Invalid arguments: ");
+    while (GNoptind < argc)
+      LOG(LOG_WARNING, 
+         "%s ", argv[GNoptind++]);
+    LOG(LOG_FATAL,
+       "FATAL: Invalid arguments. Exiting.\n");
+    return SYSERR;
+  }
+  return cont;
+}
+
+#define DSO_PREFIX "libgnunetafs_database_"
+#define TEST_DB "/tmp/GNUnet_high_db_test/"
+
+int main(int argc, char *argv[]) {
+  HighAPI l;
+  void * lib;
+  int ok;
+
+  if (OK != initUtil(argc, argv, &parser))
+    return SYSERR;
+  /* we may not have write-rights in the default
+     directory; use /tmp! */
+  FREENONNULL(setConfigurationString("AFS",
+                                    "AFSDIR",
+                                    TEST_DB));
+  if (tselect == NULL)
+    tselect = getConfigurationString("AFS",
+                                    "DATABASETYPE");
+  if (tselect == NULL)
+    errexit("You must specify the DB type with option -t.\n");
+  
+  lib = loadDynamicLibrary(DSO_PREFIX,
+                           tselect);
+  if (lib == NULL)
+    errexit("FATAL: could not load plugin %s\n",
+           tselect);
+  l.initContentDatabase 
+    = bindDynamicMethod(lib,
+                       "",
+                       "initContentDatabase");
+  l.doneContentDatabase
+    = bindDynamicMethod(lib,
+                       "",
+                       "doneContentDatabase");
+  l.countContentEntries
+    = bindDynamicMethod(lib,
+                       "",
+                       "countContentEntries");
+  l.readContent
+    = bindDynamicMethod(lib,
+                       "",
+                       "readContent");
+  l.writeContent
+    = bindDynamicMethod(lib,
+                       "",
+                       "writeContent");
+  l.unlinkFromDB
+    = bindDynamicMethod(lib,
+                       "",
+                       "unlinkFromDB");
+  l.getRandomContent
+    = bindDynamicMethod(lib,
+                       "",
+                       "getRandomContent");
+  l.deleteContent
+    = bindDynamicMethod(lib,
+                       "",
+                       "deleteContent");
+  l.forEachEntryInDatabase
+    = bindDynamicMethod(lib,
+                       "",
+                       "forEachEntryInDatabase");
+  l.getMinimumPriority
+    = bindDynamicMethod(lib,
+                       "",
+                       "getMinimumPriority");
+  l.estimateAvailableBlocks
+    = bindDynamicMethod(lib,
+                       "",
+                       "estimateAvailableBlocks");
+  l.deleteDatabase
+    = bindDynamicMethod(lib,
+                       "",
+                       "deleteDatabase");
+  ok = testTAPI(&l);
+  unloadDynamicLibrary(lib);  
+  FREE(tselect);
+  doneUtil();
+  if (ok == SYSERR) {
+    fprintf(stderr, "\nFAILED!\n");
+    return 1;
+  } else {
+    return 0;
+  }
+}
+
+

Added: freeway/src/org/gnu/freeway/test/low_db_test.c
===================================================================
--- freeway/src/org/gnu/freeway/test/low_db_test.c      2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/test/low_db_test.c      2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,301 @@
+/*
+ * @file test/low_db_test.c
+ * @brief Test for the low-level DB API implementations.
+ * @author Christian Grothoff
+ */
+
+#include "gnunet_util.h"
+#include "platform.h"
+#include "low_backend.h"
+
+
+typedef struct {
+  LowDBHandle (*lowInitContentDatabase)(char * dir);
+  void (*lowDeleteContentDatabase)(LowDBHandle handle);  
+  void (*lowDoneContentDatabase)(LowDBHandle handle);
+  int (*lowUnlinkFromDB)(LowDBHandle handle, 
+                        HashCode160 * fn);
+  int (*lowCountContentEntries)(LowDBHandle handle);
+  int (*lowReadContent)(LowDBHandle handle,
+                       HashCode160 * fn,
+                       void ** result);
+  int (*lowWriteContent)(LowDBHandle handle,
+                        HashCode160 * fn, 
+                        int len,
+                        void * block);
+  int (*lowForEachEntryInDatabase)(LowDBHandle handle,
+                                  LowEntryCallback callback,
+                                  void * data);
+  int (*lowEstimateSize)(LowDBHandle handle);  
+} LowAPI;
+
+#define TEST_DB "/tmp/GNUnet_low_db_test/"
+
+void do_error(HashCode160 * key,
+             int * e) {
+  *e = SYSERR;
+}
+
+int testTAPI(LowAPI * lapi) {
+  LowDBHandle h;
+  int error;
+  void * v;
+  void * v2;
+  HashCode160 hc;
+  
+  /* get into well-defined state first! */
+  h = lapi->lowInitContentDatabase(TEST_DB);
+  if (h == NULL)
+    return SYSERR;
+  fprintf(stderr,".");
+  lapi->lowDeleteContentDatabase(h); 
+  h = lapi->lowInitContentDatabase(TEST_DB);
+  error = OK;
+  if (0 != lapi->lowForEachEntryInDatabase(h, 
+                                          (LowEntryCallback)&do_error, 
+                                          &error))
+    return SYSERR;
+  fprintf(stderr,".");
+  if (error == SYSERR)
+    return SYSERR;
+  v = NULL;
+  fprintf(stderr,".");
+  memset(&hc, 0x01, sizeof(hc));
+  if (SYSERR != lapi->lowReadContent(h, &hc, &v))
+    return SYSERR; /* db is emtpy! */
+  fprintf(stderr,".");
+  if (v != NULL)
+    return SYSERR; /* v may not change! */
+  fprintf(stderr,".");
+  if (OK != lapi->lowWriteContent(h, &hc, 1, &hc))
+    return SYSERR;
+  fprintf(stderr,".");
+  if (1 != lapi->lowCountContentEntries(h))
+    return SYSERR; /* wrong count */
+  fprintf(stderr,".");
+  if (1 != lapi->lowReadContent(h, &hc, &v))
+    return SYSERR; /* db is emtpy! */
+  FREE(v); v = NULL;
+  fprintf(stderr,".");
+  if (OK != lapi->lowUnlinkFromDB(h, &hc))
+    return SYSERR;
+  fprintf(stderr,".");
+  if (0 != lapi->lowCountContentEntries(h))
+    return SYSERR; /* wrong count */
+  fprintf(stderr,".");
+  if (SYSERR != lapi->lowReadContent(h, &hc, &v))
+    return SYSERR; /* db is emtpy! */
+  fprintf(stderr,".");
+  v2 = MALLOC(46);
+  memset(v2, 42, 46);
+  if (OK != lapi->lowWriteContent(h, &hc, 46, v2))
+    return SYSERR;
+  fprintf(stderr,".");
+  if (46 != lapi->lowReadContent(h, &hc, &v))
+    return SYSERR; /* wrong size! */
+  fprintf(stderr,".");
+  if (0 != memcmp(v, v2, 46))
+    return SYSERR; /* wrong data */
+  fprintf(stderr,".");
+  FREE(v); v = NULL;
+  if (1 != lapi->lowCountContentEntries(h))
+    return SYSERR; /* wrong count */
+  fprintf(stderr,".");
+  if (OK != lapi->lowWriteContent(h, &hc, 4, v2))
+    return SYSERR; /* destructive, truncating write! */
+  fprintf(stderr,".");
+  if (4 != lapi->lowReadContent(h, &hc, &v))
+    return SYSERR; /* wrong size! */
+  fprintf(stderr,".");
+  if (0 != memcmp(v, v2, 4))
+    return SYSERR; /* wrong data */
+  fprintf(stderr,".");
+  FREE(v); v = NULL;
+  if (OK != lapi->lowUnlinkFromDB(h, &hc))
+    return SYSERR;  
+  fprintf(stderr,".");
+  if (0 != lapi->lowCountContentEntries(h))
+    return SYSERR; /* wrong count */
+  fprintf(stderr,".");
+  if (OK != lapi->lowWriteContent(h, &hc, 4, v2))
+    return SYSERR; /* destructive, truncating write! */
+  fprintf(stderr,".");
+  lapi->lowDoneContentDatabase(h);
+  h = lapi->lowInitContentDatabase(TEST_DB);
+  if (1 != lapi->lowCountContentEntries(h))
+    return SYSERR; /* wrong count */
+  fprintf(stderr,".");
+  if (4 != lapi->lowReadContent(h, &hc, &v))
+    return SYSERR; /* wrong size! */
+  fprintf(stderr,".");
+  if (0 != memcmp(v, v2, 4))
+    return SYSERR; /* wrong data */
+  fprintf(stderr,".");
+  FREE(v); v = NULL;
+  FREE(v2);
+  lapi->lowDeleteContentDatabase(h); 
+  h = lapi->lowInitContentDatabase(TEST_DB);
+  if (0 != lapi->lowCountContentEntries(h))
+    return SYSERR; /* wrong count */
+  fprintf(stderr,".");
+  if (SYSERR != lapi->lowReadContent(h, &hc, &v))
+    return SYSERR; /* db is emtpy! */
+  fprintf(stderr,".");
+  lapi->lowDoneContentDatabase(h);
+  fprintf(stderr,".\n");
+  return OK;
+}
+
+static char * tselect = NULL;
+
+/**
+ * Perform option parsing from the command line. 
+ **/
+static int parser(int argc, 
+                 char * argv[]) {
+  int cont = OK;
+  int c;
+
+  FREENONNULL(setConfigurationString("GNUNETD",
+                                    "_MAGIC_",
+                                    "YES"));
+  FREENONNULL(setConfigurationString("",
+                                    "GNUNETD_HOME",
+                                    "/tmp/gnunet_test/"));
+  FREENONNULL(setConfigurationString("FILES",
+                                    "gnunet.conf",
+                                    "/tmp/gnunet_test/gnunet.conf"));
+  while (1) {
+    int option_index = 0;
+    static struct GNoption long_options[] = {
+      { "loglevel",1, 0, 'L' },
+      { "config",  1, 0, 'c' },
+      { "version", 0, 0, 'v' },
+      { "help",    0, 0, 'h' },
+      { "type",    1, 0, 't' },
+      { 0,0,0,0 }
+    };
+    
+    c = GNgetopt_long(argc,
+                     argv, 
+                     "vhc:L:t:", 
+                     long_options, 
+                     &option_index);
+    
+    if (c == -1) 
+      break;  /* No more flags to process */
+    
+    switch(c) {
+    case 'c':
+      FREENONNULL(setConfigurationString("FILES",
+                                        "gnunet.conf",
+                                        GNoptarg));
+      break;
+    case 't':
+      tselect = STRDUP(GNoptarg);
+      break;
+    case 'v': 
+      printf("GNUnet Low-level DB API Tester v%s\n",
+            VERSION);
+      cont = SYSERR;
+      break;
+    case 'h': 
+      printf("GNUnet Low-level DB API Tester. Options:"
+            " -c config, -L loglevel, -h help,"
+            " -v version, -t DBAPI\n");
+      cont = SYSERR;
+      break;
+    case 'L':
+      FREENONNULL(setConfigurationString("GNUNETD",
+                                        "LOGLEVEL",
+                                        GNoptarg));
+      break;
+    default:
+      LOG(LOG_FAILURE, 
+         "FAILURE: Unknown option %c. Aborting.\n"\
+         "Use --help to get a list of options.\n",
+         c);
+      cont = SYSERR;    
+    } /* end of parsing commandline */
+  }
+  if (GNoptind < argc) {
+    LOG(LOG_WARNING, 
+       "WARNING: Invalid arguments: ");
+    while (GNoptind < argc)
+      LOG(LOG_WARNING, 
+         "%s ", argv[GNoptind++]);
+    LOG(LOG_FATAL,
+       "FATAL: Invalid arguments. Exiting.\n");
+    return SYSERR;
+  }
+  return cont;
+}
+
+#define DSO_PREFIX "libgnunetafs_database_"
+
+int main(int argc, char *argv[]) {
+  LowAPI l;
+  void * lib;
+  int ok;
+
+  if (OK != initUtil(argc, argv, &parser))
+    return SYSERR;
+  if (tselect == NULL)
+    tselect = getConfigurationString("AFS",
+                                    "DATABASETYPE");
+  if (tselect == NULL)
+    errexit("You must specify the DB type with option -t or in gnunet.conf\n");
+  
+  lib = loadDynamicLibrary(DSO_PREFIX,
+                           tselect);
+  if (lib == NULL)
+    errexit("FATAL: could not load plugin %s\n",
+           tselect);
+  l.lowInitContentDatabase 
+    = bindDynamicMethod(lib,
+                       "",
+                       "lowInitContentDatabase");
+  l.lowDeleteContentDatabase 
+    = bindDynamicMethod(lib,
+                       "",
+                       "lowDeleteContentDatabase");
+  l.lowDoneContentDatabase 
+    = bindDynamicMethod(lib,
+                       "",
+                       "lowDoneContentDatabase");
+  l.lowCountContentEntries 
+    = bindDynamicMethod(lib,
+                       "",
+                       "lowCountContentEntries");
+  l.lowReadContent 
+    = bindDynamicMethod(lib,
+                       "",
+                       "lowReadContent");
+  l.lowUnlinkFromDB
+    = bindDynamicMethod(lib,
+                       "",
+                       "lowUnlinkFromDB");
+  l.lowWriteContent
+    = bindDynamicMethod(lib,
+                       "",
+                       "lowWriteContent");
+  l.lowForEachEntryInDatabase
+    = bindDynamicMethod(lib,
+                       "",
+                       "lowForEachEntryInDatabase");
+  l.lowEstimateSize
+    = bindDynamicMethod(lib,
+                       "",
+                       "lowEstimateSize");  
+  ok = testTAPI(&l);
+  unloadDynamicLibrary(lib);  
+  FREE(tselect);
+  doneUtil();
+  if (ok == SYSERR) {
+    fprintf(stderr, "\nFAILED!\n");
+    return 1;
+  } else
+    return 0;
+}
+
+

Added: freeway/src/org/gnu/freeway/test/weakkeytest.c
===================================================================
--- freeway/src/org/gnu/freeway/test/weakkeytest.c      2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/test/weakkeytest.c      2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,159 @@
+/**
+ * SymCipher weak key testcode.
+ * @author Krista Bennett
+ * @author Christian Grothoff
+ * @file test/weakkeytest.c
+ **/
+
+#if USE_GCRYPT
+  #include <gcrypt.h>
+#endif
+
+#include "gnunet_util.h"
+
+#define MAX_WEAK_KEY_TRIALS 10000
+#define GENERATE_WEAK_KEYS 0
+#define WEAK_KEY_TESTSTRING "I hate weak keys."
+
+#if USE_GCRYPT
+void printWeakKey(SESSIONKEY* key) {
+    int i;
+    for (i = 0; i < SESSIONKEY_LEN; i++) {
+       printf("%x ", (int)(key->key[i]));
+    }
+}
+
+int testWeakKey() {
+
+  char result[100];  
+  char res[100];
+  int size;
+  SESSIONKEY weak_key;
+
+  weak_key.key[0]= (char)(0x4c); 
+  weak_key.key[1]= (char)(0x31);
+  weak_key.key[2]= (char)(0xc6); 
+  weak_key.key[3]= (char)(0x2b); 
+  weak_key.key[4]= (char)(0xc1);
+  weak_key.key[5]= (char)(0x5f);
+  weak_key.key[6]= (char)(0x4d);
+  weak_key.key[7]= (char)(0x1f);
+  weak_key.key[8]= (char)(0x31);
+  weak_key.key[9]= (char)(0xaa);
+  weak_key.key[10]= (char)(0x12); 
+  weak_key.key[11]= (char)(0x2e);
+  weak_key.key[12]= (char)(0xb7);
+  weak_key.key[13]= (char)(0x82);
+  weak_key.key[14]= (char)(0xc0);
+  weak_key.key[15]= (char)(0xb6);
+
+  size = encryptBlock(WEAK_KEY_TESTSTRING,
+                      strlen(WEAK_KEY_TESTSTRING)+1,
+                      &weak_key,
+                      INITVALUE,
+                      result);
+   
+  if (size == -1) {
+    printf("weakkeytest failed: encryptBlock returned %d\n",
+           size);
+    return 1;
+  }
+
+  size = decryptBlock(&weak_key,
+                      result,
+                      size,
+                      INITVALUE,
+                      res);
+
+  if ((strlen(WEAK_KEY_TESTSTRING)+1) != size) {
+    printf("weakkeytest failed: decryptBlock returned %d\n",
+           size);
+    return 1;
+  }
+  if (0 != strcmp(res,WEAK_KEY_TESTSTRING)) {
+    printf("weakkeytest failed: %s != %s\n", res, WEAK_KEY_TESTSTRING);
+    return 1;
+  } 
+  else
+    return 0;
+}
+
+int getWeakKeys() {
+
+  SESSIONKEY sessionkey;
+  int number_of_weak_keys = 0;
+  int number_of_runs;
+
+  gcry_cipher_hd_t handle;
+  int rc;
+  
+  for (number_of_runs = 0; number_of_runs < MAX_WEAK_KEY_TRIALS; 
+       number_of_runs++) {
+
+    if (number_of_runs % 1000 == 0) printf(".");
+      /*printf("Got to run number %d.\n", number_of_runs);*/
+    makeSessionkey(&sessionkey);
+
+    rc = gcry_cipher_open(&handle,
+                         GCRY_CIPHER_BLOWFISH,
+                         GCRY_CIPHER_MODE_CFB,
+                         0);
+
+    if (rc) {
+      printf("testweakkey: gcry_cipher_open failed on trial %d. %s\n",
+            number_of_runs, gcry_strerror(rc));
+      rc = 0;
+      continue;
+    }
+
+    rc = gcry_cipher_setkey(handle,
+                           &sessionkey, 
+                           sizeof(SESSIONKEY));
+
+    if ((char)rc == GPG_ERR_WEAK_KEY) {    
+      printf("\nWeak key (in hex): ");
+      printWeakKey(&sessionkey);
+      printf("\n");
+      number_of_weak_keys++;
+    }
+    else if (rc) {
+      printf("\nUnexpected error generating keys. Error is %s\n", 
+             gcry_strerror(rc));
+    }
+
+    gcry_cipher_close(handle);
+           
+  }
+
+  return number_of_weak_keys;
+}
+#endif
+
+int main(int argc, char * argv[]) {
+
+#if USE_GCRYPT
+  int weak_keys; 
+
+  if (GENERATE_WEAK_KEYS) {
+    weak_keys = getWeakKeys();
+  
+    if (weak_keys == 0) {
+      printf("No weak keys found in %d runs.", MAX_WEAK_KEY_TRIALS);
+    }
+    else {
+      printf("%d weak keys found in %d.\n",weak_keys, MAX_WEAK_KEY_TRIALS);
+    }
+  }
+
+  if (testWeakKey() == 0)
+     return 0;
+  else {
+    printf("WEAK KEY TEST FAILED.\n");
+    return -1;
+  }
+#else
+  return 0;
+#endif
+} 
+
+/* end of weakkeytest.c */

Added: freeway/src/org/gnu/freeway/transport/AbstractSession.java
===================================================================
--- freeway/src/org/gnu/freeway/transport/AbstractSession.java  2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/transport/AbstractSession.java  2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,208 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.transport;
+
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+import java.util.logging.*;
+
+/**
+ * Transport session identifying a connection.
+ */
+
+public abstract class AbstractSession extends LoggedObject implements Session
+{
+       /** */
+       protected AbstractTransport     transport;
+
+       /** To whom are we talking to ? */
+       private HostIdentity                    remote;
+
+       /** Number of users of this session. */
+       private int                             lock;
+
+       /** */
+       private boolean                 ended;
+
+
+       protected AbstractSession( AbstractTransport t )
+       {
+               super(true);
+               transport=t;
+               remote=null;
+               lock=1;
+               ended=false;
+       }
+
+       public String toString()
+       {
+               return "Abstract session";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public boolean send( Persistent p )
+       {
+               ByteBuffer      buf;
+
+               buf=ByteBuffer.allocateDirect(p.getByteSize());
+               PersistentHelper.writeFully(p,buf);
+               return send(buf,Crypto.crc32(buf),false);
+       }
+
+       public boolean send( Persistent[] p )
+       {
+               byte[]          b;
+               ByteBuffer      buf;
+
+               b=PersistentHelper.toBytes(p);
+
+               buf=ByteBuffer.allocateDirect(b.length);
+               buf.put(b);
+               return send(buf,Crypto.crc32(buf),false);
+       }
+
+       public boolean send( ByteBuffer buf )
+       {
+               return send(buf,Crypto.crc32(buf),false);
+       }
+
+       public boolean send( ByteBuffer buf, SessionKey skey )
+       {
+               int     crc;
+
+               crc=Crypto.crc32(buf);
+
+               buf=skey.encrypt(buf);
+               if (buf==null) {
+                       log(Level.SEVERE,"Failed to encrypt message !");
+                       return false;
+                       }
+               return send(buf,crc,true);
+       }
+
+       public boolean send( ByteBuffer buf, int crc, boolean encrypted )
+       {
+               int     percent;
+
+               percent=transport.getPercentRandomOutboundDrop();
+               if (percent>0 && percent>Crypto.nextInt(100)) {
+                       debug("Simulated random network loss, message 
dropped.");
+                       return true;    // simulate random network loss
+                       }
+               return sendImpl(buf,crc,encrypted);
+       }
+
+       /**
+        * Send a message to the remote node. Drop if the operation would block.
+        *
+        * @param buf the message to send whose size should be <= mtu
+        * @param crc   the CRC of the (plaintext) message
+        * @param encrypted true if the message is encrypted
+        * @return false on error, true on success; after any error,
+        *         the caller must call "disconnect" and not continue
+        *         using the session afterwards (useful if the other
+        *         side closed the connection).
+        */
+
+       public abstract boolean sendImpl( ByteBuffer buf, int crc, boolean 
encrypted );
+
+       public boolean sendReliable( ByteBuffer buf, SessionKey skey )
+       {
+               int     crc;
+
+               crc=Crypto.crc32(buf);
+
+               buf=skey.encrypt(buf);
+               if (buf==null) {
+                       log(Level.SEVERE,"Failed to encrypt message !");
+                       return false;
+                       }
+               return sendReliableImpl(buf,crc,true);
+       }
+
+       /**
+        * Send a message to the remote node with increased reliablility 
(whatever that means is up to the transport).
+        * If an error is returned, the caller must call "disconnect" and not 
continue using the session afterwards
+        * (useful if the other side closed the connection).
+        *
+        * @param buf           The message to send whose size should be <= mtu.
+        * @param crc           The CRC of the *plain* message.
+        * @param encrypted     Is the message encrypted ?
+        * @return                      True on success, false on error.
+        */
+
+       public abstract boolean sendReliableImpl( ByteBuffer buf, int crc, 
boolean encrypted );
+
+       public HostIdentity getLocal()
+       {
+               return transport.getHost();
+       }
+
+       public HostIdentity getRemote()
+       {
+               return remote;
+       }
+
+       public void setRemote( HostIdentity h )
+       {
+               remote=(HostIdentity) PersistentHelper.copy(h);
+       }
+
+       public Transport getTransport()
+       {
+               return transport;
+       }
+
+       public synchronized int getLockCount()
+       {
+               return lock;
+       }
+
+       public synchronized boolean lock()
+       {
+               if (ended) {
+                       log(Level.WARNING,"Could not lock, session has been 
ended !");
+                       return false;
+                       }
+
+//debug("LOCK : "+lock+" -> "+(lock+1));
+               lock++;
+               return true;
+       }
+
+       public synchronized boolean unlock()
+       {
+               if (ended) {
+                       log(Level.WARNING,"Could not unlock, session has been 
ended !");
+                       return false;
+                       }
+//debug("UNLOCK : "+lock+" -> "+(lock-1));
+               lock--;
+               if (lock==0) {
+                       log(Level.FINE,"Session no longer associated, mark it 
as ended !");
+                       ended=true;
+                       }
+               return true;
+       }
+
+       public boolean isEnded()
+       {
+               return ended;
+       }
+
+       public void markEnded()
+       {
+               ended=true;
+       }
+
+       public void destroy()
+       {
+       }
+}

Added: freeway/src/org/gnu/freeway/transport/AbstractTransport.java
===================================================================
--- freeway/src/org/gnu/freeway/transport/AbstractTransport.java        
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/transport/AbstractTransport.java        
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,102 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.transport;
+
+import org.gnu.freeway.server.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+
+/**
+ *
+ */
+
+public abstract class AbstractTransport extends LoggedObject implements 
Transport
+{
+       private int                                     protocol;
+       private String                          name;
+       protected CoreForTransport      coreAPI;
+
+       /** What percentage of outbound messages should be randomly dropped ?
+        (for testing unreliability of the network). */
+       private int                             percentRandomOutboundDrop;
+
+
+       protected AbstractTransport( int p, String str )
+       {
+               super(true);
+               protocol=p;
+               name=str;
+               percentRandomOutboundDrop=0;
+       }
+
+       public String toString()
+       {
+               return "Abstract transport [name="+name+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int getPercentRandomOutboundDrop()
+       {
+               return percentRandomOutboundDrop;
+       }
+
+       /**
+        * Which percentage of outbound messages should gnunetd drop at random 
(to simulate network unreliability or congestion) ?
+        *
+        * @param value
+        */
+
+       public void setPercentRandomOutboundDrop( int value )
+       {
+               percentRandomOutboundDrop = value;
+       }
+
+       public int getProtocol()
+       {
+               return protocol;
+       }
+
+       public String getName()
+       {
+               return name;
+       }
+
+       public HostIdentity getHost()
+       {
+               return (coreAPI!=null ? coreAPI.getIdentity() : null);
+       }
+
+       public void init( CoreForTransport api )
+       {
+               coreAPI=api;
+       }
+
+       public void done()
+       {
+               coreAPI=null;
+       }
+
+       /**
+        * Create signed HELO for this transport.
+        *
+        * @param priv The private key of the owner. Used to authenticate and 
to sign the message.
+        * @return A signed HELO message.
+        */
+
+       public P2PHello createSignedHELO( PrivateKey priv )
+       {
+               P2PHello        helo;
+
+               helo=createHELO();
+               if (helo!=null) {
+                       if (!helo.sign(priv)) {
+                               helo=null;
+                               }
+                       }
+               return helo;
+       }
+}

Added: freeway/src/org/gnu/freeway/transport/MessageHandler.java
===================================================================
--- freeway/src/org/gnu/freeway/transport/MessageHandler.java   2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/transport/MessageHandler.java   2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,16 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.transport;
+
+import org.gnu.freeway.util.net.*;
+
+/**
+ *
+ */
+
+public interface MessageHandler
+{
+       public boolean nextMessage( MessagePack pack, Persistent msg );
+}

Added: freeway/src/org/gnu/freeway/transport/MessagePack.java
===================================================================
--- freeway/src/org/gnu/freeway/transport/MessagePack.java      2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/transport/MessagePack.java      2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,295 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.transport;
+
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.io.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+import java.util.*;
+import java.util.logging.*;
+
+/**
+ * Type of a struct passed to receive.
+ */
+
+public class MessagePack extends LoggedObject
+{
+       /** The session associated with the message on the transport layer 
side. */
+       private Session                 session;
+
+       /** The message itself (note that it can point to multiple p2p 
messages). */
+       private ByteBuffer              parts;
+       private int     bufferSize;
+
+       /** The checksum of the message (over size bytes from 
<code>parts</code>)/CRC checksum of the packet. */
+       private int                             checksum;
+
+       /** True if the message was encrypted, false otherwise. */
+       private boolean                 encrypted;
+
+
+       protected MessagePack()
+       {
+               super(true);
+               session=null;
+               parts=null;
+               bufferSize=0;
+               checksum=0;
+               encrypted=false;
+       }
+
+       public MessagePack( Session s )
+       {
+               this();
+               setSession(s);
+       }
+
+       public String toString()
+       {
+               return "Message pack [session="+session+", size="+getSize()+", 
encrypted="+encrypted+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       /**
+        * Returns the session / may be null
+        * @return The session associated with this message pack, which may be 
null.
+        */
+
+       public Session getSession()
+       {
+               return session;
+       }
+
+       public void setSession( Session s )
+       {
+               session=s;
+       }
+
+       public void printTo( String title, String str )
+       {
+               FileLocation    loc;
+               MappedFile      f;
+
+               loc=new FileLocation(str);
+               loc.create();
+
+               f=loc.open();
+               f.seekEnd();
+               f.writeString("====== "+title+"\n");
+               f.writeString("  infos     : "+getMessagesInfo()+"\n");
+               f.writeString("  session   : "+session+"\n");
+               f.writeString("  checksum  : "+Utils.toHex(checksum)+"\n");
+               f.writeString("  encrypted : "+encrypted+"\n");
+               f.writeString("  buffer    : 
"+Utils.toString(parts,0,parts.position(),"DUMP",32)+"\n");
+               f.writeString("\n");
+               f.writeString("\n");
+               f.close();
+       }
+//     public boolean checkCRC()
+//     {
+//     }
+       public int getCRC()
+       {
+               return checksum;
+       }
+
+       public boolean isEncrypted()
+       {
+               return encrypted;
+       }
+
+       public boolean decryptWith( SessionKey key )
+       {
+               if (!encrypted) {
+                       return false;
+                       }
+               if (!key.decrypt(parts,SessionKey.INITVALUE)) {
+                       return false;
+                       }
+               encrypted=false;
+               return true;
+       }
+
+       public int getMessagesCount()
+       {
+               int     len,i,j;
+
+               if (encrypted) {
+                       return -1;
+                       }
+
+               j=0;
+               if (parts!=null) {
+                       for (i=0; i<parts.position()-4; i+=len) {
+                               len=parts.getShort(i) & 0x0000ffff;
+                               j++;
+                               }
+                       }
+               return j;
+       }
+
+       public int[] getMessagesTypes()
+       {
+               int[]   types;
+               int             len,i,j;
+
+               if (encrypted) {
+                       return null;
+                       }
+
+               types=new int[getMessagesCount()];
+               for (i=j=0; j<types.length; i+=len) {
+                       len=parts.getShort(i) & 0x0000ffff;
+                       types[j++]=parts.getShort(i+2) & 0x0000ffff;
+                       }
+               return types;
+       }
+
+       protected void addInfo( StringBuffer buf, int id, int count )
+       {
+               String  str;
+
+               str=P2PMessage.nameFor(id);
+               buf.append(" ");
+               buf.append(str!=null ? str : String.valueOf(id));
+               if (count>1) {
+                       buf.append("*"+count);
+                       }
+               buf.append(",");
+       }
+
+       public String getMessagesInfo()
+       {
+               int[]                   types;
+               StringBuffer    buf;
+               int                             last,count,i;
+
+               buf=new StringBuffer();
+               buf.append("[");
+
+               if (!encrypted) {
+                       last=-1;
+                       count=0;
+
+                       types=getMessagesTypes();
+                       for (i=0; i<types.length; i++) {
+                               if (types[i]!=last) {
+                                       if (last!=-1) {
+                                               addInfo(buf,last,count);
+                                               }
+                                       last=types[i];
+                                       count=0;
+                                       }
+                               count++;
+                               }
+
+                       if (last!=-1) {
+                               addInfo(buf,last,count);
+                               }
+
+                       if (types.length>0) {
+                               buf.setLength(buf.length()-1);
+                               }
+                       }
+               else {
+                       buf.append(" encrypted");
+                       }
+
+               buf.append(" ]");
+               return buf.toString();
+       }
+
+//     public Persistent[] extractMessages( PersistentDecoder dec );
+
+       public boolean forEachMessage( PersistentDecoder decoder, 
MessageHandler handler  )
+       {
+               PersistentReader        queue;
+               Persistent              p;
+               boolean                 done;
+
+               if (Crypto.crc32(parts)!=checksum) {
+                       log(Level.WARNING,"CRC check failed ("+checksum+" 
expected, got "+Crypto.crc32(parts)+"), message of size "+parts.position()+" 
ignored.");
+                       return false;
+                       }
+
+               queue=new PersistentReader(parts);
+               done=false;
+               while (queue.canConsume() && !done) {
+                       p=queue.consume(decoder);
+
+                       if (!handler.nextMessage(this,p)) {
+                               log(Level.WARNING,"Unsupported message (for 
plaintext) : "+p);
+                               done=true;
+                               }
+                       }
+
+               if (done) {
+                       queue.clear();
+                       return false;
+                       }
+
+               if (queue.getNotConsumed()>0) {
+                       trace("Not all bytes have been consumed, data 
corruption ?");
+                       queue.clear();
+                       return false;
+                       }
+
+               queue.clear();
+               return true;
+       }
+
+       public Iterator messages( final PersistentDecoder decoder  )
+       {
+               if (Crypto.crc32(parts)!=checksum) {
+                       log(Level.WARNING,"CRC check failed ("+checksum+" 
expected, got "+Crypto.crc32(parts)+"), message of size "+parts.position()+" 
ignored.");
+                       return AbstractIterator.EMPTY;
+                       }
+
+               final PersistentReader  _reader = new PersistentReader(parts);
+               return new AbstractIterator() {
+                       public Object fetch()
+                       {
+                               if (_reader.canConsume()) {
+                                       return _reader.consume(decoder);
+                                       }
+                               //todo: super dangereux, on est pas sur de tout 
parcourir l'iterateur !!!!!
+                               if (_reader.getNotConsumed()>0) {
+                                       trace("Not all bytes have been 
consumed, data corruption ?");
+                                       }
+                               _reader.clear();
+                               return null;
+                       }
+                       };
+       }
+
+       public int getSize()
+       {
+               return bufferSize;
+       }
+
+       public ByteBuffer getMessages()
+       {
+               return parts;
+       }
+
+       /**
+        * @param buf   The buffer containing messages to exchange. Should not 
be modified, no copy is made.
+        * @param c
+        * @param e
+        */
+
+       public void setMessages( ByteBuffer buf, int c, boolean e )
+       {
+               parts=buf;
+               bufferSize=(parts!=null ? parts.position() : 0);
+               checksum=c;
+               encrypted=e;
+       }
+}

Added: freeway/src/org/gnu/freeway/transport/Session.java
===================================================================
--- freeway/src/org/gnu/freeway/transport/Session.java  2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/transport/Session.java  2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,86 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.transport;
+
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * A session representation on the transport layer side.
+ */
+
+public interface Session
+{
+       /**
+        * @return
+        */
+
+       public HostIdentity getLocal();
+
+       /**
+        * @return
+        */
+
+       public HostIdentity getRemote();
+
+       /**
+        * @return
+        */
+
+       public String getRemoteAddress();
+
+       /**
+        * Returns the transport used to exchange messages with remote host.
+        * @return
+        */
+
+       public Transport getTransport();
+
+       /**
+        * A (core) Session is to be associated with a transport session. The
+        * transport service may want to know in order to call back on the
+        * core if the connection is being closed. Associate can also be
+        * called to test if it would be possible to associate the session
+        * later, in this case, call disconnect afterwards. This can be used
+        * to test if the connection must be closed by the core or if the core
+        * can assume that it is going to be self-managed (if associate
+        * returns true and session was null, the transport layer is responsible
+        * for eventually freeing resources associated with the tesession). If
+        * session is not null, the core takes responsbility for eventually
+        * calling disconnect.
+        *
+        * @return true if the session could be associated,
+        *         false if not.
+        */
+
+       public boolean lock();
+
+       /**
+        * Disconnect from a remote node. A session can be closed
+        * by either the transport layer calling "closeSession" on
+        * the core API or by the core API calling "disconnect"
+        * on the transport API. Neither closeSession nor
+        * disconnect should call the other method. Due to
+        * potentially concurrent actions (both sides close the
+        * connection simultaneously), either API must tolerate
+        * being called from the other side.
+        *
+        * @return true on success, false if the operation failed
+        */
+
+       public boolean unlock();//todo: renommer disconnect ???
+
+       public boolean send( Persistent p );
+
+       public boolean send( Persistent[] p );
+
+       public boolean send( ByteBuffer buf );
+
+       public boolean send( ByteBuffer buf, SessionKey skey );
+
+       public boolean sendReliable( ByteBuffer buf, SessionKey skey );
+}

Added: freeway/src/org/gnu/freeway/transport/Transport.java
===================================================================
--- freeway/src/org/gnu/freeway/transport/Transport.java        2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/transport/Transport.java        2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,159 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.transport;
+
+import org.gnu.freeway.server.*;
+import org.gnu.freeway.util.crypto.*;
+
+/**
+ * Interface defining a list of methods that every transport layer must 
provide.
+ */
+
+public interface Transport
+{
+       /**
+        * Just the version number of GNUnet-transport implementation.
+        * Encoded as
+        * 0.6.1d  => 0x00060100
+        * 4.5.2   => 0x04050200
+        *
+        * Note that this version number is only changed if
+        * something changes in the transport API.  It follows
+        * roughly the main GNUnet version scheme, but is
+        * more a compatibility ID.
+        */
+       public static final int GNUNET_TRANSPORT_VERSION        =       
0x00060105;
+
+
+       /** protocol number for "unspecified" */
+       public static final int ANY_PROTOCOL_NUMBER     =       0;
+
+       /** protocol number for 'NAT'.  Used as the advertisements for peers 
behind a NAT box. */
+       public static final int NAT_PROTOCOL_NUMBER     =       1;
+
+       /** protocol number of TCP. Do NEVER change, also used in other context 
! */
+       public static final int TCP_PROTOCOL_NUMBER     =       6;
+
+       /** protocol number for HTTP (80 is too big, so 8 will have to do) */
+       public static final int HTTP_PROTOCOL_NUMBER    =       8;
+
+       /** Protocol number for TCP on IPv6 (TCP+6) */
+       public static final int TCP6_PROTOCOL_NUMBER    =       12;
+
+       /** protocol number of UDP. Do NEVER change, also used in other context 
! */
+       public static final int UDP_PROTOCOL_NUMBER     =       17;
+
+       /** Protocol number for UDP on IPv6 (UDP+6) */
+       public static final int UDP6_PROTOCOL_NUMBER    =       23;
+
+       /** protocol number for SMTP */
+       public static final int SMTP_PROTOCOL_NUMBER    =       25;
+
+
+       /**
+        * Returns the user-friendly name of the protocol.
+        * @return
+        */
+
+       public String getName();
+
+       /**
+        * The number of the protocol that is supported by this transport API 
(i.e. 6 tcp, 17 udp, 80 http, 25 smtp, etc.)
+        * @return
+        */
+
+       public int getProtocol();
+
+       /**
+        * The MTU for the protocol (e.g. 1472 for UDP). Can be up to 65535 for 
stream-oriented transport protocols)
+        * @return
+        */
+
+       public int getMTU();
+
+       /**
+        * How costly is this transport protocol/mechanism (compared to the 
other transports, UDP and TCP are scaled to be both 100).
+        * The cost is used by GNUnet to select the most preferable mode of 
transportation.
+        *
+        * @return
+        */
+
+       public int getCost();
+
+       /**
+        * @return
+        */
+
+       public HostIdentity getHost();
+
+       /**
+        * @param api
+        */
+
+       public void init( CoreForTransport api );
+
+       /**
+        *
+        */
+
+       public void done();
+
+       /**
+        * Start the server process to receive inbound traffic.
+        * @return true on success, false if the operation failed
+        */
+
+       public boolean launch();
+
+       /**
+        * Shutdown the server process (stop receiving inbound
+        * traffic). Maybe restarted later!
+        * @return
+        */
+
+       public boolean shutdown();
+
+       /**
+        * Create a HELO-Message for the current node. The HELO is
+        * created without signature, timestamp, senderIdentity
+        * or publicKey. The GNUnet core will sign the message
+        * and add these other fields. The callee is only
+        * responsible for filling in the protocol number,
+        * senderAddressSize and the senderAddress itself.
+        *
+        * @return the HELO message, null on error (e.g. send-only transports 
return null here)
+        */
+
+       public P2PHello createHELO();
+
+       public P2PHello createSignedHELO( PrivateKey priv );
+
+       /**
+        * Convert transport address to human readable string.
+        * @param helo
+        * @return
+        */
+
+       public String addressToString( P2PHello helo );
+
+       /**
+        * Verify that a HELO-Message is correct (a node is potentially 
reachable at that address).
+        * Core will only play ping pong after this verification passed.
+        *
+        * @param helo the HELO message to verify (the signature/crc have been 
verified before)
+        * @return true if the helo is well-formed
+        */
+
+       public boolean verifyHELO( P2PHello helo );
+
+       /**
+        * Establish a connection to a remote node.
+        *
+        * @param helo the HELO-Message for the target node
+        * @return the session handle on success, null if the operation failed
+        */
+
+       public Session connect( P2PHello helo );
+}

Added: freeway/src/org/gnu/freeway/transport/http.c
===================================================================
--- freeway/src/org/gnu/freeway/transport/http.c        2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/transport/http.c        2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,1594 @@
+/*
+     This file is part of GNUnet
+
+     GNUnet is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with GNUnet; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file transports/http.c
+ * @brief Implementation of the HTTP transport service
+ * @author Christian Grothoff
+ *
+ * The basic protocol looks like this:
+ * - client sends: 
+ *   POST / HTTP/1.1 CRLF
+ *   Host:IP CRLF
+ *   Transfer-Encoding: chunked CRLF
+ *   Content-Type: text/html CRLF
+ *
+ *   Then a first chunk of 24 bytes with the
+ *   welcome-message.
+ *
+ *   And then an arbitrary number of chunks (CRLF HEX, CRLF, Data)
+ *
+ *
+ * - server replies to the welcome-message: 
+ *   HTTP/1.1 200 OK CRLF
+ *   Server: Apache/1.3.27 CRLF
+ *   Transfer-Encoding: chunked CRLF
+ *   Content-Type: text/html CRLF
+ *
+ *   And then transmits an arbitrary number of chunks (CRLF HEX, CRLF, Data)
+ *
+ * Todo:
+ * - fix "sleep on connect": add queue for outbound messages
+ *   (at least of size 2 messages)
+ * - increase http compliancy; so far, the implementation of the
+ *   protocol is very flawed (no good error-responses if non-peers
+ *   connect, and even for the P2P basic protocol, I'm not sure how
+ *   close it is to actual HTTP.   
+ * - the code is not really pretty
+ **/
+
+#include "gnunet_util.h"
+#include "gnunet_transport.h"
+#include "platform.h"
+
+#define DEBUG_HTTP NO
+
+/**
+ * after how much time of the core not being associated with a http
+ * connection anymore do we close it? 
+ **/
+#define HTTP_TIMEOUT 30 * cronSECONDS
+
+/**
+ * Host-Address in a HTTP network.
+ **/
+typedef struct {
+  /**
+   * claimed IP of the sender, network byte order 
+   **/  
+  IPaddr ip;
+
+  /**
+   * claimed port of the sender, network byte order 
+   **/
+  unsigned short port; 
+
+  /**
+   * reserved (set to 0 for signature verification) 
+   **/
+  unsigned short reserved; 
+
+} HostAddress;
+
+/**
+ * HTTP Message-Packet header.  Size is transmitted as part of the
+ * HTTP protocol.  
+ **/
+typedef struct {
+
+  /**
+   * CRC checksum of the packet  (network byte order)
+   **/ 
+  int checkSum;
+  
+  int isEncrypted;
+
+  /**
+   * This struct is followed by MESSAGE_PARTs - until size is reached 
+   * There is no "end of message".
+   **/
+  p2p_HEADER parts[0];
+} HTTPMessagePack;
+
+/* How much do we read from a buffer at least? Answer: for now, at
+   MOST the size of the message, since we MUST not read the next
+   header(s) by accident! */
+#define MIN_BUF_READ (4 + sizeof(HTTPMessagePack))
+/* how long do we allow an http-header to be at
+   most? */
+#define MAX_HTTP_HEADER 2048
+
+/**
+ * Initial handshake message.  Note that the beginning
+ * must match the CS_HEADER since we are using tcpio.
+ **/
+typedef struct {
+  /**
+   * size of the handshake message, in nbo, value is 24 
+   **/    
+  unsigned short size;
+
+  /**
+   * "message type", HTTP version number, always 0.
+   **/
+  unsigned short version;
+
+  /**
+   * Identity of the node connecting (HTTP client) 
+   **/
+  HostIdentity clientIdentity;
+} HTTPWelcome;
+
+/**
+ * Transport Session handle.
+ **/
+typedef struct {
+  /**
+   * the http socket 
+   **/
+  int sock;
+
+  /**
+   * IP & port of the remote host
+   **/
+  unsigned int hostIP;
+  unsigned int hostPort;
+
+  /**
+   * number of users of this session 
+   **/
+  unsigned int users;
+
+  /**
+   * Last time this connection was used
+   **/
+  cron_t lastUse;
+
+  /**
+   * mutex for synchronized access to 'users' 
+   **/
+  Mutex lock;
+
+  /**
+   * To whom are we talking to (set to our identity
+   * if we are still waiting for the welcome message)
+   **/
+  HostIdentity sender;
+
+  /**
+   * Are we still expecting the welcome? (YES/NO)
+   **/
+  int expectingWelcome;
+
+  /**
+   * Current read position in rbuff.
+   **/
+  unsigned int rpos;  
+
+  /**
+   * Current size of the read buffer.
+   **/
+  unsigned int size;
+
+  /**
+   * The read buffer (used only for the actual data).
+   **/
+  char * rbuff;
+  
+  /**
+   * Input buffer used for the http header lines.
+   * Read fills this buffer until we hit the end of
+   * the request header (CRLF).  Then we switch
+   * to rbuff.
+   **/
+  char * httpReadBuff;
+
+  /**
+   * Current write-position in httpReadBuff;
+   **/
+  unsigned int httpRPos;
+  
+  /**
+   * Space available in httpReadBuff
+   **/
+  unsigned int httpRSize;
+
+  /**
+   * Position in the write buffer (how many bytes are still waiting to
+   * be transmitted? -- always the first wsize bytes in wbuff are
+   * guaranteed to be valid and are pending).
+   **/
+  unsigned int wsize;
+
+  /**
+   * The write buffer.
+   **/
+  char * wbuff;
+
+  /**
+   * Output buffer used for the http header lines.
+   * Read fills this buffer until we hit the end of
+   * the request header (CRLF).  Then we switch
+   * to rbuff.
+   **/
+  char * httpWriteBuff;
+
+  /**
+   * Total size of httpWriteBuff
+   **/
+  unsigned int httpWSize;
+} HTTPSession;
+
+/* *********** globals ************* */
+
+/**
+ * apis (our advertised API and the core api ) 
+ **/
+static CoreAPIForTransport * coreAPI;
+static TransportAPI httpAPI;
+
+/**
+ * one thread for listening for new connections,
+ * and for reading on all open sockets 
+ **/
+static PTHREAD_T listenThread;
+
+/**
+ * sock is the http socket that we listen on for new inbound
+ * connections.
+ **/
+static int http_sock;
+
+/**
+ * http_pipe is used to signal the thread that is
+ * blocked in a select call that the set of sockets to listen
+ * to has changed.
+ **/
+static int http_pipe[2];
+
+/**
+ * Array of currently active HTTP sessions. 
+ **/
+static TSession ** tsessions = NULL;
+static int tsessionCount;
+static int tsessionArrayLength;
+
+/**
+ * handles for statistics 
+ **/
+static int stat_octets_total_http_in;
+static int stat_octets_total_http_out;
+
+/* configuration */
+static CIDRNetwork * filteredNetworks_;
+
+/**
+ * Lock for access to mutable state of the module,
+ * that is the configuration and the tsessions array.
+ * Note that we ONLY need to synchronize access to
+ * the tsessions array when adding or removing sessions,
+ * since removing is done only by one thread and we just
+ * need to avoid another thread adding an element at the
+ * same point in time. We do not need to synchronize at
+ * every access point since adding new elements does not
+ * prevent the select thread from operating and removing
+ * is done by the only therad that reads from the array.
+ **/
+static Mutex httplock;
+
+/**
+ * Semaphore used by the server-thread to signal that
+ * the server has been started -- and later again to
+ * signal that the server has been stopped.
+ **/
+static Semaphore * serverSignal = NULL;
+static int http_shutdown = YES;
+
+/**
+ * The HTTP proxy (optional)
+ **/
+static struct sockaddr_in theProxy;
+
+/* ******************** helper functions *********************** */
+
+/**
+ * Check if we are allowed to connect to the given IP.
+ **/
+static int isBlacklisted(IPaddr ip) {
+  int ret;
+
+  MUTEX_LOCK(&httplock);
+  ret = checkIPListed(filteredNetworks_,
+                     ip);
+  MUTEX_UNLOCK(&httplock);
+  return ret;
+}
+
+/**
+ * Write to the pipe to wake up the select thread (the set of
+ * files to watch has changed).
+ **/
+static void signalSelect() {
+  char i = 0;
+  int ret;
+
+  ret = WRITE(http_pipe[1],
+             &i,
+             sizeof(char));
+  if (ret != sizeof(char))
+      LOG(LOG_ERROR,
+         "ERROR: write to http pipe (signalSelect) failed: %s\n",
+         STRERROR(errno));
+}
+
+/**
+ * Disconnect from a remote node. May only be called
+ * on sessions that were aquired by the caller first.
+ * For the core, aquiration means to call associate or
+ * connect. The number of disconnects must match the
+ * number of calls to connect+associate.
+ *
+ * @param tsession the session that is closed
+ * @return OK on success, SYSERR if the operation failed
+ **/
+static int httpDisconnect(TSession * tsession) {
+  if (tsession->internal != NULL) {
+    HTTPSession * httpsession = tsession->internal;
+
+    MUTEX_LOCK(&httpsession->lock);
+    httpsession->users--;
+    if (httpsession->users > 0) {
+      MUTEX_UNLOCK(&httpsession->lock);
+      return OK;
+    }
+    MUTEX_UNLOCK(&httpsession->lock);
+    MUTEX_DESTROY(&httpsession->lock);
+    FREENONNULL(httpsession->rbuff);
+    FREENONNULL(httpsession->httpReadBuff);
+    FREENONNULL(httpsession->wbuff);
+    FREENONNULL(httpsession->httpWriteBuff);
+    FREE(httpsession);
+    FREE(tsession);
+  }
+  return OK;
+}
+
+/**
+ * Remove a session, either the other side closed the connection
+ * or we have otherwise reason to believe that it should better
+ * be killed. Destroy session closes the session as far as the
+ * HTTP layer is concerned, but since the core may still have
+ * references to it, httpDisconnect may not instantly free all
+ * the associated resources. <p>
+ *
+ * destroySession may only be called if the httplock is already
+ * held.
+ *
+ * @param i index to the session handle
+ **/
+static void destroySession(int i) {  
+  HTTPSession * httpSession;
+
+  httpSession = tsessions[i]->internal;
+  if (httpSession->sock != -1)
+    if (0 != SHUTDOWN(httpSession->sock, SHUT_RDWR))
+      LOG(LOG_EVERYTHING,
+         "EVERYTHING: error shutting down socket %d: %s\n",
+         httpSession->sock,
+         STRERROR(errno));
+  CLOSE(httpSession->sock);
+  httpSession->sock = -1;
+  httpDisconnect(tsessions[i]);
+  tsessions[i] = tsessions[--tsessionCount];
+  tsessions[tsessionCount] = NULL;
+}
+
+/**
+ * Get the GNUnet HTTP port from the configuration, or from
+ * /etc/services if it is not specified in the config file.
+ **/
+static unsigned short getGNUnetHTTPPort() {
+  struct servent * pse;        /* pointer to service information entry */
+  unsigned short port;
+
+  port = (unsigned short) getConfigurationInt("HTTP",
+                                             "PORT");
+  if (port == 0) { /* try lookup in services */
+    if ((pse = getservbyname("tcp", "http"))) 
+      port = htons(pse->s_port);      
+  }
+  return port;
+}
+
+/**
+ * A (core) Session is to be associated with a transport session. The
+ * transport service may want to know in order to call back on the
+ * core if the connection is being closed. Associate can also be
+ * called to test if it would be possible to associate the session
+ * later, in this case the argument session is NULL. This can be used
+ * to test if the connection must be closed by the core or if the core
+ * can assume that it is going to be self-managed (if associate
+ * returns OK and session was NULL, the transport layer is responsible
+ * for eventually freeing resources associated with the tesession). If
+ * session is not NULL, the core takes responsbility for eventually
+ * calling disconnect.
+ * 
+ * @param tsession the session handle passed along
+ *   from the call to receive that was made by the transport
+ *   layer
+ * @return OK if the session could be associated,
+ *         SYSERR if not.
+ **/
+static int httpAssociate(TSession * tsession) {
+  HTTPSession * httpSession;
+
+  if (tsession == NULL) {
+    LOG(LOG_FAILURE,
+       "FAILURE: assertFailed: httpAssociate called with tsession NULL\n");
+    return SYSERR;
+  }
+  httpSession = (HTTPSession*) tsession->internal;
+  MUTEX_LOCK(&httpSession->lock);
+  httpSession->users++;
+  MUTEX_UNLOCK(&httpSession->lock);
+  return OK;
+}
+
+/**
+ * We're done processing a message.  Reset buffers as needed to
+ * prepare for receiving the next chunk.
+ **/
+static void messageProcessed(HTTPSession * httpSession) {
+  /* deallocate read buffer, we don't really know
+     how big the next chunk will be */
+  GROW(httpSession->rbuff,
+       httpSession->size,
+       0);
+  /* allocate read-buffer for the next header,
+     ALWAYS start with minimum size!!! */
+  GROW(httpSession->httpReadBuff,
+       httpSession->httpRSize,
+       MIN_BUF_READ);
+  httpSession->httpRPos = 0;
+}
+
+/**
+ * We have received more header-bytes.  Check if the HTTP header is
+ * complete, and if yes allocate rbuff and move the data-portion that
+ * was received over to rbuff (and reset the header-reader).
+ **/
+static void checkHeaderComplete(HTTPSession * httpSession) {
+  /* we expect 3 possible strings; either
+     "HTTP/1.1 200 OK%c%c"
+     "Server: Apache/1.3.27%c%c"
+     "Transfer-Encoding: chunked%c%c"
+     "Content-Type: text/html%c%c%"
+     (which we ignore)
+
+     or
+
+     POST / HTTP/1.1 CRLF
+     Host:IP CRLF
+     Transfer-Encoding: chunked CRLF
+     Content-Type: text/html CRLF
+     (which we also ignore)     
+
+     or just "CRLF%xCRLF" where "%x" is the length of
+     the next chunk (in hex); in this case, we grow rbuff to %x
+     and copy the rest of the httpReadBuff to rbuff (and reset 
+     httpReadBuff to NULL).        
+
+     */
+  unsigned int i;
+
+  for (i=0;i+4<httpSession->httpRPos;i++) {
+    if ( (httpSession->httpReadBuff[i] == '\r') &&
+        (httpSession->httpReadBuff[i+1] == '\n') ) {
+      unsigned int k;
+
+      k = i+2;
+      while ( (k < httpSession->httpRPos-1) &&
+             (httpSession->httpReadBuff[k] != '\r') )
+       k++;
+      if ( (k < httpSession->httpRPos-1) &&
+          (httpSession->httpReadBuff[k] == '\r') &&
+          (httpSession->httpReadBuff[k+1] == '\n') ) {
+       unsigned int len;
+       char * endPtr;
+
+       httpSession->httpReadBuff[k] = '\n';
+       len = strtol(&httpSession->httpReadBuff[i+2],
+                    &endPtr,
+                    16);
+       httpSession->httpReadBuff[k] = '\r';
+       if (endPtr == &httpSession->httpReadBuff[k]) {    
+         if (len >= 65536) {
+           LOG(LOG_WARNING,
+               "WARNING: size of http fragment too big (%d).\n",
+               len);
+         } else {          
+           GROW(httpSession->rbuff,
+                httpSession->size,
+                len);
+           memcpy(httpSession->rbuff,
+                  &httpSession->httpReadBuff[k+2],
+                  httpSession->httpRPos - (k+2));
+           httpSession->rpos = httpSession->httpRPos - (k+2);
+           GROW(httpSession->httpReadBuff,
+                httpSession->httpRSize,
+                0);
+           httpSession->httpRPos = 0;
+         }
+       } 
+      }
+    }
+  }
+}
+
+/**
+ * The socket of session i has data waiting, process!
+ * 
+ * This function may only be called if the httplock is
+ * already held by the caller.
+ **/
+static int readAndProcess(int i) {
+  TSession * tsession;
+  HTTPSession * httpSession;
+  int len;
+  HTTPMessagePack * pack;
+  MessagePack * mp;
+
+  tsession = tsessions[i];
+  if (SYSERR == httpAssociate(tsession))
+    return SYSERR;
+  httpSession = tsession->internal;
+  if (httpSession->size == 0) {
+    /* chunk read mode */
+    if (httpSession->httpRSize - httpSession->httpRPos < MIN_BUF_READ) {
+      if (httpSession->httpRSize >= MAX_HTTP_HEADER) {
+       len = -1; /* error! */
+       errno = 0; /* make sure it's not set to retry */
+      } else {
+       GROW(httpSession->httpReadBuff,
+            httpSession->httpRSize,
+            httpSession->httpRSize + MIN_BUF_READ);
+       len = READ(httpSession->sock,
+                  &httpSession->httpReadBuff[httpSession->httpRPos],
+                  httpSession->httpRSize - httpSession->httpRPos);      
+      }
+    } else
+      len = READ(httpSession->sock,
+                &httpSession->httpReadBuff[httpSession->httpRPos],
+                httpSession->httpRSize - httpSession->httpRPos);
+    if (len >= 0) {
+      httpSession->httpRPos += len;
+      checkHeaderComplete(httpSession);
+    }
+  } else {
+    /* data read mode */
+    len = READ(httpSession->sock,
+              &httpSession->rbuff[httpSession->rpos],
+              httpSession->size - httpSession->rpos);
+    if (len >= 0)
+      httpSession->rpos += len;
+  }
+  cronTime(&httpSession->lastUse);
+  if (len == 0) {
+    httpDisconnect(tsession);
+#if DEBUG_HTTP
+    LOG(LOG_DEBUG,
+       "DEBUG: READ on socket %d returned 0 bytes, closing connection\n",
+       httpSession->sock);
+#endif
+    return SYSERR; /* other side closed connection */
+  }
+  if (len < 0) {
+    if ( (errno == EINTR) ||
+        (errno == EAGAIN) ) { 
+#if DEBUG_HTTP
+      LOG(LOG_DEBUG,
+         "DEBUG: READ on socket %d returned %s, closing connection\n",
+         httpSession->sock,
+         STRERROR(errno));
+#endif
+      httpDisconnect(tsession);
+      return SYSERR;    
+    }
+#if DEBUG_HTTP
+    LOG(LOG_INFO,
+       "INFO: read failed on peer http connection (%d), closing (%s).\n",
+       len,
+       STRERROR(errno));
+#endif
+    httpDisconnect(tsession);
+    return SYSERR;
+  }
+  incrementBytesReceived(len);
+  statChange(stat_octets_total_http_in,
+            len);
+#if DEBUG_HTTP
+  LOG(LOG_DEBUG,
+      "DEBUG: Read %d bytes on socket %d, now having %d of %d (%d)\n",
+      len,
+      httpSession->sock, 
+      httpSession->rpos,
+      httpSession->size,
+      httpSession->httpRPos);
+#endif
+  if ( (httpSession->rpos < 2) ||
+       (httpSession->rpos < httpSession->size) ) {
+    httpDisconnect(tsession);
+    return OK;
+  }
+ 
+  /* complete message received, let's check what it is */
+  if (YES == httpSession->expectingWelcome) {
+    HTTPWelcome * welcome;
+#if DEBUG_HTTP
+    HexName hex;
+#endif
+
+    welcome = (HTTPWelcome*) &httpSession->rbuff[0];
+    if ( (ntohs(welcome->version) != 0) ||
+        (ntohs(welcome->size) != sizeof(HTTPWelcome)) ) {
+      LOG(LOG_WARNING,
+         "WARNING: expected welcome on http connection, got garbage (%d, %d). 
Closing.\n",
+         ntohs(welcome->version),
+         ntohs(welcome->size));
+      httpDisconnect(tsession);
+      return SYSERR;
+    }
+    httpSession->expectingWelcome = NO;
+    memcpy(&httpSession->sender,
+          &welcome->clientIdentity,
+          sizeof(HostIdentity));     
+#if DEBUG_HTTP
+    IFLOG(LOG_DEBUG,
+         hash2hex(&httpSession->sender.hashPubKey,
+                  &hex));
+    LOG(LOG_DEBUG,
+       "DEBUG: http welcome message from %s received\n",
+       &hex);
+#endif
+    httpSession->rpos = 0;
+    messageProcessed(httpSession);
+    GROW(httpSession->httpWriteBuff,
+        httpSession->httpWSize,
+        256);
+    len = sprintf(httpSession->httpWriteBuff,
+                 "HTTP/1.1 200 OK\r\n"
+                 "Server: Apache/1.3.27\r\n"
+                 "Transfer-Encoding: chunked\r\n"
+                 "Content-Type: text/html\r\n"
+                 "\r\n");
+    GROW(httpSession->httpWriteBuff,
+        httpSession->httpWSize,
+        len); 
+    httpDisconnect(tsession);
+    return OK;
+  }
+     
+  pack = (HTTPMessagePack*)&httpSession->rbuff[0];
+  /* send msg to core! */
+  if (httpSession->size <= sizeof(HTTPMessagePack)) {
+    LOG(LOG_WARNING,
+       "WARNING: received malformed message from http-peer connection. 
Closing.\n");
+    httpDisconnect(tsession);
+    return SYSERR;
+  }
+  mp      = MALLOC(sizeof(MessagePack));
+  mp->msg = MALLOC(httpSession->size);
+  memcpy(mp->msg,
+        &pack->parts[0],
+        httpSession->size - sizeof(HTTPMessagePack));
+  memcpy(&mp->sender,
+        &httpSession->sender,
+        sizeof(HostIdentity));
+  mp->crc         = ntohl(pack->checkSum);
+  mp->isEncrypted = ntohs(pack->isEncrypted);
+  mp->size        = httpSession->size - sizeof(HTTPMessagePack);
+  mp->tsession    = tsession;
+#if DEBUG_HTTP
+  LOG(LOG_DEBUG,
+      "DEBUG: http transport received %d bytes, forwarding to core\n",
+      mp->size);
+#endif
+  coreAPI->receive(mp);
+
+  httpSession->rpos = 0;          
+  messageProcessed(httpSession);
+  
+  httpDisconnect(tsession);
+  return OK;
+}
+
+/**
+ * Add a new session to the array watched by the select thread.  Grows
+ * the array if needed.  If the caller wants to do anything useful
+ * with the return value, it must have the lock on httplock before
+ * calling.  It is ok to call this function without holding httplock if
+ * the return value is ignored.
+ **/
+static int addTSession(TSession * tsession) {
+  int i;
+
+  MUTEX_LOCK(&httplock);
+  if (tsessionCount == tsessionArrayLength) 
+    GROW(tsessions,
+        tsessionArrayLength,
+        tsessionArrayLength * 2);
+  i = tsessionCount;
+  tsessions[tsessionCount++] = tsession;
+  MUTEX_UNLOCK(&httplock);
+  return i;
+}
+
+/**
+ * Create a new session for an inbound connection on the given
+ * socket. Adds the session to the array of sessions watched
+ * by the select thread.
+ **/
+static void createNewSession(int sock) {
+  TSession * tsession;
+  HTTPSession * httpSession;
+
+  httpSession = MALLOC(sizeof(HTTPSession));
+  httpSession->rpos = 0;
+  httpSession->size = 0;
+  httpSession->rbuff = NULL;
+  httpSession->wsize = 0;
+  httpSession->wbuff = NULL;
+  httpSession->httpReadBuff = NULL;
+  httpSession->httpRPos = 0;
+  httpSession->httpRSize = 0;
+  httpSession->httpWriteBuff = NULL;
+  httpSession->httpWSize = 0;
+  httpSession->sock = sock;
+  /* fill in placeholder identity to mark that we 
+     are waiting for the welcome message */
+  memcpy(&httpSession->sender,
+        coreAPI->myIdentity,
+        sizeof(HostIdentity));
+  httpSession->expectingWelcome = YES;
+  MUTEX_CREATE_RECURSIVE(&httpSession->lock);
+  httpSession->users = 1; /* us only, core has not seen this tsession! */
+  cronTime(&httpSession->lastUse);
+  tsession = MALLOC(sizeof(TSession));
+  tsession->ttype = HTTP_PROTOCOL_NUMBER;
+  tsession->internal = httpSession;
+  addTSession(tsession);
+}                                       
+
+/**
+ * Main method for the thread listening on the http socket and all http
+ * connections. Whenever a message is received, it is forwarded to the
+ * core. This thread waits for activity on any of the HTTP connections
+ * and processes deferred (async) writes and buffers reads until an
+ * entire message has been received.
+ **/
+static void * httpListenMain() {
+  struct sockaddr_in clientAddr;
+  fd_set readSet;
+  fd_set errorSet;
+  fd_set writeSet;
+  struct stat buf;
+  int lenOfIncomingAddr;
+  int i;
+  int max;
+  int ret;
+  
+  if (http_sock != -1)
+    LISTEN(http_sock, 5);
+  SEMAPHORE_UP(serverSignal); /* we are there! */
+  MUTEX_LOCK(&httplock);
+  while (http_shutdown == NO) {
+    FD_ZERO(&readSet);
+    FD_ZERO(&errorSet);
+    FD_ZERO(&writeSet);
+
+    if (http_sock != -1) {
+      if (isSocketValid(http_sock)) {
+       FD_SET(http_sock, &readSet);
+      } else {
+       LOG(LOG_ERROR,
+           "ERROR: http_sock %d invalid: %s\n",
+           http_sock,
+           STRERROR(errno));
+       http_sock = -1; /* prevent us from error'ing all the time */
+      }
+    }
+    if (http_pipe[0] != -1) {
+      if (-1 != FSTAT(http_pipe[0], &buf)) {
+       FD_SET(http_pipe[0], &readSet);
+      } else {
+       LOG(LOG_ERROR,
+           "ERROR: http_pipe %d invalid: %s\n",
+           http_pipe[0],
+           STRERROR(errno));
+       http_pipe[0] = -1; /* prevent us from error'ing all the time */ 
+      }
+    }
+    max = http_pipe[0];
+    if (http_sock > http_pipe[0])
+      max = http_sock;
+    for (i=0;i<tsessionCount;i++) {
+      HTTPSession * httpSession = tsessions[i]->internal;
+      int sock = httpSession->sock;
+      if (sock != -1) {
+       if (isSocketValid(sock)) {
+         FD_SET(sock, &readSet);
+         FD_SET(sock, &errorSet);
+         if ( (httpSession->wsize > 0) ||
+              (httpSession->httpWSize > 0) ) {
+           FD_SET(sock, &writeSet); /* do we have a pending write request? */
+         }
+       } else {
+         LOG(LOG_ERROR,
+             "ERROR: sock %d of session %d invalid: %s -- closing.\n",       
+             sock, 
+             i,
+             STRERROR(errno));
+         destroySession(i);
+       }
+      } else {
+       LOG(LOG_ERROR,
+           "ERROR: assertion failed: socket in tsessions array %d is -1 
(%s:%d) -- closing.\n",
+           i,
+           __FILE__, 
+           __LINE__);
+       destroySession(i);
+      }
+      if (sock > max)
+       max = sock;
+    }    
+    MUTEX_UNLOCK(&httplock);
+    ret = SELECT(max+1, &readSet, &writeSet, &errorSet, NULL);    
+    MUTEX_LOCK(&httplock);
+    if ( (ret == -1) &&
+        ( (errno == EAGAIN) || (errno == EINTR) ) ) 
+      continue;    
+    if (ret == -1) {
+      if (errno == EBADF) {
+       LOG(LOG_ERROR,
+           "ERROR: %s in select.\n",
+           STRERROR(errno));
+      } else {
+       errexit("FATAL: unexpected error in select: %s (that's the end)\n",
+               STRERROR(errno));
+      }
+    }
+    if (http_sock != -1) {
+      if (FD_ISSET(http_sock, &readSet)) {
+       int sock;
+       
+       lenOfIncomingAddr = sizeof(clientAddr);               
+       sock = ACCEPT(http_sock, 
+                     (struct sockaddr *)&clientAddr, 
+                     &lenOfIncomingAddr);
+       if (sock != -1) {         
+         /* verify clientAddr for eligibility here (ipcheck-style,
+            user should be able to specify who is allowed to connect,
+            otherwise we just close and reject the communication! */  
+         IPaddr ipaddr;
+         if (sizeof(struct in_addr) != sizeof(IPaddr))
+           errexit("FATAL: assertion failed at %s:%d\n",
+                   __FILE__, __LINE__);
+         memcpy(&ipaddr,
+                &clientAddr.sin_addr,
+                sizeof(struct in_addr));
+
+
+         if (YES == isBlacklisted(ipaddr)) {
+           LOG(LOG_INFO,
+               "INFO: Rejected blacklisted connection from %d.%d.%d.%d.\n",
+               PRIP(ntohl(*(int*)&clientAddr.sin_addr)));
+           CLOSE(sock);
+         } else 
+           createNewSession(sock);      
+       } else {
+         LOG(LOG_INFO,
+             "INFO: P2P HTTP server accept failed: %s\n",
+             STRERROR(errno));
+       }
+      }
+    }
+    if (FD_ISSET(http_pipe[0], &readSet)) {
+      /* allow reading multiple signals in one go in case we get many
+        in one shot... */
+#define MAXSIG_BUF 128
+      char buf[MAXSIG_BUF];
+
+      /* just a signal to refresh sets, eat and continue */
+      if (0 >= READ(http_pipe[0], 
+                   &buf[0], 
+                   MAXSIG_BUF)) {
+       LOG(LOG_WARNING,
+           "WARNING: reading signal on HTTP pipe failed (%s)\n",
+           STRERROR(errno));
+      }
+    }
+    for (i=0;i<tsessionCount;i++) {
+      HTTPSession * httpSession = tsessions[i]->internal;
+      int sock = httpSession->sock;
+      if (FD_ISSET(sock, &readSet)) {
+       if (SYSERR == readAndProcess(i)) {
+         destroySession(i);
+         i--;
+         continue;
+       }
+      }
+      if (FD_ISSET(sock, &writeSet)) {
+       int ret;
+
+       if (httpSession->httpWSize > 0) {         
+         ret = SEND_NONBLOCKING(sock,
+                                httpSession->httpWriteBuff,
+                                httpSession->httpWSize);
+         if (ret == SYSERR) {
+           LOG(LOG_WARNING,
+               "WARNING: send failed on socket %d (%s), closing session %d.\n",
+               sock, 
+               STRERROR(errno),
+               i);
+           destroySession(i);
+           i--;
+           continue;
+         }
+         if (ret == 0) {
+           /* send only returns 0 on error (other side closed connection),
+            * so close the session */
+           destroySession(i);
+           i--;
+           continue;
+         }
+         if ((unsigned int)ret == httpSession->httpWSize) {
+           GROW(httpSession->httpWriteBuff,
+                httpSession->httpWSize,
+                0);
+         } else {
+           memmove(httpSession->httpWriteBuff,
+                   &httpSession->httpWriteBuff[ret],
+                   httpSession->httpWSize - ret);
+           httpSession->httpWSize -= ret;
+         }
+       } else { /* httpSession->httpWSize == 0 */
+         if (httpSession->wsize == 0) 
+           errexit("FATAL: wsize %d for socket %d\n",
+                   httpSession->wsize,
+                   sock);
+         ret = SEND_NONBLOCKING(sock,
+                                httpSession->wbuff,
+                                httpSession->wsize);
+         if (ret < 0) {
+           LOG(LOG_WARNING,
+               "WARNING: send failed on socket %d (%s), closing session %d.\n",
+               sock, 
+               STRERROR(errno),
+               i);
+           destroySession(i);
+           i--;
+           continue;
+         }
+         if (ret == 0) {
+           /* send only returns 0 on error (other side closed connection),
+            * so close the session */
+           destroySession(i);
+           i--;
+           continue;
+         }
+         if ((unsigned int)ret == httpSession->wsize) {
+           GROW(httpSession->wbuff,
+                httpSession->wsize,
+                0);
+         } else {
+           memmove(httpSession->wbuff,
+                   &httpSession->wbuff[ret],
+                   httpSession->wsize - ret);
+           httpSession->wsize -= ret;
+         }
+       }
+      }
+      if (FD_ISSET(sock, &errorSet)) {
+       destroySession(i);
+       i--;
+       continue;
+      }
+      if ( ( httpSession->users == 1) &&
+          (cronTime(NULL) > httpSession->lastUse + HTTP_TIMEOUT) ) {
+       destroySession(i);
+       i--;
+       continue;
+      }
+    }
+  }
+  /* shutdown... */
+  if (http_sock != -1) {
+    CLOSE(http_sock);
+    http_sock = -1;
+  }
+  /* close all sessions */
+  while (tsessionCount > 0) 
+    destroySession(0);
+  MUTEX_UNLOCK(&httplock);
+  SEMAPHORE_UP(serverSignal); /* we are there! */
+  return NULL;
+} /* end of http listen main */
+
+/**
+ * Send a message (already encapsulated if needed) via the
+ * http socket (or enqueue if sending now would block).
+ *
+ * @param httpSession the session to use for sending
+ * @param doPost should an HTTP post prefix be created?
+ * @param mp the message to send
+ * @param ssize the size of the message
+ * @return OK if message send or queued, SYSERR if queue is full and
+ * message was dropped.
+ **/
+static int httpDirectSend(HTTPSession * httpSession,
+                         int doPost,
+                         void * mp,
+                         unsigned int ssize) {
+  int len;
+
+  if (httpSession->sock == -1) {
+#if DEBUG_HTTP
+    LOG(LOG_INFO,
+       "INFO: httpDirectSend called, but socket is closed\n");
+#endif
+    return SYSERR;
+  }
+  if (ssize > httpAPI.mtu + sizeof(HTTPMessagePack)) {
+    LOG(LOG_ERROR,
+       "ERROR: message passed to httpDirectSend larger than MTU (%u > %u)\n",
+       ssize, 
+       httpAPI.mtu);
+    return SYSERR;
+  }
+
+  if (httpSession->wbuff != NULL) {
+#if DEBUG_HTTP
+    LOG(LOG_INFO,
+       "INFO: httpTransport has already message "
+       "pending, will not queue more.\n");
+#endif
+    return SYSERR; /* already have msg pending */ 
+  }
+  if (httpSession->httpWriteBuff != NULL)
+    errexit("FATAL: HTTP-Transport: httpSession->wbuff"
+           " is NULL, but httpWriteBuff != NULL??\n");
+  
+  if (doPost == YES) {
+    IPaddr ip;
+
+    if (SYSERR == getPublicIPAddress(&ip))
+      return SYSERR;
+    GROW(httpSession->httpWriteBuff,
+        httpSession->httpWSize,
+        256);
+    strcpy(httpSession->httpWriteBuff, "POST ");
+    /* In case we're talking to a proxy, we need an absolute URI */
+    if (theProxy.sin_addr.s_addr != 0)
+    {
+     len = sprintf(httpSession->httpWriteBuff + 5,
+                   "http://%d.%d.%d.%d:%d";,
+                  PRIP(ntohl(httpSession->hostIP)),
+                   ntohs(httpSession->hostPort)) + 5;
+    }
+    else
+    {
+     len = 5;
+    }
+    len += sprintf(httpSession->httpWriteBuff + len,
+                 "/ HTTP/1.1\r\n"
+                 "Host: %d.%d.%d.%d\r\n"
+                 "Transfer-Encoding: chunked\r\n"
+                 "Content-Type: text/html\r\n"
+                 "\r\n"
+                 "%x\r\n",
+                 PRIP(ntohl(*(int*)&ip)),
+                 ssize);
+    GROW(httpSession->httpWriteBuff,
+        httpSession->httpWSize,
+        len);
+  } else {
+    GROW(httpSession->httpWriteBuff,
+        httpSession->httpWSize,
+        64);
+    len = sprintf(httpSession->httpWriteBuff,
+                 "\r\n%x\r\n",
+                 ssize);
+    GROW(httpSession->httpWriteBuff,
+        httpSession->httpWSize,
+        len);
+  }                
+  GROW(httpSession->wbuff,
+       httpSession->wsize,
+       ssize);
+  memcpy(httpSession->wbuff,
+        mp,
+        ssize);
+  signalSelect(); /* select set changed! */
+  cronTime(&httpSession->lastUse);
+  incrementBytesSent(ssize);
+  statChange(stat_octets_total_http_out,
+            ssize);
+  return OK;
+}
+
+/**
+ * Verify that a HELO-Message is correct (a node
+ * is reachable at that address). Since the reply
+ * will be asynchronous, a method must be called on
+ * success. 
+ * @param helo the HELO message to verify
+ *        (the signature/crc have been verified before)
+ * @return OK on success, SYSERR on error
+ **/
+static int verifyHelo(HELO_Message * helo) {
+  HostAddress * haddr;
+
+  haddr = (HostAddress*) &((HELO_Message_GENERIC*)helo)->senderAddress[0];
+  if ( (ntohs(helo->senderAddressSize) != sizeof(HostAddress)) ||
+       (ntohs(helo->header.size) != HELO_Message_size(helo)) ||
+       (ntohs(helo->header.requestType) != p2p_PROTO_HELO) ||
+       (ntohs(helo->protocol) != HTTP_PROTOCOL_NUMBER) ||
+       (YES == isBlacklisted(haddr->ip)) )
+    return SYSERR; /* obviously invalid */
+  else
+    return OK;
+}
+
+/**
+ * Create a HELO-Message for the current node. The HELO is
+ * created without signature and without a timestamp. The
+ * GNUnet core will sign the message and add an expiration time. 
+ *
+ * @param helo address where to store the pointer to the HELO
+ *        message
+ * @return OK on success, SYSERR on error
+ **/
+static int createHELO(HELO_Message ** helo) {
+  HELO_Message * msg;
+  HostAddress * haddr;
+  unsigned short port;
+
+  port = getGNUnetHTTPPort();
+  if (0 == port) {
+    LOG(LOG_DEBUG,
+       "DEBUG: HTTP port is 0, will only send using HTTP\n");
+    return SYSERR; /* HTTP transport is configured SEND-only! */
+  }
+  msg = (HELO_Message *) MALLOC(sizeof(HELO_Message) + sizeof(HostAddress));
+  haddr = (HostAddress*) &((HELO_Message_GENERIC*)msg)->senderAddress[0];
+
+  if (SYSERR == getPublicIPAddress(&haddr->ip)) {
+    FREE(msg);
+    LOG(LOG_WARNING,
+       "WARNING: Could not determine my public IP address.\n");
+    return SYSERR;
+  }
+  haddr->port = htons(port); 
+  haddr->reserved = htons(0);
+  msg->senderAddressSize = htons(sizeof(HostAddress));
+  msg->protocol = htons(HTTP_PROTOCOL_NUMBER);
+  msg->MTU = htonl(httpAPI.mtu);
+  *helo = msg;
+  return OK;
+}
+
+/**
+ * Establish a connection to a remote node.
+ *
+ * @param helo the HELO-Message for the target node
+ * @param tsessionPtr the session handle that is set
+ * @return OK on success, SYSERR if the operation failed
+ **/
+static int httpConnect(HELO_Message * helo,
+                     TSession ** tsessionPtr) {
+  int i;
+  HostAddress * haddr;
+  HTTPWelcome welcome;
+  int sock;
+  TSession * tsession;
+  HTTPSession * httpSession;
+  struct sockaddr_in soaddr;
+  
+  if (http_shutdown == YES)
+    return SYSERR;
+  haddr = (HostAddress*) &((HELO_Message_GENERIC*)helo)->senderAddress[0];
+#if DEBUG_HTTP
+  LOG(LOG_DEBUG,
+      "DEBUG: creating HTTP connection to %d.%d.%d.%d:%d\n",
+      PRIP(ntohl(*(int*)&haddr->ip.addr)), 
+      ntohs(haddr->port));
+#endif
+  sock = SOCKET(PF_INET, SOCK_STREAM, 6);/* 6: TCP */
+  if (sock == -1) {
+    LOG(LOG_FAILURE,
+       "FAILURE: Can not create socket (%s).\n",
+       STRERROR(errno));
+    return SYSERR;
+  }
+  if (0 != setBlocking(sock, NO)) {
+    CLOSE(sock);
+    LOG(LOG_FAILURE,
+       "FAILURE: could not put http socket into non-blocking mode (%s)\n",
+       STRERROR(errno));
+    return SYSERR;
+  }
+  memset(&soaddr,
+        0,
+        sizeof(soaddr));
+  soaddr.sin_family = AF_INET;
+  
+  /* Do we have to use a proxy? */
+  if (theProxy.sin_addr.s_addr != 0) {
+    soaddr.sin_addr = theProxy.sin_addr;
+    soaddr.sin_port = theProxy.sin_port;
+  } else {
+    if (sizeof(struct in_addr) != sizeof(IPaddr))
+      errexit("FATAL: assertion failed at %s:%d\n",
+             __FILE__, __LINE__);
+    memcpy(&soaddr.sin_addr,
+          &haddr->ip,
+          sizeof(IPaddr));
+    soaddr.sin_port = haddr->port;
+  }
+  i = CONNECT(sock, 
+             (struct sockaddr*)&soaddr,
+             sizeof(soaddr));
+  if ( (i < 0) &&
+       (errno != EINPROGRESS) ) {
+    LOG(LOG_ERROR,
+       "ERROR: Can not connect to %d.%d.%d.%d:%d (%s)\n",
+       PRIP(ntohl(*(int*)&haddr->ip)),
+       ntohs(haddr->port),
+       STRERROR(errno));
+    CLOSE(sock);
+    return SYSERR;
+  }  
+  httpSession = MALLOC(sizeof(HTTPSession));
+  httpSession->sock = sock;
+  httpSession->hostIP = haddr->ip.addr;
+  httpSession->hostPort = haddr->port;
+  httpSession->wsize = 0;
+  httpSession->wbuff = NULL;
+  httpSession->size = 0;
+  httpSession->rbuff = NULL;
+  httpSession->httpReadBuff = NULL;
+  httpSession->httpRPos = 0;
+  httpSession->httpRSize = 0;
+  httpSession->httpWriteBuff = NULL;
+  httpSession->httpWSize = 0;
+  tsession = MALLOC(sizeof(TSession));
+  tsession->internal = httpSession;
+  tsession->ttype = httpAPI.protocolNumber;
+  MUTEX_CREATE_RECURSIVE(&httpSession->lock);
+  httpSession->users = 2; /* caller + us */
+  httpSession->rpos = 0;
+  cronTime(&httpSession->lastUse);
+  memcpy(&httpSession->sender,
+        &helo->senderIdentity,
+        sizeof(HostIdentity));
+  httpSession->expectingWelcome = NO;
+  MUTEX_LOCK(&httplock);
+  i = addTSession(tsession);
+
+  /* send our node identity to the other side to fully establish the
+     connection! */
+
+  welcome.size = htons(sizeof(HTTPWelcome));
+  welcome.version = htons(0);
+  memcpy(&welcome.clientIdentity,
+        coreAPI->myIdentity,
+        sizeof(HostIdentity));
+  if (SYSERR == httpDirectSend(httpSession,
+                              YES,
+                              &welcome,
+                              sizeof(HTTPWelcome))) {
+    destroySession(i);
+    httpDisconnect(tsession);
+    MUTEX_UNLOCK(&httplock);
+    return SYSERR;
+  }
+  MUTEX_UNLOCK(&httplock);
+
+  gnunet_util_sleep(50 * cronMILLIS);
+  *tsessionPtr = tsession;
+  FREE(helo);
+  return OK;
+}
+
+/**
+ * Send a message to the specified remote node.
+ *
+ * @param tsession the HELO_Message identifying the remote node
+ * @param msg the message
+ * @param size the size of the message
+ * @param isEncrypted is the message encrypted (YES/NO)
+ * @param crc CRC32 of the plaintext
+ * @return SYSERR on error, OK on success
+ **/
+static int httpSend(TSession * tsession,
+                  const void * msg,
+                  const unsigned int size,
+                  int isEncrypted,
+                  const int crc) {
+  HTTPMessagePack * mp;
+  int ok;
+  int ssize;
+
+  if (http_shutdown == YES) {
+    LOG(LOG_ERROR,
+       "ERROR: http transport service not running, can not send\n");
+    return SYSERR;
+  }
+  if (size > httpAPI.mtu) {
+    LOG(LOG_FAILURE,
+       "FAILURE: message larger than allowed by http transport (%d > %d)\n",
+       size, 
+       httpAPI.mtu);
+    return SYSERR;
+  }
+  if (((HTTPSession*)tsession->internal)->sock == -1)
+    return SYSERR; /* other side closed connection */
+  mp = MALLOC(sizeof(HTTPMessagePack) + size);
+  mp->checkSum = htonl(crc);
+  mp->isEncrypted = htons(isEncrypted);
+  memcpy(&mp->parts[0],
+        msg,
+        size);
+  ssize = size + sizeof(HTTPMessagePack);
+  
+  ok = httpDirectSend(tsession->internal,
+                     NO,
+                     mp,
+                     ssize);
+  FREE(mp);
+  return ok;
+}
+
+/**
+ * Start the server process to receive inbound traffic.
+ * @return OK on success, SYSERR if the operation failed
+ **/
+static int startTransportServer(void) {
+  struct sockaddr_in serverAddr;
+  const int on = 1;
+  unsigned short port;
+  
+  if (serverSignal != NULL) {
+    LOG(LOG_FAILURE,
+       "FAILURE: can not start HTTP server, already running!?\n");
+    return SYSERR;
+  }
+  serverSignal = SEMAPHORE_NEW(0);
+  http_shutdown = NO;
+    
+  if (0 != PIPE(http_pipe)) {
+    LOG(LOG_ERROR,
+       "ERROR: could not create pipe (%s)\n",
+       STRERROR(errno));
+    return SYSERR;
+  }
+  setBlocking(http_pipe[1], NO);
+ 
+  port = getGNUnetHTTPPort();
+  if (port != 0) { /* if port == 0, this is a read-only
+                     business! */
+    http_sock = SOCKET(PF_INET, SOCK_STREAM, 0);
+    if (http_sock < 0) 
+      errexit("ERROR opening http socket (%s).\n",
+             STRERROR(errno));  
+    if ( SETSOCKOPT(http_sock,
+                   SOL_SOCKET, 
+                   SO_REUSEADDR, 
+                   &on, sizeof(on)) < 0 ) 
+      errexit("ERROR: setsockopt for http socket failed (%s)\n",
+             STRERROR(errno));  
+    memset((char *) &serverAddr, 
+          0,
+          sizeof(serverAddr));
+    serverAddr.sin_family      = AF_INET;
+    serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
+    serverAddr.sin_port        = htons(getGNUnetHTTPPort());
+#if DEBUG_HTTP
+    LOG(LOG_INFO,
+       "INFO: starting http peer server on port %d\n",
+       ntohs(serverAddr.sin_port));
+#endif
+    if (BIND(http_sock, 
+            (struct sockaddr *) &serverAddr,
+            sizeof(serverAddr)) < 0) {
+      LOG(LOG_ERROR, 
+         "ERROR (%s) binding the HTTP listener to port %d. "\
+         "No transport service started.\n",
+         STRERROR(errno),
+         getGNUnetHTTPPort());
+      CLOSE(http_sock);
+      SEMAPHORE_FREE(serverSignal);
+      serverSignal = NULL;
+      return SYSERR;
+    }
+  } else
+    http_sock = -1;
+  if (0 == PTHREAD_CREATE(&listenThread, 
+                         (void * (*)(void *)) &httpListenMain,
+                         NULL,
+                         2048)) {
+      SEMAPHORE_DOWN(serverSignal); /* wait for server to be up */
+  } else {
+    LOG(LOG_ERROR,
+       "ERROR: could not start http listen thread (%s)\n",
+       STRERROR(errno));
+    CLOSE(http_sock);
+    SEMAPHORE_FREE(serverSignal);
+    serverSignal = NULL;
+    return SYSERR;
+  }
+  return OK;
+}
+
+/**
+ * Shutdown the server process (stop receiving inbound
+ * traffic). Maybe restarted later!
+ **/
+static int stopTransportServer() {
+  void * unused;
+
+  http_shutdown = YES;  
+  signalSelect();
+  SEMAPHORE_DOWN(serverSignal);
+  SEMAPHORE_FREE(serverSignal);
+  serverSignal = NULL; 
+  CLOSE(http_pipe[1]);
+  CLOSE(http_pipe[0]);
+  if (http_sock != -1) {
+    CLOSE(http_sock);
+    http_sock = -1;
+  }
+  PTHREAD_JOIN(&listenThread, &unused);
+  return OK;
+}
+
+/**
+ * Reload the configuration. Should never fail (keep old
+ * configuration on error, syslog errors!)
+ **/
+static void reloadConfiguration(void) {
+  char * ch;
+
+  MUTEX_LOCK(&httplock);
+  FREENONNULL(filteredNetworks_);
+  ch = getConfigurationString("HTTP",
+                             "BLACKLIST");
+  if (ch == NULL)
+    filteredNetworks_ = parseRoutes("");
+  else {
+    filteredNetworks_ = parseRoutes(ch);
+    FREE(ch);
+  }
+  MUTEX_UNLOCK(&httplock);
+}
+
+/**
+ * Convert HTTP address to a string.
+ **/
+static char * addressToString(HELO_Message * helo) {
+  char * ret;
+  HostAddress * haddr;
+  
+  haddr = (HostAddress*) &((HELO_Message_GENERIC*)helo)->senderAddress[0];  
+  ret = MALLOC(4*4+6+6);
+  sprintf(ret,
+         "%d.%d.%d.%d:%d (HTTP)",
+         PRIP(ntohl(*(int*)&haddr->ip.addr)), 
+         ntohs(haddr->port));
+  return ret;
+}
+
+ 
+/* ******************** public API ******************** */
+ 
+/**
+ * The exported method. Makes the core api available
+ * via a global and returns the udp transport API.
+ **/ 
+TransportAPI * inittransport_http(CoreAPIForTransport * core) {
+  int mtu;
+  struct hostent *ip;
+  char *proxy, *proxyPort;
+
+  MUTEX_CREATE_RECURSIVE(&httplock);
+  reloadConfiguration();
+  tsessionCount = 0;
+  tsessionArrayLength = 32;
+  tsessions = MALLOC(sizeof(TSession*) * tsessionArrayLength);
+  coreAPI = core;
+  stat_octets_total_http_in 
+    = statHandle("# bytes received via http");
+  stat_octets_total_http_out 
+    = statHandle("# bytes sent via http");
+  mtu = getConfigurationInt("HTTP",
+                           "MTU");
+  if (mtu == 0)
+    mtu = 1400;
+  if (mtu < 1200)
+    LOG(LOG_ERROR,
+       "ERROR: MTU for HTTP is probably to low (fragmentation not 
implemented!)\n");
+ 
+  proxy = getConfigurationString("GNUNETD", "HTTP-PROXY");
+  if (proxy != NULL)
+  {
+   ip = GETHOSTBYNAME(proxy);
+   if (ip == NULL)
+   {
+    LOG(LOG_ERROR, "Couldn't resolve name of HTTP proxy %s\n",
+        proxy);
+    theProxy.sin_addr.s_addr = 0;
+   }
+   else
+   {
+    theProxy.sin_addr.s_addr = ((struct in_addr *)ip->h_addr)->s_addr;
+    proxyPort = getConfigurationString("GNUNETD", "HTTP-PROXY-PORT");
+    if (proxyPort == NULL)
+    {
+     theProxy.sin_port = htons(8080);
+    }
+    else
+    {
+     theProxy.sin_port = htons(atoi(proxyPort));
+     FREE(proxyPort);
+    }
+   }
+   FREE(proxy);
+  }
+  else
+  {
+   theProxy.sin_addr.s_addr = 0;
+  }
+
+  httpAPI.protocolNumber       = HTTP_PROTOCOL_NUMBER;
+  httpAPI.mtu                  = mtu - sizeof(HTTPMessagePack);
+  httpAPI.cost                 = 20000; /* about equal to udp */
+  httpAPI.verifyHelo           = &verifyHelo;
+  httpAPI.createHELO           = &createHELO;
+  httpAPI.connect              = &httpConnect;
+  httpAPI.associate            = &httpAssociate;
+  httpAPI.send                 = &httpSend;
+  httpAPI.sendReliable         = &httpSend; /* FIXME: we should be able to 
increase reliability here (by growing wbuff over frame size, like in tcp code)! 
*/
+  httpAPI.disconnect           = &httpDisconnect;
+  httpAPI.startTransportServer = &startTransportServer;
+  httpAPI.stopTransportServer  = &stopTransportServer;
+  httpAPI.reloadConfiguration  = &reloadConfiguration;
+  httpAPI.addressToString      = &addressToString;
+
+  return &httpAPI;
+}
+
+void donetransport_http() {
+  FREE(tsessions);
+  tsessions = NULL;
+  tsessionArrayLength = 0;
+  FREENONNULL(filteredNetworks_);
+  MUTEX_DESTROY(&httplock);
+}
+
+/* end of http.c */

Added: freeway/src/org/gnu/freeway/transport/nat/NATAddress.java
===================================================================
--- freeway/src/org/gnu/freeway/transport/nat/NATAddress.java   2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/transport/nat/NATAddress.java   2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,46 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.transport.nat;
+
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * Host-Address in a NAT network.
+ * Since the idea behind NAT is that it can not be contacted from the outside, 
the address is empty.
+ */
+
+public class NATAddress extends Object implements Persistent
+{
+       public static final int SIZE    =       0;
+
+
+       public NATAddress()
+       {
+               super();
+       }
+
+       public String toString()
+       {
+               return "NAT address";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int getByteSize()
+       {
+               return SIZE;
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+       }
+}

Added: freeway/src/org/gnu/freeway/transport/nat/NATTransport.java
===================================================================
--- freeway/src/org/gnu/freeway/transport/nat/NATTransport.java 2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/transport/nat/NATTransport.java 2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,159 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.transport.nat;
+
+import org.gnu.freeway.server.*;
+import org.gnu.freeway.transport.*;
+import org.gnu.freeway.util.*;
+
+/**
+ * Implementation of the NAT transport service
+ */
+
+public class NATTransport extends AbstractTransport
+{
+       /** */
+       private Prefs   prefs;
+
+
+       public NATTransport()
+       {
+               super(NAT_PROTOCOL_NUMBER,"NAT");
+       }
+
+       public String toString()
+       {
+               return "NAT transport";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       /**
+        * The exported method. Makes the core api available via a global and
+        * returns the nat transport API.
+        * @param core
+        */
+
+       public void init( CoreForTransport core )
+       {
+               super.init(core);
+               prefs=core.getApplication().getPreferences();
+       }
+
+       public void done()
+       {
+               super.done();
+       }
+
+       public int getMTU()
+       {
+               return 0;
+       }
+
+       public int getCost()
+       {
+               return 30000;
+       }
+
+       /**
+        * Verify that a HELO-Message is correct (a node is reachable at that
+        * address).
+        *
+        * @param helo the HELO message to verify
+        *        (the signature/crc have been verified before)
+        * @return OK on success, false on failure
+        */
+
+       public boolean verifyHELO( P2PHello helo )
+       {
+               NATAddress      addr;
+
+               addr=(NATAddress) helo.decodeSenderAddress(NATAddress.class);
+               if (addr==null) {
+                       return false;   // obviously invalid
+                       }
+
+               if (prefs.testString("NAT","LIMITED","YES")) {
+                       // if WE are a NAT and this is not our HELO, it is 
invalid since NAT-to-NAT is not possible !
+                       return 
(coreAPI.getIdentity().equals(helo.getSenderIdentity()));
+                       }
+               return true;
+       }
+
+       /**
+        * Create a HELO-Message for the current node. The HELO is created
+        * without signature and without a timestamp. The GNUnet core will
+        * sign the message and add an expiration time.
+        *
+        * @return the HELO message on success, null on error
+        */
+
+       public P2PHello createHELO()
+       {
+               return (prefs.testString("NAT","LIMITED","YES") ? new 
P2PHello(NAT_PROTOCOL_NUMBER,0) : null);
+       }
+
+       /**
+        * Establish a connection to a remote node.
+        * @param helo the HELO-Message for the target node
+        * @return fails, always returns null
+        */
+
+       public Session connect( P2PHello helo )
+       {
+               return null;
+       }
+
+       /**
+        * A (core) Session is to be associated with a transport session. The
+        * transport service may want to know in order to call back on the
+        * core if the connection is being closed.
+        *
+        * @param tsession the session handle passed along
+        *   from the call to receive that was made by the transport
+        *   layer
+        * @return OK if the session could be associated,
+        *         false if not.
+        */
+
+       public boolean associate( Session tsession )
+       {
+               return false; /* NAT connections can never be associated */
+       }
+
+       /**
+        * Start the server process to receive inbound traffic.
+        *
+        * @return OK on success, false if the operation failed
+        */
+
+       public boolean launch()
+       {
+               return true;
+       }
+
+       /**
+        * Shutdown the server process (stop receiving inbound traffic). Maybe
+        * restarted later!
+        * @return
+        */
+
+       public boolean shutdown()
+       {
+               return true;
+       }
+
+       /**
+        * Convert NAT address to a string.
+        * @param helo
+        * @return
+        */
+
+       public String addressToString( P2PHello helo )
+       {
+               return "NAT";
+       }
+}

Added: freeway/src/org/gnu/freeway/transport/smtp.c
===================================================================
--- freeway/src/org/gnu/freeway/transport/smtp.c        2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/transport/smtp.c        2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,947 @@
+/*
+     This file is part of GNUnet
+
+     GNUnet is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with GNUnet; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file transports/smtp.c
+ * @brief Implementation of the SMTP transport service
+ * @author Christian Grothoff
+ * @author Renaldo Ferreira
+ **/
+
+#include "gnunet_util.h"
+#include "gnunet_transport.h"
+#include "platform.h"
+
+#define FILTER_STRING_SIZE 64
+#define CONTENT_TYPE_MULTIPART "Content-Type: Multipart/Mixed;"
+#define BOUNDARY_SPECIFIER "-EL-GNUNET-"
+/* how long can a line in base64 encoded
+   mime text be? (in characters, excluding "\n") */
+#define MAX_CHAR_PER_LINE 76
+
+
+/**
+ * Host-Address in a SMTP network.
+ **/
+typedef struct {
+  
+  /**
+   * Filter line that every sender must include in the E-mails such
+   * that the receiver can effectively filter out the GNUnet traffic
+   * from the E-mail.
+   **/
+  char filter[FILTER_STRING_SIZE];
+
+  /**
+   * Claimed E-mail address of the sender. 
+   * Format is "address@hidden" with null termination, padded to be
+   * of a multiple of 8 bytes long.
+   **/  
+  char senderAddress[0];
+
+} EmailAddress;
+
+/**
+ * Encapsulation of a GNUnet message in the SMTP mail body (before
+ * base64 encoding).
+ **/
+typedef struct {
+  /* this struct is always preceeded by n bytes of p2p messages
+     that the GNUnet core will process */
+
+  /** 
+   * size of the message, in bytes, including this header; max
+   * 65536-header (network byte order)
+   **/
+  unsigned short size;
+
+  /**
+   * Is the message encrypted? 
+   **/
+  unsigned short isEncrypted;
+
+  /**
+   * CRC checksum of the plaintext  (network byte order)
+   **/ 
+  int checkSum;
+
+  /**
+   * What is the identity of the sender (hash of public key) 
+   **/
+  HostIdentity sender;
+
+} SMTPMessage;
+
+/* *********** globals ************* */
+
+/**
+ * apis (our advertised API and the core api ) 
+ **/
+static CoreAPIForTransport * coreAPI;
+static TransportAPI smtpAPI;
+
+/**
+ * thread that listens for inbound messages 
+ **/
+static PTHREAD_T dispatchThread;
+
+/**
+ * Socket to talk to the SMTP server
+ **/
+static int smtp_sock;
+
+/**
+ * Pipe used to read from SMTP server
+ */
+static int smtp_pipe;
+/**
+ * Lock to guard access to smtp_sock
+ **/
+static Mutex smtpLock;
+
+/**
+ *   Semaphore used to signal that server has
+ *   been started -- and later again to
+ *   signal that the server has been stopped.
+ */
+static Semaphore * serverSignal = NULL;
+/**
+ * Flag to indicate that server has been shut down.
+ **/
+static int smtp_shutdown = YES;
+
+/**
+ * Statistics handles.
+ **/
+static int stat_octets_total_smtp_in;
+static int stat_octets_total_smtp_out;
+
+/** ******************** Base64 encoding ************/
+
+#define FILLCHAR '='
+static char * cvt = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"\
+                    "abcdefghijklmnopqrstuvwxyz"\
+                    "0123456789+/";
+
+/**
+ * Encode into Base64.
+ *
+ * @param data the data to encode
+ * @param len the length of the input
+ * @param output where to write the output (*output should be NULL,
+ *   is allocated)
+ * @return the size of the output
+ **/
+static unsigned int base64_encode(char * data,
+                                 unsigned int len,
+                                 char ** output) {
+  unsigned int i;
+  char c;
+  unsigned int ret;
+
+/*    (*output)[ret++] = '\r'; \*/
+#define CHECKLINE \
+  if ( (ret % MAX_CHAR_PER_LINE) == 0) { \
+    (*output)[ret++] = '\n'; \
+  }
+  ret = 0;
+  *output = MALLOC( (((len * 4 / 3) + 8) * (MAX_CHAR_PER_LINE+2))/
+                    MAX_CHAR_PER_LINE);
+  for (i = 0; i < len; ++i) {
+    c = (data[i] >> 2) & 0x3f;
+    (*output)[ret++] = cvt[(int)c];
+    CHECKLINE;
+    c = (data[i] << 4) & 0x3f;
+    if (++i < len)
+      c |= (data[i] >> 4) & 0x0f;
+    (*output)[ret++] = cvt[(int)c];
+    CHECKLINE;
+    if (i < len) {
+      c = (data[i] << 2) & 0x3f;
+      if (++i < len)
+       c |= (data[i] >> 6) & 0x03;      
+      (*output)[ret++] = cvt[(int)c];
+      CHECKLINE;
+    } else {
+      ++i;
+      (*output)[ret++] = FILLCHAR;
+      CHECKLINE;
+    }    
+    if (i < len) {
+      c = data[i] & 0x3f;
+      (*output)[ret++] = cvt[(int)c];
+      CHECKLINE;
+    } else {
+      (*output)[ret++] = FILLCHAR;
+      CHECKLINE;
+    }
+  }
+  (*output)[ret++] = FILLCHAR;
+  return ret;
+}
+
+#define cvtfind(a)( (((a) >= 'A')&&((a) <= 'Z'))? (a)-'A'\
+                   :(((a)>='a')&&((a)<='z')) ? (a)-'a'+26\
+                   :(((a)>='0')&&((a)<='9')) ? (a)-'0'+52\
+                  :((a) == '+') ? 62\
+                  :((a) == '/') ? 63 : -1)
+/**
+ * Decode from Base64.
+ *
+ * @param data the data to encode
+ * @param len the length of the input
+ * @param output where to write the output (*output should be NULL,
+ *   is allocated)
+ * @return the size of the output
+ **/
+static unsigned int base64_decode(char * data,
+                                 unsigned int len,
+                                 char ** output) {
+  unsigned int i;
+  char c;
+  char c1;
+  unsigned int ret=0;
+
+#define CHECK_CRLF     while (data[i] == '\r' || data[i] == '\n') {\
+                               LOG(LOG_DEBUG, "DEBUG: ignoring CR/LF\n"); \
+                               i++; \
+                               if (i >= len) goto END;  \
+                       }
+
+  *output = MALLOC((len * 3 / 4) + 8);
+  LOG(LOG_DEBUG, "DEBUG: base64_decode decoding len=%d\n", len);
+  for (i = 0; i < len; ++i) {
+    CHECK_CRLF;
+    if (data[i] == FILLCHAR)
+             break;
+    c = (char) cvtfind(data[i]);
+    ++i;
+    CHECK_CRLF;
+    c1 = (char) cvtfind(data[i]);
+    c = (c << 2) | ((c1 >> 4) & 0x3);
+    (*output)[ret++] = c;
+    if (++i < len) {
+      CHECK_CRLF;
+      c = data[i];
+      if (FILLCHAR == c)
+       break;      
+      c = (char) cvtfind(c);
+      c1 = ((c1 << 4) & 0xf0) | ((c >> 2) & 0xf);
+      (*output)[ret++] = c1;
+    } 
+    if (++i < len) {
+      CHECK_CRLF;
+      c1 = data[i];
+      if (FILLCHAR == c1)
+       break;
+      
+      c1 = (char) cvtfind(c1);
+      c = ((c << 6) & 0xc0) | c1;
+      (*output)[ret++] = c;
+    }
+  }  
+ END:
+  return ret;
+}
+
+/* ********************* the real stuff ******************* */
+
+#define strAUTOncmp(a,b) strncmp(a,b,strlen(b))
+
+/**
+ * Get the GNUnet SMTP port from the configuration, or from
+ * /etc/services if it is not specified in the config file.
+ *
+ * @return the port in host byte order
+ **/
+static unsigned short getSMTPPort() {
+  struct servent * pse;        /* pointer to service information entry */
+  unsigned short port;
+
+  port = (unsigned short) getConfigurationInt("SMTP",
+                                             "PORT");
+  if (port == 0) { /* try lookup in services */
+    if ((pse = getservbyname("gnunet", "smtp"))) 
+      port = ntohs(pse->s_port);      
+    else 
+      errexit("Cannot determine port to bind to. "\
+             " Define in configuration file in section %s under %s "\
+             "or in /etc/services under smtp/gnunet.\n",
+             "SMTP", "PORT");
+  }
+  return port;
+}
+
+/**
+ * Connect to the local SMTP server, return
+ * the socket, -1 on error.
+ **/
+static int connectToSMTPServer() {
+  int res;
+  struct sockaddr_in soaddr;
+  char * hostname;
+  struct hostent * ip; /* for the lookup of the IP in gnunet.conf */
+  int one = 1;
+
+  hostname = getConfigurationString("SMTP",
+                                   "SERVER");
+  if (hostname == NULL)
+    hostname = STRDUP("localhost");
+  ip = GETHOSTBYNAME(hostname);
+  if (ip == NULL) {
+    LOG(LOG_ERROR,
+       "ERROR: Couldn't resolve name of SMTP server '%s' (%s)",
+       hostname, hstrerror(h_errno));
+    FREE(hostname);
+    return -1;
+  } 
+  FREE(hostname);
+  res = SOCKET(PF_INET, SOCK_STREAM, 6);/* 6: TCP */
+  if (res == -1) {
+    LOG(LOG_FAILURE,
+       "FAILURE: Can not open socket (%s)\n",
+       STRERROR(errno));
+    return SYSERR;
+  }
+  SETSOCKOPT(res, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one));
+  soaddr.sin_family = AF_INET;
+  memcpy(&soaddr.sin_addr,
+        &((struct in_addr*)ip->h_addr)->s_addr,
+        sizeof(struct in_addr));
+  soaddr.sin_port = htons(getSMTPPort());
+  if (0 > CONNECT(res,
+                 (struct sockaddr*)&soaddr,
+                 sizeof(soaddr))) {
+    LOG(LOG_FAILURE,
+       "FAILURE: Can not connect to SMTP server (%s)\n",
+       STRERROR(errno));
+    CLOSE(res);
+    return -1;
+  }
+  return res;
+}
+
+#define MAX_SMTP_LINE 128
+
+/**
+ * Read a single line from the socket and check if 
+ * it starts with the expected string. If the server
+ * sends more than one line, an arbitrary amount of
+ * data from the next line may be read!
+ * @param sock the socket to read from
+ * @param expect the expected beginning of the line form the server,
+ *        e.g. "250 ".
+ * @return OK if the line matches the expectation, SYSERR if not
+ **/
+static int readSMTPLine(int sock,
+                       char * expect) {
+  int pos;
+  char buff[MAX_SMTP_LINE];
+  int i;
+
+  pos = 0;
+
+  while (pos < MAX_SMTP_LINE) {
+    i = RECV_NONBLOCKING(sock,
+                        &buff[pos],
+                        MAX_SMTP_LINE - pos);
+    if (i <= 0)
+      return SYSERR;
+    while (i > 0) {
+      if (buff[pos++] == '\n')
+       goto END;
+      i--;
+    }
+  }
+ END:
+  buff[pos] = '\0';
+  if (strncmp(expect, &buff[0], strlen(expect)) == 0)
+    return OK;
+  else {
+    return SYSERR;
+  }
+}
+
+/**
+ * Build a string and write the result to the SMTP socket.
+ **/
+static int writeSMTPLine(int sock,
+                        char * format,
+                        ...) {
+  va_list args;
+  char * target;
+  int size;
+  int ret;
+  
+  size = 256;
+  ret = -1;
+  target = MALLOC(size);
+  while (ret == -1) {
+    va_start(args, format);
+    ret = vsnprintf(target, size, format, args);
+    va_end(args);  
+    if (ret == -1) {
+      FREE(target);
+      size *= 2;
+      target = MALLOC(size);
+    }
+  }  
+  if (ret == SEND_BLOCKING_ALL(sock, target, ret))
+    ret = OK;
+  FREE(target);
+  return ret;
+}
+
+/**
+ * Listen to the pipe, decode messages and send to core.
+ **/
+static void * listenAndDistribute() {
+  char * pipename;
+  char * line;
+  unsigned int LINESIZE;
+  SMTPMessage * mp;
+
+  pipename = getFileName("SMTP",
+                        "PIPE",
+                        "ERROR: you must specify the name of a "\
+                        "pipe for the SMTP transport in section %s under 
%s\n");
+  if (pipename == NULL) {
+    errexit("ERROR: could not create pipe to receive email (no filename 
specified)\n");
+  }
+  UNLINK(pipename);
+  if (0 != mkfifo(pipename,
+                 S_IWUSR|S_IRUSR)) {
+    errexit("ERROR: could not create pipe %s (%s)\n",
+           pipename, 
+           STRERROR(errno));
+  }
+  LINESIZE = ((smtpAPI.mtu * 4 / 3) + 8) * (MAX_CHAR_PER_LINE+2)/
+             MAX_CHAR_PER_LINE; /* maximum size of a line supported */
+  line = MALLOC(LINESIZE + 2);  /* 2 bytes for off-by-one errors, just to be 
safe... */
+
+#define READLINE(l,limit) \
+  do { retl = fgets(l, limit, fdes); \
+    if ((retl == NULL) || (smtp_shutdown == YES)) {\
+       goto END; \
+    }\
+    incrementBytesReceived(strlen(retl));\
+    statChange(stat_octets_total_smtp_in, strlen(retl));\
+  } while (0)
+
+
+  SEMAPHORE_UP(serverSignal); /* we are there! */
+  while ( smtp_shutdown == NO ) {    
+    FILE * fdes;
+    char * retl;
+    char * boundary;
+    char * out;
+    unsigned int size;
+    MessagePack * coreMP;
+    
+    smtp_pipe = OPEN(pipename, O_RDONLY);
+    fdes = fdopen(smtp_pipe, "r");
+    while ( smtp_shutdown == NO ) {
+      do {
+       READLINE(line, LINESIZE);
+      } while (0 != strAUTOncmp(line, CONTENT_TYPE_MULTIPART));
+      READLINE(line, LINESIZE);
+      if (strlen(line) < strlen("  boundary=\"")) {
+       goto END;
+      }
+      boundary = STRDUP(&line[strlen("  boundary=\"")-2]);
+      if (boundary[strlen(boundary)-2] != '\"') {
+       FREE(boundary);
+       goto END; /* format error */
+      } else {
+       boundary[strlen(boundary)-2] = '\0';      
+       boundary[0] = boundary[1] = '-';
+      }
+      do {
+       READLINE(line, LINESIZE);
+      } while (0 != strAUTOncmp(line, boundary));
+      do {
+       READLINE(line, LINESIZE); /* content type, etc. */
+      } while (0 != strAUTOncmp(line, ""));
+      READLINE(line, LINESIZE); /* read base64 encoded message; decode, 
process */
+      while ( (line[strlen(line)-2] != FILLCHAR) &&
+             (strlen(line) < LINESIZE) )
+       READLINE(&line[strlen(line)-1], LINESIZE - strlen(line));
+      size = base64_decode(line, strlen(line)-1, &out);
+      if (size < sizeof(SMTPMessage)) {
+       LOG(LOG_WARNING,
+           "WARNING: received malformed message via SMTP (size %d smaller than 
encapsulation header)\n",
+           sizeof(SMTPMessage));
+       FREE(out);
+       goto END;
+      }
+
+      mp = (SMTPMessage*)&out[size-sizeof(SMTPMessage)];
+      if (ntohs(mp->size) != size) {
+       LOG(LOG_WARNING,
+           "WARNING: received malformed message via SMTP (size mismatch)\n");
+       LOG(LOG_DEBUG, 
+           "DEBUG: size returned by base64=%d, in the msg=%d\n", 
+           size,
+           ntohl(mp->size));
+       goto END;
+      }
+      coreMP = MALLOC(sizeof(MessagePack));
+      coreMP->msg = (p2p_HEADER*)out;
+      coreMP->size = size - sizeof(SMTPMessage);
+      coreMP->tsession = NULL;
+      memcpy(&coreMP->sender,
+            &mp->sender,
+            sizeof(HostIdentity));
+      coreMP->isEncrypted = ntohs(mp->isEncrypted);
+      coreMP->crc = ntohl(mp->checkSum);
+
+      LOG(LOG_DEBUG, 
+         "DEBUG: SMTP message passed to the core\n");
+
+      coreAPI->receive(coreMP);
+      READLINE(line, LINESIZE); /* new line at the end */
+    }
+  END:
+    LOG(LOG_DEBUG, 
+       "DEBUG: SMTP message processed\n");
+    fclose(fdes);    
+  }
+  SEMAPHORE_UP(serverSignal); /* we are there! */
+  return NULL;
+}
+
+/* *************** API implementation *************** */
+
+/**
+ * Verify that a HELO-Message is correct (a node is reachable at that
+ * address). Since the reply will be asynchronous, a method must be
+ * called on success.
+ *
+ * @param helo the HELO message to verify
+ *        (the signature/crc have been verified before)
+ * @return OK on success, SYSERR on error
+ **/
+static int verifyHelo(HELO_Message * helo) {
+  EmailAddress * maddr;
+
+  maddr = (EmailAddress*) &((HELO_Message_GENERIC*)helo)->senderAddress[0];
+  if ((ntohs(helo->header.size)!=
+       sizeof(HELO_Message)+ntohs(helo->senderAddressSize)) ||
+      
(maddr->senderAddress[ntohs(helo->senderAddressSize)-1-FILTER_STRING_SIZE]!='\0'))
 {
+    LOG(LOG_WARNING,
+       "WARNING: received invalid SMTP address advertisement (HELO) %d != %d 
or %d != 0\n",
+       ntohs(helo->header.size),
+       sizeof(HELO_Message)+ntohs(helo->senderAddressSize),
+       
maddr->senderAddress[ntohs(helo->senderAddressSize)-1-FILTER_STRING_SIZE]);    
+    BREAK();
+    return SYSERR; /* obviously invalid */
+  } else {
+    LOG(LOG_DEBUG,
+       "DEBUG: verified SMTP helo from %s\n",
+       &maddr->senderAddress[0]);    
+    return OK;
+  }
+}
+
+/**
+ * Create a HELO-Message for the current node. The HELO is created
+ * without signature and without a timestamp. The GNUnet core will
+ * sign the message and add an expiration time.
+ *
+ * @param helo address where to store the pointer to the HELO
+ *        message
+ * @return OK on success, SYSERR on error
+ **/
+static int createHELO(HELO_Message ** helo) {
+  HELO_Message * msg;
+  char * email;
+  char * filter;
+  EmailAddress * haddr;
+  int i;
+  
+  email = getConfigurationString("SMTP", 
+                                "EMAIL");
+  if (email == NULL) {
+    LOG(LOG_DEBUG,
+       "DEBUG: no email-address specified, can not create SMTP HELO\n");
+    return SYSERR;
+  }
+  filter = getConfigurationString("SMTP",
+                                 "FILTER");
+  if (filter == NULL) { 
+    LOG(LOG_ERROR, 
+       "ERROR: no filter for E-mail specified, will not create SMTP HELO\n");
+    FREE(email);
+    return SYSERR;
+  }
+  if (strlen(filter) > FILTER_STRING_SIZE) {
+    filter[FILTER_STRING_SIZE] = '\0';
+    LOG(LOG_WARNING,
+       "WARNING: filter string to long, capped to %s\n",
+       filter);
+  }
+  i = (strlen(email) + 8) & (~7); /* make multiple of 8 */
+  msg = MALLOC(sizeof(HELO_Message) + sizeof(EmailAddress) + i);
+  memset(msg,
+        0,
+        sizeof(HELO_Message) + sizeof(EmailAddress) + i);
+  haddr = (EmailAddress*) &((HELO_Message_GENERIC*)msg)->senderAddress[0];
+  memset(&haddr->filter[0],
+        0,
+        FILTER_STRING_SIZE);
+  strcpy(&haddr->filter[0],
+        filter);
+  memcpy(&haddr->senderAddress[0],
+        email,
+        strlen(email)+1);
+  msg->senderAddressSize = htons(strlen(email)+1+sizeof(EmailAddress));
+  msg->protocol          = htons(SMTP_PROTOCOL_NUMBER);
+  msg->MTU               = htonl(smtpAPI.mtu);
+  msg->header.size
+      = htons(HELO_Message_size(msg));
+  *helo = msg;
+  FREE(email);
+  if (verifyHelo(*helo) == SYSERR) 
+    errexit("FATAL: my own SMTP helo advertisement does not verify!\n");
+  return OK;
+}
+
+/**
+ * Establish a connection to a remote node.
+ * @param helo the HELO-Message for the target node
+ * @param tsessionPtr the session handle that is to be set
+ * @return OK on success, SYSERR if the operation failed
+ **/
+static int smtpConnect(HELO_Message * helo,
+                      TSession ** tsessionPtr) {
+  TSession * tsession;
+  
+  tsession = MALLOC(sizeof(TSession));
+  tsession->internal = helo;
+  (*tsessionPtr) = tsession;
+  return OK;
+}
+
+#define MIN(a,b) (((a)<(b))?(a):(b))
+
+/**
+ * A (core) Session is to be associated with a transport session. The
+ * transport service may want to know in order to call back on the
+ * core if the connection is being closed.
+ *
+ * @param tsession the session handle passed along
+ *   from the call to receive that was made by the transport
+ *   layer
+ * @return OK if the session could be associated,
+ *         SYSERR if not.
+ **/
+int smtpAssociate(TSession * tsession) {
+  return SYSERR; /* SMTP connections can never be associated */
+}
+
+/**
+ * Send a message to the specified remote node.
+ *
+ * @param tsession the HELO_Message identifying the remote node
+ * @param message what to send
+ * @param size the size of the message
+ * @param isEncrypted is the message encrypted? (YES/NO)
+ * @param crc CRC32 of the plaintext
+ * @return SYSERR on error, OK on success
+ **/
+static int smtpSend(TSession * tsession,
+                   const void * message,
+                   const unsigned int size,
+                   int isEncrypted,
+                   const int crc) {
+  char * msg;
+  SMTPMessage * mp;
+  HELO_Message * helo;
+  EmailAddress * haddr;
+  char * ebody;
+  int res;
+  int ssize, ssize2;
+  
+  if (smtp_shutdown == YES)
+    return SYSERR;
+  if (size > smtpAPI.mtu) {
+    LOG(LOG_FAILURE,
+       "FAILURE: message larger than allowed by smtp transport (%d > %d)\n",
+       size, 
+       smtpAPI.mtu);
+    return SYSERR;
+  }
+  helo = (HELO_Message*)tsession->internal;
+  if (helo == NULL) 
+    return SYSERR;
+
+  haddr = (EmailAddress*) &((HELO_Message_GENERIC*)helo)->senderAddress[0];
+  ssize2 = ssize = size + sizeof(SMTPMessage);
+  msg = MALLOC(ssize);
+  mp              = (SMTPMessage*) &msg[size];
+  mp->checkSum    = htonl(crc);
+  mp->isEncrypted = htons(isEncrypted);
+  mp->size        = htons(ssize);
+  memcpy(&mp->sender,
+        coreAPI->myIdentity,
+        sizeof(HostIdentity));
+  memcpy(msg,
+        message,
+        size);
+  ebody = NULL;
+  LOG(LOG_DEBUG,
+      "DEBUG: base64-encoding %d byte message\n",
+      ssize);
+  ssize = base64_encode(msg, ssize, &ebody);
+  LOG(LOG_DEBUG,
+      "DEBUG: base64-encoded message size %d bytes\n",
+      ssize);
+  
+  FREE(msg);
+  MUTEX_LOCK(&smtpLock);
+  res = SYSERR;
+  /*
+    The mail from field is left empty, so mailing list servers
+    will interpret the message as a bounce message.    
+    MAIL FROM: <>    
+    RCPT TO: address@hidden
+    DATA
+    FILTER
+    ebody
+    .    
+   */
+  if (OK == writeSMTPLine(smtp_sock,
+                         "MAIL FROM: <>\r\n"))
+    if (OK == readSMTPLine(smtp_sock,
+                          "250 "))
+      if (OK == writeSMTPLine(smtp_sock,
+                             "RCPT TO: <%s>\r\n",
+                             &haddr->senderAddress[0]))
+       if (OK == readSMTPLine(smtp_sock,
+                              "250 "))
+         if (OK == writeSMTPLine(smtp_sock,
+                                 "DATA\r\n"))
+           if (OK == readSMTPLine(smtp_sock,
+                                  "354 ")) 
+             if (OK == writeSMTPLine(smtp_sock,
+                                     "%-*s\r\n",
+                                     MIN(FILTER_STRING_SIZE,
+                                         strlen(&haddr->filter[0])),
+                                     &haddr->filter[0]))
+               if (OK == writeSMTPLine(smtp_sock,
+                                       "%s\r\n  boundary=\"%s\"\r\n\r\n",
+                                       CONTENT_TYPE_MULTIPART,
+                                       BOUNDARY_SPECIFIER))
+                 if (OK == writeSMTPLine(smtp_sock,
+                                         "--%s\r\n\r\n",
+                                         BOUNDARY_SPECIFIER))
+                   if (SYSERR != SEND_BLOCKING_ALL(smtp_sock,
+                                                   ebody,
+                                                   ssize))
+                     if (OK == writeSMTPLine(smtp_sock, 
+                                             "\r\n--%s\r\n",
+                                             BOUNDARY_SPECIFIER))
+                       if (OK == writeSMTPLine(smtp_sock,
+                                               "\r\n.\r\n"))
+                         if (OK == readSMTPLine(smtp_sock,
+                                                "250 "))
+                           res = OK;  
+  MUTEX_UNLOCK(&smtpLock);
+  if (res != OK)
+    LOG(LOG_WARNING,
+       "WARNING: sending E-mail to %s failed\n",
+       &haddr->senderAddress[0]);
+  incrementBytesSent(ssize);
+  statChange(stat_octets_total_smtp_out,
+            ssize);
+  FREE(ebody);
+  return res;
+}
+
+/**
+ * Disconnect from a remote node.
+ *
+ * @param tsession the session that is closed
+ * @return OK on success, SYSERR if the operation failed
+ **/
+static int smtpDisconnect(TSession * tsession) {
+  if (tsession != NULL) {
+    if (tsession->internal != NULL)
+      FREE(tsession->internal);
+    FREE(tsession);
+  }
+  return OK;
+}
+
+/**
+ * Start the server process to receive inbound traffic.
+ * @return OK on success, SYSERR if the operation failed
+ **/
+static int startTransportServer(void) {
+  char * email;
+
+  if (serverSignal != NULL) {
+    LOG(LOG_FAILURE, 
+      "FAILURE: can not start SMTP server, already running!?\n");
+    return SYSERR;
+  }
+  serverSignal = SEMAPHORE_NEW(0);
+  smtp_shutdown = NO;
+
+   /* initialize SMTP network */
+  smtp_sock = connectToSMTPServer();
+  if ( smtp_sock == -1) {
+    LOG(LOG_ERROR,
+       "ERROR: could not create socket! (%d)\n",
+       smtp_sock);
+    CLOSE(smtp_sock);
+    return SYSERR;
+  }
+  LOG(LOG_DEBUG,
+      "DEBUG: checking SMTP server\n");
+  /* read welcome from SMTP server */
+  if (SYSERR == readSMTPLine(smtp_sock,
+                            "220 ")) {
+    LOG(LOG_ERROR,
+       "ERROR: SMTP server send unexpected message\n");
+    CLOSE(smtp_sock);
+    return SYSERR;
+  } 
+  email = NULL; /* abusing email as a flag... */
+  if (OK == writeSMTPLine(smtp_sock,
+                         "HELO %s\r\n",
+                         getConfigurationString("SMTP",
+                                                "SENDERHOSTNAME")))
+    if (OK == readSMTPLine(smtp_sock,
+                          "250 "))
+      email = getConfigurationString("SMTP", 
+                                    "EMAIL");
+    
+  if (email == NULL) {
+    LOG(LOG_DEBUG,
+       "DEBUG: no email-address specified, will not advertise SMTP address\n");
+    return OK;
+  }
+  FREE(email);
+  LOG(LOG_DEBUG,
+      "DEBUG: creating listen thread\n");
+  if (0 != PTHREAD_CREATE(&dispatchThread,
+                         (PThreadMain) &listenAndDistribute,
+                         NULL,
+                         1024)) {
+    LOG(LOG_ERROR,
+       "ERROR: could not create thread to listen to inbound mail\n");
+    return SYSERR;
+  }
+  SEMAPHORE_DOWN(serverSignal); /* wait for server to be up */
+  return OK;
+}
+
+/**
+ * Shutdown the server process (stop receiving inbound traffic). Maybe
+ * restarted later!
+ **/
+static int stopTransportServer() {
+  void * unused;
+
+  smtp_shutdown = YES;
+  CLOSE(smtp_pipe); /* close pipe. Waiting fgets should return NULL*/
+  SEMAPHORE_DOWN(serverSignal);
+  SEMAPHORE_FREE(serverSignal);
+  CLOSE(smtp_sock);
+  PTHREAD_JOIN(&dispatchThread, &unused);
+  return OK;
+}
+
+/**
+ * Reload the configuration. Should never fail.
+ **/
+static void reloadConfiguration(void) {
+}
+
+/**
+ * Convert TCP address to a string.
+ **/
+static char * addressToString(HELO_Message * helo) {
+  char * ret;
+  EmailAddress * addr;
+  
+  addr = (EmailAddress*) &((HELO_Message_GENERIC*)helo)->senderAddress[0];  
+  ret = MALLOC(FILTER_STRING_SIZE +
+              strlen(addr->senderAddress)
+              + 16);
+  sprintf(ret,
+         "%.*s filter %s (SMTP)",
+         FILTER_STRING_SIZE,
+         addr->filter,
+         addr->senderAddress);
+  return ret;
+}
+
+/**
+ * The default maximum size of each outbound SMTP message.
+ **/
+#define MESSAGE_SIZE 65536
+
+/**
+ * The exported method. Makes the core api available via a global and
+ * returns the smtp transport API.
+ **/ 
+TransportAPI * inittransport_smtp(CoreAPIForTransport * core) {
+  int mtu;
+
+  coreAPI = core;
+  stat_octets_total_smtp_in 
+    = statHandle("# bytes received via smtp");
+  stat_octets_total_smtp_out 
+    = statHandle("# bytes sent via smtp");
+
+  MUTEX_CREATE(&smtpLock);
+  reloadConfiguration();
+  mtu = getConfigurationInt("SMTP",
+                           "MTU");
+  if (mtu == 0)
+    mtu = MESSAGE_SIZE;
+  if (mtu < 1200)
+    LOG(LOG_ERROR,
+       "ERROR: MTU for SMTP is probably to low (fragmentation not 
implemented!)\n");
+
+  smtpAPI.protocolNumber       = SMTP_PROTOCOL_NUMBER;
+  smtpAPI.mtu                  = mtu - sizeof(SMTPMessage);
+  smtpAPI.cost                 = 50;
+  smtpAPI.verifyHelo           = &verifyHelo;
+  smtpAPI.createHELO           = &createHELO;
+  smtpAPI.connect              = &smtpConnect;
+  smtpAPI.send                 = &smtpSend;
+  smtpAPI.sendReliable         = &smtpSend; /* is always blocking, so we can't 
really do better */
+  smtpAPI.associate            = &smtpAssociate;
+  smtpAPI.disconnect           = &smtpDisconnect;
+  smtpAPI.startTransportServer = &startTransportServer;
+  smtpAPI.stopTransportServer  = &stopTransportServer;
+  smtpAPI.reloadConfiguration  = &reloadConfiguration;
+  smtpAPI.addressToString      = &addressToString;
+
+  return &smtpAPI;
+}
+
+void donetransport_smtp() {
+  MUTEX_DESTROY(&smtpLock);
+}
+
+/* end of smtp.c */

Added: freeway/src/org/gnu/freeway/transport/tcp/TCPAddress.java
===================================================================
--- freeway/src/org/gnu/freeway/transport/tcp/TCPAddress.java   2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/transport/tcp/TCPAddress.java   2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,91 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.transport.tcp;
+
+import org.gnu.freeway.util.net.*;
+
+import java.net.*;
+import java.nio.*;
+
+/**
+ * Host-Address in a TCP network.
+ */
+
+public class TCPAddress extends Object implements Persistent
+{
+       public static final int SIZE    =       8;
+
+       /** claimed IP of the sender */
+       private InetAddress     ip;
+
+       /** claimed port of the sender */
+       private int                     port;
+
+
+       public TCPAddress()
+       {
+               super();
+               ip=null;
+               port=0;
+       }
+
+       public TCPAddress( InetAddress addr, int p )
+       {
+               this();
+               ip=addr;
+               port=p;
+       }
+
+       public String toString()
+       {
+               return "TCP address [ip="+ip+", port="+port+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public InetAddress getIP()
+       {
+               return ip;
+       }
+
+       public int getPort()
+       {
+               return port;
+       }
+
+       public int getByteSize()
+       {
+               return SIZE;
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               byte[]  b;
+               int             reserved;
+
+               b=new byte[4];
+               buf.get(b);
+               try {
+                       ip=InetAddress.getByAddress(b);
+                       }
+               catch( UnknownHostException x ) {
+                       err.reportIf(true,"bad address");
+                       }
+
+               port=buf.getShort() & 0x0000ffff;
+               err.reportIf(port>65535,"bad port");
+
+               reserved=buf.getShort() & 0x0000ffff;
+               err.reportIf(reserved!=0,"0 expected");
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               buf.put(ip.getAddress());
+               buf.putShort((short) port);
+               buf.putShort((short) 0);
+       }
+}

Added: freeway/src/org/gnu/freeway/transport/tcp/TCPMessagePack.java
===================================================================
--- freeway/src/org/gnu/freeway/transport/tcp/TCPMessagePack.java       
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/transport/tcp/TCPMessagePack.java       
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,83 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.transport.tcp;
+
+import org.gnu.freeway.transport.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * TCP message packet.
+ */
+
+public class TCPMessagePack extends MessagePack implements Persistent
+{
+       public static final int SIZE            =       8;
+
+
+       public TCPMessagePack()
+       {
+               this(null);
+       }
+
+       public TCPMessagePack( Session s )
+       {
+               super(s);
+       }
+
+       public String toString()
+       {
+               return "TCP message pack [size="+getSize()+", 
encrypted="+isEncrypted()+", crc="+getCRC()+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int getByteSize()
+       {
+               return SIZE+getSize();
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               byte[]          b;
+               ByteBuffer      p;
+               int                     size,crc;
+               boolean         encrypted;
+
+               size=buf.getShort() & 0x0000ffff;
+               err.reportIf(size<SIZE,"bad size");
+
+               encrypted=buf.getShort()!=0;
+               crc=buf.getInt();
+
+               b=new byte[size-SIZE];
+               buf.get(b);
+
+               p=ByteBuffer.allocate(b.length);
+               p.put(b);
+               setMessages(p,crc,encrypted);
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               ByteBuffer      p;
+
+               buf.putShort((short) getByteSize());
+               buf.putShort((short) (isEncrypted() ? 1 : 0));
+               buf.putInt(getCRC());
+
+               p=getMessages();
+               p.flip();
+               try {
+                       buf.put(p);
+                       }
+               finally {
+                       p.flip();
+                       p.position(p.limit());
+                       }
+       }
+}

Added: freeway/src/org/gnu/freeway/transport/tcp/TCPSession.java
===================================================================
--- freeway/src/org/gnu/freeway/transport/tcp/TCPSession.java   2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/transport/tcp/TCPSession.java   2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,330 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.transport.tcp;
+
+import org.gnu.freeway.server.*;
+import org.gnu.freeway.transport.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+import java.nio.channels.*;
+import java.util.logging.*;
+
+/**
+ * Transport Session handle.
+ */
+
+public class TCPSession extends org.gnu.freeway.util.net.TCPSession implements 
Session
+{
+       /** after how much time of the core not being associated with a tcp 
connection anymore do we close it ? */
+       public static final long        TIME_OUT        =       
Scheduler.SECS_30;
+
+       /** */
+       private AbstractTransport               transport;
+
+       /** To whom are we talking to ? */
+       private HostIdentity                    remote;
+
+       /** Number of users of this session. */
+       private int                                     lock;
+
+       /** */
+       private String                          remoteAddr;
+
+       /** Are we still expecting the welcome ? (true/false) */
+       private boolean                         expectingWelcome;
+
+       /** Last time there was a reap op. */
+       private long                                    lastRead;
+
+       /** Last time there was a write op. */
+       private long                                    lastWrite;
+
+
+       public TCPSession( TCPServer s, TCPTransport t )
+       {
+               super(s);
+               transport=t;
+               remote=null;
+               lock=1;
+               remoteAddr="";
+               expectingWelcome=false;
+               lastRead=Scheduler.now();
+               lastWrite=Scheduler.now();
+       }
+
+       public String toString()
+       {
+               return "TCP session";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public boolean connect( P2PHello helo )
+       {
+               TCPAddress      addr;
+
+               addr=(TCPAddress) helo.decodeSenderAddress(TCPAddress.class);
+               debug("Creating TCP connection to 
"+addr.getIP()+":"+addr.getPort()+".");
+               remoteAddr="TCP("+addr.getIP()+":"+addr.getPort()+")";
+
+               expectingWelcome=false;
+               if (connect(addr.getIP(),addr.getPort(),false)) {
+                       setBlocking(false);
+
+                       remote=(HostIdentity) 
PersistentHelper.copy(helo.getSenderIdentity());
+                       // send our node identity to the other side to fully 
establish the connection !
+                       flushAndSend(new TCPWelcome(transport.getHost()));
+                       return true;
+                       }
+               return false;
+       }
+
+       public boolean connect( SocketChannel c )
+       {
+               
remoteAddr="TCP("+c.socket().getInetAddress()+":"+c.socket().getPort()+")";
+               expectingWelcome=true;
+               return connect(c,false);        // not blocking
+       }
+
+       public int doReceive()
+       {
+               long            last,now;
+
+               synchronized(readLock) {
+                       last=Math.max(lastRead,lastWrite);
+                       now=Scheduler.now();
+
+                       if (lock==1 && now>last+TIME_OUT) {
+                               log(Level.INFO,getLabel()+" Time out detected 
("+Scheduler.toSeconds(now-last)+"s > "+Scheduler.toSeconds(TIME_OUT)+"s).");
+                               disconnect();
+                               return -1;
+                               }
+                       lastRead=Scheduler.now();
+                       return super.doReceive();
+                       }
+       }
+
+       public MessagePack receive()
+       {
+               TCPWelcome              welcome;
+               TCPMessagePack  pack;
+
+               if (!hasReceived()) {
+                       return null;
+                       }
+
+               if (expectingWelcome) {
+                       welcome=(TCPWelcome) receive(TCPWelcome.class);
+                       if (welcome==null) {
+                               log(Level.WARNING,"Expected welcome on 
connection, got garbage. Closing.");
+                               disconnect();
+                               return null;
+                               }
+
+                       expectingWelcome=false;
+
+                       remote=(HostIdentity) 
PersistentHelper.copy(welcome.getClientIdentity());
+                       debug(getRemote().getName()+" Received welcome 
message.");
+
+                       if (!hasReceived()) {
+                               return null;
+                               }
+                       }
+
+               pack=(TCPMessagePack) receive(TCPMessagePack.class);
+               if (pack==null) {
+                       log(Level.WARNING,"Received malformed message from 
connection. Closing.");
+                       disconnect();
+                       return null;
+                       }
+               pack.setSession(this);
+               return pack;
+       }
+
+       public int doSend()
+       {
+               long            last,now;
+
+               synchronized(writeLock) {
+                       last=Math.max(lastRead,lastWrite);
+                       now=Scheduler.now();
+
+                       if (lock==1 && now>last+TIME_OUT) {
+                               log(Level.INFO,getLabel()+" Time out detected 
("+Scheduler.toSeconds(now-last)+"s > "+Scheduler.toSeconds(TIME_OUT)+"s).");
+                               disconnect();
+                               return -1;
+                               }
+                       lastWrite=Scheduler.now();
+                       return super.doSend();
+                       }
+       }
+
+       public boolean send( Persistent p )
+       {
+               ByteBuffer      buf;
+
+               buf=ByteBuffer.allocateDirect(p.getByteSize());
+               PersistentHelper.writeFully(p,buf);
+               return send(buf,null);
+       }
+
+       public boolean send( Persistent[] p )
+       {
+               byte[]          b;
+               ByteBuffer      buf;
+
+               b=PersistentHelper.toBytes(p);
+
+               buf=ByteBuffer.allocateDirect(b.length);
+               buf.put(b);
+               return send(buf,null);
+       }
+
+       public boolean send( ByteBuffer buf )
+       {
+               return send(buf,null);
+       }
+
+       public boolean send( ByteBuffer buf, SessionKey skey )
+       {
+               int     crc,percent;
+
+               crc=Crypto.crc32(buf);
+
+               if (skey!=null) {
+                       buf=skey.encrypt(buf);
+                       if (buf==null) {
+                               log(Level.SEVERE,"Failed to encrypt message !");
+                               return false;
+                               }
+                       }
+
+               percent=transport.getPercentRandomOutboundDrop();
+               if (percent>0 && percent>Crypto.nextInt(100)) {
+                       debug("Simulated random network loss, message 
dropped.");
+                       return true;    // simulate random network loss
+                       }
+
+               return sendImpl(buf,crc,skey!=null);
+       }
+
+       /**
+        * Send a message to the remote node with increased reliablility 
(whatever that means is up to the transport).
+        * If an error is returned, the caller must call "disconnect" and not 
continue using the session afterwards
+        * (useful if the other side closed the connection).
+        *
+        * @param buf   The message to send whose size should be <= mtu.
+        * @param skey  The session key used to encrypt the message. If null, 
the message is not encrypted and sent plain.
+        * @return              True on success, false on error.
+        */
+
+       public boolean sendReliable( ByteBuffer buf, SessionKey skey )
+       {
+               int     crc;
+
+               crc=Crypto.crc32(buf);
+
+               if (skey!=null) {
+                       buf=skey.encrypt(buf);
+                       if (buf==null) {
+                               log(Level.SEVERE,"Failed to encrypt message !");
+                               return false;
+                               }
+                       }
+
+               return sendImpl(buf,crc,skey!=null);
+       }
+
+       /**
+        * Send a message to the remote node. Drop if the operation would block.
+        * Send a message to the connected node.
+        * Send a message to the connected node with increased reliability 
(grow TCP send buffer above one frame if needed).
+        *
+        * @param buf           the message to send whose size should be <= mtu
+        * @param crc           the CRC of the (plaintext) message/checksum of 
the plain message
+        * @param encrypted     is the message encrypted ?
+        * @return                      true if message was sent or queued, 
false if queue is full and message was dropped.
+        */
+
+       public synchronized boolean sendImpl( ByteBuffer buf, int crc, boolean 
encrypted )
+       {
+               TCPMessagePack  pack;
+
+               if (!isConnected()) {
+                       log(Level.WARNING,"Can't send message, session is 
closed !");
+                       return false;
+                       }
+
+               if (buf.position()==0) {
+                       log(Level.SEVERE,"Can't send an empty message.");
+                       return false;
+                       }
+
+               if (buf.position()>transport.getMTU()) {
+                       log(Level.SEVERE,"Can't send a message larger than MTU 
("+buf.position()+" > "+transport.getMTU()+").");
+                       return false;
+                       }
+
+               pack=new TCPMessagePack(this);
+               pack.setMessages(buf,crc,encrypted);
+               flushAndSend(pack);
+               return true;
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public HostIdentity getLocal()
+       {
+               return transport.getHost();
+       }
+
+       public HostIdentity getRemote()
+       {
+               return remote;
+       }
+
+       public String getRemoteAddress()
+       {
+               return remoteAddr;
+       }
+
+       public Transport getTransport()
+       {
+               return transport;
+       }
+
+       public synchronized boolean lock()
+       {
+               if (!isConnected()) {
+                       log(Level.WARNING,getLabel()+" Disconnected, could not 
lock !");
+                       return false;
+                       }
+
+               lock++;
+               return true;
+       }
+
+       public synchronized boolean unlock()
+       {
+               if (!isConnected()) {
+                       log(Level.WARNING,getLabel()+" Disconnected, could not 
unlock !");
+                       return false;
+                       }
+
+               lock--;
+               if (lock==0) {
+                       log(Level.FINE,getLabel()+" No more user, disconnect if 
needed !");
+                       if (isConnected()) {
+                               disconnect();
+                               }
+                       }
+               return true;
+       }
+}

Added: freeway/src/org/gnu/freeway/transport/tcp/TCPTransport.java
===================================================================
--- freeway/src/org/gnu/freeway/transport/tcp/TCPTransport.java 2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/transport/tcp/TCPTransport.java 2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,309 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.transport.tcp;
+
+import org.gnu.freeway.server.*;
+import org.gnu.freeway.transport.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.net.*;
+
+import java.net.*;
+import java.nio.channels.*;
+import java.util.logging.*;
+
+/**
+ * Implementation of the TCP transport service
+ */
+
+public class TCPTransport extends AbstractTransport implements CSSessionHandler
+{
+       /** */
+       private Prefs                           prefs;
+
+       /** */
+       private StatusCallsService      status;
+
+       /** */
+       private TCPServer                       server;
+
+       /** */
+       private int                                     mtu;
+
+       /** handles for statistics */
+       private Stat                                    bytesIn;
+
+       /** */
+       private Stat                                    bytesOut;
+
+       /** */
+       private CIDRNetworks                    blackList;
+
+
+       public TCPTransport()
+       {
+               super(TCP_PROTOCOL_NUMBER,"TCP");
+               server=new TCPServer("TCP PEER SERVER",this);
+       }
+
+       public String toString()
+       {
+               return "TCP transport";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int getMTU()
+       {
+               return mtu;
+       }
+
+       public int getCost()
+       {
+               return 20000;
+       }
+
+       /**
+        * The exported method. Makes the core api available
+        * via a global and returns the tcp transport API.
+        * @param core
+        */
+
+       public void init( CoreForTransport core )
+       {
+               Statistics      stats;
+               int                     n;
+
+               super.init(core);
+
+               prefs=core.getApplication().getPreferences();
+               status=(StatusCallsService) 
core.service(StatusCallsService.class);
+
+               
blackList=CIDRNetworks.parse(prefs.getString("TCP","BLACKLIST",""));
+               log(Level.INFO,"Filtered networks : 
"+blackList.getDescription());
+
+               stats=core.getApplication().getStatistics();
+               bytesIn=stats.getHandle("# bytes received via tcp");
+               bytesOut= stats.getHandle("# bytes sent via tcp");
+
+               n=prefs.getInt("TCP","MTU",1460);       //todo: 1440 ?, ou 
trouver la valeur systeme ?
+               if (n<1200) {
+                       log(Level.SEVERE,"MTU for TCP is probably to low 
(fragmentation is not implemented) !");
+                       }
+               mtu = n - TCPMessagePack.SIZE;
+       }
+
+       /**
+        * Start the server process to receive inbound traffic.
+        * @return true on success, false if the operation failed
+        */
+
+       public boolean launch()
+       {
+               int     port;
+
+               if (server.isLaunched()) {
+                       log(Level.SEVERE,"Could not start TCP server, already 
running !?");
+                       return false;
+                       }
+
+               port=getTCPPort();
+               if (!server.launch(port)) {
+                       log(Level.SEVERE,"Could not start TCP server !");
+                       return false;
+                       }
+               return true;
+       }
+
+       /**
+        * Shutdown the server process (stop receiving inbound traffic). May be 
restarted later !
+        * @return
+        */
+
+       public boolean shutdown()
+       {
+               return server.shutdown();
+       }
+
+       /**
+        * Create a HELO-Message for the current node. The HELO is
+        * created without signature and without a timestamp. The
+        * GNUnet core will sign the message and add an expiration time.
+        *
+        * @return the HELO message on success, null on error
+        */
+
+       public P2PHello createHELO()
+       {
+               P2PHello        helo;
+               InetAddress     ip;
+               int                     port;
+
+               port=getTCPPort();
+               if (port==0) {
+                       log(Level.FINEST,"TCP port is 0, will only send using 
TCP.");
+                       return null;    // TCP transport is configured 
send-only !
+                       }
+
+               ip=((IdentityService) 
coreAPI.service(IdentityService.class)).getPublicIPAddress();
+               if (ip==null) {
+                       log(Level.WARNING,"Could not determine my public IP 
address.");
+                       return null;
+                       }
+
+               helo=new P2PHello(TCP_PROTOCOL_NUMBER,getMTU());
+               helo.encodeSenderAddress(new TCPAddress(ip,port));
+               return helo;
+       }
+
+       /**
+        * Verify that a HELO-Message is correct (a node is reachable at that 
address).
+        * Since the reply will be asynchronous, a method must be called on 
success.
+        *
+        * @param helo  the HELO message to verify (the signature/crc have been 
verified before)
+        * @return              true on success, false on error
+        */
+
+       public boolean verifyHELO( P2PHello helo )
+       {
+               TCPAddress      addr;
+
+               if (helo.getProtocol()!=TCP_PROTOCOL_NUMBER) {
+                       return false;
+                       }
+
+               // check if we are allowed to connect to the given ip
+               addr=(TCPAddress) helo.decodeSenderAddress(TCPAddress.class);
+               if (addr==null || blackList.check(addr.getIP())) {
+                       return false;
+                       }
+               debug(helo.getSenderIdentity().getName()+" Verified HELO from 
"+addr.getIP().getHostAddress()+":"+addr.getPort()+".");
+               return true;
+       }
+
+       /**
+        * Establish a connection to a remote node.
+        *
+        * @param helo the HELO-Message for the target node
+        * @return the session handle on success, null if the operation failed
+        */
+
+       public Session connect( P2PHello helo )
+       {
+               TCPSession      session;
+               TCPAddress      addr;
+
+               if (!server.isLaunched()) {
+                       return null;
+                       }
+
+               addr=(TCPAddress) helo.decodeSenderAddress(TCPAddress.class);
+               debug("Creating TCP connection to 
"+addr.getIP()+":"+addr.getPort()+" from "+coreAPI.getIdentity().getName()+".");
+
+               session=new TCPSession(server,this);
+               if (session.connect(helo)) {
+                       if (server.register(session)) {
+                               session.lock();
+                               }
+                       else {
+                               session.disconnect();
+                               session=null;
+                               }
+                       }
+               else {
+                       session=null;
+                       }
+               return session;
+       }
+
+       /**
+        * Convert TCP address to a string.
+        * @param helo
+        * @return
+        */
+
+       public String addressToString( P2PHello helo )
+       {
+               TCPAddress      addr;
+
+               addr=(TCPAddress) helo.decodeSenderAddress(TCPAddress.class);
+               return addr.getIP()+":"+addr.getPort()+" (TCP)";
+       }
+
+       /**
+        * Get the GNUnet UDP port from the configuration,
+        * or from /etc/services if it is not specified in
+        * the config file.
+        * @return
+        */
+
+       protected int getTCPPort()
+       {
+               int     port;
+
+               port=prefs.getInt("TCP","PORT",0);
+               if (port==0) {
+                       // try lookup in services
+                       port=OSAccess.getServicePort("gnunet","tcp");
+                       }
+               return port;
+       }
+
+       public CSSession handleAccept( SocketChannel socket )
+       {
+               TCPSession      session;
+               String          str;
+
+               
str=socket.socket().getInetAddress().getHostAddress()+":"+socket.socket().getPort();
+
+               // verify ip for eligibility here
+               // user should be able to specify who is allowed to connect, 
otherwise we just close and reject the communication !
+               // check if we are allowed to connect to the given ip
+               if (blackList.check(socket.socket().getInetAddress())) {
+                       log(Level.INFO,"Rejected connection from "+str+" 
(blacklisted).");
+                       return null;
+                       }
+
+               log(Level.INFO,"Accepted connection from "+str+".");
+
+               session=new TCPSession(server,this);
+               session.connect(socket);
+               return session;
+       }
+
+       public boolean handleRead( CSSession s, int len )
+       {
+               TCPSession      sess;
+               MessagePack     mp;
+
+               status.incrementBytesReceived(len);
+               bytesIn.add(len);
+
+               sess=(TCPSession) s;
+               sess.lock();
+               try {
+                       mp=sess.receive();
+                       if (mp!=null) {
+                               debug("Received "+mp.getSize()+" bytes, 
forwarding to core.");
+                               coreAPI.receive(mp);
+                               }
+                       }
+               finally {
+                       sess.unlock();
+                       }
+               return true;
+       }
+
+       public boolean handleWrite( CSSession s, int len )
+       {
+               status.incrementBytesSent(len);
+               bytesOut.add(len);
+               return true;
+       }
+
+       public void handleDestroy( CSSession s )
+       {
+       }
+}

Added: freeway/src/org/gnu/freeway/transport/tcp/TCPWelcome.java
===================================================================
--- freeway/src/org/gnu/freeway/transport/tcp/TCPWelcome.java   2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/transport/tcp/TCPWelcome.java   2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,74 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.transport.tcp;
+
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * Initial handshake message. Note that the beginning must match the CS_HEADER 
since we are using tcpio.
+ */
+
+public class TCPWelcome extends Object implements Persistent
+{
+       public static final int SIZE    =       4+HostIdentity.SIZE;
+
+       /** Identity of the node connecting (TCP client) */
+       private HostIdentity    clientIdentity;
+
+
+       public TCPWelcome()
+       {
+               super();
+               clientIdentity=null;
+       }
+
+       public TCPWelcome( HostIdentity hi )
+       {
+               this();
+               clientIdentity=(HostIdentity) PersistentHelper.copy(hi);
+       }
+
+       public String toString()
+       {
+               return "TCP welcome [clientIdentity="+clientIdentity+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public HostIdentity getClientIdentity()
+       {
+               return clientIdentity;
+       }
+
+       public int getByteSize()
+       {
+               return SIZE;
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               int     size,version;
+
+               size=buf.getShort() & 0x0000ffff;
+               version=buf.getShort() & 0x0000ffff;
+
+               err.reportIf(size!=SIZE,"bad size");
+               err.reportIf(version!=0,"bad version");
+
+               clientIdentity=new HostIdentity();
+               clientIdentity.readBytes(buf,err);
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               buf.putShort((short) SIZE);
+               buf.putShort((short) 0);
+               clientIdentity.writeBytes(buf);
+       }
+}

Added: freeway/src/org/gnu/freeway/transport/tcp6.c
===================================================================
--- freeway/src/org/gnu/freeway/transport/tcp6.c        2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/transport/tcp6.c        2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,1493 @@
+/*
+     This file is part of GNUnet
+
+     GNUnet is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with GNUnet; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file transports/tcp6.c
+ * @brief Implementation of the TCP6 transport service over IPv6
+ * @author Christian Grothoff
+ **/
+
+#include "gnunet_util.h"
+#include "gnunet_transport.h"
+#include "platform.h"
+
+#define DEBUG_TCP6 NO
+
+/**
+ * after how much time of the core not being associated with a tcp6
+ * connection anymore do we close it? 
+ **/
+#define TCP6_TIMEOUT 30 * cronSECONDS
+
+/**
+ * @brief Host-Address in a TCP6 network.
+ **/
+typedef struct {
+  /**
+   * claimed IP of the sender, network byte order 
+   **/  
+  IP6addr ip;
+
+  /**
+   * claimed port of the sender, network byte order 
+   **/
+  unsigned short port; 
+
+  /**
+   * reserved (set to 0 for signature verification) 
+   **/
+  unsigned short reserved; 
+
+} Host6Address;
+
+/**
+ * @brief TCP6 Message-Packet header. 
+ **/
+typedef struct {
+  /**
+   * size of the message, in bytes, including this header; 
+   * max 65536-header (network byte order) 
+   **/
+  unsigned short size;
+
+  /**
+   * reserved, must be 0 (network byte order) 
+   **/
+  unsigned short isEncrypted;
+
+  /**
+   * CRC checksum of the packet  (network byte order)
+   **/ 
+  int checkSum;
+  
+  /**
+   * This struct is followed by MESSAGE_PARTs - until size is reached 
+   * There is no "end of message".
+   **/
+  p2p_HEADER parts[0];
+} TCP6MessagePack;
+
+/**
+ * Initial handshake message. Note that the beginning
+ * must match the CS_HEADER since we are using tcp6io.
+ **/
+typedef struct {
+  /**
+   * size of the handshake message, in nbo, value is 24 
+   **/    
+  unsigned short size;
+
+  /**
+   * "message type", TCP6 version number, always 0.
+   **/
+  unsigned short version;
+
+  /**
+   * Identity of the node connecting (TCP6 client) 
+   **/
+  HostIdentity clientIdentity;
+} TCP6Welcome;
+
+/**
+ * @brief TCP6 Transport Session handle.
+ **/
+typedef struct {
+  /**
+   * the tcp6 socket 
+   **/
+  int sock;
+
+  /**
+   * number of users of this session 
+   **/
+  int users;
+
+  /**
+   * Last time this connection was used
+   **/
+  cron_t lastUse;
+
+  /**
+   * mutex for synchronized access to 'users' 
+   **/
+  Mutex lock;
+
+  /**
+   * To whom are we talking to (set to our identity
+   * if we are still waiting for the welcome message)
+   **/
+  HostIdentity sender;
+
+  /**
+   * Are we still expecting the welcome? (YES/NO)
+   **/
+  int expectingWelcome;
+
+  /**
+   * Current read position in the buffer.
+   **/
+  unsigned int pos;  
+
+  /**
+   * Current size of the buffer.
+   **/
+  unsigned int size;
+
+  /**
+   * The read buffer.
+   **/
+  char * rbuff;
+
+  /**
+   * Position in the write buffer
+   **/
+  unsigned int wpos;
+
+  /**
+   * The write buffer.
+   **/
+  char * wbuff;
+
+} TCP6Session;
+
+/* *********** globals ************* */
+
+/**
+ * apis (our advertised API and the core api ) 
+ **/
+static CoreAPIForTransport * coreAPI;
+static TransportAPI tcp6API;
+
+/**
+ * one thread for listening for new connections,
+ * and for reading on all open sockets 
+ **/
+static PTHREAD_T listenThread;
+
+/**
+ * sock is the tcp6 socket that we listen on for new inbound
+ * connections.
+ **/
+static int tcp6_sock;
+
+/**
+ * tcp6_pipe is used to signal the thread that is
+ * blocked in a select call that the set of sockets to listen
+ * to has changed.
+ **/
+static int tcp6_pipe[2];
+
+/**
+ * Array of currently active TCP6 sessions. 
+ **/
+static TSession ** tsessions = NULL;
+static int tsessionCount;
+static int tsessionArrayLength;
+
+/**
+ * handles for statistics 
+ **/
+static int stat_octets_total_tcp6_in;
+static int stat_octets_total_tcp6_out;
+
+/* configuration */
+static CIDR6Network * filteredNetworks_;
+
+/**
+ * Lock for access to mutable state of the module,
+ * that is the configuration and the tsessions array.
+ * Note that we ONLY need to synchronize access to
+ * the tsessions array when adding or removing sessions,
+ * since removing is done only by one thread and we just
+ * need to avoid another thread adding an element at the
+ * same point in time. We do not need to synchronize at
+ * every access point since adding new elements does not
+ * prevent the select thread from operating and removing
+ * is done by the only therad that reads from the array.
+ **/
+static Mutex tcp6lock;
+
+/**
+ * Semaphore used by the server-thread to signal that
+ * the server has been started -- and later again to
+ * signal that the server has been stopped.
+ **/
+static Semaphore * serverSignal = NULL;
+static int tcp6_shutdown = YES;
+
+/* ******************** helper functions *********************** */
+
+/**
+ * Check if we are allowed to connect to the given IP.
+ **/
+static int isBlacklisted(IP6addr * ip) {
+  int ret;
+
+  MUTEX_LOCK(&tcp6lock);
+  ret = checkIP6Listed(filteredNetworks_,
+                      ip);
+  MUTEX_UNLOCK(&tcp6lock);
+  return ret;
+}
+
+/**
+ * Write to the pipe to wake up the select thread (the set of
+ * files to watch has changed).
+ **/
+static void signalSelect() {
+  char i = 0;
+  int ret;
+
+  LOG(LOG_DEBUG,
+      "DEBUG: signaling select\n");
+  ret = WRITE(tcp6_pipe[1],
+             &i,
+             sizeof(char));
+  if (ret != sizeof(char))
+      LOG(LOG_ERROR,
+         "ERROR: write to tcp6 pipe (signalSelect) failed: %s\n",
+         STRERROR(errno));
+}
+
+/**
+ * Disconnect from a remote node. May only be called
+ * on sessions that were aquired by the caller first.
+ * For the core, aquiration means to call associate or
+ * connect. The number of disconnects must match the
+ * number of calls to connect+associate.
+ *
+ * @param tsession the session that is closed
+ * @return OK on success, SYSERR if the operation failed
+ **/
+static int tcp6Disconnect(TSession * tsession) {
+  if (tsession->internal != NULL) {
+    TCP6Session * tcp6session = tsession->internal;
+
+    MUTEX_LOCK(&tcp6session->lock);
+    tcp6session->users--;
+    if (tcp6session->users > 0) {
+      MUTEX_UNLOCK(&tcp6session->lock);
+      return OK;
+    }
+    MUTEX_UNLOCK(&tcp6session->lock);
+    MUTEX_DESTROY(&tcp6session->lock);
+    FREE(tcp6session->rbuff);
+    FREENONNULL(tcp6session->wbuff);
+    FREE(tcp6session);
+    FREE(tsession);
+  }
+  return OK;
+}
+
+/**
+ * Remove a session, either the other side closed the connection
+ * or we have otherwise reason to believe that it should better
+ * be killed. Destroy session closes the session as far as the
+ * TCP6 layer is concerned, but since the core may still have
+ * references to it, tcp6Disconnect may not instantly free all
+ * the associated resources. <p>
+ *
+ * destroySession may only be called if the tcp6lock is already
+ * held.
+ *
+ * @param i index to the session handle
+ **/
+static void destroySession(int i) {  
+  TCP6Session * tcp6Session;
+
+  tcp6Session = tsessions[i]->internal;
+  if (-1 != tcp6Session->sock)
+    if (0 != SHUTDOWN(tcp6Session->sock, SHUT_RDWR))
+      LOG(LOG_EVERYTHING,
+         "EVERYTHING: error shutting down socket %d: %s\n",
+         tcp6Session->sock,
+         STRERROR(errno));
+  CLOSE(tcp6Session->sock);
+  tcp6Session->sock = -1;
+  tcp6Disconnect(tsessions[i]);
+  tsessions[i] = tsessions[--tsessionCount];
+  tsessions[tsessionCount] = NULL;
+}
+
+/**
+ * Get the GNUnet UDP port from the configuration,
+ * or from /etc/services if it is not specified in 
+ * the config file.
+ **/
+static unsigned short getGNUnetTCP6Port() {
+  struct servent * pse;        /* pointer to service information entry */
+  unsigned short port;
+
+  port = (unsigned short) getConfigurationInt("TCP6",
+                                             "PORT");
+  if (port == 0) { /* try lookup in services */
+    if ((pse = getservbyname("gnunet", "tcp6"))) 
+      port = htons(pse->s_port);      
+  }
+  return port;
+}
+
+/**
+ * A (core) Session is to be associated with a transport session. The
+ * transport service may want to know in order to call back on the
+ * core if the connection is being closed. Associate can also be
+ * called to test if it would be possible to associate the session
+ * later, in this case the argument session is NULL. This can be used
+ * to test if the connection must be closed by the core or if the core
+ * can assume that it is going to be self-managed (if associate
+ * returns OK and session was NULL, the transport layer is responsible
+ * for eventually freeing resources associated with the tesession). If
+ * session is not NULL, the core takes responsbility for eventually
+ * calling disconnect.
+ * 
+ * @param tsession the session handle passed along
+ *   from the call to receive that was made by the transport
+ *   layer
+ * @return OK if the session could be associated,
+ *         SYSERR if not.
+ **/
+static int tcp6Associate(TSession * tsession) {
+  TCP6Session * tcp6Session;
+
+  if (tsession == NULL) {
+    LOG(LOG_FAILURE,
+       "FAILURE: assertFailed: tcp6Associate called with tsession NULL\n");
+    return SYSERR;
+  }
+  tcp6Session = (TCP6Session*) tsession->internal;
+  MUTEX_LOCK(&tcp6Session->lock);
+  tcp6Session->users++;
+  MUTEX_UNLOCK(&tcp6Session->lock);
+  return OK;
+}
+
+/**
+ * The socket of session i has data waiting, process!
+ * 
+ * This function may only be called if the tcp6lock is
+ * already held by the caller.
+ **/
+static int readAndProcess(int i) {
+  TSession * tsession;
+  TCP6Session * tcp6Session;
+  unsigned int len;
+  int ret;
+  TCP6MessagePack * pack;
+  MessagePack * mp;
+
+  tsession = tsessions[i];
+  if (SYSERR == tcp6Associate(tsession))
+    return SYSERR;
+  tcp6Session = tsession->internal;
+  ret = RECV_NONBLOCKING(tcp6Session->sock,
+                        &tcp6Session->rbuff[tcp6Session->pos],
+            tcp6Session->size - tcp6Session->pos);
+  cronTime(&tcp6Session->lastUse);
+  if (ret == 0) {
+    tcp6Disconnect(tsession);
+#if DEBUG_TCP6
+    LOG(LOG_DEBUG,
+       "DEBUG: READ on socket %d returned 0 bytes, closing connection\n",
+       tcp6Session->sock);
+#endif
+    return SYSERR; /* other side closed connection */
+  }
+  if (ret < 0) {
+    if ( (errno == EINTR) ||
+        (errno == EAGAIN) ) { 
+#if DEBUG_TCP6
+      LOG(LOG_DEBUG,
+         "DEBUG: READ on socket %d returned %s, closing connection\n",
+         tcp6Session->sock,
+         STRERROR(errno));
+#endif
+      tcp6Disconnect(tsession);
+      return OK;    
+    }
+#if DEBUG_TCP6
+    LOG(LOG_INFO,
+       "INFO: read failed on peer tcp6 connection (%d), closing (%s).\n",
+       ret,
+       STRERROR(errno));
+#endif
+    tcp6Disconnect(tsession);
+    return SYSERR;
+  }
+  incrementBytesReceived(ret);
+  statChange(stat_octets_total_tcp6_in,
+            ret);
+  tcp6Session->pos += ret;
+  len = ntohs(((TCP6MessagePack*)&tcp6Session->rbuff[0])->size);
+  if (len > tcp6Session->size) /* if MTU larger than expected, grow! */
+    GROW(tcp6Session->rbuff,
+        tcp6Session->size,
+        len);
+#if DEBUG_TCP6
+  LOG(LOG_DEBUG,
+      "DEBUG: Read %d bytes on socket %d, expecting %d for full message\n",
+      tcp6Session->pos,
+      tcp6Session->sock, 
+      len);
+#endif
+  if ( (tcp6Session->pos < 2) ||
+       (tcp6Session->pos < len) ) {
+    tcp6Disconnect(tsession);
+    return OK;
+  }
+ 
+  /* complete message received, let's check what it is */
+  if (YES == tcp6Session->expectingWelcome) {
+    TCP6Welcome * welcome;
+#if DEBUG_TCP6
+    HexName hex;
+#endif
+    
+    welcome = (TCP6Welcome*) &tcp6Session->rbuff[0];
+    if ( (ntohs(welcome->version) != 0) ||
+        (ntohs(welcome->size) != sizeof(TCP6Welcome)) ) {
+      LOG(LOG_WARNING,
+         "WARNING: expected welcome on tcp6 connection, got garbage (%d, %d). 
Closing.\n",
+         ntohs(welcome->version),
+         ntohs(welcome->size));
+      tcp6Disconnect(tsession);
+      return SYSERR;
+    }
+    tcp6Session->expectingWelcome = NO;
+    memcpy(&tcp6Session->sender,
+          &welcome->clientIdentity,
+          sizeof(HostIdentity));     
+#if DEBUG_TCP6
+    IFLOG(LOG_DEBUG,
+         hash2hex(&tcp6Session->sender.hashPubKey,
+                  &hex));
+    LOG(LOG_DEBUG,
+       "DEBUG: tcp6 welcome message from %s received\n",
+       &hex);
+#endif
+    memmove(&tcp6Session->rbuff[0],
+           &tcp6Session->rbuff[sizeof(TCP6Welcome)],
+           tcp6Session->pos - sizeof(TCP6Welcome));
+    tcp6Session->pos -= sizeof(TCP6Welcome); 
+    len = ntohs(((TCP6MessagePack*)&tcp6Session->rbuff[0])->size);
+  } 
+  if ( (tcp6Session->pos < 2) ||
+       (tcp6Session->pos < len) ) {
+    tcp6Disconnect(tsession);
+    return OK;
+  }
+     
+  pack = (TCP6MessagePack*)&tcp6Session->rbuff[0];
+  /* send msg to core! */
+  if (len <= sizeof(TCP6MessagePack)) {
+    LOG(LOG_WARNING,
+       "WARNING: received malformed message from tcp6-peer connection. 
Closing.\n");
+    tcp6Disconnect(tsession);
+    return SYSERR;
+  }
+  mp      = MALLOC(sizeof(MessagePack));
+  mp->msg = MALLOC(len);
+  memcpy(mp->msg,
+        &pack->parts[0],
+        len - sizeof(TCP6MessagePack));
+  memcpy(&mp->sender,
+        &tcp6Session->sender,
+        sizeof(HostIdentity));
+  mp->crc         = ntohl(pack->checkSum);
+  mp->isEncrypted = ntohs(pack->isEncrypted);
+  mp->size        = len - sizeof(TCP6MessagePack);
+  mp->tsession    = tsession;
+#if DEBUG_TCP6
+  LOG(LOG_DEBUG,
+      "DEBUG: tcp6 transport received %d bytes, forwarding to core\n",
+      mp->size);
+#endif
+  coreAPI->receive(mp);
+
+  if (tcp6Session->pos < len) { 
+    LOG(LOG_FAILURE,
+       "FAILURE: assert failed, pos (%d) < len (%d)\n",
+       tcp6Session->pos, len);
+    tcp6Disconnect(tsession);
+    return SYSERR;
+  }
+  /* finally, shrink buffer adequately */
+  memmove(&tcp6Session->rbuff[0],
+         &tcp6Session->rbuff[len],
+         tcp6Session->pos - len);
+  tcp6Session->pos -= len;        
+  
+  tcp6Disconnect(tsession);
+  return OK;
+}
+
+/**
+ * Add a new session to the array watched by the select thread.  Grows
+ * the array if needed.  If the caller wants to do anything useful
+ * with the return value, it must have the lock on tcp6lock before
+ * calling.  It is ok to call this function without holding tcp6lock if
+ * the return value is ignored.
+ **/
+static int addTSession(TSession * tsession) {
+  int i;
+
+  MUTEX_LOCK(&tcp6lock);
+  if (tsessionCount == tsessionArrayLength) 
+    GROW(tsessions,
+        tsessionArrayLength,
+        tsessionArrayLength * 2);
+  i = tsessionCount;
+  tsessions[tsessionCount++] = tsession;
+  MUTEX_UNLOCK(&tcp6lock);
+  return i;
+}
+
+/**
+ * Create a new session for an inbound connection on the given
+ * socket. Adds the session to the array of sessions watched
+ * by the select thread.
+ **/
+static void createNewSession(int sock) {
+  TSession * tsession;
+  TCP6Session * tcp6Session;
+
+  tcp6Session = MALLOC(sizeof(TCP6Session));
+  tcp6Session->pos = 0;
+  tcp6Session->size = tcp6API.mtu + sizeof(TCP6MessagePack);
+  tcp6Session->rbuff = MALLOC(tcp6Session->size);
+  tcp6Session->wpos = 0;
+  tcp6Session->wbuff = NULL;
+  tcp6Session->sock = sock;
+  /* fill in placeholder identity to mark that we 
+     are waiting for the welcome message */
+  memcpy(&tcp6Session->sender,
+        coreAPI->myIdentity,
+        sizeof(HostIdentity));
+  tcp6Session->expectingWelcome = YES;
+  MUTEX_CREATE_RECURSIVE(&tcp6Session->lock);
+  tcp6Session->users = 1; /* us only, core has not seen this tsession! */
+  cronTime(&tcp6Session->lastUse);
+  tsession = MALLOC(sizeof(TSession));
+  tsession->ttype = TCP6_PROTOCOL_NUMBER;
+  tsession->internal = tcp6Session;
+  addTSession(tsession);
+}                                       
+
+/**
+ * Main method for the thread listening on the tcp6 socket and all tcp6
+ * connections. Whenever a message is received, it is forwarded to the
+ * core. This thread waits for activity on any of the TCP6 connections
+ * and processes deferred (async) writes and buffers reads until an
+ * entire message has been received.
+ **/
+static void * tcp6ListenMain() {
+  struct sockaddr_in6 clientAddr;
+  fd_set readSet;
+  fd_set errorSet;
+  fd_set writeSet;
+  struct stat buf;
+  int lenOfIncomingAddr;
+  int i;
+  int max;
+  int ret;
+  
+  if (tcp6_sock != -1)
+    if (0 != LISTEN(tcp6_sock, 5)) 
+      LOG(LOG_ERROR,
+         "ERROR: listen (tcp6) failed (%s)\n",
+         STRERROR(errno));
+  SEMAPHORE_UP(serverSignal); /* we are there! */
+  MUTEX_LOCK(&tcp6lock);
+  while (tcp6_shutdown == NO) {
+    FD_ZERO(&readSet);
+    FD_ZERO(&errorSet);
+    FD_ZERO(&writeSet);
+    if (tcp6_sock != -1) {
+      if (isSocketValid(tcp6_sock)) {
+       FD_SET(tcp6_sock, &readSet);
+       FD_SET(tcp6_sock, &writeSet);
+       FD_SET(tcp6_sock, &errorSet);
+      } else {
+       LOG(LOG_ERROR,
+           "ERROR: tcp6_sock %d invalid: %s\n",
+           tcp6_sock,
+           STRERROR(errno));
+       tcp6_sock = -1; /* prevent us from error'ing all the time */
+      }
+    } else
+      LOG(LOG_DEBUG,
+         "DEBUG: TCP6 server socket not open!\n");
+    if (tcp6_pipe[0] != -1) {
+      if (-1 != FSTAT(tcp6_pipe[0], &buf)) {
+       FD_SET(tcp6_pipe[0], &readSet);
+      } else {
+       LOG(LOG_ERROR,
+           "ERROR: tcp6_pipe %d invalid: %s\n",
+           tcp6_pipe[0],
+           STRERROR(errno));
+       tcp6_pipe[0] = -1; /* prevent us from error'ing all the time */ 
+      }
+    }
+    max = tcp6_pipe[0];
+    if (tcp6_sock > tcp6_pipe[0])
+      max = tcp6_sock;
+    for (i=0;i<tsessionCount;i++) {
+      TCP6Session * tcp6Session = tsessions[i]->internal;
+      int sock = tcp6Session->sock;
+      if (sock != -1) {
+       if (isSocketValid(sock)) {
+         FD_SET(sock, &readSet);
+         FD_SET(sock, &errorSet);
+         if (tcp6Session->wpos > 0)
+           FD_SET(sock, &writeSet); /* do we have a pending write request? */
+       } else {
+         LOG(LOG_ERROR,
+             "ERROR: sock %d of session %d invalid: %s -- closing.\n",       
+             sock, 
+             i,
+             STRERROR(errno));
+         destroySession(i);
+       }
+      } else {
+       LOG(LOG_ERROR,
+           "ERROR: assertion failed: socket in tsessions array %d is -1 
(%s:%d) -- closing.\n",
+           i,
+           __FILE__, 
+           __LINE__);
+       destroySession(i);
+      }
+      if (sock > max)
+       max = sock;
+    }    
+    LOG(LOG_DEBUG,
+       "DEBUG: blocking on select!\n");
+    MUTEX_UNLOCK(&tcp6lock);
+    ret = SELECT(max+1, &readSet, &writeSet, &errorSet, NULL);    
+    MUTEX_LOCK(&tcp6lock);
+    LOG(LOG_DEBUG,
+       "DEBUG: select returned!\n");
+    if ( (ret == -1) &&
+        ( (errno == EAGAIN) || (errno == EINTR) ) ) 
+      continue;    
+    if (ret == -1) {
+      if (errno == EBADF) {
+       LOG(LOG_ERROR,
+           "ERROR: %s in select.\n",
+           STRERROR(errno));
+      } else {
+       errexit("FATAL: unexpected error in select: %s (that's the end)\n",
+               STRERROR(errno));
+      }
+    }
+    if (tcp6_sock != -1) {
+      if (FD_ISSET(tcp6_sock, &readSet)) {
+       int sock;
+       
+       LOG(LOG_DEBUG,
+           "DEBUG: accepting inbound connection\n");
+       lenOfIncomingAddr = sizeof(clientAddr);               
+       sock = ACCEPT(tcp6_sock, 
+                     (struct sockaddr *)&clientAddr, 
+                     &lenOfIncomingAddr);
+       if (sock != -1) {         
+         /* verify clientAddr for eligibility here (ipcheck-style,
+            user should be able to specify who is allowed to connect,
+            otherwise we just close and reject the communication! */     
+         if (sizeof(struct in6_addr) != sizeof(IP6addr))
+           errexit("FATAL: assertion failed at %s:%d\n",
+                   __FILE__, __LINE__);
+         if (YES == isBlacklisted((IP6addr*)&clientAddr.sin6_addr)) {
+           char * tmp = MALLOC(INET6_ADDRSTRLEN);
+           LOG(LOG_INFO,
+               "INFO: Rejected blacklisted connection from %s.\n",
+               inet_ntop(AF_INET6,
+                         &clientAddr,
+                         tmp,
+                         INET6_ADDRSTRLEN));
+           FREE(tmp);
+           SHUTDOWN(sock, 2);
+           CLOSE(sock);
+         } else 
+           createNewSession(sock);      
+       } else {
+         LOG(LOG_INFO,
+             "INFO: P2P TCP6 server accept failed: %s\n",
+             STRERROR(errno));
+       }
+      }
+    }
+    if (FD_ISSET(tcp6_pipe[0], &readSet)) {
+      /* allow reading multiple signals in one go in case we get many
+        in one shot... */
+
+#define MAXSIG_BUF 128
+      char buf[MAXSIG_BUF];
+      /* just a signal to refresh sets, eat and continue */
+      if (0 >= READ(tcp6_pipe[0], 
+                   &buf[0], 
+                   MAXSIG_BUF)) {
+       LOG(LOG_WARNING,
+           "WARNING: reading signal on TCP6 pipe failed (%s)\n",
+           STRERROR(errno));
+      }
+    }
+    for (i=0;i<tsessionCount;i++) {
+      TCP6Session * tcp6Session = tsessions[i]->internal;
+      int sock = tcp6Session->sock;
+      if (FD_ISSET(sock, &readSet)) {
+       if (SYSERR == readAndProcess(i)) {
+         destroySession(i);
+         i--;
+         continue;
+       }
+      }
+      if (FD_ISSET(sock, &writeSet)) {
+       int ret;
+
+       ret = SEND_NONBLOCKING(sock,
+                              tcp6Session->wbuff,
+                              tcp6Session->wpos);
+       if (ret == SYSERR) {
+         LOG(LOG_WARNING,
+             "WARNING: send failed on socket %d (%s), closing session %d.\n",
+             sock, 
+             STRERROR(errno),
+             i);
+         destroySession(i);
+         i--;
+         continue;
+       }
+       if (ret == 0) {
+          /* send only returns 0 on error (other side closed connection),
+          * so close the session */
+         destroySession(i);
+         i--;
+         continue;
+       }
+       if ((unsigned int)ret == tcp6Session->wpos) {
+         FREENONNULL(tcp6Session->wbuff);
+         tcp6Session->wbuff = NULL;
+         tcp6Session->wpos = 0;
+       } else {
+         memmove(tcp6Session->wbuff,
+                 &tcp6Session->wbuff[ret],
+                 tcp6Session->wpos - ret);
+         tcp6Session->wpos -= ret;
+       }
+      }
+      if (FD_ISSET(sock, &errorSet)) {
+       destroySession(i);
+       i--;
+       continue;
+      }
+      if ( ( tcp6Session->users == 1) &&
+          (cronTime(NULL) > tcp6Session->lastUse + TCP6_TIMEOUT) ) {
+       destroySession(i);
+       i--;
+       continue;
+      }
+    }
+  }
+  /* shutdown... */
+  if (tcp6_sock != -1) {
+    CLOSE(tcp6_sock);
+    tcp6_sock = -1;
+  }
+  /* close all sessions */
+  while (tsessionCount > 0) 
+    destroySession(0);
+  MUTEX_UNLOCK(&tcp6lock);
+  SEMAPHORE_UP(serverSignal); /* we are there! */
+  return NULL;
+} /* end of tcp6 listen main */
+
+/**
+ * Send a message (already encapsulated if needed) via the
+ * tcp6 socket (or enqueue if sending now would block).
+ *
+ * @param tcp6Session the session to use for sending
+ * @param mp the message to send
+ * @param ssize the size of the message
+ * @return OK if message send or queued, SYSERR if queue is full and
+ * message was dropped.
+ **/
+static int tcp6DirectSend(TCP6Session * tcp6Session,
+                         void * mp,
+                         unsigned int ssize) {
+  int ok;
+  int ret;
+
+  if (tcp6Session->sock == -1) {
+#if DEBUG_TCP6
+    LOG(LOG_INFO,
+       "INFO: tcp6DirectSend called, but socket is closed\n");
+#endif
+    return SYSERR;
+  }
+  if (ssize == 0) {
+    LOG(LOG_ERROR,
+       "ERROR: message passed to tcp6DirectSend has size 0, which is not 
allowed.\n");
+    return SYSERR;
+  }
+  if (ssize > tcp6API.mtu + sizeof(TCP6MessagePack)) {
+    LOG(LOG_ERROR,
+       "ERROR: message passed to tcp6DirectSend larger than MTU (%d > %d)\n",
+       ssize, 
+       tcp6API.mtu);
+    return SYSERR;
+  }
+  ok = SYSERR;
+  MUTEX_LOCK(&tcp6lock);
+  if (tcp6Session->wpos > 0) {
+    ret = 0;
+  } else {
+    ret = SEND_NONBLOCKING(tcp6Session->sock,
+                          mp,
+                          ssize);
+  }
+  if (ret == SYSERR) {
+    if ( (errno == EAGAIN) ||
+        (errno == EWOULDBLOCK)) {
+      LOG(LOG_DEBUG,
+         "DEBUG: send failed: %s\n",
+         STRERROR(errno));
+      ret = 0;
+    } else {
+      LOG(LOG_INFO,
+         "INFO: write to tcp6 peer failed (%s)\n",
+         STRERROR(errno));
+      MUTEX_UNLOCK(&tcp6lock);
+      return SYSERR;
+    }
+  }
+  if ((unsigned int) ret <= ssize) { /* some bytes send or blocked */
+    if ((unsigned int)ret < ssize) {
+      if (tcp6Session->wbuff == NULL) {
+       tcp6Session->wbuff = MALLOC(tcp6API.mtu + sizeof(TCP6MessagePack));
+       tcp6Session->wpos = 0;
+      }
+      if ((unsigned int) (ssize - ret) > 
+         tcp6API.mtu + sizeof(TCP6MessagePack) - tcp6Session->wpos) {
+       ssize = 0;
+       ok = SYSERR; /* buffer full, drop */
+      } else {
+       memcpy(&tcp6Session->wbuff[tcp6Session->wpos],
+              mp,
+              ssize - ret);
+       tcp6Session->wpos += ssize - ret;
+       if (tcp6Session->wpos == ssize - ret)
+         signalSelect(); /* select set changed! */
+       ok = OK; /* all buffered */
+      }      
+    } else 
+      ok = OK; /* all written */
+  } else {
+    LOG(LOG_WARNING, 
+       "WARNING: send failed (%s) - %d %d\n",
+       STRERROR(errno),
+       errno,
+       ret);
+    ssize = 0;
+    ok = SYSERR; /* write failed for real */
+  }
+  MUTEX_UNLOCK(&tcp6lock);
+  cronTime(&tcp6Session->lastUse);
+  incrementBytesSent(ssize);
+  statChange(stat_octets_total_tcp6_out,
+            ssize);
+  return ok;
+}
+
+
+/**
+ * Send a message (already encapsulated if needed) via the
+ * tcp6 socket.  Block if required.
+ *
+ * @param tcp6Session the session to use for sending
+ * @param mp the message to send
+ * @param ssize the size of the message
+ * @return OK if message send or queued, SYSERR if queue is full and
+ * message was dropped.
+ **/
+static int tcp6DirectSendReliable(TCP6Session * tcp6Session,
+                                 void * mp,
+                                 unsigned int ssize) {
+  int ok;
+
+  if (tcp6Session->sock == -1) {
+#if DEBUG_TCP6
+    LOG(LOG_INFO,
+       "INFO: tcp6DirectSendReliable called, but socket is closed\n");
+#endif
+    return SYSERR;
+  }
+  if (ssize == 0) {
+    LOG(LOG_ERROR,
+       "ERROR: message passed to tcp6DirectSendReliable has size 0, which is 
not allowed.\n");
+    return SYSERR;
+  }
+  if (ssize > tcp6API.mtu + sizeof(TCP6MessagePack)) {
+    LOG(LOG_ERROR,
+       "ERROR: message passed to tcp6DirectSend larger than MTU (%d > %d)\n",
+       ssize, 
+       tcp6API.mtu);
+    return SYSERR;
+  }
+  MUTEX_LOCK(&tcp6lock);
+  if (tcp6Session->wpos > 0) {
+    unsigned int old = tcp6Session->wpos;
+    /* reliable: grow send-buffer above limit! */
+    GROW(tcp6Session->wbuff,
+        tcp6Session->wpos,
+        tcp6Session->wpos + ssize);
+    memcpy(&tcp6Session->wbuff[old],
+          mp,
+          ssize);    
+    ok = OK;
+  } else {
+    ok = tcp6DirectSend(tcp6Session,
+                       mp,
+                       ssize);
+  }
+  MUTEX_UNLOCK(&tcp6lock);
+  return ok;
+}
+
+/**
+ * Send a message to the specified remote node.
+ *
+ * @param tsession the HELO_Message identifying the remote node
+ * @param msg the message
+ * @param size the size of the message
+ * @param isEncrypted is the message encrypted (YES/NO)
+ * @param crc CRC32 of the plaintext
+ * @return SYSERR on error, OK on success
+ **/
+static int tcp6SendReliable(TSession * tsession,
+                          const void * msg,
+                          const unsigned int size,
+                          int isEncrypted,
+                          const int crc) {
+  TCP6MessagePack * mp;
+  int ok;
+  int ssize;
+  
+  if (tcp6_shutdown == YES)
+    return SYSERR;
+  if (size == 0) {
+    LOG(LOG_ERROR,
+       "ERROR: message passed to tcp6SendReliable has size 0, which is not 
allowed.\n");
+    return SYSERR;
+  }
+  if (size > tcp6API.mtu) {
+    LOG(LOG_FAILURE,
+       "FAILURE: message larger than allowed by tcp6 transport (%d > %d)\n",
+       size, 
+       tcp6API.mtu);
+    return SYSERR;
+  }
+  if (((TCP6Session*)tsession->internal)->sock == -1)
+    return SYSERR; /* other side closed connection */
+  mp = MALLOC(sizeof(TCP6MessagePack) + size);
+  mp->checkSum = htonl(crc);
+  mp->isEncrypted = htons(isEncrypted);
+  memcpy(&mp->parts[0],
+        msg,
+        size);
+  ssize = size + sizeof(TCP6MessagePack);
+  mp->size = htons(ssize);
+  
+  ok = tcp6DirectSendReliable(tsession->internal,
+                            mp,
+                            ssize);
+  FREE(mp);
+  return ok;
+}
+
+
+/**
+ * Verify that a HELO-Message is correct (a node
+ * is reachable at that address). Since the reply
+ * will be asynchronous, a method must be called on
+ * success. 
+ * @param helo the HELO message to verify
+ *        (the signature/crc have been verified before)
+ * @return OK on success, SYSERR on error
+ **/
+static int verifyHelo(HELO_Message * helo) {
+  Host6Address * haddr;
+
+  haddr = (Host6Address*) &((HELO_Message_GENERIC*)helo)->senderAddress[0];
+  if ( (ntohs(helo->senderAddressSize) != sizeof(Host6Address)) ||
+       (ntohs(helo->header.size) != HELO_Message_size(helo)) ||
+       (ntohs(helo->header.requestType) != p2p_PROTO_HELO) ||
+       (ntohs(helo->protocol) != TCP6_PROTOCOL_NUMBER) ||
+       (YES == isBlacklisted(&haddr->ip)) )
+    return SYSERR; /* obviously invalid */
+  else
+    return OK;
+}
+
+/**
+ * Create a HELO-Message for the current node. The HELO is
+ * created without signature and without a timestamp. The
+ * GNUnet core will sign the message and add an expiration time. 
+ *
+ * @param helo address where to store the pointer to the HELO
+ *        message
+ * @return OK on success, SYSERR on error
+ **/
+static int createHELO(HELO_Message ** helo) {
+  HELO_Message * msg;
+  Host6Address * haddr;
+  unsigned short port;
+
+  port = getGNUnetTCP6Port();
+  if (0 == port) {
+    LOG(LOG_DEBUG,
+       "DEBUG: TCP6 port is 0, will only send using TCP6\n");
+    return SYSERR; /* TCP6 transport is configured SEND-only! */
+  }
+  msg = (HELO_Message *) MALLOC(sizeof(HELO_Message) + sizeof(Host6Address));
+  haddr = (Host6Address*) &((HELO_Message_GENERIC*)msg)->senderAddress[0];
+
+  if (SYSERR == getPublicIP6Address(&haddr->ip)) {
+    FREE(msg);
+    LOG(LOG_WARNING,
+       "WARNING: Could not determine my public IP address.\n");
+    return SYSERR;
+  }
+  haddr->port = htons(port); 
+  haddr->reserved = htons(0);
+  msg->senderAddressSize = htons(sizeof(Host6Address));
+  msg->protocol = htons(TCP6_PROTOCOL_NUMBER);
+  msg->MTU = htonl(tcp6API.mtu);
+  *helo = msg;
+  return OK;
+}
+
+/**
+ * Establish a connection to a remote node.
+ *
+ * @param helo the HELO-Message for the target node
+ * @param tsessionPtr the session handle that is set
+ * @return OK on success, SYSERR if the operation failed
+ **/
+static int tcp6Connect(HELO_Message * helo,
+                      TSession ** tsessionPtr) {
+  int i;
+  Host6Address * haddr;
+  TCP6Welcome welcome;
+  int sock;
+  TSession * tsession;
+  TCP6Session * tcp6Session;
+  char * hostname;
+  struct addrinfo hints, *res, *res0;
+  int rtn;
+
+#if DEBUG_TCP6
+  char * tmp;
+#endif
+
+  if (tcp6_shutdown == YES)
+    return SYSERR;
+  haddr = (Host6Address*) &((HELO_Message_GENERIC*)helo)->senderAddress[0];
+
+  memset(&hints, 0, sizeof(hints));
+  hints.ai_family = PF_INET6;
+  hints.ai_socktype = SOCK_STREAM;
+  hostname = MALLOC(INET6_ADDRSTRLEN);
+  inet_ntop(AF_INET6,
+           haddr,
+           hostname,
+           INET6_ADDRSTRLEN);
+  rtn = getaddrinfo(hostname, NULL, &hints, &res0);
+  FREE(hostname);
+  if (rtn != 0) {
+    LOG(LOG_WARNING,   
+       "WARNING: tcp6connect: unknown service: %s\n", 
+       gai_strerror(rtn));
+    return SYSERR;
+  }
+
+#if DEBUG_TCP6
+  tmp = MALLOC(INET6_ADDRSTRLEN);
+  LOG(LOG_DEBUG,
+      "DEBUG: creating TCP6 connection to %s:%d\n",
+      inet_ntop(AF_INET6,
+               haddr,
+               tmp,
+               INET6_ADDRSTRLEN), 
+      ntohs(haddr->port));
+  FREE(tmp);
+#endif
+
+  sock = -1;
+  for (res=res0; res; res=res->ai_next) {
+    if (res->ai_family != PF_INET6)
+      continue;
+    sock = SOCKET(res->ai_family, 
+                 res->ai_socktype, 
+                 res->ai_protocol);
+    if (sock < 0)
+      continue;
+    if (0 != setBlocking(sock, NO)) {
+      CLOSE(sock);
+      LOG(LOG_FAILURE,
+         "FAILURE: could not put tcp socket into non-blocking mode (%s)\n",
+         STRERROR(errno));
+      return SYSERR;
+    }
+    ((struct sockaddr_in6*)(res->ai_addr))->sin6_port 
+      = haddr->port;
+    if (CONNECT(sock, 
+               res->ai_addr, 
+               res->ai_addrlen) < 0) {
+      LOG(LOG_WARNING,
+         "WARNING: TCP6 could not connect (%s)\n",
+         STRERROR(errno));
+      CLOSE(sock);
+      sock = -1;
+      continue;
+    }
+    break;
+  }
+  freeaddrinfo(res0);
+  if (sock == -1) {
+    LOG(LOG_FAILURE,
+       "FAILURE: Can not create socket (%s).\n",
+       STRERROR(errno));
+    return SYSERR;
+  }
+  if (0 != setBlocking(sock, NO)) {
+    CLOSE(sock);
+    LOG(LOG_FAILURE,
+       "FAILURE: could not put tcp6 socket into non-blocking mode (%s)\n",
+       STRERROR(errno));
+    return SYSERR;
+  }
+
+  tcp6Session = MALLOC(sizeof(TCP6Session));
+  tcp6Session->sock = sock;
+  tcp6Session->wpos = 0;
+  tcp6Session->wbuff = NULL;
+  tcp6Session->size = tcp6API.mtu + sizeof(TCP6MessagePack);
+  tcp6Session->rbuff = MALLOC(tcp6Session->size);
+  tsession = MALLOC(sizeof(TSession));
+  tsession->internal = tcp6Session;
+  tsession->ttype = tcp6API.protocolNumber;
+  MUTEX_CREATE_RECURSIVE(&tcp6Session->lock);
+  tcp6Session->users = 2; /* caller + us */
+  tcp6Session->pos = 0;
+  cronTime(&tcp6Session->lastUse);
+  memcpy(&tcp6Session->sender,
+        &helo->senderIdentity,
+        sizeof(HostIdentity));
+  tcp6Session->expectingWelcome = NO;
+  MUTEX_LOCK(&tcp6lock);
+  i = addTSession(tsession);
+
+  /* send our node identity to the other side to fully establish the
+     connection! */
+  welcome.size = htons(sizeof(TCP6Welcome));
+  welcome.version = htons(0);
+  memcpy(&welcome.clientIdentity,
+        coreAPI->myIdentity,
+        sizeof(HostIdentity));
+  if (SYSERR == tcp6DirectSend(tcp6Session,
+                              &welcome,
+                              sizeof(TCP6Welcome))) {
+    destroySession(i);
+    tcp6Disconnect(tsession);
+    MUTEX_UNLOCK(&tcp6lock);
+    return SYSERR;
+  }
+  MUTEX_UNLOCK(&tcp6lock);
+  signalSelect();
+  
+  *tsessionPtr = tsession;
+  FREE(helo);
+  return OK;
+}
+
+/**
+ * Send a message to the specified remote node.
+ *
+ * @param tsession the HELO_Message identifying the remote node
+ * @param msg the message
+ * @param size the size of the message
+ * @param isEncrypted is the message encrypted (YES/NO)
+ * @param crc CRC32 of the plaintext
+ * @return SYSERR on error, OK on success
+ **/
+static int tcp6Send(TSession * tsession,
+                   const void * msg,
+                   const unsigned int size,
+                   int isEncrypted,
+                   const int crc) {
+  TCP6MessagePack * mp;
+  int ok;
+  int ssize;
+  
+  if (tcp6_shutdown == YES)
+    return SYSERR;
+  if (size == 0) {
+    LOG(LOG_ERROR,
+       "ERROR: message passed to tcp6Send has size 0, which is not 
allowed.\n");
+    return SYSERR;
+  }
+  if (size > tcp6API.mtu) {
+    LOG(LOG_FAILURE,
+       "FAILURE: message larger than allowed by tcp6 transport (%d > %d)\n",
+       size, 
+       tcp6API.mtu);
+    return SYSERR;
+  }
+  if (((TCP6Session*)tsession->internal)->sock == -1)
+    return SYSERR; /* other side closed connection */
+  mp = MALLOC(sizeof(TCP6MessagePack) + size);
+  mp->checkSum = htonl(crc);
+  mp->isEncrypted = htons(isEncrypted);
+  memcpy(&mp->parts[0],
+        msg,
+        size);
+  ssize = size + sizeof(TCP6MessagePack);
+  mp->size = htons(ssize);
+  
+  ok = tcp6DirectSend(tsession->internal,
+                     mp,
+                     ssize);
+  FREE(mp);
+  return ok;
+}
+
+/**
+ * Start the server process to receive inbound traffic.
+ * @return OK on success, SYSERR if the operation failed
+ **/
+static int startTransportServer(void) {
+  struct sockaddr_in6 serverAddr;
+  const int on = 1;
+  unsigned short port;
+  int flags;
+  
+  if (serverSignal != NULL) {
+    LOG(LOG_FAILURE,
+       "FAILURE: can not start TCP6 server, already running!?\n");
+    return SYSERR;
+  }
+  serverSignal = SEMAPHORE_NEW(0);
+  tcp6_shutdown = NO;
+    
+  if (0 != PIPE(tcp6_pipe)) {
+    LOG(LOG_ERROR,
+       "ERROR: could not create pipe (%s)\n",
+       STRERROR(errno));
+    return SYSERR;
+  }
+  flags = fcntl(tcp6_pipe[1], F_GETFL, 0);
+  fcntl(tcp6_pipe[1], F_SETFL, flags | O_NONBLOCK);
+ 
+  port = getGNUnetTCP6Port();
+  if (port != 0) { /* if port == 0, this is a read-only
+                     business! */
+    tcp6_sock = SOCKET(PF_INET6, 
+                      SOCK_STREAM, 
+                      0);
+    if (tcp6_sock < 0) 
+      errexit("ERROR opening tcp6 socket (%s).\n",
+             STRERROR(errno));  
+    if ( SETSOCKOPT(tcp6_sock,
+                   SOL_SOCKET, 
+                   SO_REUSEADDR, 
+                   &on, 
+                   sizeof(on)) < 0 ) 
+      errexit("ERROR: setsockopt for tcp6 socket failed (%s)\n",
+             STRERROR(errno));  
+    memset((char *) &serverAddr, 
+          0,
+          sizeof(serverAddr));
+    serverAddr.sin6_family   = AF_INET6;
+    serverAddr.sin6_flowinfo = 0;
+    serverAddr.sin6_addr     = in6addr_any;
+    serverAddr.sin6_port     = htons(getGNUnetTCP6Port());
+#if DEBUG_TCP6
+    LOG(LOG_INFO,
+       "INFO: starting tcp6 peer server on port %d\n",
+       ntohs(serverAddr.sin6_port));
+#endif
+    if (BIND(tcp6_sock, 
+            (struct sockaddr *) &serverAddr,
+            sizeof(serverAddr)) < 0) {
+      LOG(LOG_ERROR, 
+         "ERROR (%s) binding the TCP6 listener to port %d. "\
+         "No transport service started.\n",
+         STRERROR(errno),
+         getGNUnetTCP6Port());
+      CLOSE(tcp6_sock);
+      SEMAPHORE_FREE(serverSignal);
+      serverSignal = NULL;
+      return SYSERR;
+    }
+  } else
+    tcp6_sock = -1;
+  if (0 == PTHREAD_CREATE(&listenThread, 
+                         (PThreadMain) &tcp6ListenMain,
+                         NULL,
+                         2048)) {
+      SEMAPHORE_DOWN(serverSignal); /* wait for server to be up */      
+  } else {
+    LOG(LOG_ERROR,
+       "ERROR: could not start tcp6 listen thread (%s)\n",
+       STRERROR(errno));
+    CLOSE(tcp6_sock);
+    SEMAPHORE_FREE(serverSignal);
+    serverSignal = NULL;
+    return SYSERR;
+  }
+  return OK;
+}
+
+/**
+ * Shutdown the server process (stop receiving inbound
+ * traffic). Maybe restarted later!
+ **/
+static int stopTransportServer() {
+  void * unused;
+
+  if (serverSignal == NULL) {
+    LOG(LOG_ERROR,
+       "ERROR: stopTransportServer called by TCP6 server not running.");
+    return SYSERR;
+  }
+  tcp6_shutdown = YES;  
+  signalSelect();
+  SEMAPHORE_DOWN(serverSignal);
+  SEMAPHORE_FREE(serverSignal);
+  serverSignal = NULL; 
+  CLOSE(tcp6_pipe[1]);
+  CLOSE(tcp6_pipe[0]);
+  if (tcp6_sock != -1) 
+    CLOSE(tcp6_sock);
+  PTHREAD_JOIN(&listenThread, &unused);
+  return OK;
+}
+
+/**
+ * Reload the configuration. Should never fail (keep old
+ * configuration on error, syslog errors!)
+ **/
+static void reloadConfiguration(void) {
+  char * ch;
+
+  MUTEX_LOCK(&tcp6lock);
+  FREENONNULL(filteredNetworks_);
+  ch = getConfigurationString("TCP6",
+                             "BLACKLIST");
+  if (ch == NULL)
+    filteredNetworks_ = parseRoutes6("");
+  else {
+    filteredNetworks_ = parseRoutes6(ch);
+    FREE(ch);
+  }
+  MUTEX_UNLOCK(&tcp6lock);
+}
+
+/**
+ * Convert TCP6 address to a string.
+ **/
+static char * addressToString(HELO_Message * helo) {
+  char * ret;
+  char * tmp;
+  Host6Address * haddr;
+  
+  haddr = (Host6Address*) &((HELO_Message_GENERIC*)helo)->senderAddress[0];  
+  ret = MALLOC(INET6_ADDRSTRLEN+6);
+  tmp = MALLOC(INET6_ADDRSTRLEN);  
+  sprintf(ret,
+         "%s:%d (TCP6)",
+         inet_ntop(AF_INET6,
+                   haddr,
+                   tmp,
+                   INET6_ADDRSTRLEN), 
+         ntohs(haddr->port));
+  FREE(tmp);
+  return ret;
+}
+
+ 
+/* ******************** public API ******************** */
+ 
+/**
+ * The exported method. Makes the core api available
+ * via a global and returns the udp transport API.
+ **/ 
+TransportAPI * inittransport_tcp6(CoreAPIForTransport * core) {
+  int mtu;
+
+  MUTEX_CREATE_RECURSIVE(&tcp6lock);
+  reloadConfiguration();
+  tsessionCount = 0;
+  tsessionArrayLength = 32;
+  tsessions = MALLOC(sizeof(TSession*) * tsessionArrayLength);
+  coreAPI = core;
+  stat_octets_total_tcp6_in 
+    = statHandle("# bytes received via tcp6");
+  stat_octets_total_tcp6_out 
+    = statHandle("# bytes sent via tcp6");
+  mtu = getConfigurationInt("TCP6",
+                           "MTU");
+  if (mtu == 0)
+    mtu = 1440;
+  if (mtu < 1200)
+    LOG(LOG_ERROR,
+       "ERROR: MTU for TCP6 is probably to low (fragmentation not 
implemented!)\n");
+ 
+  tcp6API.protocolNumber       = TCP6_PROTOCOL_NUMBER;
+  tcp6API.mtu                  = mtu - sizeof(TCP6MessagePack);
+  tcp6API.cost                 = 19950; /* about equal to udp6 */
+  tcp6API.verifyHelo           = &verifyHelo;
+  tcp6API.createHELO           = &createHELO;
+  tcp6API.connect              = &tcp6Connect;
+  tcp6API.associate            = &tcp6Associate;
+  tcp6API.send                 = &tcp6Send;
+  tcp6API.sendReliable         = &tcp6SendReliable;
+  tcp6API.disconnect           = &tcp6Disconnect;
+  tcp6API.startTransportServer = &startTransportServer;
+  tcp6API.stopTransportServer  = &stopTransportServer;
+  tcp6API.reloadConfiguration  = &reloadConfiguration;
+  tcp6API.addressToString      = &addressToString;
+
+  return &tcp6API;
+}
+
+void donetransport_tcp6() {
+  int i;
+
+  for (i=0;i<tsessionCount;i++)
+    LOG(LOG_DEBUG,
+       "DEBUG: tsessions array contains %x\n",
+       (int)tsessions[i]);
+  FREE(tsessions);
+  tsessions = NULL;
+  tsessionArrayLength = 0;
+  FREENONNULL(filteredNetworks_);
+  MUTEX_DESTROY(&tcp6lock);
+}
+
+/* end of tcp6.c */

Added: freeway/src/org/gnu/freeway/transport/udp/UDPAddress.java
===================================================================
--- freeway/src/org/gnu/freeway/transport/udp/UDPAddress.java   2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/transport/udp/UDPAddress.java   2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,91 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.transport.udp;
+
+import org.gnu.freeway.util.net.*;
+
+import java.net.*;
+import java.nio.*;
+
+/**
+ * Host-Address in a UDP network.
+ */
+
+public class UDPAddress extends Object implements Persistent
+{
+       public static final int SIZE    =       8;
+
+       /** claimed IP of the sender */
+       private InetAddress     ip;
+
+       /** claimed port of the sender */
+       private int                     port;
+
+
+       public UDPAddress()
+       {
+               super();
+               ip=null;
+               port=0;
+       }
+
+       public UDPAddress( InetAddress addr, int p )
+       {
+               this();
+               ip=addr;
+               port=p;
+       }
+
+       public String toString()
+       {
+               return "UDP address [ip="+ip+", port="+port+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public InetAddress getIP()
+       {
+               return ip;
+       }
+
+       public int getPort()
+       {
+               return port;
+       }
+
+       public int getByteSize()
+       {
+               return SIZE;
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               byte[]  b;
+               int             reserved;
+
+               b=new byte[4];
+               buf.get(b);
+               try {
+                       ip=InetAddress.getByAddress(b);
+                       }
+               catch( UnknownHostException x ) {
+                       err.reportIf(true,"bad address");
+                       }
+
+               port=buf.getShort() & 0x0000ffff;
+               err.reportIf(port>65535,"bad port");
+
+               reserved=buf.getShort() & 0x0000ffff;
+               err.reportIf(reserved!=0,"0 expected");
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               buf.put(ip.getAddress());
+               buf.putShort((short) port);
+               buf.putShort((short) 0);
+       }
+}

Added: freeway/src/org/gnu/freeway/transport/udp/UDPMessage.java
===================================================================
--- freeway/src/org/gnu/freeway/transport/udp/UDPMessage.java   2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/transport/udp/UDPMessage.java   2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,160 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.transport.udp;
+
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * Message-Packet header.
+ *
+ * this struct is *preceded* by MESSAGE_PARTs - until
+ * size-sizeof(UDPMessage)!
+ */
+
+public class UDPMessage extends Object implements Persistent
+{
+       public static final int         SIZE    =       8+HostIdentity.SIZE;
+
+       /** Is the message encrypted ? */
+       private boolean                 encrypted;
+
+       /** CRC checksum of the plaintext */
+       private int                             checkSum;
+
+       /** What is the identity of the sender (hash of public key) */
+       private HostIdentity    sender;
+
+       /** */
+       private ByteBuffer              data;
+
+
+       public UDPMessage()
+       {
+               super();
+               encrypted=false;
+               checkSum=0;
+               sender=null;
+               data=null;
+       }
+
+       public UDPMessage( HostIdentity hi, boolean enc, int crc )
+       {
+               this();
+               encrypted=enc;
+               checkSum=crc;
+               sender=(HostIdentity) PersistentHelper.copy(hi);
+       }
+
+       public String toString()
+       {
+               return "UDP message";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*
+todo : a faire
+
+       public boolean send( SocketChannel c, InetAddress ip, int port )
+       {
+               try {
+                       c.configureBlocking(true);
+
+                       if (c.send(PersistentHelper.toBuffer(mp),new 
InetSocketAddress(ip,port))!=mp.getByteSize()) {
+                               return false;
+                               }
+                       }
+               catch( IOException x ) {
+                       err("Failed to send message of size 
"+mp.getByteSize()+" !",x);
+                       return false;
+                       }
+               return true;
+       }
+*/
+       public HostIdentity getSender()
+       {
+               return sender;
+       }
+
+       public boolean isEncrypted()
+       {
+               return encrypted;
+       }
+
+       public int getCheckSum()
+       {
+               return checkSum;
+       }
+
+       public ByteBuffer getData()
+       {
+               return data;
+       }
+
+       public void setData( ByteBuffer buf )
+       {
+               data=buf;
+       }
+
+       public int getByteSize()
+       {
+               return SIZE+data.position();
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               byte[]  b;
+               int             size;
+
+               // the bad thing with UDP : never sure that all stuff has been 
transmitted
+               // synchro is done by ending with 'important' data : sender, 
crc and so on
+               // -> assumes that entire message is in buf and has size 
buf.remaining()...
+               // (further tests are performed to check validty)
+
+               b=new byte[buf.remaining()-SIZE];
+               buf.get(b);
+
+               data=ByteBuffer.allocateDirect(b.length);
+               data.put(b);
+
+               size=buf.getShort() & 0x0000ffff;
+               err.reportIf(size!=SIZE+b.length,"bad size");
+
+               encrypted=buf.getShort()!=0;
+               checkSum=buf.getInt();
+               sender=new HostIdentity();
+               sender.readBytes(buf,err);
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               int     i;
+
+               data.flip();
+               try {
+                       buf.put(data);
+                       }
+               finally {
+                       data.flip();
+                       data.position(data.limit());
+                       }
+
+               buf.putShort((short) getByteSize());
+               buf.putShort((short) (encrypted ? 1 : 0));
+               buf.putInt(checkSum);
+               if (sender!=null) {
+                       sender.writeBytes(buf);
+                       }
+               else {
+                       for (i=0; i<HostIdentity.SIZE; i++) {
+                               buf.put((byte) 0);
+                               }
+                       }
+       }
+}

Added: freeway/src/org/gnu/freeway/transport/udp/UDPSession.java
===================================================================
--- freeway/src/org/gnu/freeway/transport/udp/UDPSession.java   2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/transport/udp/UDPSession.java   2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,106 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.transport.udp;
+
+import org.gnu.freeway.server.*;
+import org.gnu.freeway.transport.*;
+import org.gnu.freeway.util.crypto.*;
+
+import java.nio.*;
+import java.net.*;
+import java.util.logging.*;
+
+/**
+ * Strange concept : since UDP is stateless, UDP sessions are one-shot, 
volatile sessions...
+ */
+
+public class UDPSession extends AbstractSession
+{
+       private InetAddress             ip;
+       private int                             port;
+
+
+       public UDPSession( UDPTransport t, P2PHello helo )
+       {
+               super(t);
+               ip=null;
+               port=0;
+               setRemote(helo.getSenderIdentity());
+
+               init(helo);
+       }
+
+       public UDPSession( UDPTransport t, InetAddress addr, int p, 
HostIdentity h )
+       {
+               super(t);
+               ip=addr;
+               port=p;
+               setRemote(h);
+       }
+
+       public String toString()
+       {
+               return "UDP session [ip="+ip+", port="+port+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public String getRemoteAddress()
+       {
+               return "UDP("+ip.getHostAddress()+":"+port+")";
+       }
+
+       protected void init( P2PHello helo )
+       {
+               UDPAddress      addr;
+
+               addr=(UDPAddress) (helo!=null ? 
helo.decodeSenderAddress(UDPAddress.class) : null);
+               if (addr!=null) {
+                       ip=addr.getIP();
+                       port=addr.getPort();
+                       }
+       }
+
+       public InetAddress getIP()
+       {
+               return ip;
+       }
+
+       public int getPort()
+       {
+               return port;
+       }
+
+       public boolean sendImpl( ByteBuffer buf, int crc, boolean encrypted )
+       {
+               UDPMessage      mp;
+
+               if (ip==null) {
+                       return false;
+                       }
+
+               if (buf.position()==0) {
+                       log(Level.SEVERE,"Given message has size 0, which is 
not allowed.");
+                       return false;
+                       }
+
+               if (buf.position()>transport.getMTU()) {
+                       log(Level.SEVERE,"Given message larger than allowed by 
UDP transport ("+buf.position()+" > "+transport.getMTU()+").");
+                       return false;
+                       }
+
+               //todo: faire plus efficace (ecriture directe des buffers)
+               mp=new UDPMessage(transport.getHost(),encrypted,crc);
+               mp.setData(buf);
+               return ((UDPTransport) transport).sendTo(mp,ip,port);
+       }
+
+       public boolean sendReliableImpl( ByteBuffer buf, int crc, boolean 
encrypted )
+       {
+               // can't increase reliability
+               return sendImpl(buf,crc,encrypted);
+       }
+}

Added: freeway/src/org/gnu/freeway/transport/udp/UDPTransport.java
===================================================================
--- freeway/src/org/gnu/freeway/transport/udp/UDPTransport.java 2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/transport/udp/UDPTransport.java 2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,391 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.transport.udp;
+
+import org.gnu.freeway.server.*;
+import org.gnu.freeway.transport.*;
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.net.*;
+
+import java.io.*;
+import java.net.*;
+import java.nio.*;
+import java.nio.channels.*;
+import java.util.logging.*;
+
+/**
+ * Implementation of the UDP transport service
+ */
+
+public class UDPTransport extends AbstractTransport
+{
+       /** The default maximum size of each outbound UDP message, optimal 
value for Ethernet (10 or 100 MBit). */
+       public static final int DEFAULT_MTU     =       1472;
+
+       private Prefs                           prefs;
+       private StatusCallsService      status;
+       private IdentityService         identity;
+
+       /** thread that listens for inbound messages */
+       private Task                                    dispatchTask;
+       private boolean                         running;
+
+       /** the socket that we receive all data from */
+       private DatagramChannel         channel;
+
+       /** Statistics handles. */
+       private Stat                                    bytesIn;
+       private Stat                                    bytesOut;
+
+
+       /** configuration */
+       private CIDRNetworks                    filtered;
+
+       private int                                     mtu;
+
+
+       public UDPTransport()
+       {
+               super(UDP_PROTOCOL_NUMBER,"UDP");
+               running=false;
+       }
+
+       public String toString()
+       {
+               return "UDP transport";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       /**
+        * The exported method. Makes the core api available via a global and
+        * returns the udp transport API.
+        * @param core
+        */
+
+       public void init( CoreForTransport core )
+       {
+               Statistics      stats;
+
+               super.init(core);
+
+               prefs=core.getApplication().getPreferences();
+               status=(StatusCallsService) 
core.service(StatusCallsService.class);
+               identity=(IdentityService) core.service(IdentityService.class);
+
+               stats=core.getApplication().getStatistics();
+               bytesIn=stats.getHandle("# bytes received via udp");
+               bytesOut=stats.getHandle("# bytes sent via udp");
+
+               
filtered=CIDRNetworks.parse(prefs.getString("UDP","BLACKLIST",""));
+               log(Level.INFO,"Filtered networks : 
"+filtered.getDescription());
+
+               mtu=prefs.getInt("UDP","MTU",0);
+               if (mtu==0) {
+                       mtu=DEFAULT_MTU;
+                       }
+               if (mtu<1200) {
+                       log(Level.SEVERE,"MTU is probably to low, fragmentation 
is not implemented !");
+                       }
+               mtu-=UDPMessage.SIZE;
+       }
+
+       public int getMTU()
+       {
+               return mtu;
+       }
+
+       public int getCost()
+       {
+               return 20000;
+       }
+
+       /**
+        * Get the GNUnet UDP port from the configuration, or from
+        * /etc/services if it is not specified in the config file.
+        *
+        * @return the port in host byte order
+        */
+
+       protected int getUDPPort()
+       {
+               int     port;
+
+               port=prefs.getInt("UDP","PORT",0);
+               if (port==0) {
+                       // try lookup in services
+                       port=OSAccess.getServicePort("gnunet","udp");
+                       }
+               return port;
+       }
+
+       /**
+        * Check if we are explicitly forbidden to communicate with this IP.
+        * @param ip
+        * @return
+        */
+
+       protected boolean isBlacklisted( InetAddress ip )
+       {
+               return filtered.check(ip);
+       }
+
+       /**
+        * Listen on the given socket and distribute the packets to the UDP 
handler.
+        */
+
+       public void listenAndDistribute()
+       {
+               ByteBuffer                      data;
+               MessagePack                     mp;
+               UDPMessage                      udpm;
+               InetSocketAddress       incoming;
+               int                                     size;
+
+               data=ByteBuffer.allocateDirect(mtu+UDPMessage.SIZE);    //todo: 
UDPMessage.MAX_DATA_SIZE
+
+               while (running) {
+                       try {
+                               data.clear();
+                               incoming=(InetSocketAddress) 
channel.receive(data);
+                               data.flip();
+                               size=data.limit();
+                               }
+                       catch( IOException x ) {
+                               err("Error while receiving data, aborting !",x);
+                               break;
+                               }
+
+                       if (!running) {
+                               break;
+                               }
+
+                       status.incrementBytesReceived(size);
+                       bytesIn.add(size);
+
+                       if (size <= UDPMessage.SIZE) {
+                               log(Level.INFO,"Received invalid datagram from 
"+incoming.getAddress()+":"+incoming.getPort()+", dropping.");
+                               continue;
+                               }
+
+                       udpm=(UDPMessage) 
PersistentHelper.readFully(UDPMessage.class,data);
+                       if (udpm==null) {
+                               log(Level.WARNING,"Received message is invalid, 
discard it !");
+                               continue;
+                               }
+
+                       debug(udpm.getSender().getName()+" Received "+size+" 
bytes from "+incoming.getAddress().getHostAddress()+":"+incoming.getPort()+".");
+
+                       if (isBlacklisted(incoming.getAddress())) {
+                               log(Level.WARNING,"Sender 
"+incoming.getAddress()+" is blacklisted, dropping message.");
+                               continue;       // drop on the floor
+                               }
+
+                       // message ok, fill in mp and pass to core
+                       mp = new MessagePack(new 
UDPSession(this,incoming.getAddress(),incoming.getPort(),udpm.getSender()));
+                       
mp.setMessages(udpm.getData(),udpm.getCheckSum(),udpm.isEncrypted());
+
+                       coreAPI.receive(mp);
+                       }
+       }
+
+       public boolean sendTo( Persistent p, InetAddress ip, int port )
+       {
+               boolean ok;
+
+               if (!running) {
+                       return false;
+                       }
+
+               debug("Sending message of "+p.getByteSize()+" bytes to 
"+ip.getHostAddress()+":"+port+".");
+
+               ok=false;
+               try {
+                       channel.configureBlocking(true);
+                       if (channel.send(PersistentHelper.toBuffer(p),new 
InetSocketAddress(ip,port))==p.getByteSize()) {
+                               ok = true;
+                               }
+                       }
+               catch( IOException x ) {
+                       err("Failed to send message of size "+p.getByteSize()+" 
!",x);
+                       }
+
+               status.incrementBytesSent(p.getByteSize());
+               bytesOut.add(p.getByteSize());
+               return ok;
+       }
+
+       /**
+        * Verify that a HELO-Message is correct (a node is reachable at that
+        * address). Since the reply will be asynchronous, a method must be
+        * called on success.
+        *
+        * @param helo the HELO message to verify
+        *        (the signature/crc have been verified before)
+        * @return OK on success, false on failure
+        */
+
+       public boolean verifyHELO( P2PHello helo )
+       {
+               UDPAddress      addr;
+
+               if (helo.getProtocol()!=UDP_PROTOCOL_NUMBER) {
+                       return false;
+                       }
+
+               addr=(UDPAddress) helo.decodeSenderAddress(UDPAddress.class);
+               if (addr==null || isBlacklisted(addr.getIP())) {
+                       return false;   // obviously invalid
+                       }
+
+               debug(helo.getSenderIdentity().getName()+" Verified HELO from 
"+addr.getIP().getHostAddress()+":"+addr.getPort()+".");
+               return true;
+       }
+
+       /**
+        * Create a HELO-Message for the current node. The HELO is created
+        * without signature and without a timestamp. The GNUnet core will
+        * sign the message and add an expiration time.
+        *
+        * @return OK on success, false on error
+        */
+
+       public P2PHello createHELO()
+       {
+               P2PHello        helo;
+               InetAddress     ip;
+               int                     port;
+
+               port=getUDPPort();
+               if (port==0) {
+                       return null;    // UDP transport configured send-only
+                       }
+
+               ip=identity.getPublicIPAddress();
+               if (ip==null) {
+                       log(Level.WARNING,"Could not determine my public IP 
address.");
+                       return null;
+                       }
+
+               helo=new P2PHello(UDP_PROTOCOL_NUMBER,mtu);
+               helo.encodeSenderAddress(new UDPAddress(ip,port));
+               return helo;
+       }
+
+       /**
+        * Establish a connection to a remote node.
+        * @param helo the HELO-Message for the target node
+        * @return OK on success, false if the operation failed
+        */
+
+       public Session connect( P2PHello helo )
+       {
+               UDPSession      session;
+
+               session=new UDPSession(this,helo);
+               session.lock();
+               debug("Connecting to 
"+session.getIP()+":"+session.getPort()+".");
+               return session;
+       }
+
+       /**
+        * Allocate and bind a server socket for the UDP transport.
+        * @param port
+        * @return
+        */
+
+       protected DatagramChannel createServer( int port )
+       {
+               DatagramChannel c;
+
+               try {
+                       c=DatagramChannel.open();
+                       c.configureBlocking(true);
+                       c.socket().setReuseAddress(true);
+
+                       // bind if port<>0, ie. not configured as send-only
+                       if (port!=0) {
+                               c.socket().bind(new InetSocketAddress(port));
+                               }
+                       }
+               catch( IOException x ) {
+                       err("Could not create/configure datagram socket !",x);
+                       return null;
+                       }
+               return c;
+       }
+
+       /**
+        * Start the server process to receive inbound traffic.
+        *
+        * @return OK on success, false if the operation failed
+        */
+
+       public boolean launch()
+       {
+               int     port;
+
+               port=getUDPPort();
+               channel=createServer(port);
+
+               if (port!=0) {
+                       running=true;
+                       dispatchTask=new Task("UDP-DISTRIBUTE",new 
EvalAction(this,"listenAndDistribute"));
+                       dispatchTask.launch();
+                       }
+               else {
+                       dispatchTask=null;
+                       }
+               return true;
+       }
+
+       /**
+        * Shutdown the server process (stop receiving inbound traffic). Maybe
+        * restarted later!
+        * @return
+        */
+
+       public boolean shutdown()
+       {
+               try {
+                       if (running) {
+                               // stop the thread, first set shutdown to YES, 
then ensure that the thread
+                               //actually sees the flag by sending a dummy 
message of 1 byte
+
+                               running=false;
+                               if (dispatchTask != null) {
+                                       // send to loopback
+                                       channel.configureBlocking(true);
+                                       channel.send(ByteBuffer.wrap(new byte[] 
{ (byte) 0 }),new InetSocketAddress(InetAddress.getLocalHost(),getUDPPort()));
+
+                                       dispatchTask.join();
+                                       }
+                               }
+
+                       channel.close();
+                       channel=null;
+                       }
+               catch( Throwable x ) {
+                       err("",x);
+                       return false;
+                       }
+               return true;
+       }
+
+       /**
+        * Convert UDP address to a string.
+        * @param helo
+        * @return
+        */
+
+       public String addressToString( P2PHello helo )
+       {
+               UDPAddress      addr;
+
+               addr=(UDPAddress) helo.decodeSenderAddress(UDPAddress.class);
+               return addr.getIP()+":"+addr.getPort()+" (UDP)";
+       }
+}

Added: freeway/src/org/gnu/freeway/transport/udp6.c
===================================================================
--- freeway/src/org/gnu/freeway/transport/udp6.c        2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/transport/udp6.c        2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,700 @@
+/*
+     This file is part of GNUnet
+
+     GNUnet is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with GNUnet; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file transports/udp6.c
+ * @brief Implementation of the UDP transport service over IPv6
+ * @author Christian Grothoff
+ **/
+
+#include "gnunet_util.h"
+#include "gnunet_transport.h"
+#include "platform.h"
+
+#define DEBUG_UDP6 NO
+
+/**
+ * Host-Address in a UDP6 network.
+ **/
+typedef struct {
+  /**
+   * claimed IP of the sender, network byte order 
+   **/  
+  IP6addr senderIP;
+
+  /**
+   * claimed port of the sender, network byte order 
+   **/
+  unsigned short senderPort; 
+
+  /**
+   * reserved (set to 0 for signature verification) 
+   **/
+  unsigned short reserved; 
+
+} Host6Address;
+
+/**
+ * Message-Packet header. 
+ **/
+typedef struct {
+  /**
+   * this struct is *preceded* by MESSAGE_PARTs - until
+   * size-sizeof(UDP6Message)!
+   **/ 
+
+  /** 
+   * size of the message, in bytes, including this header; max
+   * 65536-header (network byte order)
+   **/
+  unsigned short size;
+
+  /**
+   * Is the message encrypted? 
+   **/
+  unsigned short isEncrypted;
+
+  /**
+   * CRC checksum of the plaintext  (network byte order)
+   **/ 
+  int checkSum;
+
+  /**
+   * What is the identity of the sender (hash of public key) 
+   **/
+  HostIdentity sender;
+
+} UDP6Message;
+
+/* *********** globals ************* */
+
+/* apis (our advertised API and the core api ) */
+static CoreAPIForTransport * coreAPI;
+static TransportAPI udp6API;
+
+/**
+ * thread that listens for inbound messages 
+ **/
+static PTHREAD_T dispatchThread;
+
+/**
+ * the socket that we receive all data from 
+ **/
+static int udp6_sock;
+
+/**
+ * Statistics handles.
+ **/
+static int stat_octets_total_udp6_in;
+static int stat_octets_total_udp6_out;
+
+/**
+ * Semaphore for communication with the
+ * udp6 server thread.
+ **/
+static Semaphore * serverSignal;
+static int udp6_shutdown = YES;
+
+/**
+ * configuration 
+ **/
+static CIDR6Network * filteredNetworks_ = NULL;
+static Mutex configLock;
+
+/**
+ * Get the GNUnet UDP6 port from the configuration, or from
+ * /etc/services if it is not specified in the config file.
+ *
+ * @return the port in host byte order
+ **/
+static unsigned short getGNUnetUDP6Port() {
+  struct servent * pse;        /* pointer to service information entry */
+  unsigned short port;
+
+  port = (unsigned short) getConfigurationInt("UDP6",
+                                             "PORT");
+  if (port == 0) { /* try lookup in services */
+    if ((pse = getservbyname("gnunet", "udp6"))) 
+      port = ntohs(pse->s_port);      
+    else 
+      errexit("Cannot determine port to bind to. "\
+             " Define in configuration file in section %s under %s "\
+             "or in /etc/services under udp6/gnunet.\n",
+             "UDP6", 
+             "PORT");
+  }
+  return port;
+}
+
+/**
+ * Allocate and bind a server socket for the UDP6 transport.
+ **/
+static int passivesock(unsigned short port) {
+  struct sockaddr_in6 sin;
+  int sock;
+  const int on = 1;
+
+  sock = SOCKET(PF_INET6, 
+               SOCK_DGRAM, 
+               UDP_PROTOCOL_NUMBER);
+  if (sock < 0) 
+    errexit("UDP6 transport: Can not create socket: %s\n", 
+           STRERROR(errno));  
+  if ( SETSOCKOPT(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0 )
+      perror("setsockopt");
+  if (port != 0) {
+    memset(&sin, 0, sizeof(sin)); 
+    sin.sin6_family = AF_INET6;
+    sin.sin6_port   = htons(port);
+    memcpy(&sin.sin6_addr,
+          &in6addr_any,
+          sizeof(IP6addr));
+    if (BIND(sock, (struct sockaddr *)&sin, sizeof(sin)) < 0)
+      errexit("UDP6 transport: Can not bind to UDP6 port: %s\n",
+             STRERROR(errno));
+  } /* do not bind if port == 0, then we use
+       send-only! */
+  return sock;
+}
+
+/**
+ * Check if we are explicitly forbidden to communicate with this IP.
+ **/
+static int isBlacklisted(IP6addr * ip) {
+  int ret;
+
+  MUTEX_LOCK(&configLock);
+  ret = checkIP6Listed(filteredNetworks_,
+                      ip);
+  MUTEX_UNLOCK(&configLock);
+  return ret;
+}
+
+/**
+ * Listen on the given socket and distribute the packets to the UDP6
+ * handler.
+ **/
+static void * listenAndDistribute() {
+  struct sockaddr_in6 incoming;
+  socklen_t addrlen = sizeof(incoming);  
+  int size;
+  HexName hex;
+  MessagePack * mp;
+  UDP6Message udp6m;
+#if DEBUG_UDP6
+  char * tmp;
+#endif
+
+  SEMAPHORE_UP(serverSignal);
+  while (udp6_shutdown == NO) {
+    mp = MALLOC(sizeof(MessagePack));
+    mp->msg = MALLOC(udp6API.mtu + sizeof(UDP6Message));
+  RETRY:
+    memset(&incoming, 
+          0, 
+          sizeof(struct sockaddr_in6));
+    if (udp6_shutdown == YES) {
+      FREE(mp->msg);
+      FREE(mp);
+      break;
+    }
+    size = RECVFROM(udp6_sock, 
+                   mp->msg, 
+                   udp6API.mtu + sizeof(UDP6Message), 
+                   0,
+                   (struct sockaddr * )&incoming, 
+                   &addrlen);
+    if ( (size < 0) || 
+        (udp6_shutdown == YES) ) {
+      if (udp6_shutdown == NO) {
+       if ( (errno == EINTR) || 
+            (errno == EAGAIN) ||
+            (errno == ECONNREFUSED) )
+         goto RETRY;
+      }
+      FREE(mp->msg);
+      FREE(mp);
+      if (udp6_shutdown == NO)
+       LOG(LOG_WARNING,
+           "WARNING: error with UDP6 server (%s), aborting UDP6 server.\n",
+           STRERROR(errno));
+      break; /* die/shutdown */
+    }
+    incrementBytesReceived(size);
+    statChange(stat_octets_total_udp6_in,
+              size);
+    if ((unsigned int)size <= sizeof(UDP6Message)) {
+      char * tmp = MALLOC(INET6_ADDRSTRLEN);
+      LOG(LOG_INFO,
+         "INFO: received invalid UDP6 message from %s:%d, dropping\n",
+         inet_ntop(AF_INET6,
+                   &incoming,
+                   tmp,
+                   INET6_ADDRSTRLEN), 
+         ntohs(incoming.sin6_port));
+      FREE(tmp);
+      goto RETRY;
+    }
+    memcpy(&udp6m,
+          &((char*)mp->msg)[size - sizeof(UDP6Message)],
+          sizeof(UDP6Message));
+
+    IFLOG(LOG_DEBUG,
+         hash2hex(&udp6m.sender.hashPubKey,
+                  &hex));
+#if DEBUG_UDP6
+    tmp = MALLOC(INET6_ADDRSTRLEN);
+    LOG(LOG_DEBUG,
+       "DEBUG: received %d bytes via UDP6 from %s:%d (%s)\n",
+       size,
+       inet_ntop(AF_INET6,
+                 &incoming,
+                 tmp,
+                 INET6_ADDRSTRLEN), 
+       ntohs(incoming.sin6_port),
+       &hex);
+    FREE(tmp);
+#endif
+    /* quick test of the packet, if failed, repeat! */
+    if (size != ntohs(udp6m.size)) {
+      LOG(LOG_WARNING,
+         "WARNING: Received packet failed format check "\
+         "(size %d, header would indicate %d), discarded!\n",
+         size,
+         ntohs(udp6m.size));
+      goto RETRY;
+    }
+    if (sizeof(struct in6_addr) != sizeof(IP6addr))
+      errexit("FATAL: assertion failed at %s:%d\n",
+             __FILE__, __LINE__);
+    if (YES == isBlacklisted((IP6addr*)&incoming.sin6_addr)) {
+      char * tmp = MALLOC(INET6_ADDRSTRLEN);
+      LOG(LOG_WARNING,
+         "WARNING: sender %s is blacklisted, dropping message\n",
+         inet_ntop(AF_INET6,
+                   &incoming,
+                   tmp,
+                   INET6_ADDRSTRLEN));
+      FREE(tmp);
+      goto RETRY; /* drop on the floor */
+    }
+    /* message ok, fill in mp and pass to core */
+    mp->tsession     = NULL;
+    mp->size        = ntohs(udp6m.size) - sizeof(UDP6Message);    
+    mp->isEncrypted = ntohs(udp6m.isEncrypted);
+    mp->crc         = ntohl(udp6m.checkSum);
+    memcpy(&mp->sender,
+          &udp6m.sender,
+          sizeof(HostIdentity));
+    coreAPI->receive(mp);
+  }
+  /* shutdown */
+  SEMAPHORE_UP(serverSignal);
+  return NULL;
+}
+
+
+/* *************** API implementation *************** */
+
+/**
+ * Verify that a HELO-Message is correct (a node is reachable at that
+ * address). Since the reply will be asynchronous, a method must be
+ * called on success.
+ *
+ * @param helo the HELO message to verify
+ *        (the signature/crc have been verified before)
+ * @return OK on success, SYSERR on failure
+ **/
+static int verifyHelo(HELO_Message * helo) {
+  Host6Address * haddr;
+
+  haddr = (Host6Address*) &((HELO_Message_GENERIC*)helo)->senderAddress[0];
+  if ( (ntohs(helo->senderAddressSize) != sizeof(Host6Address)) ||
+       (ntohs(helo->header.size) != HELO_Message_size(helo)) ||
+       (ntohs(helo->header.requestType) != p2p_PROTO_HELO) ||
+       (YES == isBlacklisted(&haddr->senderIP)) )
+    return SYSERR; /* obviously invalid */
+  else {
+#if DEBUG_UDP6
+    char * tmp = MALLOC(INET6_ADDRSTRLEN);
+    LOG(LOG_DEBUG,
+       "DEBUG: verified UDP6 helo from %d.%d.%d.%d:%d\n",
+       inet_ntop(AF_INET6,
+                 &haddr->senderIP,
+                 tmp,
+                 INET6_ADDRSTRLEN), 
+       ntohs(haddr->senderPort));
+    FREE(tmp);
+#endif    
+    return OK;
+  }
+}
+
+/**
+ * Create a HELO-Message for the current node. The HELO is created
+ * without signature and without a timestamp. The GNUnet core will
+ * sign the message and add an expiration time.
+ *
+ * @param helo where to store the HELO message
+ * @return OK on success, SYSERR on error
+ **/
+static int createHELO(HELO_Message ** helo) {
+  HELO_Message * msg;
+  Host6Address * haddr;
+  unsigned short port;
+
+  port = getGNUnetUDP6Port();
+  if (port == 0)
+    return SYSERR; /* UDP6 transport configured send-only */
+
+  msg = MALLOC(sizeof(HELO_Message) + sizeof(Host6Address));
+  haddr = (Host6Address*) &((HELO_Message_GENERIC*)msg)->senderAddress[0];
+
+  if (SYSERR == getPublicIP6Address(&haddr->senderIP)) {
+    FREE(msg);
+    LOG(LOG_WARNING,
+       "UDP6: Could not determine my public IP address.\n");
+    return SYSERR;
+  }
+  haddr->senderPort      = htons(port); 
+  haddr->reserved        = htons(0);
+  msg->senderAddressSize = htons(sizeof(Host6Address));
+  msg->protocol          = htons(UDP6_PROTOCOL_NUMBER);
+  msg->MTU               = htonl(udp6API.mtu);
+  *helo = msg;
+  return OK;
+}
+
+/**
+ * Establish a connection to a remote node.
+ * @param helo the HELO-Message for the target node
+ * @param tsessionPtr the session handle that is to be set
+ * @return OK on success, SYSERR if the operation failed
+ **/
+static int udp6Connect(HELO_Message * helo,
+                      TSession ** tsessionPtr) {
+  TSession * tsession;
+  Host6Address * haddr;
+#if DEBUG_UDP6
+  char * tmp;
+#endif
+
+  tsession = MALLOC(sizeof(TSession));
+  tsession->internal = helo;
+  haddr = (Host6Address*) &((HELO_Message_GENERIC*)helo)->senderAddress[0];  
+#if DEBUG_UDP6
+  tmp = MALLOC(INET6_ADDRSTRLEN);
+  LOG(LOG_DEBUG,
+      "DEBUG: connecting via UDP6 to %s:%d\n",
+      inet_ntop(AF_INET6,
+               &haddr->senderIP,
+               tmp,
+               INET6_ADDRSTRLEN), 
+      ntohs(haddr->senderPort));
+  FREE(tmp);
+#endif
+   (*tsessionPtr) = tsession;
+  return OK;
+}
+
+/**
+ * A (core) Session is to be associated with a transport session. The
+ * transport service may want to know in order to call back on the
+ * core if the connection is being closed.
+ *
+ * @param tsession the session handle passed along
+ *   from the call to receive that was made by the transport
+ *   layer
+ * @return OK if the session could be associated,
+ *         SYSERR if not.
+ **/
+int udp6Associate(TSession * tsession) {
+  return SYSERR; /* UDP6 connections can never be associated */
+}
+
+/**
+ * Send a message to the specified remote node.
+ *
+ * @param tsession the HELO_Message identifying the remote node
+ * @param message what to send
+ * @param size the size of the message
+ * @param isEncrypted is the message encrypted?
+ * @param crc CRC32 checksum of the plaintext
+ * @return SYSERR on error, OK on success
+ **/
+static int udp6Send(TSession * tsession,
+                   const void * message,
+                   const unsigned int size,
+                   int isEncrypted,
+                   const int crc) {
+  char * msg;
+  UDP6Message mp;
+  HELO_Message * helo;
+  Host6Address * haddr;
+  struct sockaddr_in6 sin; /* an Internet endpoint address */
+  int ok;
+  int ssize;
+#if DEBUG_UDP6
+  char * tmp;
+#endif
+  
+  if (udp6_shutdown == YES)
+    return SYSERR;
+  if (size == 0) {
+    LOG(LOG_ERROR,
+       "ERROR: message passed to udp6Send has size 0, which is not 
allowed.\n");
+    return SYSERR;
+  }
+  if (size > udp6API.mtu) {
+    LOG(LOG_FAILURE,
+       "FAILURE: message larger than allowed by udp6 transport (%d > %d)\n",
+       size, 
+       udp6API.mtu);
+    return SYSERR;
+  }
+  helo = (HELO_Message*)tsession->internal;
+  if (helo == NULL) 
+    return SYSERR;
+
+  haddr = (Host6Address*) &((HELO_Message_GENERIC*)helo)->senderAddress[0];
+  ssize = size + sizeof(UDP6Message);
+  msg = MALLOC(ssize);
+  mp.checkSum    = htonl(crc);
+  mp.isEncrypted = htons(isEncrypted);
+  mp.size        = htons(ssize);
+  memcpy(&mp.sender,
+        coreAPI->myIdentity,
+        sizeof(HostIdentity));
+  memcpy(&msg[size],
+        &mp,
+        sizeof(UDP6Message));
+  memcpy(msg,
+        message,
+        size);
+  ok = SYSERR;
+  memset(&sin, 0, sizeof(sin));
+  sin.sin6_family = AF_INET6;
+  sin.sin6_port = haddr->senderPort;
+  memcpy(&sin.sin6_addr,
+        &haddr->senderIP.addr,
+        sizeof(IP6addr));
+#if DEBUG_UDP6
+  tmp = MALLOC(INET6_ADDRSTRLEN);
+  LOG(LOG_DEBUG,
+      "DEBUG: sending message of %d bytes to UDP6 %s:%d\n",
+      ssize,
+      inet_ntop(AF_INET6,
+               &sin,
+               tmp,
+               INET6_ADDRSTRLEN), 
+      ntohs(sin.sin_port));
+  FREE(tmp);
+#endif
+  if (ssize == SENDTO(udp6_sock,
+                     msg,
+                     ssize,
+                     0, /* no flags */
+                     (struct sockaddr*) &sin,
+                     sizeof(sin))) {
+    ok = OK;
+  } else {
+    LOG(LOG_WARNING,
+       "WARNING: Failed to send message of size %d via UDP6 (%s)\n",
+       ssize,
+       STRERROR(errno));
+  }
+  incrementBytesSent(ssize);
+  statChange(stat_octets_total_udp6_out,
+            ssize);
+  FREE(msg);
+  return ok;
+}
+
+/**
+ * Disconnect from a remote node.
+ *
+ * @param tsession the session that is closed
+ * @return OK on success, SYSERR if the operation failed
+ **/
+static int udp6Disconnect(TSession * tsession) {
+  if (tsession != NULL) {
+    if (tsession->internal != NULL)
+      FREE(tsession->internal);
+    FREE(tsession);
+  }
+  return OK;
+}
+
+/**
+ * Start the server process to receive inbound traffic.
+ *
+ * @return OK on success, SYSERR if the operation failed
+ **/
+static int startTransportServer(void) {
+  unsigned short port;
+
+   /* initialize UDP6 network */
+  port = getGNUnetUDP6Port();
+  udp6_sock = passivesock(port);
+  if (port != 0) {
+    udp6_shutdown = NO;
+    serverSignal = SEMAPHORE_NEW(0);
+    if (0 != PTHREAD_CREATE(&dispatchThread,
+                           (PThreadMain) &listenAndDistribute,
+                           NULL,
+                           4*1024))
+      return SYSERR;
+    SEMAPHORE_DOWN(serverSignal);
+  } else
+    memset(&dispatchThread,
+          0,
+          sizeof(PTHREAD_T)); /* zero-out */
+  return OK;
+}
+
+/**
+ * Shutdown the server process (stop receiving inbound traffic). Maybe
+ * restarted later!
+ **/
+static int stopTransportServer() {
+  if (udp6_shutdown == NO) {
+    /* stop the thread, first set shutdown
+       to YES, then ensure that the thread
+       actually sees the flag by sending
+       a dummy message of 1 char */
+    udp6_shutdown = YES;
+    if (serverSignal != NULL) {
+      char msg = '\0';
+      struct sockaddr_in sin;
+      void * unused;
+      
+      /* send to loopback */
+      sin.sin_family = AF_INET;
+      sin.sin_port = htons(getGNUnetUDP6Port());
+      *(int*)&sin.sin_addr = htonl(0x7F000001); /* 127.0.0.1 = localhost */
+      SENDTO(udp6_sock, 
+            &msg, 
+            sizeof(msg), 
+            0, /* no flags */
+            (struct sockaddr*) &sin,
+            sizeof(sin));
+      SEMAPHORE_DOWN(serverSignal);
+      SEMAPHORE_FREE(serverSignal);
+      PTHREAD_JOIN(&dispatchThread, &unused);
+    }
+  }
+  CLOSE(udp6_sock);  
+  udp6_sock = -1;
+  return OK;
+}
+
+/**
+ * Reload the configuration. Should never fail.
+ **/
+static void reloadConfiguration(void) {
+  char * ch;
+
+  MUTEX_LOCK(&configLock);
+  FREENONNULL(filteredNetworks_);
+  ch = getConfigurationString("UDP6",
+                             "BLACKLIST");
+  if (ch == NULL)
+    filteredNetworks_ = parseRoutes6("");
+  else {
+    filteredNetworks_ = parseRoutes6(ch);
+    FREE(ch);    
+  }
+  MUTEX_UNLOCK(&configLock);
+}
+
+/**
+ * Convert UDP6 address to a string.
+ **/
+static char * addressToString(HELO_Message * helo) {
+  char * ret;
+  char * tmp;
+  Host6Address * haddr;
+  
+  haddr = (Host6Address*) &((HELO_Message_GENERIC*)helo)->senderAddress[0];  
+  ret = MALLOC(INET6_ADDRSTRLEN+6);
+  tmp = MALLOC(INET6_ADDRSTRLEN);  
+  sprintf(ret,
+         "%s:%d (UDP6)",
+         inet_ntop(AF_INET6,
+                   haddr,
+                   tmp,
+                   INET6_ADDRSTRLEN), 
+         ntohs(haddr->senderPort));
+  FREE(tmp);
+  return ret;
+}
+
+/**
+ * The default maximum size of each outbound UDP6 message, 
+ * optimal value for Ethernet (10 or 100 MBit).
+ **/
+#define MESSAGE_SIZE 1452
+
+/**
+ * The exported method. Makes the core api available via a global and
+ * returns the udp6 transport API.
+ **/ 
+TransportAPI * inittransport_udp6(CoreAPIForTransport * core) {
+  int mtu;
+
+  coreAPI = core;
+  stat_octets_total_udp6_in 
+    = statHandle("# bytes received via udp6");
+  stat_octets_total_udp6_out 
+    = statHandle("# bytes sent via udp6");
+
+  MUTEX_CREATE(&configLock);
+  reloadConfiguration();
+  mtu = getConfigurationInt("UDP6",
+                           "MTU");
+  if (mtu == 0)
+    mtu = MESSAGE_SIZE;
+  if (mtu < 1200)
+    LOG(LOG_ERROR,
+       "ERROR: MTU for UDP6 is probably to low (fragmentation not 
implemented!)\n");
+
+  udp6API.protocolNumber       = UDP6_PROTOCOL_NUMBER;
+  udp6API.mtu                  = mtu - sizeof(UDP6Message);
+  udp6API.cost                 = 19950;
+  udp6API.verifyHelo           = &verifyHelo;
+  udp6API.createHELO           = &createHELO;
+  udp6API.connect              = &udp6Connect;
+  udp6API.send                 = &udp6Send;
+  udp6API.sendReliable         = &udp6Send;  /* can't increase reliability */
+  udp6API.associate            = &udp6Associate;
+  udp6API.disconnect           = &udp6Disconnect;
+  udp6API.startTransportServer = &startTransportServer;
+  udp6API.stopTransportServer  = &stopTransportServer;
+  udp6API.reloadConfiguration  = &reloadConfiguration;
+  udp6API.addressToString      = &addressToString;
+
+  return &udp6API;
+}
+
+void donetransport_udp6() {
+  MUTEX_DESTROY(&configLock);
+  FREENONNULL(filteredNetworks_);
+}
+
+/* end of udp6.c */

Added: freeway/src/org/gnu/freeway/util/AbstractAction.java
===================================================================
--- freeway/src/org/gnu/freeway/util/AbstractAction.java        2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/util/AbstractAction.java        2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,51 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util;
+
+import org.gnu.freeway.util.crypto.*;
+
+/**
+ *
+ */
+
+public abstract class AbstractAction extends LoggedObject implements Action
+{
+       private String  name;
+
+
+       protected AbstractAction()
+       {
+               this(null);
+       }
+
+       protected AbstractAction( String str )
+       {
+               super(true);
+               name=(str!=null ? str : Crypto.nextString(5));
+       }
+
+       public String toString()
+       {
+               return "Abstract action [name="+name+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int hashCode()
+       {
+               return name.hashCode();
+       }
+
+       public boolean equals( Object obj )
+       {
+               return ((obj instanceof Action) && ((Action) 
obj).getName().equals(name));
+       }
+
+       public String getName()
+       {
+               return name;
+       }
+}

Added: freeway/src/org/gnu/freeway/util/AbstractFormatter.java
===================================================================
--- freeway/src/org/gnu/freeway/util/AbstractFormatter.java     2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/util/AbstractFormatter.java     2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,64 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util;
+
+import java.text.*;
+import java.util.logging.*;
+
+/**
+ *
+ */
+
+public abstract class AbstractFormatter extends Format
+{
+       private Format  formatter;
+
+
+       public AbstractFormatter()
+       {
+               this(null);
+       }
+
+       public AbstractFormatter( Format f )
+       {
+               super();
+               formatter=f;
+       }
+
+       public StringBuffer format( Object obj, StringBuffer toAppendTo, 
FieldPosition pos )
+       {
+               Logger  logger;
+               String  str;
+
+               try {
+                       str=formatObject(obj);
+                       }
+               catch( IllegalArgumentException x ) {
+                       // special documented case of failure
+                       logger=Logger.getLogger(getClass().getName());
+                       logger.log(Level.WARNING,"Bad argument : 
"+obj+(obj!=null ? " ("+obj.getClass().getName()+")" : ""));
+
+                       str="";
+                       }
+               catch( Throwable x ) {
+                       logger=Logger.getLogger(getClass().getName());
+                       logger.log(Level.SEVERE,"Could not format object !",x);
+
+                       str="";
+                       }
+               toAppendTo.append(str);
+               return toAppendTo;
+       }
+
+       public Object parseObject( String source, ParsePosition pos )
+       {
+               return (formatter!=null ? formatter.parseObject(source,pos) : 
null);
+       }
+
+       protected String formatObject( Object obj ) throws Throwable
+       {
+               return (formatter!=null ? formatter.format(obj) : "");
+       }
+}

Added: freeway/src/org/gnu/freeway/util/AbstractIterator.java
===================================================================
--- freeway/src/org/gnu/freeway/util/AbstractIterator.java      2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/util/AbstractIterator.java      2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,67 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util;
+
+import java.util.*;
+
+/**
+ *
+ */
+
+public abstract class AbstractIterator extends Object implements Iterator
+{
+       public static final Iterator    EMPTY   =       new AbstractIterator() {
+               public Object fetch()
+               {
+                       return null;
+               }
+               };
+
+       private Object  next;
+       private boolean ended;
+
+
+       protected AbstractIterator()
+       {
+               super();
+               next=null;
+               ended=false;
+       }
+
+       public boolean hasNext()
+       {
+               fetchIfNeeded();
+               return (next!=null);
+       }
+
+       public Object next()
+       {
+               Object  obj;
+
+               fetchIfNeeded();
+               if (next==null) {
+                       throw new NoSuchElementException();
+                       }
+
+               obj=next;
+               next=null;
+               return obj;
+       }
+
+       public void remove()
+       {
+               throw new UnsupportedOperationException();
+       }
+
+       protected void fetchIfNeeded()
+       {
+               if (next==null && !ended) {
+                       next=fetch();
+                       ended=(next==null);
+                       }
+       }
+
+       public abstract Object fetch();
+}

Added: freeway/src/org/gnu/freeway/util/AbstractLogFormatter.java
===================================================================
--- freeway/src/org/gnu/freeway/util/AbstractLogFormatter.java  2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/util/AbstractLogFormatter.java  2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,113 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util;
+
+import java.util.logging.*;
+import java.util.regex.*;
+
+/**
+ *
+ */
+
+public abstract class AbstractLogFormatter extends Formatter
+{
+       public static final String      EOL     =       
System.getProperty("line.separator");
+
+       private Pattern splitter;
+
+
+       protected AbstractLogFormatter()
+       {
+               super();
+               splitter=Pattern.compile("[\n\r]+");
+       }
+
+       public String toString()
+       {
+               return "Abstract log formatter";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public String getHead( Handler hd )
+       {
+               return "";
+       }
+
+       public String getTail( Handler hd )
+       {
+               return "";
+       }
+
+       public synchronized String format( LogRecord record )
+       {
+               StringBuffer    buf;
+
+               buf=new StringBuffer();
+               format(record,buf);
+               return buf.toString();
+       }
+
+       public abstract void format( LogRecord record, StringBuffer buf );
+
+       protected void addFrom( String clazz, String method, StringBuffer buf )
+       {
+               if (clazz!=null) {
+                       clazz=clazz.substring(clazz.lastIndexOf('.')+1);
+                       }
+               else {
+                       clazz="-";
+                       }
+               buf.append(clazz);
+
+               if (method!=null) {
+                       buf.append(".");
+                       buf.append(method);
+                       }
+       }
+
+       protected void addMessage( String str, StringBuffer buf )
+       {
+               String[]        p;
+               int                     i;
+
+               if (str==null) {
+                       str="";
+                       }
+
+               p=splitter.split(str);
+               for (i=0; i<p.length; i++) {
+                       buf.append((i>0 ? "      (continued) " : 
"")+p[i]/*.trim()*/+EOL);
+                       }
+       }
+
+       protected void addException( Throwable x, StringBuffer buf )
+       {
+               StackTraceElement[]     elems;
+               String[]                        p;
+               int                                     i;
+
+               while (x!=null) {
+                       buf.append("      > ["+x.getClass().getName()+"] ");
+
+                       p=splitter.split(x.getMessage()!=null ? x.getMessage() 
: "");
+                       for (i=0; i<p.length; i++) {
+                               buf.append((i>0 ? "      (continued) " : 
"")+p[i].trim()+EOL);
+                               }
+
+                       elems=x.getStackTrace();
+                       for (i=0; i<elems.length; i++) {
+                               buf.append("      | "+elems[i]+EOL);
+                               }
+                       buf.append(EOL);
+
+                       if (x.getCause()!=null) {
+                               buf.append("--- nested ---"+EOL);
+                               }
+                       x=x.getCause();
+                       }
+       }
+}

Added: freeway/src/org/gnu/freeway/util/AbstractService.java
===================================================================
--- freeway/src/org/gnu/freeway/util/AbstractService.java       2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/util/AbstractService.java       2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,129 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util;
+
+/**
+ * Skeleton of what a service should be.
+ */
+
+public abstract class AbstractService extends LoggedObject implements Service
+{
+       /** The manager. */
+       private ServiceManager  manager;
+
+       /** The name of the service. */
+       private String                  name;
+
+       /** Internal state */
+       private int                             state;
+
+
+       protected AbstractService( String str )
+       {
+               super(true);
+               manager=null;
+               name=str;
+               state=0;
+       }
+
+       public String toString()
+       {
+               return "Abstract service [name="+name+", state="+state+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public String getName()
+       {
+               return name;
+       }
+
+       public ServiceManager getManager()
+       {
+               return manager;
+       }
+
+       public void attach( ServiceManager mgr )
+       {
+               manager=mgr;
+       }
+
+       public void detach()
+       {
+               manager=null;
+       }
+
+       public boolean isInitialized()
+       {
+               return state>=1;
+       }
+
+       public boolean isStarted()
+       {
+               return state==2;
+       }
+
+       public boolean isStopped()
+       {
+               return state==3;
+       }
+
+       public void init() throws ServiceException
+       {
+               switch (state) {
+                       case 0: break;
+                       case 1: throw new ServiceException("Already 
initialized.");
+                       case 2: throw new ServiceException("Already 
initialized.");
+                       case 3: throw new ServiceException("Already 
initialized.");
+                       case 4: throw new ServiceException("Service has been 
shutdown.");
+                       }
+
+               debug("Initialized.");
+               state=1;
+       }
+
+       public void start() throws ServiceException
+       {
+               switch (state) {
+                       case 0: init(); break;
+                       case 1: break;
+                       case 2: throw new ServiceException("Already started.");
+                       case 3: break;
+                       case 4: throw new ServiceException("Service has been 
shutdown.");
+                       }
+
+               debug("Starting...");
+               state=2;
+       }
+
+       public void stop() throws ServiceException
+       {
+               switch (state) {
+                       case 0: throw new ServiceException("Not initialized.");
+                       case 1: throw new ServiceException("Not started.");
+                       case 2: break;
+                       case 3: throw new ServiceException("Already stopped.");
+                       case 4: throw new ServiceException("Service has been 
shutdown.");
+                       }
+
+               debug("Stopping...");
+               state=3;
+       }
+
+       public void done() throws ServiceException
+       {
+               switch (state) {
+                       case 0: throw new ServiceException("Not initialized.");
+                       case 1: break;
+                       case 2: stop(); break;
+                       case 3: break;
+                       case 4: throw new ServiceException("Already shutdown.");
+                       }
+
+               debug("Done.");
+               state=4;
+       }
+}

Added: freeway/src/org/gnu/freeway/util/Action.java
===================================================================
--- freeway/src/org/gnu/freeway/util/Action.java        2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/util/Action.java        2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,17 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util;
+
+/**
+ *
+ */
+
+public interface Action
+{
+       public static final Action      NUL_ACTION      =       new NulAction();
+
+       public String getName();
+       public void perform() throws Throwable;
+}

Added: freeway/src/org/gnu/freeway/util/BloomFilter.java
===================================================================
--- freeway/src/org/gnu/freeway/util/BloomFilter.java   2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/util/BloomFilter.java   2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,578 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util;
+
+import org.gnu.freeway.util.crypto.*;
+import org.gnu.freeway.util.io.*;
+import org.gnu.freeway.util.net.*;
+
+import java.io.*;
+import java.nio.*;
+import java.nio.channels.*;
+import java.util.*;
+import java.util.logging.*;
+
+/**
+ * data structure used to reduce disk accesses.
+ *
+ * The idea basically: Create a signature for each element in the
+ * database. Add those signatures to a bit array. When doing a lookup,
+ * check if the bit array matches the signature of the requested
+ * element. If yes, address the disk, otherwise return 'not found'.
+ *
+ * A property of the bloom filter is that sometimes we will have
+ * a match even if the element is not on the disk (then we do
+ * an unnecessary disk access), but what's most important is that
+ * we never get a single "false negative".
+ *
+ * To be able to delete entries from the bloom filter, we maintain
+ * a 4 bit counter in the file on the drive (we still use only one
+ * bit in memory).
+ */
+
+public class BloomFilter extends LoggedObject
+{
+       private static final int                BUFFSIZE                =       
65536;
+
+       /** The bit counter file on disk */
+       public FileChannel                      fd;
+
+       /** How many bits we set for each stored element */
+       public int                                      addressesPerElement;
+
+       /** The actual bloomfilter bit array */
+       public byte[]                           bitArray;
+
+       /** Concurrency control */
+       public Object                           lock;
+
+       /** Statistics handle for filter hits */
+       private Stat                    hitsStat;
+
+       /** Statistics handle for filter misses */
+       private Stat                    missesStat;
+
+       /** Statistics handle for adds to filter */
+       private Stat                    addsStat;
+
+       /** Statistics handle for dels from filter */
+       private Stat                    delsStat;
+
+       /** */
+       private Statistics      stats;
+
+
+       protected BloomFilter()
+       {
+               super(true);
+       }
+
+       public String toString()
+       {
+               return "Bloom filter";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public void setInstrumentedBy( Statistics s )
+       {
+       }
+
+       public Statistics getInstrumentedBy()
+       {
+               return null;
+       }
+
+       /**
+        * Test if an element is in the filter.
+        *
+        * @param e the element
+        * @return true if the element is in the filter, false if not
+        */
+
+       public boolean test( HashCode160 e )
+       {
+               boolean[]       res=new boolean[1];
+
+               synchronized(lock) {
+                       res[0]=true;
+                       iterateBits(new BitIterator() {
+                               public void iterate( int bit, Object arg )
+                               {
+                                       if (!testBit(bit)) {
+                                               ((boolean[]) arg)[0]=false;
+                                               }
+                               }
+                               },res,e);
+
+
+                       if (res[0])
+                               hitsStat.inc();
+                       else
+                               missesStat.inc();
+                       }
+               return res[0];
+       }
+
+       /**
+        * Add an element to the filter
+        *
+        * @param e the element
+        */
+
+       public void add( HashCode160 e )
+       {
+               synchronized(lock) {
+                       iterateBits(new BitIterator() {
+                               public void iterate( int bit, Object arg )
+                               {
+                                       incrementBit(bit);
+                               }
+                               },null,e);
+
+                       addsStat.inc();
+                       }
+       }
+
+       /**
+        * Remove an element from the filter.
+        *
+        * @param e the element to remove
+        */
+
+       public void delete( HashCode160 e )
+       {
+               synchronized(lock) {
+                       iterateBits(new BitIterator() {
+                               public void iterate( int bit, Object arg )
+                               {
+                                       decrementBit(bit);
+                               }
+                               },null,e);
+
+                       delsStat.inc();
+                       }
+       }
+
+       /**
+        * Reset a bloom filter to empty. Clears the file on disk.
+        */
+
+       public void reset()
+       {
+               synchronized(lock) {
+                       Arrays.fill(bitArray,(byte) 0);
+                       makeEmptyFile(bitArray.length * 4);
+
+                       hitsStat.reset();
+                       missesStat.reset();
+                       addsStat.reset();
+                       delsStat.reset();
+                       }
+       }
+
+       /**
+        * Free the space associated with a filter
+        * in memory, flush to drive if needed (do not
+        * free the space on the drive)
+        */
+
+       public void free()
+       {
+               lock=null;
+               hitsStat.reset();   /* if we realloc w/ same stat name later */
+               missesStat.reset();
+               addsStat.reset();
+               delsStat.reset();
+
+               try {
+                       fd.close();
+                       }
+               catch( IOException x ) {
+                       err("Failed to close underlying file !",x);
+                       }
+               bitArray=null;
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       /**
+        * Checks if a bit is active in the bitArray
+        *
+        * @param index which bit to test
+        * @return true if the bit is set, false if not.
+        */
+
+       protected boolean testBit( int index )
+       {
+               int slot;
+               int targetBit;
+
+               slot = index / 8;
+               targetBit = (1 << (index % 8));
+               return (bitArray[slot] & targetBit)!=0;
+       }
+
+       protected byte safeRead( int pos ) throws IOException
+       {
+               if (pos+1>fd.size()) {
+                       return 0;
+                       }
+               ByteBuffer      buf=ByteBuffer.allocate(1);
+               fd.position(pos);
+               fd.read(buf);
+               return buf.get(0);
+       }
+
+       protected void safeWrite( int pos, byte b ) throws IOException
+       {
+               byte[]  buf;
+
+               if (pos>fd.size()) {
+                       buf=new byte[pos-(int) fd.size()];
+                       Arrays.fill(buf,(byte) 0);
+                       fd.position(fd.size());
+                       fd.write(ByteBuffer.wrap(buf));
+                       }
+               fd.position(pos);
+               fd.write(ByteBuffer.wrap(new byte[] { b }));
+       }
+
+       /**
+        * Sets a bit active in the bitArray and increments
+        * bit-specific usage counter on disk (but only if
+        * the counter was below 4 bit max (==15)).
+        *
+        * @param index which bit to test
+        */
+
+       protected void incrementBit( int index )
+       {
+               int fileSlot;
+               byte value;
+               int high;
+               int low;
+               int targetLoc;
+
+               setBit(index);
+
+               // update the counter file on disk
+               assert(fd!=null) : "incrementBit with fd == null called!";
+
+               fileSlot = index / 2;
+               targetLoc = index % 2;
+
+               /*fprintf(stderr, "index %d fileSlot %d + %d \n", index, 
fileSlot, targetLoc);*/
+               try {
+                       value=safeRead(fileSlot);
+                       }
+               catch( IOException x ) {
+                       err("I/O exception on bloom filter !",x);
+                       throw new IllegalStateException("I/O exception on bloom 
filter !");
+                       }
+
+               low = value & 0xF;
+               high = (value & (~0xF)) >> 4;
+
+               /* fprintf(stderr, "v %d high %d low %d ", value, high, low);*/
+
+               if (targetLoc == 0) {
+                       if (low < 0xF)
+                               low++;
+               } else {
+                       if (high < 0xF)
+                               high++;
+               }
+               value = (byte) ((high<<4) | low);
+               /*    fprintf(stderr, "nh %d nl %d nv %d\n", high, low, value); 
*/
+
+               try {
+                       safeWrite(fileSlot,value);
+                       }
+               catch( IOException x ) {
+                       err("I/O exception on bloom filter !",x);
+                       throw new IllegalStateException("I/O exception on bloom 
filter !");
+                       }
+       }
+
+       /**
+        * Clears a bit from bitArray if the respective usage
+        * counter on the disk hits/is zero.
+        *
+        * @param index which bit to test
+        */
+
+       protected void decrementBit( int index )
+       {
+               int fileSlot;
+               byte value;
+               int high;
+               int low;
+               int targetLoc;
+
+               assert(fd!=null) : "incrementBit with fd == null called!";
+
+               /* Each char slot in the counter file holds two 4 bit counters 
*/
+               fileSlot = index / 2;
+               targetLoc = index % 2;
+
+               try {
+                       value=safeRead(fileSlot);
+                       }
+               catch( IOException x ) {
+                       err("I/O exception on bloom filter !",x);
+                       throw new IllegalStateException("I/O exception on bloom 
filter !");
+                       }
+
+               low  = value & 0xF;
+               high = (value & 0xF0) >> 4;
+
+               /* decrement, but once we have reached the max, never go back! 
*/
+               if (targetLoc == 0) {
+                       if ( (low > 0) && (low < 0xF) )
+                               low--;
+                       if (low == 0) {
+                               clearBit(index);
+                       }
+               } else {
+                       if ( (high > 0) && (high < 0xF) )
+                               high--;
+                       if (high == 0) {
+                               clearBit(index);
+                       }
+               }
+               value = (byte) ((high<<4) | low);
+
+               try {
+                       safeWrite(fileSlot,value);
+                       }
+               catch( IOException x ) {
+                       err("I/O exception on bloom filter !",x);
+                       throw new IllegalStateException("I/O exception on bloom 
filter !");
+                       }
+       }
+
+       /**
+        * Sets a bit active in the bitArray. Increment bit-specific
+        * usage counter on disk only if below 4bit max (==15).
+        *
+        * @param index which bit to set
+        */
+
+       protected void setBit( int index )
+       {
+               int arraySlot;
+               int targetBit;
+
+               arraySlot = index / 8;
+               targetBit = (1 << (index % 8));
+               bitArray[arraySlot] |= targetBit;
+       }
+
+       /**
+        * Clears a bit from bitArray. Bit is cleared from the array
+        * only if the respective usage counter on the disk hits/is zero.
+        *
+        * @param index which bit to unset
+        */
+
+       protected void clearBit( int index )
+       {
+               int slot;
+               int targetBit;
+
+               slot = index / 8;
+               targetBit = (1 << (index % 8));
+               bitArray[slot] = (byte) (bitArray[slot] & (~targetBit));
+       }
+
+       /**
+        * Call an iterator for each bit that the bloomfilter
+        * must test or set for this element.
+        *
+        * @param callback the method to call
+        * @param arg extra argument to callback
+        * @param key the key for which we iterate over the BF bits
+        */
+
+       protected void iterateBits( BitIterator callback, Object arg, 
HashCode160 key )
+       {
+               HashCode160[]   tmp;
+               int bitCount;
+               int round;
+               int slot=0;
+
+               tmp=new HashCode160[2];
+               tmp[0]=new HashCode160(key);
+               tmp[1]=new HashCode160();
+
+               bitCount = addressesPerElement;
+
+               round = 0;
+               while (bitCount > 0) {
+                       while (slot < (HashCode160.SIZE/4)) {
+                               callback.iterate(tmp[round & 1].getInt(slot) & 
((bitArray.length*8)-1),arg);
+
+                               slot++;
+                               bitCount--;
+                               if (bitCount == 0)
+                                       break;
+                               }
+
+                       if (bitCount > 0) {
+                               tmp[(round+1) & 
1]=HashCode160.create(PersistentHelper.toBytes(tmp[round & 1]),
+                                       0,
+                                       HashCode160.SIZE);
+
+                               round++;
+                               slot = 0;
+                               }
+                       }
+       }
+
+       /**
+        * Creates a file filled with zeroes
+        *
+        * @param size the size of the file
+        * @return true if created ok, false otherwise
+        */
+
+       protected boolean makeEmptyFile( int size )
+       {
+               byte[] buffer;
+
+               if (fd == null)
+                       return false;
+
+               buffer = new byte[BUFFSIZE];
+               Arrays.fill(buffer,(byte) 0);
+
+               try {
+                       fd.position(0);
+                       fd.write(ByteBuffer.wrap(buffer));//,0,size));
+                       }
+               catch( IOException x ) {
+                       err("Could not clear bloom filter !",x);
+                       return false;
+                       }
+               return true;
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       /**
+        * Load a bloom-filter from a file.
+        * @param stats
+        *
+        * @param filename      the name of the file (or the prefix)
+        * @param size          the size of the bloom-filter (number of bytes 
of storage space to use)
+        * @param k                     the number of hash-functions to apply 
per element (number of bits set per element in the set)
+        * @return the bloomfilter
+        */
+
+       public static BloomFilter load( Statistics stats, String filename, int 
size, int k )
+       {
+               BloomFilter bf;
+               byte[] rbuff;
+               int pos;
+               int i;
+               int ui;
+               Logger  logger=Logger.getLogger(BloomFilter.class.getName());
+
+               if (filename==null || k==0 || size==0) {
+                       return null;
+                       }
+
+               if (size<BUFFSIZE)
+                       size=BUFFSIZE;
+
+               ui = 1;
+               while (ui < size)
+                       ui*=2;
+               size = ui;      // make sure it's a power of 2
+
+
+               bf = new BloomFilter();
+               bf.stats=stats;
+
+               // try to open a bloomfilter file
+               try {
+                       bf.fd = new RandomAccessFile(new 
FileLocation(filename).getPath(),"rw").getChannel();
+                       }
+               catch( IOException x ) {
+                       logger.log(Level.SEVERE,"Unable to open "+filename+" 
for writing",x);
+                       return null;
+                       }
+
+               /* Alloc block */
+               bf.lock=new Object();
+               bf.bitArray = new byte[size];
+               bf.addressesPerElement = k;
+               Arrays.fill(bf.bitArray,(byte) 0);
+
+               /* Read from the file what bits we can */
+               rbuff = new byte[BUFFSIZE];
+               pos = 0;
+               while (pos < size*8) {
+                       int res;
+
+                       try {
+                               res = bf.fd.read(ByteBuffer.wrap(rbuff));
+                               }
+                       catch( IOException x ) {
+                               logger.log(Level.SEVERE,"Could not read 
"+filename+" !",x);
+                               res=0;
+                               }
+//todo: cas res==-1
+                       if (res == 0) {
+                               break; /* is ok! we just did not use that many 
bits yet */
+                               }
+
+                       for (i=0;i<res;i++) {
+                               if ( (rbuff[i] & 0x0F) != 0)
+                                       bf.setBit(pos + i*2);
+                               if ( (rbuff[i] & 0xF0) != 0)
+                                       bf.setBit(pos + i*2 + 1);
+                               }
+                       if (res < BUFFSIZE)
+                               break;
+                       pos += BUFFSIZE * 2; /* 2 bits per byte in the buffer */
+                       }
+
+               // create some statistics handles
+
+               filename=new FileLocation(filename).getName();
+
+               bf.hitsStat=bf.stats.getHandle("# Bloomfilter ("+filename+") 
hits",Stat.VERBOSE);
+               bf.missesStat=bf.stats.getHandle("# Bloomfilter ("+filename+") 
misses",Stat.VERBOSE);
+               bf.addsStat=bf.stats.getHandle("# Bloomfilter ("+filename+") 
adds",Stat.VERBOSE);
+               bf.delsStat=bf.stats.getHandle("# Bloomfilter ("+filename+") 
dels",Stat.VERBOSE);
+               return bf;
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       /**
+        * Bloomfilter hash iterator
+        */
+
+       private static interface BitIterator
+       {
+               /**
+                * Iterator (callback) method to be called by the
+                * bloomfilter iterator on each bit that is to be
+                * set or tested for the key.
+                *
+                * @param bit   the current bit
+                * @param arg   context specific argument
+                */
+
+               public void iterate( int bit, Object arg );
+       }
+}

Added: freeway/src/org/gnu/freeway/util/CIDRNetwork.java
===================================================================
--- freeway/src/org/gnu/freeway/util/CIDRNetwork.java   2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/util/CIDRNetwork.java   2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,190 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util;
+
+import java.net.*;
+import java.util.logging.*;
+import java.util.regex.*;
+
+/**
+ * IP network in Classless Inter Domain Routing (CIDR) notation.
+ */
+
+public class CIDRNetwork extends Object
+{
+       private static final Matcher    MATCHER1        =       
Pattern.compile("([0-9]+)\\.([0-9]+)\\.([0-9]+)\\.([0-9]+)/([0-9]+)\\.([0-9]+)\\.([0-9]+)\\.([0-9]+)").matcher("");
+       private static final Matcher    MATCHER2        =       
Pattern.compile("([0-9]+)\\.([0-9]+)\\.([0-9]+)\\.([0-9]+)/([0-9]+)").matcher("");
+
+       private InetAddress     ip;
+       private InetAddress     mask;
+
+
+       public CIDRNetwork()
+       {
+               super();
+               ip=null;
+               mask=null;
+       }
+
+       public String toString()
+       {
+               return "CIDR network [ip="+ip+", mask="+mask+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public InetAddress getIP()
+       {
+               return ip;
+       }
+
+       public InetAddress getMask()
+       {
+               return mask;
+       }
+
+       public String getDescription()
+       {
+               return ip.getHostAddress()+"/"+mask.getHostAddress();
+       }
+
+       /**
+        * test if an IP matches a given subnet
+        * check IP addresses against a blacklist
+        * @param addr
+        * @return
+        */
+
+       public boolean allow( InetAddress addr )
+       {
+               byte[]  b1,b2,b;
+               int             i;
+
+               b1=ip.getAddress();
+               b2=mask.getAddress();
+               b=addr.getAddress();
+
+               if (b1.length!=b2.length || b2.length!=b.length) {
+                       return false;   // mix...
+                       }
+
+               for (i=0; i<b.length; i++) {
+                       if ((b[i] & b2[i])!=(b1[i] & b2[i])) {
+                               return false;
+                               }
+                       }
+               return true;
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public static CIDRNetwork parse( String str )
+       {
+               CIDRNetwork     c;
+               Logger          logger;
+
+               c=parseIPv4(str);
+               if (c==null) {
+                       c=parseIPv6(str);
+                       }
+
+               if (c==null) {
+                       logger=Logger.getLogger(CIDRNetwork.class.getName());
+                       logger.log(Level.WARNING,"Invalid network notation : 
\""+str+"\" (neither IPv4 nor IPv6 notation).");
+                       }
+               return c;
+       }
+
+       protected static CIDRNetwork parseIPv4( String str )
+       {
+               byte[]          b;
+               CIDRNetwork     c;
+               int                     slash,i,n,k;
+
+               b=new byte[8];
+
+               // first notation
+               MATCHER1.reset(str);
+               if (MATCHER1.matches()) {
+                       for (i=0; i<8; i++) {
+                               n=Integer.parseInt(MATCHER1.group(i+1));
+                               if (n<0 || n>255) {
+                                       return null;
+                                       }
+                               b[i]=(byte) n;
+                               }
+
+                       try {
+                               c=new CIDRNetwork();
+                               c.ip=InetAddress.getByAddress(new byte[] { 
b[0], b[1], b[2], b[3] });
+                               c.mask=InetAddress.getByAddress(new byte[] { 
b[4], b[5], b[6], b[7] });
+                               return c;
+                               }
+                       catch( UnknownHostException x ) {
+                               return null;
+                               }
+                       }
+
+               // try second notation
+               MATCHER2.reset(str);
+               if (MATCHER2.matches()) {
+                       for (i=0; i<4; i++) {
+                               n=Integer.parseInt(MATCHER2.group(i+1));
+                               if (n<0 || n>255) {
+                                       return null;
+                                       }
+                               b[i]=(byte) n;
+                               }
+
+                       slash=Integer.parseInt(MATCHER2.group(5));
+                       if (slash<=0 || slash>32) {
+                               return null;
+                               }
+
+                       k=0;
+                       while (slash>0) {
+                               k=((k>>1) | 0x80000000);
+                               slash--;
+                               }
+
+                       try {
+                               c=new CIDRNetwork();
+                               c.ip=InetAddress.getByAddress(new byte[] { 
b[0], b[1], b[2], b[3] });
+                               c.mask=InetAddress.getByAddress(new byte[] { 
(byte) (k>>24), (byte) ((k>>16) & 0x000000ff), (byte) ((k>>8) & 0x000000ff), 
(byte) (k & 0x000000ff) });
+                               return c;
+                               }
+                       catch( UnknownHostException x ) {
+                               return null;
+                               }
+                       }
+               return null;
+       }
+
+       protected static CIDRNetwork parseIPv6( String str )
+       {
+               CIDRNetwork     c;
+               int                     index;
+
+               index=str.indexOf('/');
+
+               try {
+                       c=new CIDRNetwork();
+                       if (index>=0) {
+                               
c.ip=InetAddress.getByName(str.substring(0,index));
+                               
c.mask=InetAddress.getByName(str.substring(index+1));
+                               }
+                       else {
+                               c.ip=InetAddress.getByName(str);
+                               
c.mask=InetAddress.getByName("FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF");
+                               }
+                       return c;
+                       }
+               catch( UnknownHostException x ) {
+                       }
+               return null;
+       }
+}

Added: freeway/src/org/gnu/freeway/util/CIDRNetworks.java
===================================================================
--- freeway/src/org/gnu/freeway/util/CIDRNetworks.java  2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/util/CIDRNetworks.java  2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,125 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util;
+
+import java.net.*;
+import java.util.*;
+
+/**
+ * A list of networks.
+ */
+
+public class CIDRNetworks extends Object
+{
+       private CIDRNetwork[]   networks;
+
+
+       protected CIDRNetworks( List ntw )
+       {
+               super();
+               networks=(CIDRNetwork[]) ntw.toArray(new 
CIDRNetwork[ntw.size()]);
+       }
+
+       public String toString()
+       {
+               return "CIDR networks [networks="+Arrays.asList(networks)+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public String getDescription()
+       {
+               StringBuffer    buf;
+               int                             i;
+
+               buf=new StringBuffer();
+               buf.append("(");
+               for (i=0; i<networks.length; i++) {
+                       buf.append(" ");
+                       buf.append(networks[i].getDescription());
+                       if (i<networks.length-1) {
+                               buf.append(",");
+                               }
+                       }
+               buf.append(" )");
+               return buf.toString();
+       }
+
+       /**
+        * Check if the given IP address is in the list of IP addresses.
+        *
+        * @param ip    the IP to check
+        * @return              true if the IP is in the list, false otherwise
+        */
+
+       public boolean check( InetAddress ip )
+       {
+               int     i;
+
+               for (i=0; i<networks.length; i++) {
+                       if (networks[i].allow(ip)) {
+                               return true;
+                               }
+                       }
+               return false;
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       /**
+        * Parse a network specification. The argument specifies
+        * a list of networks. The format is
+        * <tt>[network/netmask;]*</tt> (no whitespace, must be terminated
+        * with a semicolon). The network must be given in colon-hex
+        * notation.  The netmask must be given in CIDR notation (/16) or
+        * can be omitted to specify a single host.
+        * <p>
+        * The network must be given in dotted-decimal
+        * notation. The netmask can be given in CIDR notation (/16) or
+        * in dotted-decimal (/255.255.0.0).
+        * <p>
+        *
+        * Parse a network specification. The argument specifies
+        * a list of networks. The format is
+        * <tt>[network/netmask;]*</tt> (no whitespace, must be terminated
+        * with a semicolon). The network must be given in dotted-decimal
+        * notation. The netmask can be given in CIDR notation (/16) or
+        * in dotted-decimal (/255.255.0.0).
+        * <p>
+        * Parse a network specification. The argument specifies
+        * a list of networks. The format is
+        * <tt>[network/netmask;]*</tt> (no whitespace, must be terminated
+        * with a semicolon). The network must be given in dotted-decimal
+        * notation. The netmask can be given in CIDR notation (/16) or
+        * in dotted-decimal (/255.255.0.0).
+        * <p>
+        *
+        * @param str   a string specifying the networks
+        * @return              the converted list, null if the syntax is flawed
+        */
+
+       public static CIDRNetworks parse( String str )
+       {
+               String[]        p;
+               CIDRNetwork     c;
+               List            list;
+               int                     i;
+
+               list=new ArrayList();
+
+               p=str.split("[\\s;]+");
+               for (i=0; i<p.length; i++) {
+                       if (p[i].length()>0) {
+                               c=CIDRNetwork.parse(p[i]);
+                               if (c!=null) {
+                                       list.add(c);
+                                       }
+                               }
+                       }
+               return new CIDRNetworks(list);
+       }
+}

Added: freeway/src/org/gnu/freeway/util/ConfigurationParser.java
===================================================================
--- freeway/src/org/gnu/freeway/util/ConfigurationParser.java   2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/util/ConfigurationParser.java   2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,290 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util;
+
+import org.gnu.freeway.util.io.*;
+
+import java.io.*;
+import java.net.*;
+import java.nio.*;
+import java.nio.charset.*;
+import java.util.*;
+import java.util.logging.*;
+import java.util.regex.*;
+
+/**
+ * Parser for configuration files. Section names are considered 
case-insensitive and stored in uppercase.
+ */
+
+public class ConfigurationParser extends LoggedObject
+{
+       private static final String     LIT             =       
"0-9A-Za-z\\\\\\.\\u0021\\u0025\\u0026\\u002a\\u002b\\u002c\\u002d\\u002e\\u002f\\u003b\\u003f\\u0040\\u005e\\u005f\\u007c\\u007e0";
+       private static final String     STR1            =       
"'([^'\\\\]*(\\\\.)?)*'";
+       private static final String     STR2            =       
"\"([^\"\\\\]*(\\\\.)?)*\"";
+       private static final String     STR3            =       
"`([^`\\\\]*(\\\\.)?)*`";
+       private static final String     STR4            =       
"["+LIT+"\\$\\:\\-]+";
+
+       private Map             sections;
+       private Matcher inlineMatcher;
+       private Matcher sectionMatcher;
+       private Matcher assignMatcher;
+
+
+       public ConfigurationParser()
+       {
+               super(true);
+               sections=new HashMap();
+               
inlineMatcher=Pattern.compile("@address@hidden(["+LIT+"]+)").matcher("");
+               
sectionMatcher=Pattern.compile("\\[\\s*(["+LIT+"]+(\\s+["+LIT+"]+)*)\\s*\\]\\s*").matcher("");
+               
assignMatcher=Pattern.compile("(["+LIT+"]+)\\s*=((\\s*("+STR1+"|"+STR2+"|"+STR3+"|"+STR4+"))+)").matcher("");
+       }
+
+       public String toString()
+       {
+               return "Configuration parser [sections="+sections+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public Map getSections()
+       {
+               return Collections.unmodifiableMap(sections);
+       }
+
+       protected Map findSection( String name )
+       {
+               Map     m;
+
+               name=name.toUpperCase();
+
+               m=(Map) sections.get(name);
+               if (m==null) {
+                       m=new HashMap();
+                       sections.put(name,m);
+                       }
+               return m;
+       }
+
+       public boolean load( String path, Charset cs )
+       {
+               URL                             url;
+               FileLocation            f;
+
+               sections.clear();
+
+               url=getClass().getClassLoader().getResource(path);
+               if (url!=null) {
+                       return load(url,cs);
+                       }
+
+               f=new FileLocation(path);
+               if (f.exists()) {
+                       return load(f.asURL(),cs);
+                       }
+               log(Level.WARNING,"Unable to load \""+path+"\" !");
+               return false;
+       }
+
+       protected synchronized boolean load( URL url, Charset cs )
+       {
+               ParseContext    ctx;
+               BufferedReader  r;
+               StringBuffer    buf;
+               String                  str;
+
+               ctx=new ParseContext(url,cs);
+
+               try {
+                       r=new BufferedReader(new 
InputStreamReader(url.openStream(),cs));
+                       try {
+                               buf=new StringBuffer();
+
+                               for (str=r.readLine(); str!=null; 
str=r.readLine()) {
+                                       str=str.trim();
+                                       buf.append(str);
+
+                                       // extract next logical line by 
concatenating lines ended by '\'
+                                       if (str.endsWith("\\") && 
!str.endsWith("\\\\")) {
+                                               buf.setLength(buf.length()-1);
+                                               }
+                                       else {
+                                               parseLine(ctx,buf.toString());
+                                               buf.setLength(0);
+                                               }
+
+                                       ctx.currentLine++;
+                                       }
+
+                               if (buf.length()>0) {
+                                       parseLine(ctx,buf.toString());
+                                       buf.setLength(0);
+                                       }
+                               return true;
+                               }
+                       finally {
+                               r.close();
+                               }
+                       }
+               catch( IOException x ) {
+                       err("Could not read '"+url.getPath()+"'.",x);
+                       }
+               return false;
+       }
+
+       protected void parseLine( ParseContext ctx, String str )
+       {
+               char[]  line;
+               int             i;
+
+               line=str.toCharArray();
+
+               i=0;
+               while (i<line.length) {
+                       // skip spaces
+                       while (i<line.length && 
Character.isWhitespace(line[i])) {
+                               i++;
+                               }
+
+                       // empty line
+                       if (i==line.length) {
+                               return;
+                               }
+
+                       // comment line
+                       if (line[i]=='#' || line[i]=='%') {
+                               return;
+                               }
+
+                       // @INLINE@ directive
+                       
inlineMatcher.reset(CharBuffer.wrap(line,i,line.length-i));
+                       if (inlineMatcher.lookingAt()) {
+                               i+=inlineMatcher.end();
+
+                               str=inlineMatcher.group(1);
+                               if (!load(str,ctx.charset)) {
+                                       log(Level.WARNING,"Inlined 
configuration \""+str+"\" does not exist.");
+                                       }
+                               continue;
+                               }
+
+                       // [SECTION]
+                       
sectionMatcher.reset(CharBuffer.wrap(line,i,line.length-i));
+                       if (sectionMatcher.lookingAt()) {
+                               i+=sectionMatcher.end();
+
+                               
ctx.currentSection=findSection(resolve(sectionMatcher.group(1)));
+                               continue;
+                               }
+
+                       // foo = bar
+                       
assignMatcher.reset(CharBuffer.wrap(line,i,line.length-i));
+                       if (assignMatcher.lookingAt()) {
+                               i+=assignMatcher.end();
+
+                               if (ctx.currentSection==null) {
+                                       ctx.currentSection=findSection("");
+                                       }
+
+                               
ctx.currentSection.put(assignMatcher.group(1),resolve(assignMatcher.group(2)));
+                               continue;
+                               }
+
+                       // huh ?
+                       
log(Level.SEVERE,ctx.url.getPath()+":"+ctx.currentLine+": syntax error");
+                       return;
+                       }
+       }
+
+       protected String resolve( String line )
+       {
+               return resolve(line.toCharArray());
+       }
+
+       protected String resolve( char[] line )
+       {
+               StringBuffer    buf;
+               int                             i;
+               char                    c;
+
+               buf=new StringBuffer();
+
+               i=0;
+               while (i<line.length) {
+                       // skip spaces
+                       while (i<line.length && 
Character.isWhitespace(line[i])) {
+                               i++;
+                               }
+                       if (i==line.length) {
+                               break;
+                               }
+
+                       if (buf.length()>0) {
+                               buf.append(" ");        // concatenate multiple 
terms with space
+                               }
+
+                       c=line[i];
+                       if (c=='"' || c=='\'' || c=='`') {
+                               i++;
+                               while (i<line.length && line[i]!=c) {
+                                       i=appendChar(buf,line,i);
+                                       }
+                               if (i<line.length && line[i]==c) {
+                                       i++;
+                                       }
+                               }
+                       else {
+                               while (i<line.length && 
!(Character.isWhitespace(line[i]) || line[i]=='"' || line[i]=='\'' || 
line[i]=='`')) {
+                                       i=appendChar(buf,line,i);
+                                       }
+                               }
+                       }
+               return buf.toString();
+       }
+
+       protected int appendChar( StringBuffer buf, char[] line, int i )
+       {
+               char    c;
+
+               if (line[i]=='\\') {
+                       i++;
+                       if (i<line.length) {
+                               c=line[i++];
+                               switch (c) {
+                                       case 'r': buf.append('\r'); break;
+                                       case 'n': buf.append('\n'); break;
+                                       case 'b': buf.append('\b'); break;
+                                       case 't': buf.append('\t'); break;
+                                       default: buf.append(c); break;
+                                       }
+                               }
+                       }
+               else {
+                       buf.append(line[i++]);
+                       }
+               return i;
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       private static class ParseContext extends Object
+       {
+               private URL             url;
+               private Charset charset;
+               private int             currentLine;
+               private Map             currentSection;
+
+
+               private ParseContext( URL u, Charset cs )
+               {
+                       super();
+                       url=u;
+                       charset=cs;
+                       currentLine=1;
+                       currentSection=null;
+               }
+       }
+}

Added: freeway/src/org/gnu/freeway/util/ConsoleFormatter.java
===================================================================
--- freeway/src/org/gnu/freeway/util/ConsoleFormatter.java      2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/util/ConsoleFormatter.java      2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,61 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util;
+
+import java.text.*;
+import java.util.*;
+import java.util.logging.*;
+
+/**
+ * A log record formatter.
+ *
+ * @see java.util.logging.Logger
+ * @see java.util.logging.LogRecord
+ */
+
+public class ConsoleFormatter extends AbstractLogFormatter
+{
+       private boolean         printStamp;
+       private DateFormat      dateFormatter;
+
+
+       public ConsoleFormatter()
+       {
+               this(true);
+       }
+
+       public ConsoleFormatter( boolean stamp )
+       {
+               super();
+               printStamp=stamp;
+               
dateFormatter=DateFormat.getDateTimeInstance(DateFormat.SHORT,DateFormat.MEDIUM,Locale.getDefault());
+       }
+
+       public String toString()
+       {
+               return "Console (log) formatter [printStamp="+printStamp+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public void format( LogRecord record, StringBuffer buf )
+       {
+               if (printStamp) {
+                       buf.append(dateFormatter.format(new 
Date(record.getMillis())));
+                       buf.append(" ");
+                       }
+
+               buf.append("[");
+               buf.append(record.getLevel().getName());
+               buf.append(",");
+               buf.append(record.getThreadID());
+               buf.append(",");
+               
addFrom(record.getSourceClassName(),record.getSourceMethodName(),buf);
+               buf.append("] ");
+               addMessage(record.getMessage(),buf);
+               addException(record.getThrown(),buf);
+       }
+}

Added: freeway/src/org/gnu/freeway/util/Constant.java
===================================================================
--- freeway/src/org/gnu/freeway/util/Constant.java      2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/util/Constant.java      2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,99 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util;
+
+import java.io.*;
+import java.util.*;
+
+/**
+ * An immutable value, that implements the singleton pattern.
+ */
+
+public class Constant extends Object implements Serializable, Comparable
+{
+       private static Map      repository;
+
+       static {
+               repository=new HashMap();
+               }
+
+       private int             value;
+       private String  label;
+
+
+       protected Constant( String str )
+       {
+               super();
+               value=-1;
+               label=str;
+       }
+
+       public String toString()
+       {
+               return "Constant [value="+value+", label="+label+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int hashCode()
+       {
+               return value;
+       }
+
+       public boolean equals( Object obj )
+       {
+               return (obj==this);
+       }
+
+       public int compareTo( Object obj )
+       {
+               int     v;
+
+               if (obj==null || !obj.getClass().equals(getClass())) {
+                       return 1;
+                       }
+
+               v=((Constant) obj).value;
+               return (value>v ? 1 : (value<v ? -1 : 0));
+       }
+
+       protected Object readResolve()
+       {
+               return ((Constant[]) repository.get(getClass()))[value];
+       }
+
+       public int getValue()
+       {
+               return value;
+       }
+
+       public String getLabel()
+       {
+               return label;
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       protected static void init( Constant[] values )
+       {
+               int     i;
+
+               assert(values!=null && values.length>0);
+
+               for (i=0; i<values.length; i++) {
+                       assert(values[i]!=null && 
values[i].getClass().equals(values[0].getClass()));
+                       values[i].value=i;
+                       }
+
+               repository.put(values[0].getClass(),values);
+       }
+
+       public static Constant[] getValues( Class clazz )
+       {
+               return (Constant[]) repository.get(clazz);
+       }
+}

Added: freeway/src/org/gnu/freeway/util/DurationFormatter.java
===================================================================
--- freeway/src/org/gnu/freeway/util/DurationFormatter.java     2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/util/DurationFormatter.java     2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,65 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util;
+
+/**
+ * Format durations given in cron units.
+ */
+
+public class DurationFormatter extends AbstractFormatter
+{
+       private static final long       ONE_SECOND      =       1000;
+       private static final long       ONE_MINUTE      =       ONE_SECOND*60;
+       private static final long       ONE_HOUR        =       ONE_MINUTE*60;
+       private static final long       ONE_DAY         =       ONE_HOUR*24;
+
+
+       public DurationFormatter()
+       {
+               super();
+       }
+
+       protected String formatObject( Object obj ) throws Throwable
+       {
+               StringBuffer    buf;
+               long                    n;
+
+               buf=new StringBuffer();
+               if (obj!=null && (obj instanceof Number)) {
+                       n=Scheduler.toMillis(((Number) obj).longValue());
+
+                       if (n!=0) {
+                               if (n>=ONE_DAY) {
+                                       buf.append(n/ONE_DAY);
+                                       buf.append("day ");
+                                       n=(n % ONE_DAY);
+                                       }
+                               if (n>=ONE_HOUR) {
+                                       buf.append(n/ONE_HOUR);
+                                       buf.append("hour ");
+                                       n=(n % ONE_HOUR);
+                                       }
+                               if (n>=ONE_MINUTE) {
+                                       buf.append(n/ONE_MINUTE);
+                                       buf.append("mn ");
+                                       n=(n % ONE_MINUTE);
+                                       }
+                               if (n>=ONE_SECOND) {
+                                       buf.append(n/ONE_SECOND);
+                                       buf.append("s ");
+                                       n=(n % ONE_SECOND);
+                                       }
+                               if (n>0) {
+                                       buf.append(n);
+                                       buf.append("ms");
+                                       }
+                               }
+                       else {
+                               buf.append("0s");
+                               }
+                       }
+               return buf.toString();
+       }
+}

Added: freeway/src/org/gnu/freeway/util/DynamicLibrary.java
===================================================================
--- freeway/src/org/gnu/freeway/util/DynamicLibrary.java        2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/util/DynamicLibrary.java        2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,137 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+import java.util.jar.*;
+import java.util.logging.*;
+import java.util.zip.*;
+
+/**
+ * Support for loading dynamic libraries. Implemented interfaces test may be 
enforced on main class of this library.
+ */
+
+public class DynamicLibrary extends LoggedObject
+{
+       private File    library;
+
+
+       public DynamicLibrary( String str )
+       {
+               super(true);
+               library=new File(str);
+       }
+
+       public String toString()
+       {
+               return "Dynamic library [library="+library+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public Object load( Class interfaze )
+       {
+               return load(interfaze,getClass().getClassLoader());
+       }
+
+       public Object load( Class interfaze, ClassLoader loader )
+       {
+               Class   c;
+               Object  obj;
+
+               obj=null;
+               c=null;
+               try {
+                       c=getMainClass(library,loader);
+                       if (c==null) {
+                               log(Level.SEVERE,"No main class found in 
library "+library.getName()+".");
+                               }
+                       else if (interfaze.isAssignableFrom(c)) {
+                               obj=c.newInstance();
+                               }
+                       else {
+                               log(Level.SEVERE,"Main class for library 
"+library.getName()+" does not implement interface "+interfaze.getName()+".");
+                               }
+                       }
+               catch( IllegalAccessException x ) {
+                       err("Could not instantiate class "+c.getName()+" !",x);
+                       }
+               catch( InstantiationException x ) {
+                       err("Could not instantiate class "+c.getName()+" !",x);
+                       }
+               catch( IOException x ) {
+                       err("Could not open library "+library.getName()+" !",x);
+                       }
+               catch( ClassNotFoundException x ) {
+                       err("Could not open library "+library.getName()+" !",x);
+                       }
+               return obj;
+       }
+
+       protected Class getMainClass( File f, ClassLoader loader ) throws 
IOException, ClassNotFoundException
+       {
+               JarFile         jar;
+               Manifest        mf;
+               Class           c;
+               String          str;
+
+               c=null;
+
+               jar=new JarFile(f,true,ZipFile.OPEN_READ);
+               try {
+                       mf=jar.getManifest();
+                       if (mf!=null) {
+                               str=(String) 
mf.getMainAttributes().get(Attributes.Name.MAIN_CLASS);
+                               if (str!=null) {
+                                       loader=createClassLoader(new String[] { 
//fixme: should use java.library.path and other vars ??
+                                               ".",
+                                               "/usr/lib",
+                                               "/usr/local/lib"
+                                               },loader);
+                                       c=loader.loadClass(str);
+                                       }
+                               }
+                       }
+               finally {
+                       jar.close();
+                       }
+               return c;
+       }
+
+       protected ClassLoader createClassLoader( String[] paths, ClassLoader 
loader )
+       {
+               URL[]   urls;
+               List    files;
+               File    file;
+               int             i;
+
+               files=new ArrayList();
+
+               for (i=0; i<paths.length; i++) {
+                       file=new File(paths[i]);
+                       if (file.exists() && file.isDirectory()) {
+                               files.addAll(Arrays.asList(file.listFiles(new 
FileFilter() {
+                                       public boolean accept( File f )
+                                       {
+                                               return 
f.getPath().endsWith(".jar");
+                                       }
+                                       })));
+                               }
+                       }
+
+               urls=new URL[files.size()];
+               for (i=0; i<urls.length; i++) {
+                       try {
+                               urls[i]=((File) files.get(i)).toURL();
+                               }
+                       catch( MalformedURLException x ) {
+                               }
+                       }
+               return URLClassLoader.newInstance(urls,loader);
+       }
+}

Added: freeway/src/org/gnu/freeway/util/EvalAction.java
===================================================================
--- freeway/src/org/gnu/freeway/util/EvalAction.java    2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/util/EvalAction.java    2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,82 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util;
+
+import java.lang.reflect.*;
+import java.util.logging.*;
+
+/**
+ *
+ */
+
+public class EvalAction extends AbstractAction
+{
+       private Object          target;
+       private String          method;
+       private Class[]         argTypes;
+       private Object[]        args;
+
+
+       public EvalAction( Object obj, String meth )
+       {
+               super("'"+meth+"' on "+obj);
+               target=obj;
+               method=meth;
+               argTypes=null;
+               args=null;
+       }
+
+       public EvalAction( String nm, Object obj, String meth )
+       {
+               super(nm);
+               target=obj;
+               method=meth;
+               argTypes=null;
+               args=null;
+       }
+
+       //todo: faire avec les types/non object
+       public EvalAction( Object obj, String meth, Class c, Object param )
+       {
+               this(obj,meth);
+               //todo: vérifier matching param/class param
+               argTypes=new Class[] { c };
+               args=new Object[] { param };
+       }
+
+       public String toString()
+       {
+               return "Eval. action [target="+target+", method="+method+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*     public String getName()
+       {
+               return "'"+method+"' on "+target;
+       }
+*/
+       public void perform() throws Throwable
+       {
+               Method  m;
+
+               try {
+                       m=target.getClass().getMethod(method,argTypes);
+//                     m=target.getClass().getDeclaredMethod(method,argTypes);
+                       if (m!=null) {
+                               try {
+                                       m.invoke(target,args);
+                                       }
+                               catch( InvocationTargetException xx ) {
+                                       throw xx.getCause();
+                                       }
+                               }
+                       }
+               catch( NoSuchMethodException x ) {
+                       log(Level.WARNING,"No public method \""+method+"\" on 
class "+target.getClass().getName()+" !");
+                       }
+       }
+}

Added: freeway/src/org/gnu/freeway/util/FileFormatter.java
===================================================================
--- freeway/src/org/gnu/freeway/util/FileFormatter.java 2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/util/FileFormatter.java 2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,72 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util;
+
+import java.text.*;
+import java.util.*;
+import java.util.logging.*;
+
+/**
+ * A log record formatter.
+ *
+ * @see java.util.logging.Logger
+ * @see java.util.logging.LogRecord
+ */
+
+public class FileFormatter extends AbstractLogFormatter
+{
+       private Locale          locale;
+       private DateFormat      dateFormatter;
+
+
+       public FileFormatter()
+       {
+               this(Locale.getDefault());
+       }
+
+       public FileFormatter( Locale loc )
+       {
+               super();
+               locale=loc;
+               
dateFormatter=DateFormat.getDateTimeInstance(DateFormat.SHORT,DateFormat.MEDIUM,loc);
+       }
+
+       public String toString()
+       {
+               return "File (log) formatter [locale="+locale+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public String getHead( Handler hd )
+       {
+               StringBuffer    buf;
+
+               buf=new StringBuffer();
+               buf.append("STAMP          [ LEVEL THREAD SRC ]    MESSAGE      
                                                                      "+EOL);
+               
buf.append("--------------------------------------------------------------------------------------------------------------------------"+EOL);
+               return buf.toString();
+       }
+
+       public String getTail( Handler hd )
+       {
+               return EOL+EOL;
+       }
+
+       public void format( LogRecord record, StringBuffer buf )
+       {
+               buf.append(dateFormatter.format(new Date(record.getMillis())));
+               buf.append(" [");
+               buf.append(record.getLevel().getName());
+               buf.append(",");
+               buf.append(record.getThreadID());
+               buf.append(",");
+               
addFrom(record.getSourceClassName(),record.getSourceMethodName(),buf);
+               buf.append("] ");
+               addMessage(record.getMessage(),buf);
+               addException(record.getThrown(),buf);
+       }
+}

Added: freeway/src/org/gnu/freeway/util/IdentityService.java
===================================================================
--- freeway/src/org/gnu/freeway/util/IdentityService.java       2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/util/IdentityService.java       2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,222 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util;
+
+import java.net.*;
+import java.util.*;
+import java.util.logging.*;
+
+/**
+ * Determine the (external) IP of the local machine.
+ *
+ * We have many ways to get that IP:
+ * a) from the interface (ifconfig)
+ * b) via DNS from our HOSTNAME (environment)
+ * c) from the configuration (HOSTNAME specification or static IP)
+ *
+ * Which way applies depends on the OS, the configuration
+ * (dynDNS? static IP? NAT?) and at the end what the user
+ * needs.
+ *
+ * code to determine the IP of the local machine
+ * Determine the IP of the local machine. We have many
+ * ways to get that IP:
+ * a) from the interface (ifconfig)
+ * b) via DNS from our HOSTNAME (environment)
+ * c) from the configuration (HOSTNAME specification or static IP)
+ *
+ * Which way applies depends on the OS, the configuration
+ * (dynDNS? static IP? NAT?) and at the end what the user
+ * needs.
+ *
+ * Todo:
+ * * scanning of network devices for IPv6 (first: find good
+ *   API to do it, doesn't seem to exist!)
+ */
+
+public class IdentityService extends AbstractService
+{
+       private Prefs   prefs;
+       /** Our current IP address. */
+       private InetAddress                             myAddress;
+       private ScheduledTask           refreshTask;
+
+
+       public IdentityService()
+       {
+               super("Identity");
+               setDebug(true);
+
+               refreshTask=new ScheduledTask("REFRESH-IP",new 
EvalAction(this,"cronRefreshAddress"),Scheduler.MINUTES_2);
+       }
+
+       public String toString()
+       {
+               return "Identity service";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public void attach( ServiceManager mgr )
+       {
+               super.attach(mgr);
+               prefs=mgr.app().getPreferences();
+       }
+
+       /**
+        * Initialize identity module. Requires configuration.
+        */
+
+       public void init()
+       {
+               Scheduler       scheduler;
+
+               super.init();
+               myAddress=getAddress();
+               if (myAddress == null) {
+                       trace("Could not find IP for this host. Please provide 
the IP in the configuration file.");
+                       return;
+                       }
+               scheduler=getManager().app().getScheduler();
+               scheduler.addJob(refreshTask,Scheduler.MINUTES_2);
+       }
+
+       /**
+        * Shutdown identity module.
+        */
+
+       public void done()
+       {
+               Scheduler       scheduler;
+
+               super.done();
+
+               scheduler=getManager().app().getScheduler();
+               scheduler.deleteJob(refreshTask);
+       }
+
+       /**
+        * Obtain the identity information for the current node
+        * (connection information), conInfo.
+        * @return false on failure, true on success
+        */
+
+       protected InetAddress getAddressFromHostname()
+       {
+               try {
+                       return InetAddress.getLocalHost();
+                       }
+               catch( UnknownHostException x ) {
+                       err("Could not obtain IP for localhost !",x);
+                       return null;
+                       }
+       }
+
+       protected InetAddress getAddressFromIOCTL()
+       {
+               String  interfaces;
+               NetworkInterface        ni;
+               Enumeration                     enum,enum2;
+               InetAddress                     ip;
+
+               interfaces = prefs.getString("NETWORK","INTERFACE",null);
+               if (interfaces == null) {
+                       log(Level.SEVERE,"No interface specified in section 
NETWORK under INTERFACE !");
+                       return null;    // that won't work!
+                       }
+
+               try {
+                       // first, try to find exactly matching interface
+                       enum=NetworkInterface.getNetworkInterfaces();
+                       while (enum.hasMoreElements()) {
+                               ni=(NetworkInterface) enum.nextElement();
+                               if (ni.getName().equals(interfaces)) {
+                                       enum2=ni.getInetAddresses();
+                                       if (enum2.hasMoreElements()) {
+                                               //todo: laquelle prendre ???
+                                               ip=(InetAddress) 
enum2.nextElement();
+                                               return ip;
+                                               }
+
+                                       return null;
+                                       }
+                               }
+
+                       log(Level.WARNING,"Could not find interface 
"+interfaces+" in IOCTL, trying to find another one.");
+
+                       // if no such interface exists, take any interface but 
loopback
+                       enum=NetworkInterface.getNetworkInterfaces();
+                       while (enum.hasMoreElements()) {
+                               ni=(NetworkInterface) enum.nextElement();
+                               if 
(!ni.getName().toLowerCase().startsWith("lo")) {
+                                       enum2=ni.getInetAddresses();
+                                       if (enum2.hasMoreElements()) {
+                                               //todo: laquelle prendre ???
+                                               ip=(InetAddress) 
enum2.nextElement();
+                                               return ip;
+                                               }
+
+                                       return null;
+                                       }
+                               }
+                       }
+               catch( SocketException x ) {
+                       err("Could not obtain IP with ioctl",x);
+                       }
+
+
+               log(Level.WARNING,"Could not obtain IP for interface 
"+interfaces+" using ioctl.");
+               return null;
+       }
+
+       /**
+        * Get the IP address for the local machine.
+        * @return false on error, true on success
+        */
+
+       protected InetAddress getAddress()
+       {
+               String  ipString;
+               InetAddress     ip;
+
+               ipString = prefs.getString("NETWORK","IP",null);//todo: "IP6" 
utilisé ?
+               if (ipString == null) {
+                       ip=getAddressFromIOCTL();
+                       if ( ip==null)
+                               ip = getAddressFromHostname();
+                       }
+               else {
+                       log(Level.FINEST,"Obtaining local IP address from 
hostname "+ipString+".");
+                       try {
+                               ip = InetAddress.getByName(ipString);
+                               }
+                       catch( UnknownHostException x ) {
+                               err("Couldn't resolve '"+ipString+"'",x);
+                               return null;
+                               }
+                       }
+               return ip;
+       }
+
+       public void cronRefreshAddress()
+       {
+               log(Level.FINE,"enter cronRefreshAddress");
+               myAddress=getAddress();
+               if (myAddress == null)
+                       log(Level.WARNING,"Could not determine IP address of 
the local machine !");
+               log(Level.FINE,"exit cronRefreshAddress");
+       }
+
+       /**
+        * Get the IP address for the local machine.
+        * @return false on error, true on success
+        */
+
+       public InetAddress getPublicIPAddress()
+       {
+               return (isInitialized() ? myAddress : null);
+       }
+}

Added: freeway/src/org/gnu/freeway/util/LoggedObject.java
===================================================================
--- freeway/src/org/gnu/freeway/util/LoggedObject.java  2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/util/LoggedObject.java  2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,205 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util;
+
+import org.gnu.freeway.util.io.*;
+
+import java.io.*;
+import java.nio.charset.*;
+import java.util.logging.*;
+import java.util.regex.*;
+
+/**
+ * Basic logging mechanism.
+ * Basic logging mechanisms, with log-levels, logging to file or stderr and 
with or without time-prefixing.
+ */
+
+public abstract class LoggedObject extends Object
+{
+       /** Internal logger. */
+       private Logger  logger;
+
+       /** Should we print debug information ? */
+       private boolean debug;
+
+
+       protected LoggedObject( boolean flag )
+       {
+               super();
+               logger=Logger.getLogger(getClass().getName());
+               debug=flag;
+       }
+
+       public String toString()
+       {
+               return "Logged object [debug="+debug+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public boolean isDebug()
+       {
+               return debug;
+       }
+
+       public void setDebug( boolean flag )
+       {
+               debug=flag;
+       }
+
+       protected void debug( String str )
+       {
+               debug(Level.FINEST,str);
+       }
+
+       protected void debug( Level level, String str )
+       {
+               if (debug) {
+                       log(level,"(DEBUG) "+str);
+                       }
+       }
+
+       protected void log( String str )
+       {
+               log(Level.FINEST,str);
+       }
+
+       protected void log( Level level, String str )
+       {
+               LogRecord       rec;
+
+               rec=new LogRecord(level,str);
+               rec.setSourceClassName(getClass().getName());
+               logger.log(rec);
+       }
+
+       public void trace( String str )
+       {
+               err(str,new Throwable("At"));
+       }
+
+       protected void err( String str, Throwable x )
+       {
+               LogRecord       rec;
+
+               rec=new LogRecord(Level.SEVERE,str);
+               rec.setSourceClassName(getClass().getName());
+               rec.setThrown(x);
+               logger.log(rec);
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public static void sendLogsToConsole( Level minLevel )
+       {
+               Handler h;
+               Logger  logger;
+
+               flushLogs();
+
+               h=new ConsoleHandler();
+               h.setFormatter(new StarFormatter());
+               h.setLevel(minLevel);
+
+               logger=Logger.getLogger("");
+               logger.setLevel(minLevel);
+               logger.setUseParentHandlers(false);
+               logger.addHandler(h);
+       }
+
+       public static boolean sendLogsToFile( Level minLevel, FileLocation f )
+       {
+               Handler h;
+               Logger  logger;
+
+               flushLogs();
+
+               if (!f.create()) {
+                       System.err.println("Could not create log file at 
\""+f.getLabel()+"\" !");
+                       return false;
+                       }
+
+               try {
+                       h=new FileHandler(f.getPath(),true);
+                       h.setFormatter(new FileFormatter());
+                       h.setLevel(minLevel);
+
+                       logger=Logger.getLogger("");
+                       logger.setLevel(minLevel);
+                       logger.setUseParentHandlers(false);
+                       logger.addHandler(h);
+                       return true;
+                       }
+               catch( IOException x ) {
+                       System.err.println("Could not create log file at 
\""+f.getLabel()+"\" !");
+                       x.printStackTrace(System.err);
+                       }
+               return false;
+       }
+
+       public static boolean setUpLogLevels( FileLocation f )
+       {
+               char[]          line;
+               Matcher         matcher;
+               MappedFile      m;
+               LineDecoder     d;
+               Logger          logger;
+               String          str;
+               int                     len;
+
+               if (!f.exists()) {
+                       System.err.println("File \""+f.getLabel()+"\" does not 
exist !");
+                       return false;
+                       }
+
+               line=new char[512];
+               
matcher=Pattern.compile("^\\s*([\\w.]*)\\s*=\\s*([A-Z]+)\\s*$").matcher("");
+
+               m=f.open();
+               try {
+                       d=new 
LineDecoder(m.asText(Charset.forName("UTF-8")),line);
+                       for (len=d.decode(); len>=0; len=d.decode()) {
+                               str=new String(line,0,len);
+
+                               matcher.reset(str);
+                               if (matcher.matches()) {
+                                       
logger=Logger.getLogger(matcher.group(1));
+                                       
logger.setLevel(Level.parse(matcher.group(2)));
+                                       }
+                               }
+                       }
+               finally {
+                       m.close();
+                       }
+               return true;
+       }
+
+       /**
+        * Flush remaining messages and close all log handlers.
+        */
+
+       public static void flushLogs()
+       {
+               Handler[]       hd;
+               Logger          logger;
+               int                     i;
+
+               logger=Logger.getLogger("");
+               logger.setLevel(Level.FINEST);
+               logger.setUseParentHandlers(false);
+
+               hd=logger.getHandlers();
+               for (i=0; i<hd.length; i++) {
+                       logger.removeHandler(hd[i]);
+                       hd[i].flush();
+                       hd[i].close();
+                       }
+
+               System.out.flush();
+               System.err.flush();
+       }
+}

Added: freeway/src/org/gnu/freeway/util/MasterTask.java
===================================================================
--- freeway/src/org/gnu/freeway/util/MasterTask.java    2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/util/MasterTask.java    2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,79 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util;
+
+import java.util.*;
+import java.util.logging.*;
+
+/**
+ *
+ */
+
+public class MasterTask extends Task
+{
+       //todo: utiliser un CoundtDown pour le shutdown
+       private List            subtasks;
+
+
+       public MasterTask( String str, Action a )
+       {
+               super(str,a);
+               subtasks=new ArrayList();
+       }
+
+       public String toString()
+       {
+               return "Master task [name="+name+", action="+action+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public SlaveTask create( String str, Action a )
+       {
+               SlaveTask       t;
+
+               if (isRunning()) {
+                       log(Level.WARNING,"Task is running, could not create 
sub-task !");
+                       return null;
+                       }
+
+               t=new SlaveTask(str,a);
+               subtasks.add(t);
+               return t;
+       }
+
+       public boolean launch()
+       {
+               SlaveTask       t;
+               int                     i;
+
+               if (isRunning()) {
+                       return false;
+                       }
+
+               for (i=0; i<subtasks.size(); i++) {
+                       t=(SlaveTask) subtasks.get(i);
+                       t.launch();
+                       }
+               return super.launch();
+       }
+
+       public boolean shutdown()
+       {
+               SlaveTask       t;
+               int                     i;
+
+               if (!isRunning()) {
+                       return false;
+                       }
+
+               for (i=0; i<subtasks.size(); i++) {
+                       t=(SlaveTask) subtasks.get(i);
+                       t.shutdown();
+                       }
+               return join();
+       }
+}

Added: freeway/src/org/gnu/freeway/util/NulAction.java
===================================================================
--- freeway/src/org/gnu/freeway/util/NulAction.java     2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/util/NulAction.java     2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,26 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util;
+
+/**
+ *
+ */
+
+public final class NulAction extends AbstractAction
+{
+       protected NulAction()
+       {
+               super("Nul Action");
+       }
+
+       public void perform()
+       {
+       }
+
+       public boolean equals( Object obj )
+       {
+               return (obj instanceof NulAction);
+       }
+}

Added: freeway/src/org/gnu/freeway/util/OSAccess.java
===================================================================
--- freeway/src/org/gnu/freeway/util/OSAccess.java      2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/util/OSAccess.java      2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,150 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util;
+
+import java.util.logging.*;
+
+/**
+ *
+ */
+
+public class OSAccess extends Object
+{
+       private static Logger           logger;
+       private static boolean  loaded;
+
+       static {
+               logger=Logger.getLogger(OSAccess.class.getName());
+               try {
+                       System.loadLibrary("freeway-clib");
+                       loaded=true;
+                       }
+               catch( Throwable x ) {
+                       logger.log(Level.SEVERE,"Unable to load os specific 
library !",x);
+                       loaded=false;
+                       }
+               }
+
+
+       public static int getServicePort( String service, String protocol )
+       {
+               //todo: a faire lookup dans /etc/services avec 'getservbyname'
+               return 0;
+       }
+
+       public static void signalsInit()
+       {
+               if (loaded) {
+                       _signalsInit();
+                       }
+               else {
+                       logger.log(Level.INFO,"Could not init signals, library 
is not loaded.");
+                       }
+       }
+
+       protected static native void _signalsInit();
+
+       public static void signalsDone()
+       {
+               if (loaded) {
+                       _signalsDone();
+                       }
+               else {
+                       logger.log(Level.INFO,"Could not done signals, library 
is not loaded.");
+                       }
+       }
+
+       protected static native void _signalsDone();
+
+       public static boolean signalsCatch( int num )
+       {
+               if (loaded) {
+                       return _signalsCatch(num);
+                       }
+               logger.log(Level.INFO,"Could not catch signal #"+num+", library 
is not loaded.");
+               return false;
+       }
+
+       protected static native boolean _signalsCatch( int num );
+
+       public static boolean signalsLeave( int num )
+       {
+               if (loaded) {
+                       return _signalsLeave(num);
+                       }
+               logger.log(Level.INFO,"Could not leave signal #"+num+", library 
is not loaded.");
+               return false;
+       }
+
+       protected static native boolean _signalsLeave( int num );
+
+       public static int signalsWait()
+       {
+               if (loaded) {
+                       return _signalsWait();
+                       }
+               logger.log(Level.INFO,"Could not wait for signals, library is 
not loaded.");
+               Scheduler.sleep();
+               return 0;
+       }
+
+       protected static native int _signalsWait();
+
+       public static int signalsGetPID()
+       {
+               if (loaded) {
+                       return _signalsGetPID();
+                       }
+               logger.log(Level.INFO,"Could not get pid, library is not 
loaded.");
+               return -1;
+       }
+
+       protected static native int _signalsGetPID();
+
+       public static void signalsSignal( int pid, int num )
+       {
+               if (loaded) {
+                       _signalsSignal(pid,num);
+                       }
+               else {
+                       logger.log(Level.INFO,"Could not signal pid, library is 
not loaded.");
+                       }
+       }
+
+       protected static native void _signalsSignal( int pid, int num );
+
+       public static boolean fileIsLink( String path )
+       {
+               if (loaded) {
+                       return _fileIsLink(path);
+                       }
+               logger.log(Level.INFO,"Could not determine whether file 
\""+path+"\" is a link, library is not loaded.");
+               return false;
+       }
+
+       protected static native boolean _fileIsLink( String path );
+
+       public static String fileGetLinkTarget( String path )
+       {
+               if (loaded) {
+                       return _fileGetLinkTarget(path);
+                       }
+               logger.log(Level.INFO,"Could not get target of link 
\""+path+"\", library is not loaded.");
+               return null;
+       }
+
+       protected static native String _fileGetLinkTarget( String path );
+
+       public static boolean fileCreateLink( String path, String target )
+       {
+               if (loaded) {
+                       return _fileCreateLink(path,target);
+                       }
+               logger.log(Level.INFO,"Could not create symbolic link at 
\""+path+"\" for \""+target+"\", library is not loaded.");
+               return false;
+       }
+
+       protected static native boolean _fileCreateLink( String path, String 
target );
+}

Added: freeway/src/org/gnu/freeway/util/Option.java
===================================================================
--- freeway/src/org/gnu/freeway/util/Option.java        2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/util/Option.java        2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,161 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util;
+
+import org.gnu.freeway.*;
+
+import java.util.logging.*;
+import java.util.regex.*;
+
+/**
+ *
+ */
+
+public abstract class Option extends LoggedObject
+{
+       private static final Matcher    FORMAT  =       
Pattern.compile("([\\w-]{2,}+)?\\|(\\w)?(\\|[\\w-]+\\??)?").matcher("");
+
+       public static final int NO_ARGUMENT                     =       0;
+       public static final int REQUIRED_ARGUMENT       =       1;
+       public static final int OPTIONAL_ARGUMENT       =       2;
+
+       private String  name;
+       private char            shortName;
+       private int             hasArg;
+       private String  argName;
+       private String  explanation;
+       private String  value;
+
+
+       protected Option()
+       {
+               super(true);
+               name=null;
+               shortName=(char) 0;
+               hasArg=NO_ARGUMENT;
+               argName=null;
+               explanation="";
+               value="";
+       }
+
+       public Option( String desc, String str )
+       {
+               this();
+               parse(desc);
+               explanation=str;
+       }
+
+       public Option( String str, char c )
+       {
+               this();
+               name=str;
+               shortName=c;
+       }
+
+       public String toString()
+       {
+               return "Option [name="+name+", shortName="+shortName+", 
hasArg="+hasArg+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       protected synchronized void parse( String str )
+       {
+               FORMAT.reset(str);
+               if (FORMAT.matches()) {
+                       name=FORMAT.group(1);
+                       shortName=(FORMAT.group(2)!=null ? 
FORMAT.group(2).charAt(0) : (char) 0);
+
+                       str=FORMAT.group(3);
+                       if (str!=null && str.length()>1) {
+                               if (str.endsWith("?")) {
+                                       hasArg=OPTIONAL_ARGUMENT;
+                                       argName=str.substring(1,str.length()-1);
+                                       }
+                               else {
+                                       hasArg=REQUIRED_ARGUMENT;
+                                       argName=str.substring(1);
+                                       }
+                               }
+                       else {
+                               hasArg=NO_ARGUMENT;
+                               }
+                       }
+               else {
+                       log(Level.SEVERE,"Wrong format : "+str);
+                       }
+       }
+
+       public abstract boolean exec( Command cmd );
+
+       public String getName()
+       {
+               return name;
+       }
+
+       public char getShortName()
+       {
+               return shortName;
+       }
+
+       public int hasArgument()
+       {
+               return hasArg;
+       }
+
+       public String getExplanation()
+       {
+               return explanation;
+       }
+
+       public String getValue()
+       {
+               return value;
+       }
+
+       public int getIntValue( int def )
+       {
+               try {
+                       def=Integer.parseInt(value);
+                       }
+               catch( NumberFormatException x ) {
+                       }
+               return def;
+       }
+
+       public void setValue( String str )
+       {
+               value=str;
+       }
+
+       public void clearValue()
+       {
+               value="";
+       }
+
+       public String getUsage()
+       {
+               StringBuffer    buf;
+
+               buf=new StringBuffer();
+               if (shortName!=0) {
+                       buf.append("-"+shortName);
+                       if (argName!=null) {
+                               buf.append(" "+argName);
+                               }
+                       }
+               if (name!=null) {
+                       if (shortName!=0) {
+                               buf.append(", ");
+                               }
+                       buf.append("--"+name);
+                       if (argName!=null) {
+                               buf.append("="+argName);
+                               }
+                       }
+               return buf.toString();
+       }
+}

Added: freeway/src/org/gnu/freeway/util/OptionsParser.java
===================================================================
--- freeway/src/org/gnu/freeway/util/OptionsParser.java 2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/util/OptionsParser.java 2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,409 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util;
+
+import org.gnu.freeway.*;
+
+import java.util.*;
+import java.util.logging.*;
+
+/**
+ * Command line option parsing.
+ */
+
+public class OptionsParser extends LoggedObject
+{
+       private static final String     EOL     =       
System.getProperty("line.separator");
+
+       /** */
+       private String          invocation;
+
+       /** */
+       private String          description;
+
+       /** */
+       private List                    options;
+
+       /** */
+       private String[]                args;
+
+       /** */
+       private boolean[]       isArg;
+
+       /** index in <code>args</code> of the next element to be scanned. */
+       private int                     argIndex;
+
+       /** */
+       private boolean         errors;
+
+       /** Handle permutation of arguments. */
+       private boolean         strictOrder;
+
+       /** */
+       private boolean         allowArguments;
+
+
+       public OptionsParser( String invoc, String desc )
+       {
+               super(true);
+               invocation=invoc;
+               description=desc;
+               options=new ArrayList();
+               args=new String[] {};
+               isArg=new boolean[] {};
+               argIndex=0;
+               errors=false;
+               strictOrder=false;
+               allowArguments=false;
+       }
+
+       public String toString()
+       {
+               return "Options parser";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public void addOption( Option opt )
+       {
+               options.add(opt);
+       }
+
+       public void setOptions( Option[] opts )
+       {
+               options.clear();
+               options.addAll(Arrays.asList(opts));
+       }
+
+       public void clearOptions()
+       {
+               options.clear();
+       }
+
+       /**
+        * Print output of --help in GNU format.
+        * Print the usage information for this command, that consists in a 
list of the offered options.
+        */
+
+       public void printHelp()
+       {
+               Option                  opt;
+               StringBuffer    buf;
+               int                             len,i;
+
+               buf=new StringBuffer();
+
+               len=0;
+               for (i=0; i<options.size(); i++) {
+                       opt=(Option) options.get(i);
+                       len=Math.max(len,opt.getUsage().length());
+                       }
+
+               for (i=0; i<options.size(); i++) {
+                       opt=(Option) options.get(i);
+                       buf.append(Utils.alignRight(opt.getUsage(),len)+" : 
"+opt.getExplanation()+EOL);
+                       }
+
+               System.out.println("Usage: "+invocation);
+               System.out.println(description);
+               System.out.println();
+               System.out.println(buf);
+       }
+
+       public boolean doesAllowArguments()
+       {
+               return allowArguments;
+       }
+
+       public void setAllowArguments( boolean flag )
+       {
+               allowArguments=flag;
+       }
+
+       public boolean hasErrors()
+       {
+               return errors;
+       }
+
+       /**
+        * Perform option parsing from the command line.
+        *
+        * @param cmd
+        * @param a
+        * @return
+        */
+
+       public boolean parse( Command cmd, String[] a )
+       {
+               Option                  opt;
+               StringBuffer            buf;
+               int                             i;
+               boolean                 cont;
+
+               // automatically add '--help' option
+               addOption(new Option("help|h","print this help") {
+                       public boolean exec( Command c )
+                       {
+                               // prints help and then aborts the program
+                               printHelp();
+                               return false;
+                       }
+                       });
+
+               args=a;
+               argIndex=0;
+               isArg=new boolean[args.length];
+               Arrays.fill(isArg,false);
+               errors=false;
+
+               cont=true;
+               for (opt=next(); opt!=null && cont; opt=next()) {
+                       cont=opt.exec(cmd);
+                       }
+
+               // any error ? aborts program !
+               if (hasErrors()) {
+                       System.out.println("Aborting.");
+                       System.out.println("Use --help to get a list of 
options.");
+                       return false;
+                       }
+
+               if (!cont) {
+                       return false;
+                       }
+
+               a=getArguments();
+               if (!doesAllowArguments() && a.length>0) {
+                       buf=new StringBuffer();
+                       for (i=0; i<a.length; i++) {
+                               buf.append(a[i]+" ");
+                               }
+
+                       log(Level.SEVERE,"Invalid arguments : "+buf);
+                       log(Level.SEVERE,"Exiting.");
+                       return false;
+                       }
+               return cont;
+       }
+
+       public boolean hasArguments()
+       {
+               int     i;
+
+               for (i=0; i<isArg.length && !isArg[i]; i++) {}
+               return (i<isArg.length);
+       }
+
+       public String[] getArguments()
+       {
+               List    list;
+               int             i;
+
+               list=new ArrayList();
+               for (i=0; i<isArg.length; i++) {
+                       if (isArg[i]) {
+                               list.add(args[i]);
+                               }
+                       }
+               return (String[]) list.toArray(new String[list.size()]);
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       protected Option next()
+       {
+               // skip any non-options and mark them as arguments to be 
returned further
+               if (strictOrder) {
+                       if (argIndex<args.length && 
!(args[argIndex].startsWith("-") && args[argIndex].length()>1)) {
+                               while (argIndex<args.length) {
+                                       isArg[argIndex++]=true;
+                                       }
+                               }
+                       }
+               else {
+                       while (argIndex<args.length && 
!(args[argIndex].startsWith("-") && args[argIndex].length()>1)) {
+                               isArg[argIndex++]=true;
+                               }
+                       }
+
+               // the special element `--' means premature end of options
+               if (argIndex<args.length && args[argIndex].equals("--")) {
+                       argIndex++;
+                       while (argIndex<args.length) {
+                               isArg[argIndex++]=true;
+                               }
+                       }
+
+               // stop further processing if no more args to process
+               if (argIndex==args.length) {
+                       return null;
+                       }
+
+               return (args[argIndex].startsWith("--") ? nextLong() : 
nextShort());
+       }
+
+       protected Option nextLong()
+       {
+               Option  opt,o;
+               String  str,value;
+               int             i;
+               boolean exact,ambig;
+
+               i=args[argIndex].indexOf("=");
+               if (i>=0) {
+                       str=args[argIndex].substring(2,i);
+                       value=args[argIndex].substring(i+1);
+                       }
+               else {
+                       str=args[argIndex].substring(2);
+                       value="";
+                       }
+
+               // test all long options for either exact match or abbreviated 
matches
+               opt=null;
+               exact=false;
+               ambig=false;
+               for (i=0; i<options.size(); i++) {
+                       o=(Option) options.get(i);
+                       if (o.getName().startsWith(str)) {
+                               if (o.getName().equals(str)) {
+                                       // exact match found
+                                       opt=o;
+                                       exact=true;
+                                       break;
+                                       }
+                               else if (opt==null) {
+                                       // first nonexact match found
+                                       opt=o;
+                                       }
+                               else {
+                                       // second or later nonexact match found
+                                       ambig=true;
+                                       }
+                               }
+                       }
+
+               if (ambig && !exact) {
+                       argIndex++;
+                       System.err.println("Option `"+args[argIndex-1]+"' is 
ambiguous.");
+                       errors=true;
+                       return null;
+                       }
+
+               if (opt!=null) {
+                       opt.clearValue();
+                       argIndex++;
+                       if (value.length()>0) {
+                               // Don't test has_arg with >, because some C 
compilers don't allow it to be used on enums.
+                               if (opt.hasArgument()!=Option.NO_ARGUMENT) {
+                                       opt.setValue(value);
+                                       }
+                               else {
+                                       System.err.println("Option 
`--"+opt.getName()+"' doesn't allow an argument.");
+                                       errors=true;
+                                       return null;
+                                       }
+                               }
+                       else if (opt.hasArgument() == Option.REQUIRED_ARGUMENT) 
{
+                               if (argIndex < args.length) {
+                                       opt.setValue(args[argIndex++]);
+                                       }
+                               else {
+                                       System.err.println("Option 
`"+args[argIndex - 1]+"' requires an argument.");
+                                       errors=true;
+                                       return null;
+                                       }
+                               }
+                       return opt;
+                       }
+
+               // can't find it as a long option, then it's an error
+               argIndex++;
+               System.err.println("Unrecognized option `--"+str+"'.");
+               errors=true;
+               return null;
+       }
+
+       protected Option nextShort()
+       {
+               Option  opt;
+               int             i;
+               char    c;
+
+               // special case '-'
+               if (args[argIndex].length()==1) {
+                       argIndex++;
+                       System.err.println("Invalid option `-'.");
+                       errors=true;
+                       return null;
+                       }
+
+               c=args[argIndex].charAt(1);
+               for (i=0; i<options.size() && ((Option) 
options.get(i)).getShortName()!=c; i++) {}
+
+               if (i==options.size()) {
+                       argIndex++;
+                       System.err.println("Invalid option `-"+c+"'.");
+                       errors=true;
+                       return null;
+                       }
+
+               opt=(Option) options.get(i);
+               opt.clearValue();
+               switch (opt.hasArgument()) {
+                       case Option.NO_ARGUMENT:
+                               if (args[argIndex].length()>2) {
+                                       
args[argIndex]="-"+args[argIndex].substring(2);
+                                       }
+                               else {
+                                       argIndex++;
+                                       }
+                               break;
+
+                       case Option.REQUIRED_ARGUMENT:
+                               if (args[argIndex].length()>2) {
+                                       
opt.setValue(args[argIndex].substring(2));
+                                       }
+                               else if (argIndex==args.length-1) {
+                                       System.err.println("Option `-"+c+"' 
requires an argument.");
+                                       errors=true;
+                                       opt=null;
+                                       }
+                               else {
+                                       opt.setValue(args[++argIndex]);
+                                       }
+                               argIndex++;
+                               break;
+
+                       case Option.OPTIONAL_ARGUMENT:
+                               if (args[argIndex].length()>2) {
+                                       
opt.setValue(args[argIndex].substring(2));
+                                       }
+                               argIndex++;
+                               break;
+                       }
+               return opt;
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public static String[] toArgs( String argLine )
+       {
+               String[]                args;
+               StringTokenizer st;
+               int                             i;
+
+               st=new StringTokenizer(argLine);
+
+               args=new String[st.countTokens()];
+               for (i=0; i<args.length; i++) {
+                       args[i]=st.nextToken();
+                       }
+               return args;
+       }
+}

Added: freeway/src/org/gnu/freeway/util/OutputFilter.java
===================================================================
--- freeway/src/org/gnu/freeway/util/OutputFilter.java  2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/util/OutputFilter.java  2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,153 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util;
+
+import java.io.*;
+import java.nio.charset.*;
+import java.util.*;
+import java.util.logging.*;
+import java.util.regex.*;
+
+/**
+ *
+ */
+
+public class OutputFilter extends LoggedObject
+{
+       private Matcher matcher;
+
+
+       public OutputFilter( String regex )
+       {
+               this(regex,false);
+       }
+
+       public OutputFilter( String regex, boolean dbg )
+       {
+               super(dbg);
+               matcher=Pattern.compile(regex).matcher("");
+
+               debug(Level.INFO,"Regex : "+regex);
+       }
+
+       public String toString()
+       {
+               return "Output filter [matcher="+matcher+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public Iterator filter( String cmd )
+       {
+               Process p;
+
+               debug(Level.INFO,"Process : "+cmd);
+
+               try {
+                       p=Runtime.getRuntime().exec(cmd);
+                       return filter(new 
InputStreamReader(p.getInputStream()),true);
+                       }
+               catch( IOException x ) {
+                       err("I/O exception !",x);
+                       }
+               return null;
+       }
+
+       public Iterator filter( Process p )
+       {
+               return filter(new InputStreamReader(p.getInputStream()),true);
+       }
+
+       public Iterator filter( InputStream is )
+       {
+               return filter(new InputStreamReader(is),false);
+       }
+
+       public Iterator filter( InputStream is, Charset cs )
+       {
+               return filter(new InputStreamReader(is,cs),false);
+       }
+
+       public Iterator filter( Reader r )
+       {
+               return filter(r,false);
+       }
+
+       protected Iterator filter( Reader r, boolean close )
+       {
+               final Reader    _r = r;
+               final boolean   _close = close;
+
+               return new Iterator() {
+                       BufferedReader  br=new BufferedReader(_r);
+                       boolean                 processed=true;
+                       String                  str;
+                       int                             i;
+
+                       public boolean hasNext()
+                       {
+                               readIfNecessary();
+                               return (str!=null);
+                       }
+
+                       public Object next()
+                       {
+                               readIfNecessary();
+                               if (str==null) {
+                                       throw new NoSuchElementException();
+                                       }
+
+                               if (i==matcher.groupCount()) {
+                                       processed=true;
+                                       }
+                               i++;
+                               return (i>1 ? matcher.group(i-1) : str);
+                       }
+
+                       public void remove()
+                       {
+                               throw new UnsupportedOperationException();
+                       }
+
+                       protected void readIfNecessary()
+                       {
+                               boolean matched;
+
+                               if (processed) {
+                                       do {
+                                               try {
+                                                       str=br.readLine();
+                                                       }
+                                               catch( IOException x ) {
+                                                       err("Could not read 
data !",x);
+                                                       str=null;
+                                                       }
+
+                                               matched=false;
+                                               if (str!=null) {
+                                                       matcher.reset(str);
+                                                       matched=matcher.find();
+
+                                                       
debug(Level.INFO,"Line("+(matched ? "OK" : "NO")+") : "+str);
+                                                       }
+                                               else if (_close) {
+                                                       try {
+                                                               _r.close();
+                                                               }
+                                                       catch( IOException x ) {
+                                                               err("Could not 
close stream !",x);
+                                                               }
+                                                       }
+                                               }
+                                       while (!matched && str!=null);
+
+                                       processed=false;
+                                       i=0;
+                                       }
+                       }
+                       };
+       }
+}

Added: freeway/src/org/gnu/freeway/util/Prefs.java
===================================================================
--- freeway/src/org/gnu/freeway/util/Prefs.java 2005-01-31 23:47:23 UTC (rev 
136)
+++ freeway/src/org/gnu/freeway/util/Prefs.java 2005-02-01 01:07:27 UTC (rev 
137)
@@ -0,0 +1,845 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util;
+
+import org.gnu.freeway.*;
+import org.gnu.freeway.util.io.*;
+
+import java.io.*;
+import java.net.*;
+import java.nio.*;
+import java.nio.charset.*;
+import java.util.*;
+import java.util.logging.*;
+import java.util.prefs.*;
+import java.util.regex.*;
+
+/**
+ * Preferences (dynamic and persistent)
+ *
+ * high-level configuration managment (with command-line overrides)
+ * Configuration management.
+ *
+ * Configuration file parsing,
+ * This file provides code to parse a configuration file and access
+ * the data stored in it. It also provides methods to override options
+ * which can be used to add command-line options to the configuration
+ * API.
+ *
+ * tiny, stateful database too keep track of internal state
+ *
+ * Directory based implementation of a tiny, stateful database
+ * to keep track of GNUnet _internal_ configuration parameters
+ * that users are not supposed to see (e.g. *previous* quota,
+ * previous database type for AFS, etc.)
+ * Directory based implementation of a tiny, stateful database
+ * to keep track of GNUnet _internal_ configuration parameters
+ * that users are not supposed to see (e.g. *previous* quota,
+ * previous database type for AFS, etc.)
+ */
+
+public class Prefs extends LoggedObject
+{
+       private static final String     EOL                                     
        =       System.getProperty("line.separator");
+
+       public static final String      ROOT_PATH                               
=       "/org/gnu/freeway";
+
+       public static final String      CONFIG_NODE                             
=       "config";
+
+       public static final String      DAEMON_BASEDIR_NAME             =       
"daemon.basedir";
+       public static final String      DAEMON_BASEDIR_DEFAULT  =       
"~/.freeway/daemon";
+
+       public static final String      CLIENTS_BASEDIR_NAME            =       
"clients.basedir";
+       public static final String      CLIENTS_BASEDIR_DEFAULT =       
"~/.freeway/clients";
+
+       public static final String      MYSQL_HOST_NAME                 =       
"mysql.host";
+       public static final String      MYSQL_HOST_DEFAULT              =       
"localhost";
+
+       public static final String      MYSQL_USER_NAME                 =       
"mysql.user";
+       public static final String      MYSQL_USER_DEFAULT              =       
"";
+
+       public static final String      MYSQL_PASSWORD_NAME             =       
"mysql.password";
+       public static final String      MYSQL_PASSWORD_DEFAULT  =       "";
+
+       private static final String     DIR_EXT =       "state.sdb";
+
+       private DirLocation     base;
+
+       private Preferences     prefs;
+
+       /** Map of run-time options (backed by the configuration file). */
+       private Map                     dyn;
+
+       /** */
+       private List                    callbacks;
+
+
+       public Prefs()
+       {
+               super(false);
+               prefs=Preferences.userRoot().node(ROOT_PATH);
+               dyn=new HashMap();
+               callbacks=new ArrayList();
+       }
+
+       public String toString()
+       {
+               return "Preferences";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public void init( Application app )
+       {
+               base=app.getHome();
+               if (base==null) {
+                       throw new IllegalStateException("Could not open/create 
home directory !");
+                       }
+               base=base.getDirectory(DIR_EXT);
+               if (!base.create()) {
+                       throw new IllegalStateException("Could not open/create 
subdirectory '"+base.getLabel()+"' !");
+                       }
+               log(Level.INFO,"State database is located at 
\""+base.getLabel()+"\".");
+       }
+
+       //todo: renommer 'Global' en 'Persistent'
+       public int getGlobalInt( String section, String name )
+       {
+               return getGlobalInt(section,name,0);
+       }
+
+       public int getGlobalInt( String section, String name, int def )
+       {
+               return prefs.node(section.toLowerCase()).getInt(name,def);
+       }
+
+       public void setGlobalInt( String section, String name, int value )
+       {
+               prefs.node(section.toLowerCase()).putInt(name,value);
+       }
+
+       public boolean hasGlobalString( String section, String name )
+       {
+               return prefs.node(section.toLowerCase()).get(name,null)!=null;
+       }
+
+       public String getGlobalString( String section, String name )
+       {
+               return getGlobalString(section,name,null);
+       }
+
+       public String getGlobalString( String section, String name, String def )
+       {
+               return prefs.node(section.toLowerCase()).get(name,def);
+       }
+
+       public void setGlobalString( String section, String name, String value )
+       {
+               prefs.node(section.toLowerCase()).put(name,value);
+       }
+
+       public void printGlobals()
+       {
+               try {
+                       prefs.exportSubtree(System.out);
+                       }
+               catch( IOException x ) {
+                       err("Unable to print preferences !",x);
+                       }
+               catch( BackingStoreException x ) {
+                       err("Unable to print preferences !",x);
+                       }
+       }
+
+       /**
+        * This is the method you need if you want to remove all system 
preferences under "/org/gnu/freeway" tree.
+        * Be very careful, you <em>cannot</em> recover lost preferences...
+        * @return
+        */
+
+       public boolean eraseGlobals()
+       {
+               try {
+                       prefs.removeNode();
+                       return true;
+                       }
+               catch( BackingStoreException x ) {
+                       err("Failed to erase preferences !",x);
+                       }
+               return false;
+       }
+
+       public boolean flushGlobals()
+       {
+               try {
+                       prefs.flush();
+                       return true;
+                       }
+               catch( BackingStoreException x ) {
+                       err("Could not flush preferences !",x);
+                       }
+               return false;
+       }
+
+       public boolean copyTemplateWithGlobals( String resName, FileLocation 
dest )
+       {
+               MappedFile              f;
+               Matcher                 m;
+               BufferedReader  r;
+               URL                             url;
+               StringBuffer            buf;
+               String                  str;
+               int                             from;
+
+               url=getClass().getClassLoader().getResource(resName);
+               if (url==null) {
+                       return false;
+                       }
+
+               try {
+                       
m=Pattern.compile("%([\\w\\.]+)@([\\w\\./]+)%").matcher("");
+                       buf=new StringBuffer();
+
+                       r=new BufferedReader(new 
InputStreamReader(url.openStream(),"UTF-8"));
+                       try {
+                               for (str=r.readLine(); str!=null; 
str=r.readLine()) {
+                                       m.reset(str);
+
+                                       from=0;
+                                       while (m.find(from)) {
+                                               
buf.append(str.substring(from,m.start()));
+                                               
buf.append(Preferences.userRoot().node(m.group(2)).get(m.group(1),""));
+                                               from=m.end();
+                                               }
+                                       buf.append(str.substring(from));
+                                       buf.append(EOL);
+                                       }
+                               }
+                       finally {
+                               r.close();
+                               }
+
+                       f=dest.openNew();
+                       f.writeString(buf.toString(),Charset.forName("UTF-8"));
+                       f.close();
+                       }
+               catch( IOException x ) {
+                       err("Failed to copy template !",x);
+                       return false;
+                       }
+               return true;
+       }
+
+
+       /**
+        * Read the contents of a bucket to a buffer.
+        *
+        * @param name the hashcode representing the entry
+        * @return the bytes read on success, null on failure
+        */
+
+       public ByteBuffer getContent( String name )
+       {
+               MappedFile      f;
+               FileLocation    str;
+               ByteBuffer      buf;
+
+               str=base.getFile(name);
+               if (!str.exists()) {
+                       return null;
+                       }
+
+               f=str.open();
+               if (f==null) {
+                       return null;
+                       }
+               if (str.getSize()==0) {
+                       f.close();
+                       return null;
+                       }
+
+               buf=f.readBuffer();
+               f.close();
+               return buf;
+       }
+
+       public int getContentAsInt( String name, int def )
+       {
+               ByteBuffer      buf;
+
+               buf=getContent(name);
+               return ((buf!=null && buf.remaining()==4) ? buf.getInt() : def);
+       }
+
+       public String getContentAsString( String name )
+       {
+               ByteBuffer      buf;
+
+               buf=getContent(name);
+               return (buf!=null ? buf.asCharBuffer().toString() : null);
+       }
+
+       public boolean putContent( String name, int value )
+       {
+               ByteBuffer      buf;
+
+               buf=ByteBuffer.allocateDirect(4);
+               buf.putInt(value);
+               return putContent(name,buf);
+       }
+
+       public boolean putContent( String name, String value )
+       {
+               ByteBuffer      buf;
+
+               buf=ByteBuffer.allocateDirect(value.length()*2);
+               buf.asCharBuffer().put(value);
+               buf.position(buf.capacity());
+               return putContent(name,buf);
+       }
+
+       public boolean putContent( String name, byte[] b )
+       {
+               return putContent(name,b,0,b.length);
+       }
+
+       public boolean putContent( String name, byte[] b, int offset, int 
length )
+       {
+               return putContent(name,(ByteBuffer) 
ByteBuffer.wrap(b,offset,length).slice().position(length));
+       }
+
+       /**
+        * Write content to a file.
+        *
+        * @param name  the key for the entry
+        * @param buf   the data to store
+        * @return              false on error, true if ok.
+        */
+
+       public boolean putContent( String name, ByteBuffer buf )
+       {
+               MappedFile      f;
+
+               buf.flip();
+
+               f=base.getFile(name).openNew();
+               if (f==null) {
+                       log(Level.WARNING,"Failed to open file '"+name+"' 
("+base.getPath()+").");
+                       return false;
+                       }
+
+               if (!f.writeBuffer(buf)) {
+                       log(Level.WARNING,"Could not store content to file 
'"+name+"'.");
+                       f.close();
+                       return false;
+                       }
+               f.close();
+               return true;
+       }
+
+       public boolean appendContent( String name, byte[] b )
+       {
+               return appendContent(name,b,0,b.length);
+       }
+
+       public boolean appendContent( String name, byte[] b, int offset, int 
length )
+       {
+               return appendContent(name,(ByteBuffer) 
ByteBuffer.wrap(b,offset,length).slice().position(length));
+       }
+
+       /**
+        * Append content to file.
+        *
+        * @param name the key for the entry
+        * @param buf the data to store
+        * @return false on error, true if ok.
+        */
+
+       public boolean appendContent( String name, ByteBuffer buf )
+       {
+               MappedFile      f;
+               FileLocation    loc;
+
+               buf.flip();
+
+               loc=base.getFile(name);
+               loc.create();
+
+               f=loc.open();
+               if (f==null) {
+                       log(Level.WARNING,"Failed to open file '"+name+"'.");
+                       return false;
+                       }
+
+               f.seekEnd();
+               if (!f.writeBuffer(buf)) {
+                       log(Level.WARNING,"Could not append content to file 
'"+name+"'.");
+                       f.close();
+                       return false;
+                       }
+               f.close();
+               return true;
+       }
+
+       /**
+        * Free space in the database by removing one file.
+        *
+        * @param name the hashcode representing the name of the file (without 
directory)
+        * @return
+        */
+
+       public boolean unlink( String name )
+       {
+               FileLocation            f;
+
+               f=base.getFile(name);
+               return (f!=null ? f.delete() : true);
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       /**
+        * Clean up.
+        */
+
+       public void clear()
+       {
+               dyn.clear();
+       }
+
+       /**
+        * Obtain an int from the configuration.
+        *
+        * @param section from which section, must not be null
+        * @param option which option, must not be null
+        * @param def default value
+        * @return 0 if no option is specified
+        */
+
+       public int getInt( String section, String option, int def )
+       {
+               try {
+                       def=Integer.parseInt(getString(section,option,""));
+                       }
+               catch( NumberFormatException x ) {
+                       }
+               return def;
+       }
+
+       /**
+        * Set an option in a section.
+        * @param section from which section, may not be null
+        * @param option which option, may not be null
+        * @param value the value to use
+        */
+
+       public void setInt( String section, String option, int value )
+       {
+               setString(section,option,String.valueOf(value));
+       }
+
+       /**
+        * Check if a string in the configuration matches a given value.  This
+        * method should be preferred over getString since this
+        * method can avoid making a copy of the configuration string that
+        * then must be freed by the caller.
+        *
+        * @param section from which section, may not be null
+        * @param option which option, may not be null
+        * @param value the value to compare against
+        * @return true or NO
+        */
+
+       public boolean testString( String section, String option, String value )
+       {
+               String  str;
+
+               assert(section!=null && option!=null);
+
+               str=getString(section,option,null);
+               return (str!=null ? str.equals(value) : value==null);
+       }
+
+       /**
+        * Obtain a string from the configuration.
+        * @param section from which section, may not be null
+        * @param option which option, may not be null
+        * @param def default value
+        * @return a freshly allocated string, caller must free!
+        *   Note that the result can be null if the option is not set.
+        */
+
+       public String getString( String section, String option, String def )
+       {
+               Map             m;
+               String  str;
+
+               assert(section!=null && option!=null);
+
+               synchronized(dyn) {
+                       m=(Map) dyn.get(section.toUpperCase());
+                       str=(m!=null ? (String) m.get(option) : null);
+                       if (str==null) {
+                               return def;
+                               }
+                       return ((str!=null && str.startsWith("$")) ? 
expandDollar(section,str) : str);
+                       }
+       }
+
+       /**
+        * Set an option.
+        * @param section from which section, may not be null
+        * @param option which option, may not be null
+        * @param value the value to use, may be null
+        */
+
+       public void setString( String section, String option, String value )
+       {
+               Map     m;
+
+               assert(section!=null && option!=null);
+
+               synchronized(dyn) {
+                       m=(Map) dyn.get(section.toUpperCase());
+                       if (m==null) {
+                               m=new HashMap();
+                               dyn.put(section.toUpperCase(),m);
+                               }
+                       m.put(option,value);
+                       }
+       }
+
+       protected void merge( Map data )
+       {
+               Map                     m;
+               Iterator                iter;
+               String          str;
+
+               synchronized(dyn) {
+                       iter=data.keySet().iterator();
+                       while (iter.hasNext()) {
+                               str=(String) iter.next();
+
+                               m=(Map) dyn.get(str.toUpperCase());
+                               if (m==null) {
+                                       m=new HashMap();
+                                       dyn.put(str.toUpperCase(),m);
+                                       }
+                               m.putAll((Map) data.get(str));
+                               }
+                       }
+       }
+
+       /**
+        * Obtain a filename from the given section and option.
+        *
+        * @param section from which section, may not be null
+        * @param option which option, may not be null
+        * @return the specified filename, or null if failed
+        */
+
+       public DirLocation getDirLocation( String section, String option )
+       {
+               String  str;
+
+               assert(section!=null && option!=null);
+
+               str=getString(section,option,null);
+               return (str!=null ? new DirLocation(str) : null);
+       }
+
+       public FileLocation getFileLocation( String section, String option )
+       {
+               String  str;
+
+               assert(section!=null && option!=null);
+
+               str=getString(section,option,null);
+               return (str!=null ? new FileLocation(str) : null);
+       }
+
+       public String[] getArray( String section, String option )
+       {
+               return getArray(section,option,"[\\s:;]+");
+       }
+
+       public String[] getArray( String section, String option, String regex )
+       {
+               String[]        p;
+               List            list;
+               String          str;
+               int                     i;
+
+               assert(section!=null && option!=null);
+
+               list=new ArrayList();
+
+               str=getString(section,option,null);
+               if (str!=null) {
+                       p=str.split(regex);
+                       for (i=0; i<p.length; i++) {
+                               if (p[i].length()>0) {
+                                       list.add(p[i]);
+                                       }
+                               }
+                       }
+               return (String[]) list.toArray(new String[list.size()]);
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       /**
+        * Expand an expression of the form
+        * "$FOO/BAR" to "DIRECTORY/BAR" where
+        * either in the current section or
+        * globally FOO is set to DIRECTORY.
+        * @param section
+        * @param orig
+        * @return
+        */
+
+       protected String expandDollar( String section, String orig )
+       {
+               String  prefix;
+               int             i;
+
+               for (i=0; i<orig.length() && orig.charAt(i)!='/' && 
orig.charAt(i)!='\\'; i++) {}
+               if (i==orig.length()) {
+                       return orig;
+                       }
+
+               prefix=getString(section,orig.substring(1,i),null);
+               if (prefix==null) {
+                       prefix=getString("",orig.substring(1,i),null);
+                       }
+               if (prefix==null) {
+                       prefix=orig.substring(0,i);
+                       }
+               return prefix+File.separator+orig.substring(i+1);
+       }
+
+       /**
+        * Read the configuration file.  The previous configuration will be
+        * discarded if this method is invoked twice.
+        * Read the specified configuration file. The previous
+        * configuration will be discarded if this method is
+        * invoked twice. The configuration file that is read
+        * can be set using setConfigurationString on
+        * section "FILES" and option "gnunet.conf".
+        *
+        * This method should be invoked after the options have
+        * been parsed (and eventually the configuration filename
+        * default has been overriden) and if gnunetd receives
+        * a SIGHUP.
+        * @param isDaemon
+        */
+
+       public void readConfiguration( boolean isDaemon )
+       {
+               String  name;
+
+               name=getString("FILES","gnunet.conf",null);
+               if (name==null) {
+                       name=(isDaemon ? getSystemDaemonConfiguration() : 
getSystemClientsConfiguration());
+                       }
+
+               if (name==null) {
+                       log(Level.WARNING,"Unable to find any configuration 
file !");
+                       return;
+                       }
+
+               readConfiguration(name);
+       }
+
+       public void readConfiguration( String name )
+       {
+               URL             url;
+               FileLocation    f;
+               ConfigurationParser     parser;
+
+               url=null;
+
+               f=new FileLocation(name);
+               if (f.exists()) {
+                       name=f.getPath();       // expanded
+                       setString("FILES","gnunet.conf",name);
+                       }
+               else {
+                       url=getClass().getClassLoader().getResource(name);
+                       if (url==null) {
+                               log(Level.SEVERE,"Could not open configuration 
file \""+name+"\" (neither a file, nor an URL).");
+                               return;
+                               }
+                       }
+
+               synchronized(dyn) {
+                       parser=new ConfigurationParser();
+                       parser.load(name,Charset.forName("US-ASCII"));
+
+                       merge(parser.getSections());
+
+                       if (isDebug()) {
+                               dump();
+                               }
+                       }
+       }
+
+       public void dump()
+       {
+               Map                             map;
+               Iterator                        iter,it;
+               StringBuffer            buf;
+               String                  str,value;
+
+               buf=new StringBuffer(EOL);
+               buf.append("Configuration dump"+EOL);
+               buf.append("------------------"+EOL);
+
+               iter=new TreeSet(dyn.keySet()).iterator();
+               while (iter.hasNext()) {
+                       str=(String) iter.next();
+                       buf.append("["+str+"]"+EOL);
+
+                       map=(Map) dyn.get(str);
+                       it=new TreeSet(map.keySet()).iterator();
+                       while (it.hasNext()) {
+                               str=(String) it.next();
+                               value=(String) map.get(str);
+                               buf.append("  "+str+" = "+value+EOL);
+                               }
+                       buf.append(EOL);
+                       }
+               buf.append(EOL);
+
+               log(Level.FINEST,buf.toString());
+       }
+
+       /**
+        * Register a callback that is called when the configuration
+        * changes.  The API guarantees that the call is made either
+        * as a cron-job or while cron is suspended, so it is safe
+        * to edit (delete) cron jobs in the callback.
+        * @param a
+        */
+
+       public void registerConfigurationUpdateCallback( Action a )
+       {
+               synchronized(dyn) {
+                       callbacks.add(a);
+                       }
+       }
+
+       public void unregisterConfigurationUpdateCallback( Action a )
+       {
+               synchronized(dyn) {
+                       if (!callbacks.remove(a)) {
+                               log(Level.WARNING,"Could not unregister "+a+" 
that hadn't been registered !");
+                               }
+                       }
+       }
+
+       /**
+        * Call all registered configuration update callbacks, the 
configuration has changed.
+        * @param scheduler
+        */
+
+       public void triggerGlobalConfigurationRefresh( Scheduler scheduler )
+       {
+               scheduler.addJob(new ScheduledTask("REFRESH-CONFIG",new 
AbstractAction() {
+                       public void perform()
+                       {
+                               int i;
+
+                               synchronized(dyn) {
+                                       for (i=0; i<callbacks.size(); i++) {
+                                               SafeAction.safe((Action) 
callbacks.get(i)).perform();
+                                               }
+                                       }
+                       }
+                       }),0);
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public boolean isSystemInitialized()
+       {
+               try {
+                       if (!prefs.nodeExists(CONFIG_NODE)) {
+                               return false;
+                               }
+                       return hasGlobalString(CONFIG_NODE,DAEMON_BASEDIR_NAME) 
&&
+                               
hasGlobalString(CONFIG_NODE,CLIENTS_BASEDIR_NAME) &&
+                               hasGlobalString(CONFIG_NODE,MYSQL_HOST_NAME) &&
+                               hasGlobalString(CONFIG_NODE,MYSQL_USER_NAME);
+                       //todo: vérifier aussi que les repertoires existent et 
ont au moins le .conf !
+//todo: a statuer
+/*                             // create directory (~/.gnunet/) and conf. file
+                               f=File.create(name);
+                               if (f!=null) {
+                                       // try generating a configuration file
+                                       log(Level.WARNING,"No configuration 
file found, trying to create one at "+f.getPath());
+
+                                       generate_gnunetd_conf(f);
+                                       generate_gnunet_conf(f);
+                                       }
+*/
+                       }
+               catch( BackingStoreException x ) {
+                       err("Unable to access preferences !",x);
+                       return false;
+                       }
+       }
+
+       /**
+        * Default name of the daemon configuration file.
+        * @return
+        */
+
+       public String getSystemDaemonConfiguration()
+       {
+               String  str;
+
+               str=getGlobalString(CONFIG_NODE,DAEMON_BASEDIR_NAME);
+               return ((str!=null && str.length()>0) ? str+"/gnunet.conf" : 
null);
+       }
+
+       /**
+        * Default name of the client configuration file.
+        * @return
+        */
+
+       public String getSystemClientsConfiguration()
+       {
+               String  str;
+
+               str=getGlobalString(CONFIG_NODE,CLIENTS_BASEDIR_NAME);
+               return ((str!=null && str.length()>0) ? str+"/gnunet.conf" : 
null);
+       }
+
+       public File getSystemCache()
+       {
+               String  str;
+//todo: pas bon, si un repertoire n'existe pas, ne pas utiliser "." mais 
plutot File.create*temp*file
+               str=getGlobalString(CONFIG_NODE,CLIENTS_BASEDIR_NAME,"");
+               return new File(str,"cache");
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public static void ensureInitialized()
+       {
+               Prefs   prefs;
+
+               prefs=new Prefs();
+               if (!prefs.isSystemInitialized()) {
+                       GNUNetUIConfig.fromApp();
+                       }
+       }
+}

Added: freeway/src/org/gnu/freeway/util/SafeAction.java
===================================================================
--- freeway/src/org/gnu/freeway/util/SafeAction.java    2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/util/SafeAction.java    2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,39 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util;
+
+/**
+ *
+ */
+
+public class SafeAction extends AbstractAction
+{
+       private Action  action;
+
+
+       public SafeAction( Action a )
+       {
+               super(a.getName());
+               action=a;
+       }
+
+       public void perform()
+       {
+               try {
+                       action.perform();
+                       }
+               catch( Throwable x ) {
+                       err("Could not (safely) perform action 
\""+getName()+"\" !",x);
+                       }
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public static SafeAction safe( Action a )
+       {
+               return ((a instanceof SafeAction) ? (SafeAction) a : new 
SafeAction(a));
+       }
+}

Added: freeway/src/org/gnu/freeway/util/ScheduledTask.java
===================================================================
--- freeway/src/org/gnu/freeway/util/ScheduledTask.java 2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/util/ScheduledTask.java 2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,62 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util;
+
+/**
+ *
+ */
+
+public class ScheduledTask extends Task
+{
+       private long            repeatDelay;
+
+
+       public ScheduledTask( String str, Action a )
+       {
+               super(str,a);
+               repeatDelay=0;
+       }
+
+       public ScheduledTask( String str, Action a, long delay )
+       {
+               this(str,a);
+               repeatDelay=delay;
+       }
+
+       public String toString()
+       {
+               return "Scheduled task [name="+name+", action="+action+", 
repeatDelay="+repeatDelay+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+//      * @param deltaRepeat if this is a periodic, the time between
+//      *        the runs, otherwise 0.
+//      * @param deltaRepeat if this is a periodic, the time between
+//      *        the runs, otherwise 0.
+//      * @param repeat which repeat factor was chosen?
+//  /** for cron-jobs: when this should be repeated automatically, 0 if this 
was a once-only job */
+//  public int         deltaRepeat;
+//  /** data ptr (argument to the method) */
+//  public Object      data;
+
+       public boolean equals( Object obj )
+       {
+               if (!(obj instanceof ScheduledTask)) {
+                       return false;
+                       }
+               return super.equals(obj) && ((ScheduledTask) 
obj).repeatDelay==repeatDelay;
+       }
+
+       public boolean isPeriodic()
+       {
+               return repeatDelay>0;
+       }
+
+       public long getRepeatDelay()
+       {
+               return repeatDelay;
+       }
+}

Added: freeway/src/org/gnu/freeway/util/Scheduler.java
===================================================================
--- freeway/src/org/gnu/freeway/util/Scheduler.java     2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/util/Scheduler.java     2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,807 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util;
+
+import EDU.oswego.cs.dl.util.concurrent.*;
+
+import java.util.logging.*;
+
+/**
+ * Module for periodic background (cron) jobs.
+ *
+ * The module only uses one thread, thus every scheduled job must be 
short-lived, should never block
+ * for an indefinite amount of time. Specified deadlines are only a 
guide-line, the 10ms
+ * timer-resolution is only an upper-bound on the possible precision,
+ * in practice it will be worse (depending on the other cron-jobs).
+ *
+ * If you need to schedule a long-running or blocking job, run a function that 
will start another thread
+ * that will then run the actual job.
+ */
+
+public class Scheduler extends LoggedObject
+{
+       public static final long                MILLIS_1                =       
millis(1);
+       public static final long                MILLIS_10       =       
millis(10);
+       public static final long                MILLIS_50       =       
millis(50);
+       public static final long                MILLIS_100      =       
millis(100);
+       public static final long                MILLIS_150      =       
millis(150);
+       public static final long                MILLIS_200      =       
millis(200);
+       public static final long                MILLIS_250      =       
millis(250);
+       public static final long                MILLIS_500      =       
millis(500);
+
+       public static final long                SECS_1          =       
seconds(1);
+       public static final long                SECS_2          =       
seconds(2);
+       public static final long                SECS_3          =       
seconds(3);
+       public static final long                SECS_4          =       
seconds(4);
+       public static final long                SECS_5          =       
seconds(5);
+       public static final long                SECS_10         =       
seconds(10);
+       public static final long                SECS_12         =       
seconds(12);
+       public static final long                SECS_15         =       
seconds(15);
+       public static final long                SECS_16         =       
seconds(16);
+       public static final long                SECS_30         =       
seconds(30);
+
+       public static final long                MINUTES_1       =       
minutes(1);
+       public static final long                MINUTES_2       =       
minutes(2);
+       public static final long                MINUTES_4       =       
minutes(4);
+       public static final long                MINUTES_5       =       
minutes(5);
+       public static final long                MINUTES_15      =       
minutes(15);
+
+       public static final long                HOURS_1         =       
hours(1);
+       public static final long                HOURS_4         =       
hours(4);
+       public static final long                HOURS_6         =       
hours(6);
+       public static final long                HOURS_12                =       
hours(12);
+
+       public static final long                DAYS_1          =       days(1);
+
+       /** Number of scheduler units in a millisecond. */
+       private static final long               MILLIS          =       1;
+
+       /** Number of scheduler units in a second. */
+       private static final long               SECONDS         =       
1000*MILLIS;
+
+       /** Number of scheduler units in a minute. */
+       private static final long               MINUTES         =       
60*SECONDS;
+
+       /** Number of scheduler units in an hour. */
+       private static final long               HOURS           =       
60*MINUTES;
+
+       /** Number of scheduler units in a day. */
+       private static final long               DAYS                    =       
24*HOURS;
+
+       /** The initial size of the scheduled jobs table. */
+       private static final int                INITIAL_SIZE    =       16;
+
+       /** How long do we sleep at most (in ms) ? In some systems, the 
signal-interrupted sleep does not work nicely,
+        so to ensure progress, we should rather wake up periodically (but we 
don't want to burn too much CPU time
+        doing busy waiting; every 2s strikes a good balance). */
+       private static final int                MAX_SLEEP       =       2000;
+
+       /** The delta-list of waiting tasks. */
+       private DeltaList               deltaList;
+
+       /** The currently running job. */
+       private ScheduledTask           runningJob_;
+
+       /** The scheduler background thread. */
+       private Task                            scheduleTask;
+       private boolean                 running;
+       private Object                  waiter;
+
+       private Semaphore               cron_signal_up;
+       private Object                  inBlockLock_;
+       private int                             inBlock;
+
+
+       /**
+        * Create and initialize a new scheduler.
+        */
+
+       public Scheduler()
+       {
+               super(false);
+               running=false;
+               inBlock=0;
+               waiter=new Object();
+               deltaList=new DeltaList(INITIAL_SIZE);
+               inBlockLock_=new Object();
+               runningJob_=null;
+               cron_signal_up=new Semaphore(0);
+       }
+
+       public String toString()
+       {
+               return "Scheduler";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       /**
+        * Start the scheduler and its background thread.
+        */
+
+       public void start()
+       {
+               assert(scheduleTask==null);
+
+               running=true;
+
+               scheduleTask=new Task("CRON",new AbstractAction() {
+                       public void perform()
+                       {
+                               cron();
+                       }
+                       });
+               scheduleTask.launch();
+       }
+
+       /**
+        * Stop the scheduler.
+        */
+
+       public void stop()
+       {
+               DeltaEntry      e;
+
+               debug(Level.INFO,"Scheduler stopped.");
+
+               running=false;
+
+               synchronized(waiter) {
+                       waiter.notify();
+                       }
+
+               scheduleTask.join();
+
+               for (e=deltaList.pop(); e!=null; e=deltaList.pop()) {
+                       log(Level.WARNING,"Scheduled task still in queue : 
"+e.label());
+                       }
+
+               inBlockLock_=null;
+               deltaList=null;
+               cron_signal_up=null;
+       }
+
+       /**
+        * Stop running cron-jobs for a short time.  This method may only be
+        * called by a thread that is not holding any locks (otherwise
+        * there is the danger of a deadlock).
+        */
+
+       public void suspend()
+       {
+               final Semaphore blockSignal;
+
+               assert(running);
+
+               synchronized(inBlockLock_) {
+                       inBlock++;
+                       if (inBlock==1) {
+                               blockSignal=new Semaphore(0);
+                               addJob(new ScheduledTask("SUSPEND",new 
AbstractAction() {
+                                       // CronJob to suspend the cron thread 
until it is resumed.
+                                       public void perform() throws 
InterruptedException
+                                       {
+                                               boolean         ok;
+
+//                                             sig=blockSignal;
+                                               if (blockSignal!=null) {
+                                                       blockSignal.release();
+                                                       }
+
+                                               ok=false;
+                                               while (!ok) {
+                                                       
cron_signal_up.acquire();
+                                                       
synchronized(inBlockLock_) {
+                                                               inBlock--;
+                                                               if (inBlock==0) 
{
+                                                                       ok=true;
+                                                                       }
+                                                               }
+                                                       }
+                                       }
+                                       }),0);
+
+                               try {
+                                       blockSignal.acquire();
+                                       }
+                               catch( InterruptedException x ) {
+                                       err("",x);
+                                       }
+//                             blockSignal=null;
+                               }
+                       }
+       }
+
+       /**
+        * Resume running cron-jobs.
+        */
+
+       public void resume()
+       {
+               assert(running);
+               cron_signal_up.release();
+       }
+
+       /**
+        * Add a cron-job to the delta list.
+        *
+        * @param task which job should we run
+        * @param delta how many units until we run the job
+        */
+
+       public void addJob( ScheduledTask task, long delta )
+       {
+               int index;
+
+               debug(Level.FINER,"Adding job "+task.getName()+" to fire in 
"+delta+" CU.");
+
+               task.setDebug(isDebug());
+
+               synchronized(deltaList) {
+                       index=deltaList.insert(task,delta);
+                       if (index==0) {
+                               /* interrupt sleeping cron-thread! */
+                               debug(Level.FINER,"Sending signal to background 
thread.");
+                               synchronized(waiter) {
+                                       waiter.notify();
+                                       }
+                               return;
+                               }
+                       }
+       }
+
+       /**
+        * If the specified cron-job exists in th delta-list, move it to the
+        * head of the list.  If it is running, do nothing.  If it is does not
+        * exist and is not running, add it to the list to run it next.
+        *
+        * @param task which job should we run
+        */
+
+       public void advanceJob( ScheduledTask task )
+       {
+               DeltaEntry      job;
+
+               debug(Level.FINER,"Advancing job "+task.getName()+".");
+
+               synchronized(deltaList) {
+                       job=deltaList.find(task);
+                       if (job == null) {
+                               // not in queue; add if not running
+                               if (!task.equals(runningJob_)) {
+                                       addJob(task,0);
+                                       }
+                               return;
+                               }
+
+                       // ok, found it; remove, re-add with time 0
+                       deleteJob(task);
+                       addJob(task,0);
+                       }
+       }
+
+       /**
+        * Remove all matching cron-jobs from the list. This method should
+        * only be called while cron is suspended or stopped, or from a cron
+        * job that deletes another cron job.  If cron is not suspended or
+        * stopped, it may be running the method that is to be deleted, which
+        * could be bad (in this case, the deletion will not affect the
+        * running job and may return before the running job has terminated).
+        *
+        * @param task which job is listed?
+        * @return the number of jobs removed
+        */
+
+       public int deleteJob( ScheduledTask task )
+       {
+               boolean found;
+
+               debug(Level.FINER,"Deleting job "+task.getName()+".");
+
+               synchronized(deltaList) {
+                       found=deltaList.remove(task);
+                       }
+               // there may be more matches, go again !
+               return (found ? 1 + deleteJob(task) : 0);
+       }
+
+       /**
+        * Print the cron table.
+        */
+
+       public void printJobs()
+       {
+               DeltaEntry[]    entries;
+               StringBuffer    buf;
+               long                    now;
+               int                     i;
+
+               now=System.currentTimeMillis();
+
+               buf=new StringBuffer("\n");
+
+               synchronized(deltaList) {
+                       entries=deltaList.getEntries();
+                       for (i=0; i<entries.length; i++) {
+                               buf.append(i+": ");
+                               buf.append("will exec in 
"+(entries[i].delta-now)+" CU ");
+                               buf.append("--- method 
"+entries[i].task.getName()+" ");
+                               buf.append("--- repeat 
"+entries[i].task.getRepeatDelay()+" CU");
+                               buf.append("\n");
+                               }
+                       }
+
+               log(Level.FINER,buf.toString());
+       }
+
+       /**
+        * Process the cron-job at the beginning of the waiting queue, that
+        * is, remove, invoke, and re-insert if it is a periodical job. Make
+        * sure the cron job is held when calling this method, but
+        * note that it will be released briefly for the time
+        * where the job is running (the job to run may add other
+        * jobs!)
+        */
+
+       protected void runJob()
+       {
+               DeltaEntry      job;
+               ScheduledTask   task;
+               long    repeat;
+
+               job=deltaList.pop();
+               if (job==null)
+                       return; /* no job to be done */
+
+               task = job.task;
+               repeat = job.task.getRepeatDelay();
+
+               runningJob_ = job.task;
+
+               /* re-insert */
+               if (repeat > 0) {
+                       debug(Level.FINER,"Adding repeated job 
"+task.getName()+" to run again in "+repeat+" CU.");
+                       addJob(task,repeat);
+                       }
+
+               /* run */
+               debug(Level.FINER,"Running job "+task.getName()+".");
+               task.exec();
+/*
+               try {
+                       if (dump) {
+                               logger.log(Level.FINEST,"About to run job 
["+getLabel()+"].");
+                               }
+
+                       t=System.currentTimeMillis();
+                       method.perform();
+                       t=System.currentTimeMillis()-t;
+                       if (t>1000) {
+                               logger.log(Level.INFO,"Job ["+getLabel()+"] 
took "+NumberFormat.getInstance().format(new Double((double) t/(double) 
1000))+" s, is there anything wrong !?");
+                               }
+
+                       if (dump) {
+                               logger.log(Level.FINEST,"Job ["+getLabel()+"] 
done.");
+                               }
+                       }
+               catch( Throwable x ) {
+                       logger.log(Level.SEVERE,"Got error while executing job 
["+getLabel()+"] !",x);
+                       }
+ */
+               runningJob_ = null;
+               debug(Level.FINER,"Job "+task.getName()+" done.");
+       }
+
+       /**
+        * The main-method of cron, will NEVER terminate.
+        *
+        * Process the cron-job at the beginning of the waiting queue, that
+        * is, remove, invoke, and re-insert if it is a periodical job. Make
+        * sure the cron job is held when calling this method, but
+        * note that it will be released briefly for the time
+        * where the job is running (the job to run may add other
+        * jobs!)
+        * Process the cron-job at the beginning of the waiting
+        * queue, that is, remove, invoke, and re-insert if
+        * it is a periodical job. Make sure the sync is down
+        * while the job is running (it may add other jobs!)
+        */
+
+       protected void cron()
+       {
+               DeltaEntry      job;
+               long                    now,next;
+
+               while (running) {
+                       if (isDebug()) {
+                               printJobs();
+                               }
+
+                       now=System.currentTimeMillis();
+                       next = now + 0x00000000FFFFFFFFL;
+
+                       synchronized(deltaList) {
+                               for (job=deltaList.first(); job!=null; 
job=deltaList.first()) {
+                                       now=System.currentTimeMillis();
+                                       next = job.delta;
+
+                                       if (next>now) {
+                                               break;
+                                               }
+
+                                       if (isDebug()) {
+                                               debug(Level.FINER,"Running cron 
job, table is :");
+                                               printJobs();
+                                               }
+
+                                       runJob();
+
+                                       if (isDebug()) {
+                                               debug(Level.FINER,"Job run, new 
table is :");
+                                               printJobs();
+                                               }
+                                       }
+                               }
+
+                       next = next - now; /* how long to sleep */
+                       debug(Level.FINER,"Sleeping at "+now+" for "+next+" CU 
("+(next/1000)+" s, "+next+" CU).");
+/*                     if (isDebug()) {
+                               if (nxt<Long.MAX_VALUE) {
+                                       debug("Sleeping at "+d2s(ccc)+
+                                               " for "+toMillis(nxt)+" ms "+
+                                               "("+toSeconds(nxt)+" s, "+nxt+" 
CU)");
+                                       }
+                               else {
+                                       debug("Sleeping at "+d2s(ccc)+" for a 
long time...");
+                                       }
+                               }
+*/
+                       // how long do we sleep at most? In some systems, the 
signal-interrupted sleep does not work nicely,
+                       // so to ensure progress, we should rather wake up 
periodically. (But we don't want to burn too much
+                       // CPU time doing busy waiting; every 2s strikes a good 
balance)
+
+                       //todo: garder le mecanisme ???
+                       if (next > MAX_SLEEP)
+                               next = MAX_SLEEP;
+
+                       synchronized(waiter) {
+                               try {
+                                       waiter.wait(next);
+                                       }
+                               catch( InterruptedException x ) {
+                                       err("",x);
+                                       }
+                               }
+
+                       debug(Level.FINER,"Woke up at 
"+System.currentTimeMillis()+" - "+(System.currentTimeMillis()-(now+next))+" CU 
late.");
+//                             debug("Woke up at  "+d2s(ddd)+" 
("+(ddd-(ccc+nxt))+" CU late).");
+                       }
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       /**
+        * Get the current time, using the unit of time that the scheduler uses.
+        *
+        * @return the current time
+        */
+
+       public static long now()
+       {
+               return System.currentTimeMillis();
+       }
+
+       public static void sleep()
+       {
+               while (true) {
+                       sleep(MINUTES_1);
+                       }
+       }
+
+       public static boolean sleep( long u )
+       {
+               Logger  logger;
+
+               try {
+                       Thread.sleep(toMillis(u));
+                       return true;
+                       }
+               catch( InterruptedException x ) {
+                       logger=Logger.getLogger(Scheduler.class.getName());
+                       logger.log(Level.WARNING,"Interrupted while sleeping 
for "+u+" units.",x);
+                       }
+               return false;
+       }
+
+       public static long millis( long n )
+       {
+               return n*MILLIS;
+       }
+
+       public static long seconds( long n )
+       {
+               return n*SECONDS;
+       }
+
+       public static long minutes( long n )
+       {
+               return n*MINUTES;
+       }
+
+       public static long hours( long n )
+       {
+               return n*HOURS;
+       }
+
+       public static long days( long n )
+       {
+               return n*DAYS;
+       }
+
+       public static long toMillis( long n )
+       {
+               return n/MILLIS;
+       }
+
+       public static long toSeconds( long n )
+       {
+               return n/SECONDS;
+       }
+
+       public static long toMinutes( long n )
+       {
+               return n/MINUTES;
+       }
+
+       public static long toHours( long n )
+       {
+               return n/HOURS;
+       }
+
+       public static long toDays( long n )
+       {
+               return n/DAYS;
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       /**
+        * List of waiting tasks.
+        */
+
+       private static class DeltaList extends Object
+       {
+               /** The delta-list of waiting tasks. */
+               private DeltaEntry[]    entries;
+
+               /** The first empty slot in the delta-list. */
+               private int                     firstFree;
+
+               /** The first empty slot in the delta-list. */
+               private int                     firstUsed;
+
+
+               private DeltaList( int size )
+               {
+                       super();
+                       int     i;
+                       entries=new DeltaEntry[size];
+                       for (i=0; i<entries.length; i++) {
+                               entries[i]=new DeltaEntry();
+                               entries[i].next = i-1;
+                               }
+
+                       firstFree = entries.length-1;
+                       firstUsed  = -1;
+               }
+
+               public int getSize()
+               {
+                       int     jobId,n;
+
+                       //faire mieux
+                       n=0;
+                       jobId = firstUsed;
+                       while (jobId != -1) {
+                               n++;
+                               jobId = entries[jobId].next;
+                               }
+                       return n;
+               }
+
+               public DeltaEntry first()
+               {
+                       return (firstUsed != -1 ? entries[firstUsed] : null);
+               }
+
+               public DeltaEntry pop()
+               {
+                       DeltaEntry      job;
+                       int jobId;
+
+                       jobId = firstUsed;
+                       if (jobId == -1)
+                               return null; /* no job to be done */
+                       job    = entries[jobId];
+
+                       /* remove from queue */
+                       firstUsed= job.next;
+                       job.next        = firstFree;
+                       firstFree = jobId;
+                       return job;
+               }
+
+               public DeltaEntry find( ScheduledTask task )
+               {
+                       DeltaEntry      job;
+                       int     jobId;
+
+                       jobId = firstUsed;
+                       if (jobId == -1) {
+                               return null;
+                               }
+
+                       job = entries[jobId];
+                       while (!task.equals(job.task)) {
+                               if (job.next == -1) {
+                                       return null;
+                                       }
+                               jobId = job.next;
+                               job = entries[jobId];
+                               }
+                       return job;
+               }
+
+               public int insert( ScheduledTask task, long delta )
+               {
+                       DeltaEntry[]    tmp;
+                       DeltaEntry      entry,pos;
+                       int                     last,current,num;
+
+                       if (firstFree == -1) { /* need to grow */
+                               int i;
+
+                               tmp=new DeltaEntry[entries.length * 2];
+                               
System.arraycopy(entries,0,tmp,0,entries.length);
+                               entries=tmp;
+
+                               for (i=entries.length/2; i<entries.length; i++) 
{
+                                       entries[i]=new DeltaEntry();
+                                       entries[i].next = i-1;
+                                       }
+                               entries[entries.length/2].next = -1;
+                               firstFree = entries.length-1;
+                               }
+                       entry = entries[firstFree];
+                       entry.task = task;
+                       entry.delta = System.currentTimeMillis() + delta;
+                       if (firstUsed == -1) {
+                               firstUsed= firstFree;
+                               firstFree= entry.next;
+                               entry.next = -1; /* end of list */
+
+                               return 0;
+                               }
+
+                       /* no, there are jobs waiting */
+                       last = -1;
+                       current = firstUsed;
+                       pos = entries[current];
+
+                       num=0;
+                       while (entry.delta > pos.delta) {
+                               if (pos.next != -1) {
+                                       last = current;
+                                       current = pos.next;
+                                       pos = entries[current];
+                                       }
+                               else { /* append */
+                                       pos.next = firstFree;
+                                       firstFree= entry.next;
+                                       entry.next = -1;
+                                       return num+1;
+                                       }
+                               num++;
+                               }
+
+                       /* insert before pos */
+                       if (last == -1) {
+                               firstUsed = firstFree;
+                               }
+                       else
+                               entries[last].next = firstFree;
+                       firstFree= entry.next;
+                       entry.next = current;
+                       return num;
+               }
+
+               public boolean remove( ScheduledTask task )
+               {
+                       DeltaEntry      job;
+                       DeltaEntry      last;
+                       int jobId;
+
+                       jobId = firstUsed;
+                       if (jobId == -1) {
+                               return false;
+                               }
+
+                       last = null;
+                       job = entries[jobId];
+                       while (!task.equals(job.task)) {
+                               last = job;
+                               if (job.next == -1) {
+                                       return false;
+                                       }
+                               jobId = job.next;
+                               job = entries[jobId];
+                               }
+
+                       if (last != null)
+                               last.next = job.next;
+                       else
+                               firstUsed = job.next;
+
+                       job.next        = firstFree;
+                       firstFree = jobId;
+                       job.task = null;
+                       return true;
+               }
+
+               public DeltaEntry[] getEntries()
+               {
+                       int jobId,n;
+                       DeltaEntry[]    r;
+
+                       n=getSize();
+
+                       r=new DeltaEntry[n];
+                       n=0;
+
+                       jobId = firstUsed;
+                       while (jobId != -1) {
+                               r[n++]=entries[jobId];
+                               jobId = entries[jobId].next;
+                               }
+                       return r;
+               }
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       /**
+        * The list entry for the scheduled jobs.
+        */
+
+       private static class DeltaEntry extends Object
+       {
+               /** The start-time for this event (in milliseconds). */
+               private long                            delta;
+
+               /** The method to call at that point. */
+               private ScheduledTask           task;
+
+               /** The index of the next entry in the delta list after this 
one */
+               private int                             next;
+
+
+               private DeltaEntry()
+               {
+                       super();
+                       delta=0;
+                       task=null;
+                       next=-1;
+               }
+
+               public String label()
+               {
+                       return "{ what: "+task.getName()+", who: 
"+task.getAction().getClass().getName()+", when: "+toMillis(delta)+" ms, 
repeat: "+toMillis(task.getRepeatDelay())+" ms }";
+               }
+       }
+}

Added: freeway/src/org/gnu/freeway/util/Service.java
===================================================================
--- freeway/src/org/gnu/freeway/util/Service.java       2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/util/Service.java       2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,21 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util;
+
+/**
+ *
+ */
+
+public interface Service
+{
+       public String getName();
+       public void attach( ServiceManager s );
+       public void detach();
+
+       public void init() throws ServiceException;
+       public void start() throws ServiceException;
+       public void stop() throws ServiceException;
+       public void done() throws ServiceException;
+}

Added: freeway/src/org/gnu/freeway/util/ServiceException.java
===================================================================
--- freeway/src/org/gnu/freeway/util/ServiceException.java      2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/util/ServiceException.java      2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,23 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util;
+
+/**
+ *
+ */
+
+public class ServiceException extends RuntimeException
+{
+       public ServiceException( String msg )
+       {
+               super(msg);
+       }
+
+       public ServiceException( String msg, Throwable x )
+       {
+               this(msg);
+               initCause(x);
+       }
+}

Added: freeway/src/org/gnu/freeway/util/ServiceManager.java
===================================================================
--- freeway/src/org/gnu/freeway/util/ServiceManager.java        2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/util/ServiceManager.java        2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,216 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util;
+
+import org.gnu.freeway.*;
+
+import java.util.*;
+import java.util.logging.*;
+
+/**
+ *
+ */
+
+public class ServiceManager extends LoggedObject
+{
+       public static final String      EOL     =       
System.getProperty("line.separator");
+
+       private static ServiceManager   manager;
+
+       static {
+               manager=null;
+               }
+
+       private Map             services;
+       private Set             temporary;
+       private boolean started;
+
+       private int             level=1;
+       private List    all=new ArrayList();
+       private List    levels=new ArrayList();
+       private Application     app=null;
+
+
+       protected ServiceManager()
+       {
+               super(true);
+               services=new HashMap();
+               temporary=new HashSet();
+               started=false;
+       }
+
+       public String toString()
+       {
+               return "Service manager [services="+services+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public Application app()
+       {
+               return app;
+       }
+       public Service service( Class c )
+       {
+               Service s;
+
+               //todo: pister les dependances + les ordres d'instanciation
+
+               s=(Service) services.get(c);
+               if (s!=null) {
+                       return s;
+                       }
+
+               if (temporary.contains(c)) {
+                       trace("Circular loop (requested: "+c.getName()+") !");
+                       return null;
+                       }
+
+               try {
+                       s=(Service) c.newInstance();
+                       }
+               catch( Throwable x ) {
+                       err("Unable to instantiate service of class 
"+c.getName()+".",x);
+                       return null;
+                       }
+
+               all.add(s);
+               levels.add(new Integer(level));
+               level++;
+
+               temporary.add(c);
+               s.attach(this);
+               temporary.remove(c);
+
+               level--;
+               services.put(c,s);
+
+               try {
+                       s.init();
+                       if (started) {
+                               s.start();
+                               }
+                       }
+               catch( ServiceException x ) {
+                       err("Failed to init/start service '"+s.getName()+"' 
!",x);
+                       }
+               return s;
+       }
+
+       public void dump()
+       {
+               Service                 s;
+//             Iterator                iter;
+               StringBuffer    buf;
+               int     i;
+
+               //todo: afficher les dependances + les ordres d'instanciation
+               //   Service A(1) -> Service C(2), Service F(4)
+
+               buf=new StringBuffer();
+
+               for (i=0; i<all.size(); i++) {
+                       s=(Service) all.get(i);
+                       buf.append(" ["+i+"]  service '"+s.getName()+"' / level 
"+levels.get(i));
+                       buf.append(EOL);
+                       }
+
+               log(Level.INFO,"Loaded services :"+EOL+buf);
+       }
+
+       protected Iterator directIterator()
+       {
+               return new ArrayList(all).iterator();   // avoid concurrent 
modifications by iterating over a copy
+       }
+
+       protected Iterator reverseIterator()
+       {
+               List    tmp;
+
+               tmp=new ArrayList(all);         // avoid concurrent 
modifications by iterating over a copy
+               Collections.reverse(tmp);
+               return tmp.iterator();
+       }
+
+       public void start()
+       {
+               Service         s;
+               Iterator        iter;
+
+               if (started) {
+                       log(Level.SEVERE,"Already started.");
+                       return;
+                       }
+               started=true;
+
+               iter=directIterator();
+               while (iter.hasNext()) {
+                       s=(Service) iter.next();
+                       try {
+                               s.start();
+                               }
+                       catch( ServiceException x ) {
+                               err("Failed to start service '"+s.getName()+"' 
!",x);
+                               }
+                       }
+       }
+
+       public void stop()
+       {
+               Service         s;
+               Iterator        iter;
+
+               if (!started) {
+                       log(Level.SEVERE,"Not started.");
+                       return;
+                       }
+               started=false;
+
+               iter=reverseIterator();
+               while (iter.hasNext()) {
+                       s=(Service) iter.next();
+                       try {
+                               s.stop();
+                               }
+                       catch( ServiceException x ) {
+                               err("Failed to stop service '"+s.getName()+"' 
!",x);
+                               }
+                       }
+       }
+
+       public void shutdown()
+       {
+               Service         s;
+               Iterator        iter;
+
+               if (started) {
+                       stop();
+                       }
+
+               iter=reverseIterator();
+               while (iter.hasNext()) {
+                       s=(Service) iter.next();
+                       try {
+                               s.done();
+                               }
+                       catch( ServiceException x ) {
+                               err("Failed to shutdown service 
'"+s.getName()+"' !",x);
+                               }
+                       }
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public static synchronized ServiceManager getInstance( Application a )
+       {
+               if (manager==null) {
+                       manager=new ServiceManager();
+                       manager.app=a;
+                       }
+               return manager;
+       }
+}

Added: freeway/src/org/gnu/freeway/util/SignalEvent.java
===================================================================
--- freeway/src/org/gnu/freeway/util/SignalEvent.java   2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/util/SignalEvent.java   2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,41 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util;
+
+import java.util.*;
+
+/**
+ *
+ */
+
+public class SignalEvent extends EventObject
+{
+       private int     signal;
+
+
+       public SignalEvent( Object src, int sig )
+       {
+               super(src);
+               signal=sig;
+       }
+
+       public String toString()
+       {
+               return "Signal event [source="+getSource()+", 
signal="+signal+", signalName="+getSignalName()+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int getSignal()
+       {
+               return signal;
+       }
+
+       public String getSignalName()
+       {
+               return SignalManager.nameForSignal(signal);
+       }
+}

Added: freeway/src/org/gnu/freeway/util/SignalListener.java
===================================================================
--- freeway/src/org/gnu/freeway/util/SignalListener.java        2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/util/SignalListener.java        2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,14 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util;
+
+/**
+ *
+ */
+
+public interface SignalListener
+{
+       public void signalReceived( SignalEvent evt );
+}

Added: freeway/src/org/gnu/freeway/util/SignalManager.java
===================================================================
--- freeway/src/org/gnu/freeway/util/SignalManager.java 2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/util/SignalManager.java 2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,163 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util;
+
+import java.util.*;
+
+/**
+ *
+ */
+
+public class SignalManager extends Object implements Signals
+{
+       private static SignalManager    manager;
+
+       private static final String[]   NAMES   =       {
+               "SIGHUP", "SIGINT", "SIGQUIT", "SIGILL", "SIGTRAP", "SIGABRT", 
"SIGEMT", "SIGFPE",
+               "SIGKILL", "SIGBUS", "SIGSEGV", "SIGSYS", "SIGPIPE", "SIGALRM", 
"SIGTERM", "SIGURG",
+               "SIGSTOP", "SIGTSTP", "SIGCONT", "SIGCHLD", "SIGTTIN", 
"SIGTTOU", "SIGIO", "SIGXCPU",
+               "SIGXFSZ", "SIGVTALRM", "SIGPROF", "SIGWINCH", "SIGINFO", 
"SIGUSR1", "SIGUSR2"
+               };
+
+       static {
+               manager=null;
+               OSAccess.signalsInit();
+               Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
+                       public void run()
+                       {
+                               OSAccess.signalsDone();
+                       }
+                       },"SIGNALS-VM-SHUTDOWN"));
+               }
+
+       private Map     listeners;
+
+
+       protected SignalManager()
+       {
+               super();
+               listeners=new HashMap();
+               startMonitoring();
+       }
+
+       public String toString()
+       {
+               return "Signal manager";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int getPID()
+       {
+               return OSAccess.signalsGetPID();
+       }
+
+       public void signal( int sig )
+       {
+               signal(getPID(),sig);
+       }
+
+       public void signal( int pid, int sig )
+       {
+               OSAccess.signalsSignal(pid,sig);
+       }
+
+       public void addSignalListener( int sig, SignalListener l )
+       {
+               Set     set;
+
+               synchronized(listeners) {
+                       set=(Set) listeners.get(new Integer(sig));
+                       if (set==null) {
+                               set=new LinkedHashSet();
+                               listeners.put(new Integer(sig),set);
+
+                               OSAccess.signalsCatch(sig);
+                               }
+                       set.add(l);
+                       }
+       }
+
+       public void removeSignalListener( int sig, SignalListener l )
+       {
+               Set     set;
+
+               synchronized(listeners) {
+                       set=(Set) listeners.get(new Integer(sig));
+                       if (set==null) {
+                               return;
+                               }
+                       set.remove(l);
+                       if (set.size()==0) {
+                               listeners.remove(new Integer(sig));
+
+                               OSAccess.signalsLeave(sig);
+                               }
+                       }
+       }
+
+       protected void fire( int sig )
+       {
+               SignalEvent     evt;
+               Set                     set;
+               Iterator        iter;
+
+               synchronized(listeners) {
+                       set=(Set) listeners.get(new Integer(sig));
+                       if (set!=null) {
+                               evt=new SignalEvent(this,sig);
+
+                               iter=set.iterator();
+                               while (iter.hasNext()) {
+                                       ((SignalListener) 
iter.next()).signalReceived(evt);
+                                       }
+                               }
+                       }
+       }
+
+       protected void startMonitoring()
+       {
+               Task            t;
+
+               t=new Task("SIGNALS-MONITOR",new AbstractAction() {
+                       public void perform()
+                       {
+                               monitor();
+                       }
+                       });
+               t.launch();
+       }
+
+       protected void monitor()
+       {
+               int     num;
+
+               while (true) {
+                       num=OSAccess.signalsWait();
+                       fire(num);
+                       }
+
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public static synchronized SignalManager getInstance()
+       {
+               if (manager==null) {
+                       manager=new SignalManager();
+                       }
+               return manager;
+       }
+
+       public static String nameForSignal( int signal )
+       {
+               if (signal<1 || signal>31) {
+                       return null;
+                       }
+               return NAMES[signal-1];
+       }
+}

Added: freeway/src/org/gnu/freeway/util/Signals.java
===================================================================
--- freeway/src/org/gnu/freeway/util/Signals.java       2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/util/Signals.java       2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,150 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util;
+
+//TODO: doc à vérifier
+/**
+        Signal     Value     Action   Comment
+        
------------------------------------------------------------------------
+        SIGHUP        1        A      Hangup detected on controlling terminal 
or death of controlling process
+        SIGINT        2        A      Interrupt from keyboard
+        SIGQUIT       3        A      Quit from keyboard
+        SIGILL        4        A      Illegal Instruction
+        SIGABRT       6        C      Abort signal from abort(3)
+        SIGFPE        8        C      Floating point exception
+        SIGKILL       9       AEF     Kill signal
+        SIGSEGV      11        C      Invalid memory reference
+        SIGPIPE      13        A      Broken pipe: write to pipe with no 
readers
+        SIGALRM      14        A      Timer signal from alarm(1)
+        SIGTERM      15        A      Termination signal
+        SIGUSR1   30,10,16     A      User-defined signal 1
+        SIGUSR2   31,12,17     A      User-defined signal 2
+        SIGCHLD   20,17,18     B      Child stopped or terminated
+        SIGCONT   19,18,25            Continue if stopped
+        SIGSTOP   17,19,23    DEF     Stop process
+        SIGTSTP   18,20,24     D      Stop typed at tty
+        SIGTTIN   21,21,26     D      tty input for background process
+        SIGTTOU   22,22,27     D      tty output for background process
+
+        Next various other signals.
+
+        Signal       Value     Action   Comment
+        ---------------------------------------------------------------------
+        SIGTRAP        5         CG     Trace/breakpoint trap
+        SIGIOT         6         CG     IOT trap. A synonym for SIGABRT
+        SIGEMT       7,-,7       G
+        SIGBUS      10,7,10      AG     Bus error
+        SIGSYS      12,-,12      G      Bad argument to routine (SVID)
+        SIGSTKFLT    -,16,-      AG     Stack fault on coprocessor
+        SIGURG      16,23,21     BG     Urgent condition on socket (4.2 BSD)
+        SIGIO       23,29,22     AG     I/O now possible (4.2 BSD)
+        SIGPOLL                  AG     A synonym for SIGIO (System V)
+        SIGCLD       -,-,18      G      A synonym for SIGCHLD
+        SIGXCPU     24,24,30     AG     CPU time limit exceeded (4.2 BSD)
+        SIGXFSZ     25,25,31     AG     File size limit exceeded (4.2 BSD)
+        SIGVTALRM   26,26,28     AG     Virtual alarm clock (4.2 BSD)
+        SIGPROF     27,27,29     AG     Profile alarm clock
+        SIGPWR      29,30,19     AG     Power failure (System V)
+        SIGINFO      29,-,-      G      A synonym for SIGPWR
+        SIGLOST      -,-,-       AG     File lock lost
+        SIGWINCH    28,28,20     BG     Window resize signal (4.3 BSD, Sun)
+        SIGUNUSED    -,31,-      AG     Unused signal
+ */
+
+public interface Signals
+{
+       /** hangup */
+       public static final int SIGHUP          =       1;
+
+       /** interrupt */
+       public static final int SIGINT          =       2;
+
+       /** quit */
+       public static final int SIGQUIT         =       3;
+
+       /** illegal instruction (not reset when caught) */
+       public static final int SIGILL          =       4;
+
+       /** trace trap (not reset when caught) */
+       public static final int SIGTRAP         =       5;
+
+       /** abort() */
+       public static final int SIGABRT         =       6;
+
+       /** EMT instruction */
+       public static final int SIGEMT          =       7;
+
+       /** floating point exception */
+       public static final int SIGFPE          =       8;
+
+       /** kill (cannot be caught or ignored) */
+       public static final int SIGKILL         =       9;
+
+       /** bus error */
+       public static final int SIGBUS          =       10;
+
+       /** segmentation violation */
+       public static final int SIGSEGV         =       11;
+
+       /** bad argument to system call */
+       public static final int SIGSYS          =       12;
+
+       /** write on a pipe with no one to read it */
+       public static final int SIGPIPE         =       13;
+
+       /** alarm clock */
+       public static final int SIGALRM         =       14;
+
+       /** software termination signal from kill */
+       public static final int SIGTERM         =       15;
+
+       /** urgent condition on IO channel */
+       public static final int SIGURG          =       16;
+
+       /** sendable stop signal not from tty */
+       public static final int SIGSTOP         =       17;
+
+       /** stop signal from tty */
+       public static final int SIGTSTP         =       18;
+
+       /** continue a stopped process */
+       public static final int SIGCONT         =       19;
+
+       /** to parent on child stop or exit */
+       public static final int SIGCHLD         =       20;
+
+       /** to readers pgrp upon background tty read */
+       public static final int SIGTTIN         =       21;
+
+       /** like TTIN for output if (tp.t_local & LTOSTOP) */
+       public static final int SIGTTOU         =       22;
+
+       /** input/output possible signal */
+       public static final int SIGIO           =       23;
+
+       /** exceeded CPU time limit */
+       public static final int SIGXCPU         =       24;
+
+       /** exceeded file size limit */
+       public static final int SIGXFSZ         =       25;
+
+       /** virtual time alarm */
+       public static final int SIGVTALRM       =       26;
+
+       /** profiling time alarm */
+       public static final int SIGPROF         =       27;
+
+       /** window size changes */
+       public static final int SIGWINCH                =       28;
+
+       /** information request */
+       public static final int SIGINFO         =       29;
+
+       /** user defined signal 1 */
+       public static final int SIGUSR1         =       30;
+
+       /** user defined signal 2 */
+       public static final int SIGUSR2         =       31;
+}

Added: freeway/src/org/gnu/freeway/util/SlaveTask.java
===================================================================
--- freeway/src/org/gnu/freeway/util/SlaveTask.java     2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/util/SlaveTask.java     2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,89 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util;
+
+import EDU.oswego.cs.dl.util.concurrent.*;
+
+import java.util.logging.*;
+
+/**
+ *
+ */
+
+public class SlaveTask extends Task
+{
+       private Semaphore       signal;
+       private boolean         shutdown;
+
+
+       public SlaveTask( String str, Action a )
+       {
+               super(str,a);
+               signal=null;
+               shutdown=false;
+       }
+
+       public String toString()
+       {
+               return "Slave task [name="+name+", action="+action+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public boolean launch()
+       {
+               if (isRunning()) {
+                       return false;
+                       }
+
+               signal=new Semaphore(0);
+               shutdown=false;
+               return super.launch();
+       }
+
+       public boolean shutdown()
+       {
+               if (!isRunning()) {
+                       return false;
+                       }
+
+               shutdown=true;
+               signal.release();
+               return join();
+       }
+
+       public void signal()
+       {
+               if (isRunning()) {
+                       signal.release();
+                       }
+       }
+
+       public void exec()
+       {
+               log(Level.FINE,"Start task '"+name+"'...");
+
+               while (!shutdown) {
+                       try {
+                               signal.acquire();
+                               }
+                       catch( InterruptedException x ) {
+                               err("Failed to acquire signalling semaphore of 
task '"+name+"'.",x);
+                               }
+
+                       if (!shutdown) {
+                               try {
+                                       action.perform();
+                                       }
+                               catch( Throwable x ) {
+                                       err("Aarrgghhhh, action for task 
'"+name+"' abruptly aborted. Will try again !",x);
+                                       }
+                               }
+                       }
+
+               log(Level.FINE,"Executed task '"+name+"'.");
+       }
+}

Added: freeway/src/org/gnu/freeway/util/StarFormatter.java
===================================================================
--- freeway/src/org/gnu/freeway/util/StarFormatter.java 2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/util/StarFormatter.java 2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,166 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util;
+
+import java.text.*;
+import java.util.*;
+import java.util.logging.*;
+import java.util.regex.*;
+
+/**
+ * Not ASCII-art.
+ */
+
+public class StarFormatter extends Formatter
+{
+       private static final String     EOL                             =       
System.getProperty("line.separator");
+       private static final int                INDENT_DELTA            =       
10;
+       private static final boolean    INDENT_THREADS  =       false;
+
+       private Pattern         splitter;
+       private DateFormat      dateFormatter;
+       private Map                     indents;
+       private int                     currentIndent;
+
+
+       public StarFormatter()
+       {
+               super();
+               splitter=Pattern.compile("[\n\r]+");
+               dateFormatter=new SimpleDateFormat("HH:mm:ss");
+               indents=new HashMap();
+               currentIndent=0;
+       }
+
+       public String toString()
+       {
+               return "Star (log) formatter";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       protected int getIndent( int id )
+       {
+               Number  num;
+
+               num=(Number) indents.get(new Integer(id));
+               if (num==null) {
+                       num=new Integer(currentIndent);
+                       currentIndent+=INDENT_DELTA;
+                       indents.put(new Integer(id),num);
+                       }
+               return num.intValue();
+       }
+
+       public String getHead( Handler hd )
+       {
+               return "";
+       }
+
+       public String getTail( Handler hd )
+       {
+               return "";
+       }
+
+       public synchronized String format( LogRecord rec )
+       {
+               StringBuffer    buf;
+               String                  str;
+               int                             level,ind;
+
+               buf=new StringBuffer();
+               buf.append(dateFormatter.format(new Date(rec.getMillis())));
+               buf.append(" ");
+
+               str=rec.getSourceClassName();
+               if (str!=null) {
+                       str=str.substring(str.lastIndexOf('.')+1);
+                       }
+               else {
+                       str="-";
+                       }
+               buf.append(Utils.alignCenter(str,24,'_'));
+
+               if (INDENT_THREADS) {
+                       ind=getIndent(rec.getThreadID());
+                       if (ind>0) {
+                               buf.append(" ");
+                               buf.append(Utils.repeat('_',ind));
+                               }
+                       }
+
+               buf.append(" [");
+               level=rec.getLevel().intValue();
+               if (level>=1000) {      // SEVERE
+                       buf.append("******");
+                       }
+               else if (level>=900) {  // WARNING
+                       buf.append("***** ");
+                       }
+               else if (level>=800) {  // INFO
+                       buf.append("....  ");
+                       }
+               else if (level>=700) {  // CONFIG
+                       buf.append("....  ");
+                       }
+               else if (level>=500) {  // FINE
+                       buf.append("...   ");
+                       }
+               else if (level>=400) {  // FINER
+                       buf.append("..    ");
+                       }
+               else if (level>=300) {  // FINEST
+                       buf.append(".     ");
+                       }
+               buf.append("] ");
+
+               addMessage(rec.getMessage(),buf);
+               addException(rec.getThrown(),buf);
+               return buf.toString();
+       }
+
+       protected void addMessage( String str, StringBuffer buf )
+       {
+               String[]        p;
+               int                     i;
+
+               if (str==null) {
+                       str="";
+                       }
+
+               p=splitter.split(str);
+               for (i=0; i<p.length; i++) {
+                       buf.append((i>0 ? "      (continued) " : "")+p[i]+EOL);
+                       }
+       }
+
+       protected void addException( Throwable x, StringBuffer buf )
+       {
+               StackTraceElement[]     elems;
+               String[]                        p;
+               int                                     i;
+
+               while (x!=null) {
+                       buf.append("      > ["+x.getClass().getName()+"] ");
+
+                       p=splitter.split(x.getMessage()!=null ? x.getMessage() 
: "");
+                       for (i=0; i<p.length; i++) {
+                               buf.append((i>0 ? "      (continued) " : 
"")+p[i].trim()+EOL);
+                               }
+
+                       elems=x.getStackTrace();
+                       for (i=0; i<elems.length; i++) {
+                               buf.append("      | "+elems[i]+EOL);
+                               }
+                       buf.append(EOL);
+
+                       if (x.getCause()!=null) {
+                               buf.append("--- nested ---"+EOL);
+                               }
+                       x=x.getCause();
+                       }
+       }
+}

Added: freeway/src/org/gnu/freeway/util/Stat.java
===================================================================
--- freeway/src/org/gnu/freeway/util/Stat.java  2005-01-31 23:47:23 UTC (rev 
136)
+++ freeway/src/org/gnu/freeway/util/Stat.java  2005-02-01 01:07:27 UTC (rev 
137)
@@ -0,0 +1,182 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util;
+
+import org.gnu.freeway.util.net.*;
+
+import EDU.oswego.cs.dl.util.concurrent.*;
+
+import java.nio.*;
+
+/**
+ * A statistical value along with its description.
+ */
+
+public class Stat extends Object implements Persistent
+{
+       public static final int VERBOSE =       1;
+       public static final int NORMAL  =       5;
+
+       private String  name;
+       private int             level;
+       private boolean timed;
+       private long            value;
+       private Sync            lck;
+
+
+       public Stat( String str )
+       {
+               this(str,NORMAL);
+       }
+
+       public Stat( String str, int lev )
+       {
+               super();
+               name=str;
+               level=lev;
+               timed=false;
+               value=0;
+               lck=new ReentrantLock();
+       }
+
+       public String toString()
+       {
+               return "Statistic [name="+name+", level="+level+", 
timed="+timed+", value="+value+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public void lock()
+       {
+               try {
+                       lck.acquire();
+                       }
+               catch( InterruptedException x ) {
+                       }
+       }
+
+       public void unlock()
+       {
+               lck.release();
+       }
+
+       public int getLevel()
+       {
+               return level;
+       }
+
+       public void setLevel( int lev )
+       {
+               lock();
+               level=lev;
+               unlock();
+       }
+
+       public boolean isTimed()
+       {
+               return timed;
+       }
+
+       public void setTimed( boolean flag )
+       {
+               lock();
+               timed=flag;
+               unlock();
+       }
+
+       public void dump()
+       {
+               System.out.println(" "+name+"/"+level+" : "+value);
+       }
+
+       public String getName()
+       {
+               return name;
+       }
+
+       public void reset()
+       {
+               lock();
+               value=0;
+               unlock();
+       }
+
+       public long get()
+       {
+               return value;
+       }
+
+       public void set( long n )
+       {
+               lock();
+               value=n;
+               unlock();
+       }
+
+       /**
+        * Changes the associated statistic value by <code>n</code>.
+        * @param n
+        */
+
+       public void add( long n )
+       {
+               lock();
+               value+=n;
+               unlock();
+       }
+
+       public void sub( long n )
+       {
+               lock();
+               value-=n;
+               unlock();
+       }
+
+       public void inc()
+       {
+               lock();
+               value++;
+               unlock();
+       }
+
+       public void dec()
+       {
+               lock();
+               value--;
+               unlock();
+       }
+
+       public int getByteSize()
+       {
+               return name.getBytes().length+9+2;
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               byte[]  b;
+               int             pos,len;
+
+               value=buf.getLong();
+               level=buf.getShort() & 0x0000ffff;
+
+               pos=buf.position();
+               for (len=0; buf.get()!=0; len++) {}
+               buf.position(pos);
+
+               b=new byte[len];
+               buf.get(b);
+               name=new String(b);
+               err.reportIf(buf.get()!=0,"string not null-terminated");        
// last 0
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               buf.putLong(value);
+               buf.putShort((short) level);
+               buf.put(name.getBytes());
+               buf.put((byte) 0);
+       }
+}

Added: freeway/src/org/gnu/freeway/util/Statistics.java
===================================================================
--- freeway/src/org/gnu/freeway/util/Statistics.java    2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/util/Statistics.java    2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,198 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util;
+
+import org.gnu.freeway.server.*;
+import org.gnu.freeway.util.net.*;
+
+import EDU.oswego.cs.dl.util.concurrent.*;
+
+import java.util.*;
+
+/**
+ * GNUnet statistics module.
+ * keeping statistics of GNUnet activities
+ *
+ * This module keeps a mapping of strings to values.
+ * Every entry in the mapping can be accessed with a handle
+ * (int) which can be obtained from the string. The module can be used
+ * to keep track of certain statistical information, such as the
+ * number of bytes received, messages sent, kilobytes stored, and so
+ * on.<p>
+ *
+ * When used within gnunetd, the gnunet-stats tool can be used to
+ * print the statistical information stored in this module.
+ */
+
+public class Statistics extends LoggedObject
+{
+       /** When did the module start ? */
+       private long            startTime;
+
+       /** A description for each of the values. */
+       private List            stats;
+
+       /** */
+       private Sync            lock;
+
+
+       public Statistics()
+       {
+               super(true);
+               startTime=Scheduler.now();
+               stats=new ArrayList();
+               lock=new ReentrantLock();
+       }
+
+       public String toString()
+       {
+               return "Statistics [startTime="+startTime+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public void done()
+       {
+               lock();
+               stats.clear();
+               unlock();
+       }
+
+       public void lock()
+       {
+               try {
+                       lock.acquire();
+                       }
+               catch( InterruptedException x ) {
+                       }
+       }
+
+       public void unlock()
+       {
+               lock.release();
+       }
+
+       public void lockAll()
+       {
+               int     i;
+
+               lock();
+               for (i=0; i<stats.size(); i++) {
+                       ((Stat) stats.get(i)).lock();
+                       }
+       }
+
+       public void unlockAll()
+       {
+               int     i;
+
+               for (i=0; i<stats.size(); i++) {
+                       ((Stat) stats.get(i)).unlock();
+                       }
+               unlock();
+       }
+
+       /**
+        * Get a handle to a statistical entity.
+        *
+        * @param name  a description of the entity
+        * @return              a handle for updating the associated value
+        */
+
+       public Stat getHandle( String name )
+       {
+               return getHandle(name,Stat.NORMAL);
+       }
+
+       public Stat getHandle( String name, int level )
+       {
+               Stat    stat;
+               int                     i;
+
+               assert(name!=null);
+
+               lock();
+
+               for (i=0; i<stats.size(); i++) {
+                       stat=(Stat) stats.get(i);
+                       if (stat.getName().equals(name)) {
+                               return stat;
+                               }
+                       }
+
+               stat=new Stat(name,level);
+               stats.add(stat);
+               unlock();
+
+               return stat;
+       }
+
+       /**
+        * Create statistics message(s) to be sent over a TCP socket.
+        * May create multiple messages if the overall size would be too big.
+        * @param minLevel
+        * @return
+        */
+
+       public CSStatistics[] createMessages( int minLevel )
+       {
+               List                            list;
+               CSStatistics            msg;
+               Stat            stat;
+               int                             total,i;
+
+               lockAll();
+
+               list=new ArrayList();
+
+               for (i=total=0; i<stats.size(); i++) {
+                       stat=(Stat) stats.get(i++);
+                       if (stat.getLevel()>=minLevel) {
+                               total++;
+                               }
+                       }
+
+               i=0;
+               while (i<stats.size()) {
+                       msg=new CSStatistics((int) 
Scheduler.toSeconds(startTime));
+                       msg.setTotalCounters(total);
+
+                       // how many statistic numbers and their descriptions we 
can send in one message ?
+                       while (i<stats.size() && 
msg.getByteSize()<CSMessage.MAX_MESSAGE_SIZE) {
+                               stat=(Stat) stats.get(i++);
+                               if (stat.getLevel()>=minLevel) {
+                                       msg.add(stat);
+                                       }
+                               }
+
+                       if (msg.getByteSize()>=CSMessage.MAX_MESSAGE_SIZE) {
+                               msg.removeLast();
+                               i--;
+                               }
+
+                       list.add(msg);
+                       }
+
+               unlockAll();
+
+               return (CSStatistics[]) list.toArray(new 
CSStatistics[list.size()]);
+       }
+
+       public void dump()
+       {
+               int     i;
+
+               lockAll();
+
+               System.out.println("Statistics service started at 
"+Utils.formatMoment(startTime));
+               System.out.println("Handles count : "+stats.size());
+               for (i=0; i<stats.size(); i++) {
+                       ((Stat) stats.get(i)).dump();
+                       }
+
+               unlockAll();
+       }
+}

Added: freeway/src/org/gnu/freeway/util/StatusCallsService.java
===================================================================
--- freeway/src/org/gnu/freeway/util/StatusCallsService.java    2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/util/StatusCallsService.java    2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,783 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util;
+
+import java.util.*;
+import java.util.logging.*;
+
+/**
+ * Calls to determine current network and CPU load.
+ *
+ * Status calls implementation for load management.
+ * Status calls implementation.  Usage is how much we are using
+ * (relative to what is available). Load is what we are using relative
+ * to what we are allowed to use.
+ *
+ * Todo:
+ * - determining load between calls to /proc might be made
+ *   interface specific
+ * - port to other platforms
+ */
+
+public class StatusCallsService extends AbstractService
+{
+       private Scheduler                       scheduler;
+       private Prefs   prefs;
+
+       /** Pointer to the name of each interface, has numInterfaces entries */
+       private String[]        interfacePtrs;
+
+       /** how many interfaces do we have? */
+       private int                     numInterfaces;
+
+       /* configuration */
+       private int                     maxNetDownBPS;
+       private int                     maxNetUpBPS;
+       private int                     maxCPULoad;                /* in 
percent of 1 CPU */
+       private double          lastNetResultUp;   /* the max upstream load we 
saw last time */
+       private double          lastNetResultDown; /* the max dnstream load we 
saw last time */
+       private long            lastnettimeUp =  0;  /* when did we check last 
time? */
+       private long            lastnettimeDown =  0;  /* when did we check 
last time? */
+       private boolean         useBasicMethod;   /* how to measure traffic */
+
+       /* tracking */
+       private NetworkStats[]  last_net_results; /* has numInterfaces entries 
*/
+
+       /* quota */
+       private Stat            stat_handle_network_load_up;
+       private Stat            stat_handle_network_load_down;
+       private Stat            stat_handle_cpu_load;
+
+       /* for async configuration update! */
+       private Object                  statusMutex;
+
+       private NetworkStats    globalTrafficBetweenProc;
+
+       private boolean                 initialized_ = false;
+
+       private int                     lastNetworkRetUp;
+       private int                     lastNetworkRetDown;
+       private int                             lastCPUUsage;
+       private long                    lastcputime;
+       private int                             lastcpuresult;
+       private long                    lastCallNUp;
+       private long                    lastCallNDown;
+       private long                    lastCPUCall;
+       private ScheduledTask           updateTask;
+
+
+       public StatusCallsService()
+       {
+               super("Status calls");
+               interfacePtrs=null;
+               numInterfaces=0;
+               lastNetResultUp=-1;
+               lastNetResultDown=-1;
+               useBasicMethod=true;
+               lastNetworkRetUp=0;
+               lastNetworkRetDown=0;
+               lastCPUUsage=0;
+               lastcputime=0;
+               lastcpuresult=-1;
+
+               setDebug(true);
+               updateTask=new ScheduledTask("LOAD-UPDATE",new 
EvalAction(this,"cronLoadUpdate"),Scheduler.SECS_10);
+       }
+
+       public String toString()
+       {
+               return "Status calls service";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public void attach( ServiceManager mgr )
+       {
+               super.attach(mgr);
+               scheduler=mgr.app().getScheduler();
+               prefs=mgr.app().getPreferences();
+       }
+
+       /**
+        * The following method is called in order to initialize the status 
calls
+        * routines. After that it is safe to call each of the status calls 
separately
+        */
+
+       public void init()
+       {
+               Statistics      stats;
+
+               super.init();
+               initialized_ = true;
+               statusMutex=new Object();
+               last_net_results = null; /* has numInterfaces entries */
+               interfacePtrs = null;
+               last_net_results = null;
+               globalTrafficBetweenProc=new NetworkStats();
+               globalTrafficBetweenProc.last_in = 0;
+               globalTrafficBetweenProc.last_out = 0;
+
+               stats=getManager().app().getStatistics();
+               stat_handle_network_load_up = stats.getHandle("% of allowed 
network load (up)");
+               stat_handle_network_load_down = stats.getHandle("% of allowed 
network load (down)");
+               stat_handle_cpu_load= stats.getHandle("% of allowed cpu load");
+       }
+
+       public void start()
+       {
+               super.start();
+               lastnettimeUp=Scheduler.now();
+               lastnettimeDown=Scheduler.now();
+               prefs.registerConfigurationUpdateCallback(new 
EvalAction(this,"resetStatusCalls"));
+               resetStatusCalls();
+               networkUsageUp();
+               networkUsageDown();
+               cpuUsage();
+
+               scheduler.addJob(updateTask,Scheduler.SECS_10);
+       }
+
+       public void stop()
+       {
+               super.stop();
+               prefs.unregisterConfigurationUpdateCallback(new 
EvalAction(this,"resetStatusCalls"));
+               scheduler.deleteJob(updateTask);
+       }
+
+       /**
+        * Shutdown the status calls module.
+        * Shutdown the module.
+        */
+
+       public void done()
+       {
+               super.done();
+               interfacePtrs=null;
+               last_net_results=null;
+               statusMutex=null;
+               initialized_=false;
+       }
+
+       /**
+        * Increment the number of bytes sent.  Transports should use this
+        * so that statuscalls module can measure gnunet traffic usage between
+        * calls to /proc.
+        *
+        * Note: the caller doesn't know what interface it is attached to,
+        * so this type of bandwidth limitation is always global (for all
+        * network interfaces).
+        * @param delta
+        */
+
+       public void incrementBytesSent( long delta )
+       {
+               if (!initialized_) {
+                       return;
+                       }
+
+               synchronized(statusMutex) {
+                       globalTrafficBetweenProc.last_out += delta;
+                       }
+       }
+
+       /**
+        * Tell statuscalls to increment the number of bytes received
+        * @param delta
+        */
+
+       public void incrementBytesReceived( long delta )
+       {
+               if (!initialized_) {
+                       return;
+                       }
+
+               synchronized(statusMutex) {
+                       globalTrafficBetweenProc.last_in += delta;
+                       }
+       }
+
+       public void cronLoadUpdate()
+       {
+               getCPULoad();
+               getNetworkLoadUp();
+               getNetworkLoadDown();
+       }
+
+       /**
+        * Re-read the configuration for statuscalls.
+        */
+
+       protected void resetStatusCalls()
+       {
+               String  interfaces;
+               String  ifcs;
+               boolean start;
+
+               synchronized(statusMutex) {
+                       interfaces= prefs.getString("LOAD","INTERFACES",null);
+                       ifcs = interfaces;
+                       /* fail if config-file is incomplete */
+                       if (interfaces == null) {
+                               log(Level.SEVERE,"No network interfaces defined 
!");
+                               numInterfaces = 0;
+                               return;
+                               }
+
+                       /* The string containing the interfaces is formatted in 
the following way:
+                        * each comma is replaced by '\0' and the pointers to 
the beginning of every
+                        * interface are stored
+                        */
+                       numInterfaces = 0;
+                       start = true;
+                       while (true) {
+                               if (interfaces.length()==0) {
+                                       if (!start)
+                                               numInterfaces++;
+                                       break;
+                                       }
+                               char    c=interfaces.charAt(0);
+                               if ( ((c>='a') && (c<='z')) || ((c>='A') && 
(c<='Z')) || ((c>='0') && (c<='9')) ) {
+                                       start = false;
+                                       }
+                               else {
+                                       if (c != ',') {
+                                               trace("interfaces string 
("+ifcs+") invalid");
+                                               return;
+                                               }
+                                       if (!start) {
+                                               start = true;
+                                               numInterfaces++;
+                                               }
+                                       }
+                               interfaces=interfaces.substring(1);
+                               }
+
+                       if (numInterfaces <= 0) {
+                               log(Level.SEVERE,"No network interfaces 
specified in the configuration file.");
+                               return;
+                               }
+
+                       interfacePtrs = new String[numInterfaces];
+                       last_net_results = new NetworkStats[numInterfaces];
+                       for (int i=0; i<numInterfaces; i++) {
+                               last_net_results[i]=new NetworkStats();
+                               }
+
+                       /* 2nd pass, this time remember the positions */
+                       interfaces = ifcs;
+                       numInterfaces = 0;
+                       start = true;
+                       while (true) {
+                               if (interfaces.length()==0) {
+                                       if (!start)
+                                               numInterfaces++;
+                                       break;
+                                       }
+                               char c=interfaces.charAt(0);
+                               if ( ((c>='a') && (c<='z')) ||
+                                               ((c>='A') && (c<='Z')) ||
+                                               ((c>='0') && (c<='9')) ) {
+                                       if (start) {
+                                               start = false;
+                                               interfacePtrs[numInterfaces] = 
interfaces;
+                                               }
+                                       }
+                               else {
+                                       if (!start) {
+                                               start = true;
+                                               interfaces = "";
+                                               numInterfaces++;
+                                               }
+                                       }
+                               interfaces=interfaces.substring(1);
+                               }
+
+                       useBasicMethod= prefs.testString("LOAD",        
"BASICLIMITING","YES");
+                       maxNetDownBPS = 
prefs.getInt("LOAD","MAXNETDOWNBPSTOTAL",0);
+                       if (maxNetDownBPS == 0)
+                               maxNetDownBPS = 50000;
+                       maxNetUpBPS = prefs.getInt("LOAD","MAXNETUPBPSTOTAL",0);
+                       if (maxNetUpBPS == 0)
+                               maxNetUpBPS = 50000;
+                       maxCPULoad= prefs.getInt("LOAD","MAXCPULOAD",0);
+                       if (maxCPULoad == 0)
+                               maxCPULoad = 100;
+                       }
+       }
+
+       /**
+        * The basic usage meter considers only gnunetd traffic.
+        * @return
+        */
+
+       protected int networkUsageBasicUp()
+       {
+               long    now, elapsedTime;
+               double  upUsage;
+               double  etc;
+
+               synchronized(statusMutex) {
+                       /* If called less than 1 seconds ago, calc average
+                        between avg. traffic of last second and the additional
+                        gained traffic.  If more than 1 second between calls,
+                        set the avg bytes/sec as the usage of last second. */
+                       now=Scheduler.now();
+                       elapsedTime = now - lastnettimeUp;
+                       etc = (double) elapsedTime / (double) Scheduler.SECS_1;
+                       if (elapsedTime < Scheduler.SECS_1) {
+                               upUsage = ( ( lastNetResultUp +
+                                               
(globalTrafficBetweenProc.last_out * etc) )
+                               / (1.0 + etc) );
+                               }
+                       else {
+                               upUsage = globalTrafficBetweenProc.last_out / 
etc;
+                               lastNetResultUp = upUsage;
+                               globalTrafficBetweenProc.last_out = 0;
+                               lastnettimeUp = now;
+                               }
+                       }
+               return (int)(100.0 * (upUsage / maxNetUpBPS));
+       }
+
+       /**
+        * The basic usage meter considers only gnunetd traffic.
+        * @return
+        */
+
+       protected int networkUsageBasicDown()
+       {
+               long    now, elapsedTime;
+               double  downUsage;
+               double  etc;
+
+               synchronized(statusMutex) {
+                       /* If called less than 1 seconds ago, calc average
+                        between avg. traffic of last second and the additional
+                        gained traffic. If more than 1 second between calls,
+                        set the avg bytes/sec as the usage of last second. */
+                       now=Scheduler.now();
+                       elapsedTime = now - lastnettimeDown;
+                       etc = (double) elapsedTime / (double) Scheduler.SECS_1;
+                       if (elapsedTime < Scheduler.SECS_1) {
+                               downUsage = ( ( lastNetResultDown + 
(globalTrafficBetweenProc.last_in * etc) ) / (1.0 + etc) );
+                               }
+                       else {
+                               downUsage = globalTrafficBetweenProc.last_in / 
etc;
+                               lastNetResultDown = downUsage;
+                               globalTrafficBetweenProc.last_in = 0;
+                               lastnettimeDown = now;
+                               }
+                       }
+               return (int)(100.0 * downUsage / maxNetDownBPS);
+       }
+
+       /**
+        * The advanced usage meter takes into account all network traffic.
+        * This might be problematic on systems where the same interface
+        * can have different capabilities for different types of traffic
+        * (like support for very fast local traffic but capable of
+        * handling only small-scale inet traffic).
+        * @return
+        */
+
+       protected int networkUsageAdvancedDown()
+       {
+               long                    rxnew,rxdiff;
+               int                             ifnum,i;
+               long                    now, elapsedtime;
+               String                  line;
+               OutputFilter    filter;
+               Iterator                iter;
+
+               synchronized(statusMutex) {
+                       /* first, make sure maxNetDownBPS is not 0, we don't 
want to divide by 0, really. */
+                       if (maxNetDownBPS == 0) {
+                               lastNetResultDown = -1;
+                               return -1;
+                               }
+
+                       /* If we checked /proc less than 2 seconds ago, don't 
do it again, but add internal gnunet traffic increments */
+                       now=Scheduler.now();
+                       elapsedtime = now - lastnettimeDown;
+                       if (elapsedtime == 0) {
+                               return (int) lastNetResultDown;
+                               }
+                       if (elapsedtime < Scheduler.SECS_2) {
+                               /* Only take additional gnunetd traffic into 
account, don't try to
+                                measure other *system* traffic more frequently 
than every 2s */
+                               double gnunetBPS;
+                               double gnunetLOAD;
+                               int ret;
+
+                               //todo: check si perte de precision ??? -> 
devrait toujours donner 0...
+                               gnunetBPS= 
(Scheduler.MILLIS_1/Scheduler.SECS_1) * globalTrafficBetweenProc.last_in / 
elapsedtime;
+
+                               gnunetLOAD= 100 * gnunetBPS / maxNetDownBPS;
+                               /* weigh last global measurement and gnunetd 
load,
+                                with 100% global measurement at first and 
50/50 mix
+                                just before we take the next measurement */
+                               ret =(int) ( (Scheduler.SECS_2 * 
lastNetResultDown + elapsedtime * gnunetLOAD) /
+                                               (Scheduler.SECS_2 + 
elapsedtime));
+                               return ret;
+                               }
+
+                       globalTrafficBetweenProc.last_in = 0;
+                       lastnettimeDown = now;
+
+                       /* ok, full program... */
+                       rxdiff = 0;
+
+                       filter=new 
OutputFilter("^\\S+\\s+\\S+\\s+\\S+\\s+\\S+\\s+(\\d+)\\s+\\S+\\s+(\\d+)\\s+\\S+\\s+\\S+\\s?$");
+                       iter=filter.filter("netstat -n -f inet -i");
+                       if (iter==null) {
+                               log("Could not open 'netstat' !");
+                               lastNetResultDown = -1;
+                               return -1;
+                               }
+
+                       ifnum=0;
+                       while (ifnum<numInterfaces && iter.hasNext()) {
+                               line=(String) iter.next();                      
                // group 0 : printed line
+                               rxnew=Long.parseLong((String) iter.next());     
// group 1 : input packets
+                               iter.next();                                    
                        // group 2 : output packets
+
+                               debug("NetStat filter : "+line);
+                               debug("NetStat rx     : "+rxnew);
+
+                               for (i=0; i<numInterfaces; i++) {
+                                       if (line.indexOf(interfacePtrs[i])>=0) {
+                                               debug("Match interface 
'"+interfacePtrs[i]+"'.");
+
+                                               if ((rxnew - 
last_net_results[ifnum].last_in) > 0) {
+                                                       /* ignore the result if 
it is currently overflowing */
+                                                       rxdiff += rxnew - 
last_net_results[ifnum].last_in;
+                                                       }
+                                               last_net_results[ifnum].last_in 
= rxnew;
+                                               ifnum++;
+                                               break;
+                                               }
+                                       }
+                               }
+
+                       lastNetResultDown= (int) (Scheduler.seconds(100 * 
rxdiff) / (elapsedtime * maxNetDownBPS));
+                       }
+               return (int) lastNetResultDown;
+       }
+
+       /**
+        * The advanced usage meter takes into account all network traffic.
+        * This might be problematic on systems where the same interface
+        * can have different capabilities for different types of traffic
+        * (like support for very fast local traffic but capable of
+        * handling only small-scale inet traffic).
+        * @return
+        */
+
+       protected int networkUsageAdvancedUp()
+       {
+               long                    txnew,txdiff;
+               int                             ifnum,i;
+               long                    now,elapsedtime;
+               String                  line;
+               OutputFilter    filter;
+               Iterator                iter;
+
+               synchronized(statusMutex) {
+                       /* first, make sure maxNetUpBPS is not 0, we don't want
+                        to divide by 0, really. */
+                       if (maxNetUpBPS == 0) {
+                               lastNetResultUp = -1;
+                               return -1;
+                               }
+
+                       /* If we checked /proc less than 2 seconds ago, don't do
+                        it again, but add internal gnunet traffic increments */
+                       now=Scheduler.now();
+                       elapsedtime = now - lastnettimeUp;
+                       if (elapsedtime == 0) {
+                               return (int) lastNetResultUp;
+                               }
+
+                       if (elapsedtime < Scheduler.SECS_2) {
+                               /* Only take additional gnunetd traffic into 
account, don't try to
+                                measure other *system* traffic more frequently 
than every 2s */
+                               double gnunetBPS;
+                               double gnunetLOAD;
+                               int ret;
+
+                               //todo: bug probable 1/1000 = 0 en entier -> 
retourne toujours 0
+                               gnunetBPS= 
(Scheduler.MILLIS_1/Scheduler.SECS_1) * globalTrafficBetweenProc.last_out / 
elapsedtime;
+                               gnunetLOAD= 100 * gnunetBPS / maxNetUpBPS;
+                               /* weigh last global measurement and gnunetd 
load,
+                                with 100% global measurement at first and 
50/50 mix
+                                just before we take the next measurement */
+                               ret =(int) ( (Scheduler.SECS_2 * 
lastNetResultUp + elapsedtime * gnunetLOAD) /
+                                               (Scheduler.SECS_2 + 
elapsedtime));
+                               return ret;
+                               }
+
+                       globalTrafficBetweenProc.last_out = 0;
+                       lastnettimeUp = now;
+
+                       /* ok, full program... */
+                       txdiff = 0;
+
+                       filter=new 
OutputFilter("^\\S+\\s+\\S+\\s+\\S+\\s+\\S+\\s+(\\d+)\\s+\\S+\\s+(\\d+)\\s+\\S+\\s+\\S+\\s?$");
+                       iter=filter.filter("netstat -n -f inet -i");
+                       if (iter==null) {
+                               log("Could not open 'netstat' !");
+                               lastNetResultUp = -1;
+                               return -1;
+                               }
+
+                       ifnum=0;
+                       while (ifnum<numInterfaces && iter.hasNext()) {
+                               line=(String) iter.next();                      
                // group 0 : printed line
+                               iter.next();                                    
                        // group 1 : input packets
+                               txnew=Long.parseLong((String) iter.next());     
// group 2 : output packets
+
+                               debug("NetStat filter : "+line);
+                               debug("NetStat tx     : "+txnew);
+
+                               for (i=0; i<numInterfaces; i++) {
+                                       if (line.indexOf(interfacePtrs[i])>=0) {
+                                               debug("Match interface 
'"+interfacePtrs[i]+"'.");
+
+                                               if ((txnew - 
last_net_results[ifnum].last_out) > 0) {
+                                                       /* ignore the result if 
it is currently overflowing */
+                                                       txdiff += txnew - 
last_net_results[ifnum].last_out;
+                                                       }
+                                               
last_net_results[ifnum].last_out = txnew;
+                                               ifnum++;
+                                               break;
+                                               }
+                                       }
+                               }
+
+                       lastNetResultUp = (int) (Scheduler.seconds(100 * 
txdiff) / (elapsedtime * maxNetUpBPS));
+                       }
+               return (int) lastNetResultUp;
+       }
+
+       /**
+        * The following routine returns the percentage of available used
+        * bandwidth.  Example: If 81 is returned this means that 81% of the
+        * network bandwidth of the host is consumed.  The method
+        * initStatusCalls() should be called before this routine is invoked.
+        * If there is an error the method returns -1.
+        * The following routine returns the percentage of available used
+        * bandwidth. A number from 0-100 is returned.  Example: If 81 is
+        * returned this means that 81% of the network bandwidth of the host
+        * is consumed.
+        * The following routine returns the percentage of available used
+        * bandwidth.  A number from 0-100 is returned.  Example: If 81 is
+        * returned this means that 81% of the network bandwidth of the host
+        * is consumed.
+        * @return
+        */
+
+       public int networkUsageUp()
+       {
+               if (!initialized_) {
+                       return -1;
+                       }
+
+               if (useBasicMethod)
+                       return networkUsageBasicUp();
+               return networkUsageAdvancedUp();
+       }
+
+       /**
+        * The following routine returns the percentage of available used
+        * bandwidth.  Example: If 81 is returned this means that 81% of the
+        * network bandwidth of the host is consumed.  The method
+        * initStatusCalls() should be called before this routine is invoked.
+        * If there is an error the method returns -1.
+        * The following routine returns the percentage of available used
+        * bandwidth. A number from 0-100 is returned.  Example: If 81 is
+        * returned this means that 81% of the network bandwidth of the host
+        * is consumed.
+        * @return
+        */
+
+       public int networkUsageDown()
+       {
+               if (!initialized_) {
+                       return -1;
+                       }
+
+               if (useBasicMethod)
+                       return networkUsageBasicDown();
+               return networkUsageAdvancedDown();
+       }
+
+       /**
+        * The following routine returns a number between 0-100 (can be larger 
than 100
+        * if the load is > 1) which indicates the percentage CPU usage.
+        *
+        * Before its first invocation the method initStatusCalls() must be 
called.
+        * If there is an error the method returns -1
+        * The following routine returns a positive number which indicates
+        * the percentage CPU usage. 100 corresponds to one runnable process
+        * on average.
+        * @return
+        */
+
+       public int cpuUsage()
+       {
+               OutputFilter    filter;
+               Iterator                iter;
+               String                  value;
+               long                    now,elapsedtime;
+
+               if (!initialized_) {
+                       return -1;
+                       }
+
+               synchronized(statusMutex) {
+                       now=Scheduler.now();
+                       elapsedtime = now - lastcputime;
+                       if (elapsedtime<Scheduler.SECS_10 && lastcpuresult!=-1) 
{
+                               return lastcpuresult;
+                               }
+                       lastcputime = now;
+
+                       filter=new OutputFilter("CPU 
usage:\\s+([0-9]+(\\.[0-9]+)?)% user");
+                       iter=filter.filter("top -c e -l 1");
+                       if (iter==null) {
+                               log("Could not extract info from 'top' !");
+                               lastcpuresult=-1;
+                               return lastcpuresult;
+                               }
+
+                       if (iter.hasNext()) {
+                               iter.next();                            // 
group 0 : printed line
+                               value=(String) iter.next();     // group 1 : 
number
+                               iter.next();                            // 
group 2 : decimal part (if any) of the number
+
+                               lastcpuresult=(int) Double.parseDouble(value);
+                               log("Found CPU load of "+lastcpuresult+"%.");
+                               return lastcpuresult;
+                               }
+
+                       lastcpuresult=-1;
+                       return lastcpuresult;
+                       }
+       }
+
+       /**
+        * Get the load of the network relative to what is allowed.
+        * The only difference to networkUsageUp is that
+        * this function averages the values over time.
+        * @return the network load as a percentage of allowed
+        *        (100 is equivalent to full load)
+        */
+
+       public int getNetworkLoadUp()
+       {
+               int             ret;
+               long    now;
+
+               ret = networkUsageUp();
+               if (ret == -1) /* in the case of error, we do NOT go to 100%
+               since that would render GNUnet useless on
+               systems where networkUsageUp is not supported */
+                       return -1;
+
+               now=Scheduler.now();
+               if (now - lastCallNUp < Scheduler.MILLIS_250) {
+                       /* use smoothing, but do NOT update lastNetworkRetUp at 
frequencies higher
+                        than 250ms; this makes the smoothing (mostly) 
independent from
+                        the frequency at which getNetworkLoadUp is called. */
+                       return (ret + 7 * lastNetworkRetUp)/8;
+                       }
+               lastCallNUp = now;
+
+               ret = (ret + 7 * lastNetworkRetUp)/8;
+               lastNetworkRetUp = ret;
+
+               stat_handle_network_load_up.set(ret);
+               return ret;
+       }
+
+       /**
+        * Get the load of the network relative to what is allowed.
+        * The only difference to networkUsageDown is that
+        * this function averages the values over time.
+        * @return the network load as a percentage of allowed
+        *        (100 is equivalent to full load)
+        */
+
+       public int getNetworkLoadDown()
+       {
+               int             ret;
+               long    now;
+
+
+               if (!initialized_)
+                       return -1;
+               ret = networkUsageDown();
+               if (ret == -1) /*  in the case of error, we do NOT go to 100%
+               since that would render GNUnet useless on
+               systems where networkUsageUp is not supported */
+                       return -1;
+
+               now=Scheduler.now();
+               if (now - lastCallNDown < Scheduler.MILLIS_250) {
+                       /* use smoothing, but do NOT update lastNetworkRetDown 
at frequencies higher
+                        than 250ms; this makes the smoothing (mostly) 
independent from
+                        the frequency at which getNetworkLoadDown is called. */
+                       return (ret + 7 * lastNetworkRetDown)/8;
+                       }
+               lastCallNDown = now;
+
+               ret = (ret + 7 * lastNetworkRetDown)/8;
+               lastNetworkRetDown = ret;
+
+               stat_handle_network_load_down.set(ret);
+               return ret;
+       }
+
+       /**
+        * Get the load of the CPU relative to what is allowed.
+        * Get the load of the CPU relative to what is allowed.
+        * @return the CPU load as a percentage of allowed
+        *        (100 is equivalent to full load)
+        */
+
+       public int getCPULoad()
+       {
+               int             ret;
+               long    now;
+
+               if (!initialized_)
+                       return -1;
+
+               ret = (100 * cpuUsage()) / maxCPULoad;
+
+               now=Scheduler.now();
+               if (now - lastCPUCall < Scheduler.MILLIS_250) {
+                       /* use smoothing, but do NOT update lastCPUUsage at 
frequencies higher
+                        than 250ms; this makes the smoothing (mostly) 
independent from
+                        the frequency at which getCPULoad is called. */
+                       return (ret + 7 * lastCPUUsage)/8;
+                       }
+               lastCPUCall = now;
+
+               /* for CPU, we don't do the 'fast increase' since CPU is much
+                more jitterish to begin with */
+               lastCPUUsage = (ret + 7 * lastCPUUsage)/8;
+               stat_handle_cpu_load.set(lastCPUUsage);
+
+               return lastCPUUsage;
+       }
+}
+
+class NetworkStats extends Object
+{
+       public long     last_in;
+       public long     last_out;
+}

Added: freeway/src/org/gnu/freeway/util/Task.java
===================================================================
--- freeway/src/org/gnu/freeway/util/Task.java  2005-01-31 23:47:23 UTC (rev 
136)
+++ freeway/src/org/gnu/freeway/util/Task.java  2005-02-01 01:07:27 UTC (rev 
137)
@@ -0,0 +1,153 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util;
+
+import EDU.oswego.cs.dl.util.concurrent.*;
+
+import java.util.logging.*;
+
+/**
+ *
+ */
+
+public class Task extends LoggedObject
+{
+       protected String                name;
+       protected Action                action;
+       protected Semaphore     semaphore;
+       protected Thread                thread;
+
+
+       public Task( String str, Action a )
+       {
+               super(true);
+               name=str;
+               action=a;
+               semaphore=new Semaphore(0);
+               thread=null;
+       }
+
+       public String toString()
+       {
+               return "Task [name="+name+", action="+action+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int hashCode()
+       {
+               return name.hashCode();
+       }
+
+       public boolean equals( Object obj )
+       {
+               return ((obj instanceof Task) && ((Task) 
obj).name.equals(name));
+       }
+
+       public String getName()
+       {
+               return name;
+       }
+
+       public Action getAction()
+       {
+               return action;
+       }
+
+       public void setAction( Action a )
+       {
+               action=a;
+       }
+
+       public synchronized boolean isRunning()
+       {
+               return thread!=null;
+       }
+
+       public synchronized boolean launch()
+       {
+               boolean res;
+
+               res=false;
+               if (thread==null) {
+                       thread=new Thread(name) {
+                               public void run()
+                               {
+                                       semaphore.release();
+                                       exec();
+                                       semaphore.release();
+                               }
+                               };
+                       thread.setPriority(Thread.NORM_PRIORITY);
+                       thread.setDaemon(true);
+                       thread.start();
+
+                       try {
+                               semaphore.acquire();
+                               res=true;
+                               }
+                       catch( InterruptedException x ) {
+                               err("Failed to acquire semaphore of task 
\""+name+"\".",x);
+                               }
+                       }
+               else {
+                       log(Level.WARNING,"Task \""+name+"\" has already 
started.");
+                       }
+               return res;
+       }
+
+       public synchronized boolean launchAndWait()
+       {
+               return launch() && join();
+       }
+
+       public synchronized boolean join()
+       {
+               boolean res;
+
+               res=true;
+               if (thread!=null) {
+                       try {
+                               semaphore.acquire();
+                               }
+                       catch( InterruptedException x ) {
+                               err("Failed to acquire semaphore of task 
\""+name+"\".",x);
+                               res=false;
+                               }
+
+                       try {
+                               thread.join();
+                               }
+                       catch( InterruptedException x ) {
+                               err("Failed to join thread of task 
\""+name+"\".",x);
+                               res=false;
+                               }
+
+                       thread=null;
+                       }
+               else {
+                       log(Level.WARNING,"Task \""+name+"\" has already 
finished.");
+                       res=false;
+                       }
+               return res;
+       }
+
+       public void exec()
+       {
+               debug("Start task \""+name+"\"...");
+               try {
+                       if (action!=null) {
+                               action.perform();
+                               }
+                       }
+               catch( Throwable x ) {
+                       err("Aarrgghhhh, task \""+name+"\" abruptly aborted 
!",x);
+                       }
+               finally {
+                       debug("Executed task \""+name+"\".");
+                       }
+       }
+}

Added: freeway/src/org/gnu/freeway/util/TaskHome.java
===================================================================
--- freeway/src/org/gnu/freeway/util/TaskHome.java      2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/util/TaskHome.java      2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,46 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util;
+
+/**
+ *
+ */
+
+public class TaskHome extends Object
+{
+
+       public TaskHome()
+       {
+               super();
+       }
+
+       public String toString()
+       {
+               return "xxx";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public Task create( String str, Action a )
+       {
+               Task    t;
+
+               t=new Task(str,a);
+               return t;
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+/*
+. ThreadPool
+       - launch( name, Action )
+       - launchAndWait( name, Action ) : avec le Semaphore(0) double-relase()
+       - dump
+       - join(i)
+
+ */
+}

Added: freeway/src/org/gnu/freeway/util/TrafficCounter.java
===================================================================
--- freeway/src/org/gnu/freeway/util/TrafficCounter.java        2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/util/TrafficCounter.java        2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,45 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util;
+
+/**
+ * Counter for traffic.
+ */
+
+public class TrafficCounter extends Object
+{
+       public static final int SIZE    =       12;
+
+       /** This type is for messages that we send. */
+       public static final int TC_SENT                         =       0x8000;
+
+       /** This type is for messages that we receive. */
+       public static final int TC_RECEIVED                     =       0x4000;
+
+       /** */
+       public static final int TC_TYPE_MASK            =       (TC_RECEIVED | 
TC_SENT);
+
+       /** From/To how many different peers did we receive/send messages of 
this type? (bitmask) */
+       public static final int TC_DIVERSITY_MASK       =       0x00000FFF;
+
+
+       /** Flags. See TC_XXXX definitions. */
+       public int      flags;
+
+       /** What was the number of messages of this type that the peer 
processed in the last n time units ? */
+       public int      count;
+
+       /** What is the message type that this counter is concerned with ? */
+       public int      type;
+
+       /** What is the average size of the last "count" messages that the peer 
processed ? */
+       public int      avrg_size;
+
+       /** In which of the last 32 time units did the peer receive or send a 
message of this type ?
+        The lowest bit (1) corresponds to -31 seconds ago, the highest bit 
(2^31) corresponds to the current second. */
+       public int      time_slots;
+
+
+}

Added: freeway/src/org/gnu/freeway/util/Unit.java
===================================================================
--- freeway/src/org/gnu/freeway/util/Unit.java  2005-01-31 23:47:23 UTC (rev 
136)
+++ freeway/src/org/gnu/freeway/util/Unit.java  2005-02-01 01:07:27 UTC (rev 
137)
@@ -0,0 +1,57 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util;
+
+//import java.text.*;
+
+/**
+ *
+ */
+
+public class Unit extends Constant
+{
+       public static final Unit        COUNT                           =       
new Unit("#");
+       public static final Unit        PERCENT                         =       
new Unit("%");
+       public static final Unit        DURATION                                
=       new Unit("s");
+       public static final Unit        BYTES                           =       
new Unit("b");
+       public static final Unit        BYTES_PER_DURATION      =       new 
Unit("b/s");
+
+       static {
+               init(new Unit[] { COUNT, PERCENT, DURATION, BYTES, 
BYTES_PER_DURATION });
+               }
+
+
+       protected Unit( String str )
+       {
+               super(str);
+       }
+
+       public String toString()
+       {
+               return "xxx";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public String format( long num )
+       {
+               return format(new Long(num));
+       }
+
+       public String format( double num )
+       {
+               return format(new Double(num));
+       }
+
+       public String format( Number num )
+       {
+               switch (getValue()) {
+                       case 0:
+                               break;
+                       }
+               return "";
+       }
+}

Added: freeway/src/org/gnu/freeway/util/Utils.java
===================================================================
--- freeway/src/org/gnu/freeway/util/Utils.java 2005-01-31 23:47:23 UTC (rev 
136)
+++ freeway/src/org/gnu/freeway/util/Utils.java 2005-02-01 01:07:27 UTC (rev 
137)
@@ -0,0 +1,1184 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util;
+
+import java.net.*;
+import java.nio.*;
+import java.nio.channels.*;
+import java.text.*;
+import java.util.*;
+import java.util.logging.*;
+
+/**
+ * A collection of utility methods.
+ */
+
+public class Utils extends Object
+{
+       /**
+        * Just the version number of GNUnet-util implementation.
+        * Encoded as
+        * 0.6.1-4 => 0x00060104
+        * 4.5.2   => 0x04050200
+        *
+        * Note that this version number is changed whenever
+        * something changes GNUnet-util.  It does not have
+        * to match exactly with the GNUnet version number;
+        * especially the least significant bits may change
+        * frequently, even between different CVS versions.
+        */
+
+       public static final int GNUNET_UTIL_VERSION     =       0x00060200;
+
+
+       /** Uppercase hexadecimal digits. */
+       private static final char[]     HEXDIGITS_U     =       { '0', '1', 
'2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
+
+       /** Lowercase hexadecimal digits. */
+       private static final char[]     HEXDIGITS_L     =       { '0', '1', 
'2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
+
+       /** The end of line characters (platform dependent). */
+       private static final String     EOL                     =       
System.getProperty("line.separator");
+
+       /** How many milliseconds per day ? */
+       private static final long       MS_PER_DAY      =       86400000L;
+
+       /** Number of units in a millisecond. */
+       private static final long       MILLIS  =       1;
+
+       /** Number of units in a second. */
+       private static final long       SECONDS =       1000*MILLIS;
+
+       /** Number of units in a minute. */
+       private static final long       MINUTES =       60*SECONDS;
+
+       /** Number of units in an hour. */
+       private static final long       HOURS   =       60*MINUTES;
+
+       /** Number of units in a day. */
+       private static final long       DAYS    =       24*HOURS;
+
+       /** Internal logger. */
+       private static Logger   logger;
+
+       /** A not so different object. */
+       private static Object   any;
+
+       private static DateFormat                       dateFormatter;
+       private static DurationFormatter        durationFormatter;
+
+       static {
+               logger=Logger.getLogger(Utils.class.getName());
+               any=new Object();
+               }
+
+
+       //todo: documenter et checker les valeurs entrees
+       public static int makeVersion( int d1, int d2 )
+       {
+               return makeVersion(d1,d2,0,0);
+       }
+       public static int makeVersion( int d1, int d2, int d3 )
+       {
+               return makeVersion(d1,d2,d3,0);
+       }
+       public static int makeVersion( int d1, int d2, int d3, int d4 )
+       {
+               return (d1<<24)+(d2<<16)+(d3<<8)+d4;
+       }
+       public static String formatVersion( int v ) //todo: documenter
+       {
+               String  str;
+               int     d1,d2,d3,d4;
+
+               str="";
+               d1=(v>>24) & 255;
+               d2=(v>>16) & 255;
+               d3=(v>>8) & 255;
+               d4=(v) & 255;
+
+               str+=d1+"."+d2+"."+d3;
+               if (d4!=0) {
+                       assert(d4>=10 && d4<=15);
+                       str+=(char) ('a'+d4-10);
+                       }
+               return str;
+       }
+
+       public static String formatMoment( long u )
+       {
+               if (dateFormatter==null) {
+                       dateFormatter=new SimpleDateFormat("HH:mm:ss.SSS");
+//                     dateFormatter(TimeZone.getTimeZone("UTC"));
+                       }
+          return dateFormatter.format(new Date(u));
+       }
+
+       public static String formatDuration( long u )
+       {
+               if (durationFormatter==null) {
+                       durationFormatter=new DurationFormatter();
+                       }
+               return durationFormatter.format(new Long(u));
+       }
+
+       public static String toBin( byte b )
+       {
+               StringBuffer    buf;
+               int                             mask;
+
+               buf=new StringBuffer();
+               for (mask=128; mask!=0; mask>>=1) {
+                       buf.append((b & mask)!=0 ? '1' : '0');
+                       }
+               return buf.toString();
+       }
+
+       public static String toBin( byte[] b )
+       {
+               return toBin(b,false);
+       }
+
+       public static String toBin( byte[] b, boolean sep )
+       {
+               StringBuffer    buf;
+               int                             mask,i;
+
+               buf=new StringBuffer();
+               if (b!=null) {
+                       for (i=0; i<b.length; i++) {
+                               for (mask=128; mask!=0; mask>>=1) {
+                                       buf.append((b[i] & mask)!=0 ? '1' : 
'0');
+                                       }
+                               if (sep && i<b.length-1) {
+                                       buf.append(' ');
+                                       }
+                               }
+                       }
+               else {
+                       buf.append("null");
+                       }
+               return buf.toString();
+       }
+
+       public static String toHex( byte b )
+       {
+               StringBuffer    buf;
+
+               buf=new StringBuffer();
+               buf.append(HEXDIGITS_U[(b>>4) & 0x0f]);
+               buf.append(HEXDIGITS_U[b & 0x0f]);
+               return buf.toString();
+       }
+
+       public static String toHex( byte[] b )
+       {
+               return toHex(b,false);
+       }
+
+       public static String toHex( byte[] b, boolean sep )
+       {
+               StringBuffer    buf;
+               int                             i;
+
+               buf=new StringBuffer();
+               if (b!=null) {
+                       for (i=0; i<b.length; i++) {
+                               buf.append(HEXDIGITS_U[(b[i]>>4) & 0x0f]);
+                               buf.append(HEXDIGITS_U[b[i] & 0x0f]);
+                               if (sep && i<b.length-1) {
+                                       buf.append(' ');
+                                       }
+                               }
+                       }
+               else {
+                       buf.append("null");
+                       }
+               return buf.toString();
+       }
+
+       public static String toHex( char c )
+       {
+               StringBuffer    buf;
+
+               buf=new StringBuffer();
+               buf.append(HEXDIGITS_U[(c>>12) & 0x0f]);
+               buf.append(HEXDIGITS_U[(c>>8) & 0x0f]);
+               buf.append(HEXDIGITS_U[(c>>4) & 0x0f]);
+               buf.append(HEXDIGITS_U[c & 0x0f]);
+               return buf.toString();
+       }
+
+       public static String toHex( char[] c )
+       {
+               return toHex(c,false);
+       }
+
+       public static String toHex( char[] c, boolean sep )
+       {
+               StringBuffer    buf;
+               int                             i;
+
+               buf=new StringBuffer();
+               if (c!=null) {
+                       for (i=0; i<c.length; i++) {
+                               buf.append(HEXDIGITS_U[(c[i]>>12) & 0x0f]);
+                               buf.append(HEXDIGITS_U[(c[i]>>8) & 0x0f]);
+                               buf.append(HEXDIGITS_U[(c[i]>>4) & 0x0f]);
+                               buf.append(HEXDIGITS_U[c[i] & 0x0f]);
+                               if (sep && i<c.length-1) {
+                                       buf.append(' ');
+                                       }
+                               }
+                       }
+               else {
+                       buf.append("null");
+                       }
+               return buf.toString();
+       }
+
+       public static String toHex( int n )
+       {
+               StringBuffer    buf;
+
+               buf=new StringBuffer();
+               buf.append(HEXDIGITS_U[(n>>28) & 0x0f]);
+               buf.append(HEXDIGITS_U[(n>>24) & 0x0f]);
+               buf.append(HEXDIGITS_U[(n>>20) & 0x0f]);
+               buf.append(HEXDIGITS_U[(n>>16) & 0x0f]);
+               buf.append(HEXDIGITS_U[(n>>12) & 0x0f]);
+               buf.append(HEXDIGITS_U[(n>>8) & 0x0f]);
+               buf.append(HEXDIGITS_U[(n>>4) & 0x0f]);
+               buf.append(HEXDIGITS_U[n & 0x0f]);
+               return buf.toString();
+       }
+
+       public static String toHex( int[] t )
+       {
+               return toHex(t,false);
+       }
+
+       public static String toHex( int[] t, boolean sep )
+       {
+               StringBuffer    buf;
+               int                             i;
+
+               buf=new StringBuffer();
+               if (t!=null) {
+                       for (i=0; i<t.length; i++) {
+                               buf.append(toHex(t[i]));
+                               if (sep && i<t.length-1) {
+                                       buf.append(' ');
+                                       }
+                               }
+                       }
+               else {
+                       buf.append("null");
+                       }
+               return buf.toString();
+       }
+
+       public static String toHex( long n )
+       {
+               StringBuffer    buf;
+
+               buf=new StringBuffer();
+               buf.append(HEXDIGITS_U[(int) (n>>60) & 0x0f]);
+               buf.append(HEXDIGITS_U[(int) (n>>56) & 0x0f]);
+               buf.append(HEXDIGITS_U[(int) (n>>52) & 0x0f]);
+               buf.append(HEXDIGITS_U[(int) (n>>48) & 0x0f]);
+               buf.append(HEXDIGITS_U[(int) (n>>44) & 0x0f]);
+               buf.append(HEXDIGITS_U[(int) (n>>40) & 0x0f]);
+               buf.append(HEXDIGITS_U[(int) (n>>36) & 0x0f]);
+               buf.append(HEXDIGITS_U[(int) (n>>32) & 0x0f]);
+               buf.append(HEXDIGITS_U[(int) (n>>28) & 0x0f]);
+               buf.append(HEXDIGITS_U[(int) (n>>24) & 0x0f]);
+               buf.append(HEXDIGITS_U[(int) (n>>20) & 0x0f]);
+               buf.append(HEXDIGITS_U[(int) (n>>16) & 0x0f]);
+               buf.append(HEXDIGITS_U[(int) (n>>12) & 0x0f]);
+               buf.append(HEXDIGITS_U[(int) (n>>8) & 0x0f]);
+               buf.append(HEXDIGITS_U[(int) (n>>4) & 0x0f]);
+               buf.append(HEXDIGITS_U[(int) (n & 0x0f)]);
+               return buf.toString();
+       }
+
+       public static String toHex( long[] t )
+       {
+               return toHex(t,false);
+       }
+
+       public static String toHex( long[] t, boolean sep )
+       {
+               StringBuffer    buf;
+               int                             i;
+
+               buf=new StringBuffer();
+               if (t!=null) {
+                       for (i=0; i<t.length; i++) {
+                               buf.append(toHex(t[i]));
+                               if (sep && i<t.length-1) {
+                                       buf.append(' ');
+                                       }
+                               }
+                       }
+               else {
+                       buf.append("null");
+                       }
+               return buf.toString();
+       }
+
+       public static String toHex( String str )
+       {
+               return toHex(str,false);
+       }
+
+       public static String toHex( String str, boolean sep )
+       {
+               return toHex((str!=null ? str.toCharArray() : null),sep);
+       }
+
+       public static String toString( int[] array )
+       {
+               StringBuffer    buf;
+               int                             i;
+
+               buf=new StringBuffer("(");
+               if (array!=null) {
+                       for (i=0; i<array.length; i++) {
+                               buf.append(" ");
+                               buf.append(array[i]);
+                               if (i<array.length-1) {
+                                       buf.append(",");
+                                       }
+                               }
+                       }
+               buf.append(" )");
+               return buf.toString();
+       }
+
+       public static String toString( long[] array )
+       {
+               StringBuffer    buf;
+               int                             i;
+
+               buf=new StringBuffer("(");
+               if (array!=null) {
+                       for (i=0; i<array.length; i++) {
+                               buf.append(" ");
+                               buf.append(array[i]);
+                               if (i<array.length-1) {
+                                       buf.append(",");
+                                       }
+                               }
+                       }
+               buf.append(" )");
+               return buf.toString();
+       }
+
+       public static String toString( float[] array )
+       {
+               StringBuffer    buf;
+               int                             i;
+
+               buf=new StringBuffer("(");
+               if (array!=null) {
+                       for (i=0; i<array.length; i++) {
+                               buf.append(" ");
+                               buf.append(array[i]);
+                               if (i<array.length-1) {
+                                       buf.append(",");
+                                       }
+                               }
+                       }
+               buf.append(" )");
+               return buf.toString();
+       }
+
+       public static String toString( double[] array )
+       {
+               StringBuffer    buf;
+               int                             i;
+
+               buf=new StringBuffer("(");
+               if (array!=null) {
+                       for (i=0; i<array.length; i++) {
+                               buf.append(" ");
+                               buf.append(array[i]);
+                               if (i<array.length-1) {
+                                       buf.append(",");
+                                       }
+                               }
+                       }
+               buf.append(" )");
+               return buf.toString();
+       }
+
+       public static String toString( Object[] array )
+       {
+               StringBuffer    buf;
+               int                             i;
+
+               buf=new StringBuffer("(");
+               if (array!=null) {
+                       for (i=0; i<array.length; i++) {
+                               buf.append(" ");
+                               buf.append(array[i]);
+                               if (i<array.length-1) {
+                                       buf.append(",");
+                                       }
+                               }
+                       }
+               buf.append(" )");
+               return buf.toString();
+       }
+
+       public static String toString( byte[] b, int from, int to, String title 
)
+       {
+               return toString(b,from,to,title,8);
+       }
+
+       public static String toString( byte[] b, int from, int to, String 
title, int width )
+       {
+               return toString(ByteBuffer.wrap(b),from,to,title,width);
+       }
+
+       public static String toString( ByteBuffer buf, int from, int to, String 
title )
+       {
+               return toString(buf,from,to,title,8);
+       }
+
+       public static String toString( ByteBuffer buf, int from, int to, String 
title, int width )
+       {
+               StringBuffer            out;
+               int                             nmax,n,i;
+               char                            c;
+               byte                            b;
+
+               out=new StringBuffer();
+               out.append(EOL);
+               if (title!=null && title.length()>0) {
+                       out.append(title);
+                       out.append(EOL);
+                       for (i=title.length(); i>0; i--) {
+                               out.append('-');
+                               }
+                       out.append(EOL);
+                       }
+
+               nmax=(to-from+width-1)/width;
+               for (n=0; n<nmax; n++) {
+                       out.append("  ");
+                       for (i=0; i<width; i++) {
+                               if (n*width+i<(to-from)) {
+                                       b=buf.get(from+n*width+i);
+                                       out.append(HEXDIGITS_L[(b>>4) & 
0x0000000f]);
+                                       out.append(HEXDIGITS_L[b & 0x0000000f]);
+                                       out.append(' ');
+                                       }
+                               else {
+                                       out.append("-- ");
+                                       }
+                               }
+
+                       out.append(": \"");
+                       for (i=0; i<width; i++) {
+                               if (n*width+i<(to-from)) {
+                                       c=(char) buf.get(from+n*width+i);
+                                       if (c>=32 && c<128) {
+                                               out.append(c);
+                                               }
+                                       else {
+                                               out.append('.');
+                                               }
+                                       }
+                               }
+                       out.append("\"");
+                       out.append(EOL);
+                       }
+               out.append(EOL);
+               return out.toString();
+       }
+
+       public static String alignLeft( long n, int len )
+       {
+               return alignLeft(n,len,' ');
+       }
+
+       public static String alignLeft( long n, int len, char c )
+       {
+               return alignLeft(String.valueOf(n),len,c);
+       }
+
+       public static String alignLeft( String str, int len )
+       {
+               return alignLeft(str,len,' ');
+       }
+
+       public static String alignLeft( String str, int len, char c )
+       {
+               StringBuffer    buf;
+               int                             i;
+
+               buf=new StringBuffer();
+               buf.append(str);
+               for (i=len-str.length(); i>0; i--) {
+                       buf.append(c);
+                       }
+               buf.setLength(len);
+               return buf.toString();
+       }
+
+       public static String alignCenter( long n, int len )
+       {
+               return alignCenter(n,len,' ');
+       }
+
+       public static String alignCenter( long n, int len, char c )
+       {
+               return alignCenter(String.valueOf(n),len,c);
+       }
+
+       public static String alignCenter( String str, int len )
+       {
+               return alignCenter(str,len,' ');
+       }
+
+       public static String alignCenter( String str, int len, char c )
+       {
+               StringBuffer    buf;
+               int                             i;
+
+               buf=new StringBuffer();
+               for (i=(len-str.length())/2; i>0; i--) {
+                       buf.append(c);
+                       }
+               buf.append(str);
+               while (buf.length()<len) {
+                       buf.append(c);
+                       }
+               buf.setLength(len);
+               return buf.toString();
+       }
+
+       public static String alignRight( long n, int len )
+       {
+               return alignRight(n,len,' ');
+       }
+
+       public static String alignRight( long n, int len, char c )
+       {
+               return alignRight(String.valueOf(n),len,c);
+       }
+
+       public static String alignRight( String str, int len )
+       {
+               return alignRight(str,len,' ');
+       }
+
+       public static String alignRight( String str, int len, char c )
+       {
+               StringBuffer    buf;
+               int                             i;
+
+               buf=new StringBuffer();
+               for (i=len-str.length(); i>0; i--) {
+                       buf.append(c);
+                       }
+               buf.append(str);
+               buf.setLength(len);
+               return buf.toString();
+       }
+
+       public static String repeat( char c, int n )
+       {
+               StringBuffer    buf;
+
+               buf=new StringBuffer();
+               while (n>0) {
+                       buf.append(c);
+                       n--;
+                       }
+               return buf.toString();
+       }
+
+       public static String gauge( int at, int total )
+       {
+               StringBuffer    buf;
+               int                             i;
+
+               buf=new StringBuffer();
+               buf.append("(");
+               buf.append(at);
+               buf.append("/");
+               buf.append(total);
+               buf.append(" ");
+               buf.append(NumberFormat.getPercentInstance().format(new 
Double((double) at/(double) total)));
+               buf.append(" ");
+               for (i=0; i<at; i++) {
+                       buf.append('=');
+                       }
+               for (i=at; i<total; i++) {
+                       buf.append('_');
+                       }
+               buf.append(")");
+               return buf.toString();
+       }
+
+       public static void dumpMemory()
+       {
+               dumpMemory("");
+       }
+
+       public static void dumpMemory( String str )
+       {
+               NumberFormat    nf;
+               long                    free,total;
+
+               nf=NumberFormat.getInstance(Locale.US);
+               System.gc();
+
+               free=Runtime.getRuntime().freeMemory();
+               total=Runtime.getRuntime().totalMemory();
+               logger.log(Level.INFO,"---- ("+str+") memory : "+nf.format(new 
Long(free))+" "+nf.format(new Long(total)));
+       }
+
+       public static void dump( Date d )
+       {
+               DateFormat      df;
+
+               if (d!=null) {
+                       df=new SimpleDateFormat("dd/MM/yyyy HH:mm:ss.SSS zzz");
+                       df.setTimeZone(TimeZone.getTimeZone("UTC"));
+                       logger.log(Level.INFO,"Date : "+d+" *** 
"+df.format(d)+" ["+d.getTime()+"]");
+                       }
+       }
+
+       public static void dump( SocketChannel c )
+       {
+               dump(c.socket());
+       }
+
+       public static void dump( Socket s )
+       {
+               StringBuffer    buf;
+
+               buf=new StringBuffer();
+               buf.append("Socket ("+s+") : ");
+               if (!s.isBound()) {
+                       buf.append("[not bound] ");
+                       }
+               if (s.isClosed()) {
+                       buf.append("[closed] ");
+                       }
+               if (!s.isConnected()) {
+                       buf.append("[not connected] ");
+                       }
+               if (s.isInputShutdown()) {
+                       buf.append("[input shutdown] ");
+                       }
+               if (s.isOutputShutdown()) {
+                       buf.append("[output shutdown] ");
+                       }
+               logger.log(Level.INFO,buf.toString());
+       }
+
+       public static void dump( SelectionKey key )
+       {
+               StringBuffer    buf;
+
+               buf=new StringBuffer();
+               buf.append("Key ("+key+") / Channel ("+key.channel()+") : ");
+               if (key.isValid()) {
+                       buf.append("[valid] ");
+                       }
+               if (key.isAcceptable()) {
+                       buf.append("[acceptable] ");
+                       }
+               if (key.isConnectable()) {
+                       buf.append("[connectable] ");
+                       }
+               if (key.isReadable()) {
+                       buf.append("[readable] ");
+                       }
+               if (key.isWritable()) {
+                       buf.append("[writable] ");
+                       }
+               logger.log(Level.INFO,buf.toString());
+       }
+
+       public static void dump( ByteBuffer buf, int from, int to, String title 
)
+       {
+               logger.log(Level.INFO,toString(buf,from,to,title));
+       }
+
+       //todo: ameliorer
+       public static long parseLong( String str, long value )
+       {
+               try {
+                       value=Long.parseLong(str);
+                       }
+               catch( NumberFormatException x ) {
+                       logger.log(Level.WARNING,"Could not parse integer from 
\""+str+"\" !");
+                       }
+               return value;
+       }
+
+       public static int parseInt( String str, int value )
+       {
+               return parseInt(Locale.getDefault(),str,value);
+       }
+
+       public static int parseInt( Locale loc, String str, int value )
+       {
+               try {
+                       value=parseInt(loc,str);
+                       }
+               catch( ParseException x ) {
+                       logger.log(Level.WARNING,"Could not parse integer from 
\""+str+"\" (locale is "+loc+") !");
+                       }
+               return value;
+       }
+
+       public static int parseInt( String str ) throws ParseException
+       {
+               return parseInt(Locale.getDefault(),str);
+       }
+
+       public static int parseInt( Locale loc, String str ) throws 
ParseException
+       {
+               NumberFormat    nform;
+               ParsePosition   pos;
+               Object                  obj;
+
+               nform=NumberFormat.getInstance(loc);
+
+               pos=new ParsePosition(0);
+               obj=nform.parseObject(str,pos);
+               if (pos.getIndex()<str.length()) {
+                       throw new ParseException("",pos.getIndex());
+                       }
+               return ((Number) obj).intValue();
+       }
+
+       public static double parseDouble( String str, double value )
+       {
+               return parseDouble(Locale.getDefault(),str,value);
+       }
+
+       public static double parseDouble( Locale loc, String str, double value )
+       {
+               try {
+                       value=parseDouble(loc,str);
+                       }
+               catch( ParseException x ) {
+                       logger.log(Level.WARNING,"Could not parse decimal from 
\""+str+"\" (locale is "+loc+") !");
+                       }
+               return value;
+       }
+
+       public static double parseDouble( String str ) throws ParseException
+       {
+               return parseDouble(Locale.getDefault(),str);
+       }
+
+       public static double parseDouble( Locale loc, String str ) throws 
ParseException
+       {
+               NumberFormat    nform;
+               ParsePosition   pos;
+               Object                  obj;
+
+               nform=NumberFormat.getInstance(loc);
+               nform.setMinimumFractionDigits(1);
+
+               pos=new ParsePosition(0);
+               obj=nform.parseObject(str,pos);
+               if (pos.getIndex()<str.length()) {
+                       throw new ParseException("",pos.getIndex());
+                       }
+               return ((Number) obj).doubleValue();
+       }
+
+       public static Date parseDate( String str, Date value )
+       {
+               return parseDate("dd/MM/yyyy",str,value);
+       }
+
+       public static Date parseDate( String format, String str, Date value )
+       {
+               try {
+                       value=parseDate(format,str);
+                       }
+               catch( ParseException x ) {
+                       logger.log(Level.WARNING,"Could not parse date from 
\""+str+"\" (format is "+format+") !");
+                       }
+               return value;
+       }
+
+       public static Date parseDate( String format, String str ) throws 
ParseException
+       {
+               SimpleDateFormat        dform;
+               ParsePosition           pos;
+               Object                          obj;
+
+               dform=new SimpleDateFormat(format);
+               dform.setLenient(false);
+
+               pos=new ParsePosition(0);
+               obj=dform.parseObject(str,pos);
+               if (pos.getIndex()<str.length()) {
+                       throw new ParseException("",pos.getIndex());
+                       }
+               return (Date) obj;
+       }
+
+       public static Locale parseLocale( String str, Locale value )
+       {
+               if (str!=null) {
+                       if (str.length()==2) {
+                               value=new Locale(str.toLowerCase());
+                               }
+                       else if (str.length()==4) {
+                               value=new 
Locale(str.substring(0,2).toLowerCase(),str.substring(2,4).toUpperCase());
+                               }
+                       }
+               return value;
+       }
+
+       /**
+        * Return a calendar with specified year, month and day.
+        *
+        * @param       year    year.
+        * @param       month   month.
+        * @param       day             day of month.
+        * @return
+        */
+
+       public static Calendar getCalendar( int year, int month, int day )
+       {
+               Calendar        c;
+
+               c=Calendar.getInstance(TimeZone.getTimeZone("UTC"));
+               c.set(Calendar.YEAR,year);
+               c.set(Calendar.MONTH,Calendar.JANUARY+month);
+               c.set(Calendar.DAY_OF_MONTH,day);
+               c.set(Calendar.HOUR_OF_DAY,0);
+               c.set(Calendar.MINUTE,0);
+               c.set(Calendar.SECOND,0);
+               c.set(Calendar.MILLISECOND,0);
+               return c;
+       }
+
+       /**
+        * Converts a date with local timezone into an absolute date (UTC 
timezone).
+        * @param d
+        * @return
+        */
+
+       public static Date toUTC( Date d )
+       {
+               return toUTC(d,TimeZone.getDefault());
+       }
+
+       /**
+        * Converts a date with timezone <code>tz</code> into an absolute date 
(UTC timezone).
+        * @param d
+        * @param tz
+        * @return
+        */
+
+       public static Date toUTC( Date d, TimeZone tz )
+       {
+               long    t;
+
+               t=d.getTime();
+               t-=tz.getOffset(t);
+               return new Date(t);
+       }
+
+       /**
+        * Converts an absolute date (UTC timezone) into a date with local 
timezone.
+        * @param d
+        * @return
+        */
+
+       public static Date fromUTC( Date d )
+       {
+               return fromUTC(d,TimeZone.getDefault());
+       }
+
+       /**
+        * Converts an absolute date (UTC timezone) into a date with timezone 
<code>tz</code>.
+        * @param d
+        * @param tz
+        * @return
+        */
+
+       public static Date fromUTC( Date d, TimeZone tz )
+       {
+               long    t;
+
+               t=d.getTime();
+               t+=tz.getOffset(t);
+               return new Date(t);
+       }
+
+       public static Date today()
+       {
+               return new 
Date((System.currentTimeMillis()/MS_PER_DAY)*MS_PER_DAY);
+       }
+
+       public static boolean isAtMidnight( Date d )
+       {
+               return (d.getTime() % MS_PER_DAY)==0;
+       }
+
+       public static Date atMidnight( Date d )
+       {
+               return new Date((d.getTime()/MS_PER_DAY)*MS_PER_DAY);
+       }
+
+       public static Date todayMidnight()
+       {
+               return atMidnight(new java.util.Date());
+       }
+
+       public static int daysBetween( Date d1, Date d2 )
+       {
+               checkUTC(d1);
+               checkUTC(d2);
+               return (int) 
((d2.getTime()/MS_PER_DAY)-(d1.getTime()/MS_PER_DAY));
+       }
+
+       public static Date daysShift( Date d, int days )
+       {
+               checkUTC(d);
+               return new 
Date(((d.getTime()/MS_PER_DAY)*MS_PER_DAY)+(days*MS_PER_DAY));
+       }
+
+       public static boolean dayEqual( Date d1, Date d2 )
+       {
+               checkUTC(d1);
+               checkUTC(d2);
+               return (d1.getTime()/MS_PER_DAY)==(d2.getTime()/MS_PER_DAY);
+       }
+
+       public static boolean dayBefore( Date d1, Date d2 )
+       {
+               checkUTC(d1);
+               checkUTC(d2);
+               return (d1.getTime()/MS_PER_DAY)<(d2.getTime()/MS_PER_DAY);
+       }
+
+       public static boolean dayAfter( Date d1, Date d2 )
+       {
+               checkUTC(d1);
+               checkUTC(d2);
+               return (d1.getTime()/MS_PER_DAY)>(d2.getTime()/MS_PER_DAY);
+       }
+
+       public static Date dayMinimum( Date d1, Date d2 )
+       {
+               checkUTC(d1);
+               checkUTC(d2);
+               return ((d1.getTime()/MS_PER_DAY)<(d2.getTime()/MS_PER_DAY) ? 
d1 : d2);
+       }
+
+       public static Date dayMaximum( Date d1, Date d2 )
+       {
+               checkUTC(d1);
+               checkUTC(d2);
+               return ((d1.getTime()/MS_PER_DAY)>(d2.getTime()/MS_PER_DAY) ? 
d1 : d2);
+       }
+
+       public static boolean dateWithin( Date d, Date start, Date end )
+       {
+               checkUTC(d);
+               checkUTC(start);
+               checkUTC(end);
+               return ((d.getTime()/MS_PER_DAY)>=(start.getTime()/MS_PER_DAY) 
&& (d.getTime()/MS_PER_DAY)<=(end.getTime()/MS_PER_DAY));
+       }
+
+       public static boolean periodWithin( Date d1, Date d2, Date start, Date 
end )
+       {
+               checkUTC(d1);
+               checkUTC(d2);
+               checkUTC(start);
+               checkUTC(end);
+               return ((d1.getTime()/MS_PER_DAY)>=(start.getTime()/MS_PER_DAY) 
&& (d2.getTime()/MS_PER_DAY)<=(end.getTime()/MS_PER_DAY));
+       }
+
+       public static void checkUTC( Date d )
+       {
+               if (d!=null && (d.getTime() % MS_PER_DAY)!=0) {
+                       logger.log(Level.WARNING,"Unaligned date",new 
Throwable("... and it's from here !"));
+                       dump(d);
+                       }
+       }
+
+       public static long time()
+       {
+               return System.currentTimeMillis();
+       }
+
+       public static long millis( long n )
+       {
+               return n*MILLIS;
+       }
+
+       public static long seconds( long n )
+       {
+               return n*SECONDS;
+       }
+
+       public static long minutes( long n )
+       {
+               return n*MINUTES;
+       }
+
+       public static long hours( long n )
+       {
+               return n*HOURS;
+       }
+
+       public static long days( long n )
+       {
+               return n*DAYS;
+       }
+
+       public static long toMillis( long n )
+       {
+               return n/MILLIS;
+       }
+
+       public static long toSeconds( long n )
+       {
+               return n/SECONDS;
+       }
+
+       public static long toMinutes( long n )
+       {
+               return n/MINUTES;
+       }
+
+       public static long toHours( long n )
+       {
+               return n/HOURS;
+       }
+
+       public static long toDays( long n )
+       {
+               return n/DAYS;
+       }
+
+       public static void sleep()
+       {
+               while (true) {
+                       sleep(minutes(1));
+                       }
+       }
+
+       public static boolean sleep( long u )
+       {
+               try {
+                       Thread.sleep(toMillis(u));
+                       return true;
+                       }
+               catch( InterruptedException x ) {
+                       logger.log(Level.WARNING,"Interrupted while sleeping 
for "+u+" units.",x);
+                       }
+               return false;
+       }
+
+       public static Object notThis( Object obj )
+       {
+               return (obj!=null ? null : any);
+       }
+
+       public static int compare( Number n, Number m )
+       {
+               if (n==null || m==null) {
+                       return (n!=null ? 1 : (m!=null ? -1 : 0));
+                       }
+
+               if (n instanceof Double) {
+                       return (n.doubleValue()>m.doubleValue() ? 1 : 
(n.doubleValue()<m.doubleValue() ? -1 : 0));
+                       }
+               else if (n instanceof Float) {
+                       return (n.floatValue()>m.floatValue() ? 1 : 
(n.floatValue()<m.floatValue() ? -1 : 0));
+                       }
+               else if (n instanceof Long) {
+                       return (n.longValue()>m.longValue() ? 1 : 
(n.longValue()<m.longValue() ? -1 : 0));
+                       }
+               else if (n instanceof Integer) {
+                       return n.intValue()-m.intValue();
+                       }
+               else if (n instanceof Short) {
+                       return n.shortValue()-m.shortValue();
+                       }
+               else if (n instanceof Byte) {
+                       return n.byteValue()-m.byteValue();
+                       }
+               return 0;
+       }
+
+       public static int indexOf( Object[] objs, Object obj )
+       {
+               int     i;
+
+               if (obj==null) {
+                       return -1;
+                       }
+               for (i=0; i<objs.length && !objs[i].equals(obj); i++) {}
+               return (i<objs.length ? i : -1);
+       }
+
+       public static void shuffle( Object[] objs )
+       {
+               List    list;
+
+               list=new ArrayList();
+               list.addAll(Arrays.asList(objs));
+               Collections.shuffle(list);
+               System.arraycopy(list.toArray(),0,objs,0,objs.length);
+       }
+
+       public static SortedSet caseInsensitiveSort( Set in )
+       {
+               TreeSet out;
+
+               out=new TreeSet(new Comparator() {
+                       public int compare( Object o1, Object o2 )
+                       {
+                               if (o1==null || o2==null) {
+                                       return (o1!=null ? 1 : (o2!=null ? -1 : 
0));
+                                       }
+                               return 
o1.toString().compareToIgnoreCase(o2.toString());
+                       }
+                       });
+               out.addAll(in);
+               return out;
+       }
+
+       public static ByteBuffer resize( ByteBuffer buf, int newSize )
+       {
+               ByteBuffer      old;
+               int                     pos;
+
+               if (buf.capacity()<newSize) {
+                       logger.log(Level.FINEST,"Resize buffer from 
"+buf.capacity()+" bytes to "+newSize+" bytes.");
+
+                       pos=buf.position();
+                       buf.position(0);
+                       buf.limit(buf.capacity());
+
+                       old=buf;
+
+                       buf=ByteBuffer.allocateDirect(newSize);
+                       buf.put(old);
+                       buf.position(pos);
+                       }
+               return buf;
+       }
+}

Added: freeway/src/org/gnu/freeway/util/Voyager.java
===================================================================
--- freeway/src/org/gnu/freeway/util/Voyager.java       2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/util/Voyager.java       2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,543 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util;
+
+import java.io.*;
+import java.net.*;
+import java.nio.charset.*;
+import java.util.*;
+import java.util.logging.*;
+import javax.xml.parsers.*;
+import javax.xml.transform.*;
+
+import org.apache.xpath.*;
+import org.w3c.dom.*;
+import org.w3c.dom.traversal.*;
+import org.xml.sax.*;
+
+/**
+ * Instances of this class are used to traverse XML documents with the ability
+ * to select and update attributes or nodes.
+ * It maintains location information and previous visited levels.
+ *
+ * @see org.w3c.dom.Document
+ * @see org.w3c.dom.Node
+ * @see org.w3c.dom.Element
+ */
+
+public class Voyager extends Object
+{
+       private Document        document;
+       private Stack           levels;
+       private TLevel          current;
+
+
+       public Voyager()
+       {
+               this(createXML());
+       }
+
+       public Voyager( Document doc )
+       {
+               this(doc,doc.getDocumentElement());
+       }
+
+       public Voyager( Node node )
+       {
+               this(node.getOwnerDocument(),((node instanceof Element) ? 
(Element) node : null));
+       }
+
+       public Voyager( Element elem )
+       {
+               this(elem.getOwnerDocument(),elem);
+       }
+
+       protected Voyager( Document doc, Element elem )
+       {
+               super();
+               document=doc;
+               levels=new Stack();
+               if (elem!=null) {
+                       current=new TLevel(elem);
+                       }
+               else {
+                       current=null;
+                       }
+       }
+
+       public String toString()
+       {
+               return "Voyager [levels="+levels+", current="+current+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public Document getDocument()
+       {
+               return document;
+       }
+
+       public Voyager reset()
+       {
+               current=(TLevel) levels.get(0);
+               levels.clear();
+               return this;
+       }
+
+       public Voyager select()
+       {
+               List            list;
+               NodeList        nodes;
+               Node            nd;
+               int                     imax,i;
+
+               list=new ArrayList();
+
+               nodes=current.get().getChildNodes();
+
+               imax=nodes.getLength();
+               for (i=0; i<imax; i++) {
+                       nd=nodes.item(i);
+                       if (nd instanceof Element) {
+                               list.add(nd);
+                               }
+                       }
+
+               levels.push(current);
+               current=new TLevel(list);
+               return this;
+       }
+
+       public Voyager select( String str )
+       {
+               List                    list;
+               NodeIterator    iter;
+               Node                    nd;
+
+               list=new ArrayList();
+               try {
+                       iter=XPathAPI.selectNodeIterator(current.get(),str);
+                       for (nd=iter.nextNode(); nd!=null; nd=iter.nextNode()) {
+                               if (nd instanceof Element) {
+                                       list.add(nd);
+                                       }
+                               }
+                       }
+               catch( TransformerException x ) {
+                       }
+
+               levels.push(current);
+               current=new TLevel(list);
+               return this;
+       }
+
+       public Voyager selectTag( String str )
+       {
+               Element[]       elems;
+               NodeList        nodes;
+               int                     i;
+
+               nodes=current.get().getElementsByTagName(str);
+
+               elems=new Element[nodes.getLength()];
+               for (i=0; i<elems.length; i++) {
+                       elems[i]=(Element) nodes.item(i);
+                       }
+
+               levels.push(current);
+               current=new TLevel(elems);
+               return this;
+       }
+
+       public boolean next()
+       {
+               current.next();
+               return (current.get()!=null);
+       }
+
+       public Voyager up()
+       {
+               if (levels.empty()) {
+                       if (current==null) {
+                               throw new IllegalStateException();
+                               }
+
+                       current=null;
+                       }
+               else {
+                       current=(TLevel) levels.pop();
+                       }
+               return this;
+       }
+
+       public boolean has()
+       {
+               return current.get()!=null;
+       }
+
+       public boolean is( String tag )
+       {
+               return tag().equals(tag);
+       }
+
+       public String tag()
+       {
+               return current.get().getTagName();
+       }
+
+       public Element get()
+       {
+               return current.get();
+       }
+
+       public boolean get( String name, boolean def )
+       {
+               String  str;
+
+               if (current.get()!=null) {
+                       try {
+                               
str=current.get().getAttribute(name).toLowerCase();
+                               def=(str.equals("true") || str.equals("yes"));
+                               }
+                       catch( NumberFormatException x ) {
+                               }
+                       }
+               return def;
+       }
+
+       public int get( String name, int def )
+       {
+               if (current.get()!=null) {
+                       try {
+                               
def=Integer.parseInt(current.get().getAttribute(name));
+                               }
+                       catch( NumberFormatException x ) {
+                               }
+                       }
+               return def;
+       }
+
+       public long get( String name, long def )
+       {
+               if (current.get()!=null) {
+                       try {
+                               
def=Long.parseLong(current.get().getAttribute(name));
+                               }
+                       catch( NumberFormatException x ) {
+                               }
+                       }
+               return def;
+       }
+
+       public double get( String name, double def )
+       {
+               if (current.get()!=null) {
+                       try {
+                               
def=Double.parseDouble(current.get().getAttribute(name));
+                               }
+                       catch( NumberFormatException x ) {
+                               }
+                       }
+               return def;
+       }
+
+       public String get( String name, String def )
+       {
+               if (current.get()!=null && 
current.get().getAttribute(name).length()>0) {
+                       def=current.get().getAttribute(name);
+                       }
+               return def;
+       }
+
+       public String[] getArray( String name )
+       {
+               return getArray(name,"[:;, ]+");
+       }
+
+       public String[] getArray( String name, String sep )
+       {
+               String[]        p;
+               List            list;
+               int                     i;
+
+               list=new ArrayList();
+
+               if (current.get()!=null) {
+                       p=current.get().getAttribute(name).split(sep);
+                       for (i=0; i<p.length; i++) {
+                               if (p[i].length()>0) {
+                                       list.add(p[i]);
+                                       }
+                               }
+                       }
+               return (String[]) list.toArray(new String[list.size()]);
+       }
+
+       public int text( int def )
+       {
+               try {
+                       def=Integer.parseInt(text(""));
+                       }
+               catch( NumberFormatException x ) {
+                       }
+               return def;
+       }
+
+       public long text( long def )
+       {
+               try {
+                       def=Long.parseLong(text(""));
+                       }
+               catch( NumberFormatException x ) {
+                       }
+               return def;
+       }
+
+       public double text( double def )
+       {
+               try {
+                       def=Double.parseDouble(text(""));
+                       }
+               catch( NumberFormatException x ) {
+                       }
+               return def;
+       }
+
+       public String text( String def )
+       {
+               NodeList                nodes;
+               Node                    node;
+               StringBuffer    buf;
+               int                             imax,i;
+
+               buf=new StringBuffer();
+
+               if (current.get()!=null) {
+                       nodes=current.get().getChildNodes();
+
+                       imax=nodes.getLength();
+                       for (i=0; i<imax; i++) {
+                               node=nodes.item(i);
+                               if (node.getNodeType()==Node.TEXT_NODE || 
node.getNodeType()==Node.CDATA_SECTION_NODE) {
+                                       buf.append(node.getNodeValue());
+                                       }
+                               }
+                       }
+               return buf.toString();
+       }
+
+       public Voyager add( String str )
+       {
+               Element elem;
+
+               elem=document.createElement(str);
+               if (current!=null) {
+                       current.get().appendChild(elem);
+                       levels.push(current);
+                       }
+               else {
+                       document.appendChild(elem);
+                       }
+               current=new TLevel(elem);
+               return this;
+       }
+
+       public Voyager set( String str, int value )
+       {
+               return set(str,String.valueOf(value));
+       }
+
+       public Voyager set( String str, long value )
+       {
+               return set(str,String.valueOf(value));
+       }
+
+       public Voyager set( String str, double value )
+       {
+               return set(str,String.valueOf(value));
+       }
+
+       public Voyager set( String str, Object value )
+       {
+               return set(str,(value!=null ? value.toString() : ""));
+       }
+
+       public Voyager set( String str, String value )
+       {
+               current.get().setAttribute(str,value);
+               return this;
+       }
+
+       public Voyager set( int value )
+       {
+               return set(String.valueOf(value));
+       }
+
+       public Voyager set( long value )
+       {
+               return set(String.valueOf(value));
+       }
+
+       public Voyager set( double value )
+       {
+               return set(String.valueOf(value));
+       }
+
+       public Voyager set( Object value )
+       {
+               return set(value!=null ? value.toString() : "");
+       }
+
+       public Voyager set( String value )
+       {
+               Node    node;
+
+               node=document.createTextNode(value);
+               if (current!=null) {
+                       current.get().appendChild(node);
+                       }
+               else {
+                       document.appendChild(node);
+                       }
+               return this;
+       }
+
+       public Voyager comment( String value )
+       {
+               Node    node;
+
+               node=document.createComment(" "+value.trim()+" ");
+               if (current!=null) {
+                       current.get().appendChild(node);
+                       }
+               else {
+                       document.appendChild(node);
+                       }
+               return this;
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       protected class TLevel extends Object
+       {
+               private Element[]       elems;
+               private int                     index;
+
+
+               public TLevel( Element e )
+               {
+                       this(new Element[] { e });
+               }
+
+               public TLevel( List list )
+               {
+                       this((Element[]) list.toArray(new 
Element[list.size()]));
+               }
+
+               public TLevel( Element[] e )
+               {
+                       super();
+                       elems=e;
+                       index=-1;
+               }
+
+               public Element next()
+               {
+                       if (index==elems.length) {
+                               throw new IllegalStateException();
+                               }
+                       index++;
+                       return (index<elems.length ? elems[index] : null);
+               }
+
+               public Element get()
+               {
+                       if (index<0) {
+                               index++;
+                               }
+                       return (index<elems.length ? elems[index] : null);
+               }
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public static Voyager create( URL url, Charset cs )
+       {
+               Document        doc;
+
+               doc=readXML(url,cs);
+               return (doc!=null ? new Voyager(doc) : null);
+       }
+
+       public static Document createXML()
+       {
+               Document        doc;
+               Logger          logger;
+
+               doc=null;
+               try {
+                       
doc=DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
+                       }
+               catch( Throwable x ) {
+                       logger=Logger.getLogger(Voyager.class.getName());
+                       logger.log(Level.WARNING,"Could not create XML document 
!",x);
+                       }
+               return doc;
+       }
+
+       public static Document readXML( URL url, Charset cs )
+       {
+               Document        doc;
+               InputStream     is;
+               Logger          logger;
+
+               assert(cs!=null);
+
+               doc=null;
+               if (url!=null) {
+                       try {
+                               is=new BufferedInputStream(url.openStream());
+                               try {
+                                       doc=readXML(is,cs);
+                                       }
+                               finally {
+                                       is.close();
+                                       }
+                               }
+                       catch( IOException x ) {
+                               
logger=Logger.getLogger(Voyager.class.getName());
+                               logger.log(Level.WARNING,"Could not parse 
"+url+" encoded with "+cs.name()+" !",x);
+                               }
+                       }
+               return doc;
+       }
+
+       public static Document readXML( InputStream is, Charset cs )
+       {
+               Document        doc;
+               InputSource     src;
+               Logger          logger;
+
+               doc=null;
+               try {
+                       src=new InputSource(is);
+                       src.setEncoding(cs.name());
+
+                       
doc=DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(src);
+                       }
+               catch( SAXParseException x ) {
+                       logger=Logger.getLogger(Voyager.class.getName());
+                       logger.log(Level.WARNING,"Could not parse XML document 
(line: "+x.getLineNumber()+", column: "+x.getColumnNumber()+") !",x);
+                       }
+               catch( Throwable x ) {
+                       logger=Logger.getLogger(Voyager.class.getName());
+                       logger.log(Level.WARNING,"Could not parse XML document 
!",x);
+                       }
+               return doc;
+       }
+}

Added: freeway/src/org/gnu/freeway/util/crypto/Crypto.java
===================================================================
--- freeway/src/org/gnu/freeway/util/crypto/Crypto.java 2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/util/crypto/Crypto.java 2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,273 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util.crypto;
+
+import java.math.*;
+import java.nio.*;
+import java.security.*;
+import java.util.logging.*;
+
+
+/**
+ * various helper methods
+ * wrapper around CRC32 and other trivial helper functions
+ * implementation of CRC32
+ */
+
+public class Crypto extends Object
+{
+       private static final int[]      CRC_TABLE       =       {
+               0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 
0x706AF48F, 0xE963A535, 0x9E6495A3,
+               0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 
0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
+               0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 
0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
+               0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 
0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
+               0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 
0x4B04D447, 0xD20D85FD, 0xA50AB56B,
+               0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 
0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
+               0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 
0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
+               0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 
0x58684C11, 0xC1611DAB, 0xB6662D3D,
+               0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 
0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
+               0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 
0x086D3D2D, 0x91646C97, 0xE6635C01,
+               0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 
0x1B01A57B, 0x8208F4C1, 0xF50FC457,
+               0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 
0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
+               0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 
0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
+               0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 
0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
+               0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 
0x206F85B3, 0xB966D409, 0xCE61E49F,
+               0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 
0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
+               0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 
0x9DD277AF, 0x04DB2615, 0x73DC1683,
+               0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 
0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
+               0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 
0x806567CB, 0x196C3671, 0x6E6B06E7,
+               0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 
0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
+               0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 
0xA6BC5767, 0x3FB506DD, 0x48B2364B,
+               0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 
0xA867DF55, 0x316E8EEF, 0x4669BE79,
+               0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 
0xBB0B4703, 0x220216B9, 0x5505262F,
+               0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 
0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
+               0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 
0xEB0E363F, 0x72076785, 0x05005713,
+               0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 
0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
+               0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 
0xF6B9265B, 0x6FB077E1, 0x18B74777,
+               0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 
0xF862AE69, 0x616BFFD3, 0x166CCF45,
+               0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 
0xD06016F7, 0x4969474D, 0x3E6E77DB,
+               0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 
0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
+               0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 
0xCDD70693, 0x54DE5729, 0x23D967BF,
+               0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 
0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
+               };
+
+       private static final char[]     PEM     =       {
+               'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
+               'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
+               'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
+               'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
+               'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
+               'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7',
+               '8', '9', '_', '/'
+               };
+
+       private static Logger                   logger;
+       private static SecureRandom     rand;
+
+       static {
+               logger=Logger.getLogger(Crypto.class.getName());
+               try {
+                       rand=SecureRandom.getInstance("SHA1PRNG");
+                       rand.setSeed(System.currentTimeMillis());
+                       }
+               catch( NoSuchAlgorithmException x ) {
+                       logger.log(Level.SEVERE,"Algorithm not found !",x);
+                       rand=new SecureRandom();
+                       }
+               }
+
+
+       public static SecureRandom getRandom()
+       {
+               return rand;
+       }
+
+       /**
+        * Produce a random value.
+        *
+        * @param i the upper limit (exclusive) for the random number
+        * @return a random value in the interval [0,i[.
+        */
+
+       public static int nextInt( int i )
+       {
+               return rand.nextInt(i);
+       }
+
+       public static int nextInt()
+       {
+               return rand.nextInt();
+       }
+
+       public static long nextLong( long n )
+       {
+               return (rand.nextLong() % n);
+       }
+
+       public static long nextLong()
+       {
+               return rand.nextLong();
+       }
+
+       public static byte nextByte()
+       {
+               return (byte) rand.nextInt();
+       }
+
+       public static void nextBytes( byte[] b )
+       {
+               rand.nextBytes(b);
+       }
+
+       public static void nextBytes( byte[] b, int offset, int length )
+       {
+               int     i;
+
+               for (i=0; i<length; i++) {
+                       b[offset+i]=(byte) rand.nextInt(256);
+                       }
+       }
+
+       /**
+        * Get an array with a random permutation of the numbers 0...n-1.
+        * Get an array with a random permutation of the
+        * numbers 0...n-1.
+        * @param n the size of the array
+        * @return the permutation array (allocated from heap)
+        */
+
+       public static int[] permute( int n )
+       {
+               int[]   ret;
+               int             tmp,i,j;
+
+               ret=new int[n];
+               for (i=0; i<n; i++) {
+                       ret[i]=i;
+                       }
+               for (i=0; i<n; i++) {
+                       j=rand.nextInt(n);
+
+                       tmp=ret[j];
+                       ret[j]=ret[i];
+                       ret[i]=tmp;
+                       }
+               return ret;
+       }
+
+       public static int crc32( byte[] b )
+       {
+               return crc32(b,0,b.length);
+       }
+
+       /**
+        * Compute the CRC32 checksum for the first len bytes of the buffer.
+        * This computes the standard preset and inverted CRC, as used
+        * by most networking standards.  Start by passing in an initial
+        * chaining value of 0, and then pass in the return value from the
+        * previous crc32() call.  The final return value is the CRC.
+        * Note that this is a little-endian CRC, which is best used with
+        * data transmitted lsbit-first, and it should, itself, be appended
+        * to data in little-endian byte and bit order to preserve the
+        * property of detecting all burst errors of length 32 bits or less.
+        *
+        * @param buf the data over which we're taking the CRC
+        * @param offset offset
+        * @param length the length of the buffer
+        * @return the resulting CRC32 checksum
+        */
+
+       public static int crc32( byte[] buf, int offset, int length )
+       {
+               int     crc,i;
+
+               crc=0xffffffff;
+               for (i=0; i<length; i++) {
+                       crc=(crc>>>8)^CRC_TABLE[(crc^buf[offset+i]) & 
0x000000ff];
+                       }
+               return crc^0xffffffff;
+       }
+
+       public static int crc32( ByteBuffer buf )
+       {
+               int     crc,imax,i;
+
+               crc=0xffffffff;
+
+               imax=buf.position();
+               for (i=0; i<imax; i++) {
+                       crc=(crc>>>8)^CRC_TABLE[(crc^buf.get(i)) & 0x000000ff];
+                       }
+               return crc^0xffffffff;
+       }
+
+       public static void copyInt( BigInteger i, byte[] buf, int offset )
+       {
+               System.arraycopy(i.toByteArray(),((i.bitLength() & 
0x00000007)!=0 ? 0 : 1),buf,offset,(i.bitLength()+7)/8);
+       }
+
+       public static BigInteger makeInt( byte[] buf, int offset, int length )
+       {
+               byte[]  tmp;
+
+               tmp=new byte[length+1];
+               tmp[0]=0;
+               System.arraycopy(buf,offset,tmp,1,length);
+               return new BigInteger(tmp);
+       }
+
+       public static String nextString( int size )
+       {
+               byte[]  buf;
+
+               buf=new byte[(Math.max(size,4)*6)/8];
+               rand.nextBytes(buf);
+               buf=encode(buf);
+               return new String(buf);
+       }
+
+       protected static byte[] encode( byte[] buf )
+       {
+               byte[]  b;
+               byte    byte0,byte1,byte2,byte3,byte4,byte5;
+               int             i,j,k,l,i1;
+
+               if (buf.length>0) {
+                       b = new byte[((buf.length + 2) / 3) * 4];
+                       i = 0;
+                       j = 0;
+                       for (k = buf.length; k > 0; k -= 3) {
+                               if (k == 1) {
+                                       byte0 = buf[i++];
+                                       l = 0;
+                                       b[j++] = (byte)PEM[byte0 >>> 2 & 0x3f];
+                                       b[j++] = (byte)PEM[(byte0 << 4 & 0x30) 
+ (l >>> 4 & 0xf)];
+                                       b[j++] = 61;
+                                       b[j++] = 61;
+                                       }
+                               else if (k == 2) {
+                                       byte1 = buf[i++];
+                                       byte3 = buf[i++];
+                                       i1 = 0;
+                                       b[j++] = (byte)PEM[byte1 >>> 2 & 0x3f];
+                                       b[j++] = (byte)PEM[(byte1 << 4 & 0x30) 
+ (byte3 >>> 4 & 0xf)];
+                                       b[j++] = (byte)PEM[(byte3 << 2 & 0x3c) 
+ (i1 >>> 6 & 0x3)];
+                                       b[j++] = 61;
+                                       }
+                               else {
+                                       byte2 = buf[i++];
+                                       byte4 = buf[i++];
+                                       byte5 = buf[i++];
+                                       b[j++] = (byte)PEM[byte2 >>> 2 & 0x3f];
+                                       b[j++] = (byte)PEM[(byte2 << 4 & 0x30) 
+ (byte4 >>> 4 & 0xf)];
+                                       b[j++] = (byte)PEM[(byte4 << 2 & 0x3c) 
+ (byte5 >>> 6 & 0x3)];
+                                       b[j++] = (byte)PEM[byte5 & 0x3f];
+                                       }
+                               }
+                       buf=b;
+                       }
+               return buf;
+       }
+}

Added: freeway/src/org/gnu/freeway/util/crypto/EncryptedData.java
===================================================================
--- freeway/src/org/gnu/freeway/util/crypto/EncryptedData.java  2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/util/crypto/EncryptedData.java  2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,73 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util.crypto;
+
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+import java.util.*;
+
+/**
+ *
+ */
+
+public class EncryptedData extends Object implements Persistent
+{
+       public static final int SIZE    =       PrivateKey.RSA_ENC_LEN;
+
+       private byte[]  data;
+
+
+       public EncryptedData()
+       {
+               super();
+               data=new byte[PrivateKey.RSA_ENC_LEN];
+       }
+
+       public EncryptedData( byte[] b )
+       {
+               this();
+               assert(b!=null && b.length==PrivateKey.RSA_ENC_LEN) : "array 
null or with a bad size ("+PrivateKey.RSA_ENC_LEN+" required)";
+               System.arraycopy(b,0,data,0,b.length);
+       }
+
+       public String toString()
+       {
+               return "Encrypted data";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int hashCode()
+       {
+               return data[0]>>8^data[5];
+       }
+
+       public boolean equals( Object obj )
+       {
+               return ((obj instanceof EncryptedData) && 
Arrays.equals(data,((EncryptedData) obj).data));
+       }
+
+       public byte[] getData()
+       {
+               return data;
+       }
+
+       public int getByteSize()
+       {
+               return SIZE;
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               buf.put(data);
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               buf.get(data);
+       }
+}

Added: freeway/src/org/gnu/freeway/util/crypto/HashCode160.java
===================================================================
--- freeway/src/org/gnu/freeway/util/crypto/HashCode160.java    2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/util/crypto/HashCode160.java    2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,465 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util.crypto;
+
+import org.gnu.freeway.util.io.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+import java.security.*;
+import java.util.logging.*;
+
+/**
+ * A 160-bit hashcode
+ * RIPE160MD hash related functions
+ * Hashing and hash conversion methods.
+ */
+
+//todo: par essence, devrait etre immutable
+public class HashCode160 extends Object implements Persistent
+{
+       private static final char[]     HEX_DIGITS      =       {
+               '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 
'C', 'D', 'E', 'F'
+               };
+
+       public static final int SIZE    =       20;
+
+       private int     a;
+       private int     b;
+       private int     c;
+       private int     d;
+       private int     e;
+
+
+       public HashCode160()
+       {
+               this(0,0,0,0,0);
+       }
+
+       public HashCode160( HashCode160 h )
+       {
+               this(h.a,h.b,h.c,h.d,h.e);
+       }
+
+       public HashCode160( int _a, int _b, int _c, int _d, int _e )
+       {
+               super();
+               a=_a;
+               b=_b;
+               c=_c;
+               d=_d;
+               e=_e;
+       }
+
+       public String toString()
+       {
+               return "HashCode 160bits [toHex="+toHex()+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int hashCode()
+       {
+               return a;
+       }
+
+       public boolean equals( Object obj )
+       {
+               HashCode160     h;
+
+               if (!(obj instanceof HashCode160)) {
+                       return false;
+                       }
+               h=(HashCode160) obj;
+               return (h.a==a && h.b==b && h.c==c && h.d==d && h.e==e);
+       }
+
+       public int getA()
+       {
+               return a;
+       }
+
+       public int getB()
+       {
+               return b;
+       }
+
+       public int getC()
+       {
+               return c;
+       }
+
+       public int getD()
+       {
+               return d;
+       }
+
+       public int getE()
+       {
+               return e;
+       }
+
+       public int getInt( int index )
+       {
+               assert(index>=0 && index<5) : "index ("+index+") should have 
been in [0,4]";
+
+               switch (index) {
+                       case 0: return a;
+                       case 1: return b;
+                       case 2: return c;
+                       case 3: return d;
+                       }
+               return e;
+       }
+
+       /**
+        * Compute the distance with the given hashcode <code>h</code>.
+        * The computation must be fast, not involve a.a or a.e (they're used 
elsewhere), and be somewhat consistent.
+        * And of course, the result should be a positive number.
+        *
+        * @param h     hashcode the distance will be computed with
+        * @return      a positive number which is a measure for hashcode 
proximity.
+        */
+
+       public int distance( HashCode160 h )
+       {
+               int x = (b - h.b)>>16;
+               return ((x*x)>>16);
+       }
+
+       /**
+        * compute result(b) = a + delta
+        * @param h     hashcode to add
+        */
+
+       public void add( HashCode160 h )
+       {
+               a+=h.a;
+               b+=h.b;
+               c+=h.c;
+               d+=h.d;
+               e+=h.e;
+       }
+
+       /**
+        * compute result(delta) = b - a
+        * @param h     hashcode to substract
+        */
+
+       public void sub( HashCode160 h )
+       {
+               a-=h.a;
+               b-=h.b;
+               c-=h.c;
+               d-=h.d;
+               e-=h.e;
+       }
+
+       /**
+        * compute result = a ^ b
+        * @param h
+        */
+
+       public void xor( HashCode160 h )
+       {
+               a^=h.a;
+               b^=h.b;
+               c^=h.c;
+               d^=h.d;
+               e^=h.e;
+       }
+
+       public SessionKey makeKey()
+       {
+               return extractKey(null);
+       }
+
+       /**
+        * Convert a hashcode into a key.
+        * @param iv
+        * @return
+        */
+
+       public SessionKey extractKey( byte[] iv )
+       {
+               byte[]  buf;
+               int             i,n;
+
+               buf=new byte[16];
+               for (i=0; i<4; i++) {
+                       switch (i) {
+                               case 0: n=a; break;
+                               case 1: n=b; break;
+                               case 2: n=c; break;
+                               default: n=d; break;
+                               }
+                       buf[i*4]=(byte) ((n>>24) & 0x000000ff);
+                       buf[i*4+1]=(byte) ((n>>16) & 0x000000ff);
+                       buf[i*4+2]=(byte) ((n>>8) & 0x000000ff);
+                       buf[i*4+3]=(byte) (n & 0x000000ff);
+                       }
+
+               if (iv!=null) {
+                       iv[0]=iv[4]=(byte) ((e>>24) & 0x000000ff);
+                       iv[1]=iv[5]=(byte) ((e>>16) & 0x000000ff);
+                       iv[2]=iv[6]=(byte) ((e>>8) & 0x000000ff);
+                       iv[3]=iv[7]=(byte) (e & 0x000000ff);
+                       }
+               return new SessionKey(buf);
+       }
+
+       /**
+        * Convert (hash) block to hex (= filename)
+        * @return
+        */
+
+       public String toHex()
+       {
+               StringBuffer    buf;
+               int                             i,n;
+
+               buf=new StringBuffer();
+               for (i=0; i<20; i++) {
+                       switch (i>>2) {
+                               case 0: n=a; break;
+                               case 1: n=b; break;
+                               case 2: n=c; break;
+                               case 3: n=d; break;
+                               default: n=e; break;
+                               }
+                       switch (i & 3) {
+                               case 0: n=((n>>24) & 0x000000ff); break;
+                               case 1: n=((n>>16) & 0x000000ff); break;
+                               case 2: n=((n>>8) & 0x000000ff); break;
+                               default: n=(n & 0x000000ff); break;
+                               }
+
+                       buf.append(HEX_DIGITS[n & 0x0000000f]);         // get 
lower nibble
+                       buf.append(HEX_DIGITS[n>>4]);   // get higher nibble
+                       }
+               return buf.toString();
+       }
+
+       public int getByteSize()
+       {
+               return SIZE;
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               a=buf.getInt();
+               b=buf.getInt();
+               c=buf.getInt();
+               d=buf.getInt();
+               e=buf.getInt();
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               buf.putInt(a);
+               buf.putInt(b);
+               buf.putInt(c);
+               buf.putInt(d);
+               buf.putInt(e);
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       /**
+        * Generate a random hashcode.
+        * @return
+        */
+
+       public static HashCode160 random()
+       {
+               HashCode160     h;
+
+               h=new HashCode160();
+               h.a=Crypto.nextInt();
+               h.b=Crypto.nextInt();
+               h.c=Crypto.nextInt();
+               h.d=Crypto.nextInt();
+               h.e=Crypto.nextInt();
+               return h;
+       }
+
+       public static HashCode160 create( String str )
+       {
+               return create(str.getBytes());
+       }
+
+       public static HashCode160 create( byte[] block )
+       {
+               return create(block,0,block.length);
+       }
+
+       /**
+        * Hash block of given size.
+        *
+        * @param block the data to hash
+        * @param offset offset
+        * @param size the length of the data to hash
+        * @return
+        */
+
+       public static HashCode160 create( byte[] block, int offset, int size )
+       {
+               MessageDigest   md;
+               HashCode160             h;
+               Logger                  logger;
+
+               try {
+                       md=MessageDigest.getInstance("RIPEMD160");
+                       md.update(block,offset,size);
+                       h=fromBytes(md.digest());
+                       }
+               catch( GeneralSecurityException x ) {
+                       logger=Logger.getLogger(HashCode160.class.getName());
+                       logger.log(Level.SEVERE,"Could not compute digest of 
message !",x);
+                       h=null;
+                       }
+               catch( Throwable x ) {
+                       // catch *all* other exceptions, whatever they are
+                       logger=Logger.getLogger(HashCode160.class.getName());
+                       logger.log(Level.SEVERE,"Could not compute digest of 
message (unexpected exception) !",x);
+                       h=null;
+                       }
+               return h;
+       }
+
+       /**
+        * Compute the hash of an entire file. Does <em>not</em> load the 
entire file
+        * into memory but instead processes it in blocks. Very important for 
large files.
+        *
+        * @param f     the file to hash
+        *
+        * @return      the computed hashcode on success, null on error
+        */
+
+       public static HashCode160 create( FileLocation f )
+       {
+               byte[]                  b;
+               MessageDigest   md;
+               MappedFile              file;
+               HashCode160             h;
+               Logger                  logger;
+               long                            size,pos;
+               int                             len;
+
+               logger=Logger.getLogger(HashCode160.class.getName());
+
+               if (!f.exists()) {
+                       return null;
+                       }
+
+               try {
+                       md=MessageDigest.getInstance("RIPEMD160");
+
+                       b=new byte[65536];
+
+                       size=f.getSize();
+
+                       file=f.open();
+                       try {
+                               for (pos=0; pos<size; pos+=len) {
+                                       len=(int) Math.min(b.length,size-pos);
+
+                                       if (!file.readBytes(b,0,len)) {
+                                               logger.log(Level.SEVERE,"Could 
not compute digest of file \""+f.getLabel()+"\", failed to read bytes !");
+                                               return null;
+                                               }
+                                       md.update(b,0,len);
+                                       }
+                               }
+                       finally {
+                               file.close();
+                               }
+
+                       h=fromBytes(md.digest());
+                       }
+               catch( GeneralSecurityException x ) {
+                       logger.log(Level.SEVERE,"Could not compute digest of 
file \""+f.getLabel()+"\" !",x);
+                       h=null;
+                       }
+               catch( Throwable x ) {
+                       // catch *all* other exceptions, whatever they are
+                       logger.log(Level.SEVERE,"Could not compute digest of 
file \""+f.getLabel()+"\" (unexpected exception) !",x);
+                       h=null;
+                       }
+               return h;
+       }
+
+       private static HashCode160 fromBytes( byte[] b )
+       {
+               HashCode160     h;
+               int                     i,j,k,n;
+
+               h=new HashCode160();
+
+               k=0;
+               for (j=0; j<5; j++) {
+                       n=0;
+                       for (i=0; i<4; i++) {
+                               n<<=8;
+                               n|=(b[k++] & 0x000000ff);
+                               }
+                       switch (j) {
+                               case 0: h.a=n; break;
+                               case 1: h.b=n; break;
+                               case 2: h.c=n; break;
+                               case 3: h.d=n; break;
+                               default: h.e=n; break;
+                               }
+                       }
+               return h;
+       }
+
+       /**
+        * Convert hex (filename) to the hostIdentity
+        * @param hex the filename
+        * @return
+        */
+
+       public static HashCode160 parse( String hex )
+       {
+               HashCode160     h;
+               int                     i,j,n;
+               char            c;
+
+               if (hex==null || !hex.matches("[0-9A-Za-z]{40}")) {
+                       return null;
+                       }
+
+               h=new HashCode160();
+               for (i=0; i<5; i++) {
+                       n=0;
+                       for (j=0; j<8; j++) {
+                               n<<=4;
+
+                               c=hex.charAt(i*8+j+((j & 1)==0 ? 1 : -1));      
// take care of reversed nibble ( 0xA7 -> 0x7, 0xA )
+                               if (c>='a' && c<='f') {
+                                       n+=c-'a'+10;
+                                       }
+                               else if (c>='A' && c<='F') {
+                                       n+=c-'A'+10;
+                                       }
+                               else {
+                                       n+=c-'0';
+                                       }
+                               }
+
+                       switch (i) {
+                               case 0: h.a=n; break;
+                               case 1: h.b=n; break;
+                               case 2: h.c=n; break;
+                               case 3: h.d=n; break;
+                               default: h.e=n; break;
+                               }
+                       }
+               return h;
+       }
+}

Added: freeway/src/org/gnu/freeway/util/crypto/HostIdentity.java
===================================================================
--- freeway/src/org/gnu/freeway/util/crypto/HostIdentity.java   2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/util/crypto/HostIdentity.java   2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,82 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util.crypto;
+
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+
+/**
+ * The identity of the host (basically the RIPE160 hashcode of it's public 
key).
+ */
+
+//todo: par essence, devrait etre immutable
+public class HostIdentity extends Object implements Persistent
+{
+       public static final int SIZE    =       HashCode160.SIZE;
+
+       /** The RIPE160 hashcode of host's public key. */
+       private HashCode160     hash;
+
+
+       public HostIdentity()
+       {
+               this(new HashCode160());
+       }
+
+       public HostIdentity( PublicKey pub )
+       {
+               this(HashCode160.create(PersistentHelper.toBytes(pub)));
+       }
+
+       public HostIdentity( HashCode160 h )
+       {
+               super();
+               hash=h;
+       }
+
+       public String toString()
+       {
+               return "Host identity [name="+getName()+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public boolean equals( Object obj )
+       {
+               return ((obj instanceof HostIdentity) && ((HostIdentity) 
obj).hash.equals(hash));
+       }
+
+       public String getName()
+       {
+               return hash.toHex();
+       }
+
+       public int distance( HashCode160 h )
+       {
+               return hash.distance(h);
+       }
+
+       public int getHashCodeA()
+       {
+               return hash.getA();
+       }
+
+       public int getByteSize()
+       {
+               return SIZE;
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               hash.readBytes(buf,err);
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               hash.writeBytes(buf);
+       }
+}

Added: freeway/src/org/gnu/freeway/util/crypto/PrivateKey.java
===================================================================
--- freeway/src/org/gnu/freeway/util/crypto/PrivateKey.java     2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/util/crypto/PrivateKey.java     2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,397 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util.crypto;
+
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.net.*;
+
+import java.math.*;
+import java.nio.*;
+import java.security.*;
+import java.security.interfaces.*;
+import java.security.spec.*;
+import java.util.*;
+import java.util.logging.*;
+import javax.crypto.*;
+
+/**
+ * Encoded private key of a RSA keypair.
+ * public key cryptography (RSA)
+ * management of the node's main identity.
+ *
+ * We currently do not handle encryption of data
+ * that can not be done in a single call to the
+ * RSA methods (read: large chunks of data).
+ * We should never need that, as we can use
+ * the hash for larger pieces of data for signing,
+ * and for encryption, we only need to encode sessionkeys!
+ */
+
+public class PrivateKey extends LoggedObject implements Persistent
+{
+       public static final int BITS_LENGTH     =       2048;
+
+
+       /** Length of RSA encrypted data (2048 bit) */
+       public static final int RSA_ENC_LEN     =       256;
+
+
+       public static final int SIZE    =       16;
+
+       private static boolean  extraChecks;
+
+       static {
+               extraChecks=false;
+               }
+
+
+       private static final byte[]     NO_BYTES        =       new byte[] {};
+
+       private int             length;//inutile
+       private int             sizeN;
+       private int             sizeE;
+       private int             sizeD;
+       private int             sizeP;
+       private int             sizeQ;
+       private int             sizeDMPL;
+       private int             sizeDMQL;
+       private byte[]  key;
+
+
+       public PrivateKey()
+       {
+               super(true);
+               length=SIZE;
+               sizeN=0;
+               sizeE=0;
+               sizeD=0;
+               sizeP=0;
+               sizeQ=0;
+               sizeDMPL=0;
+               sizeDMQL=0;
+               key=NO_BYTES;
+       }
+
+       public String toString()
+       {
+               return "(RSA) Private key";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int hashCode()
+       {
+               return (key.length>5 ? key[0]>>8^key[5] : 17);
+       }
+
+       public boolean equals( Object obj )
+       {
+               return ((obj instanceof PrivateKey) && 
Arrays.equals(key,((PrivateKey) obj).key));
+       }
+
+       /**
+        * Decode the private key from the file-format back to the "normal", 
internal format.
+        * @return
+        */
+
+       public java.security.PrivateKey toJavaPrivateKey()
+       {
+               KeyPair pair;
+
+               pair=toJavaKeyPair();
+               return (pair!=null ? pair.getPrivate() : null);
+       }
+
+       public java.security.PublicKey toJavaPublicKey()
+       {
+               KeyPair pair;
+
+               pair=toJavaKeyPair();
+               return (pair!=null ? pair.getPublic() : null);
+       }
+
+       public KeyPair toJavaKeyPair()
+       {
+               KeyFactory      factory;
+               KeyPair         pair;
+               BigInteger      n,e,d,p,q,dmp1,dmq1,iqmp;
+               int                     sizeIQMP;
+
+               pair=null;
+
+               n=Crypto.makeInt(key,0,sizeN);
+               e=Crypto.makeInt(key,sizeN,sizeE);
+               d=Crypto.makeInt(key,sizeN+sizeE,sizeD);
+               p=(sizeP>0 ? Crypto.makeInt(key,sizeN+sizeE+sizeD,sizeP) : 
null);
+               q=(sizeQ>0 ? Crypto.makeInt(key,sizeN+sizeE+sizeD+sizeP,sizeQ) 
: null);
+               dmp1=(sizeDMPL>0 ? 
Crypto.makeInt(key,sizeN+sizeE+sizeD+sizeP+sizeQ,sizeDMPL) : null);
+               dmq1=(sizeDMQL>0 ? 
Crypto.makeInt(key,sizeN+sizeE+sizeD+sizeP+sizeQ+sizeDMPL,sizeDMQL) : null);
+
+               
sizeIQMP=key.length-(sizeN+sizeE+sizeD+sizeP+sizeQ+sizeDMPL+sizeDMQL);
+               iqmp=(sizeIQMP>0 ? 
Crypto.makeInt(key,sizeN+sizeE+sizeD+sizeP+sizeQ+sizeDMPL+sizeDMQL,sizeIQMP) : 
null);
+
+               try {
+                       factory=KeyFactory.getInstance("RSA");
+                       pair=new KeyPair(
+                               factory.generatePublic(new 
RSAPublicKeySpec(n,e)),
+                               factory.generatePrivate(new 
RSAPrivateCrtKeySpec(n,e,d,p,q,dmp1,dmq1,iqmp))
+                               );
+                       }
+               catch( GeneralSecurityException x ) {
+                       err("Could not create RSA keypair !",x);
+                       }
+               catch( Throwable x ) {
+                       // catch *all* other exceptions, whatever they are
+                       err("Could not create RSA keypair (unexpected 
exception) !",x);
+                       }
+               return pair;
+       }
+
+       /**
+        * Encode the private key in a format suitable for
+        * storing it into a file.return encoding of the private key. The first 
4 bytes give the size of the array, as usual.
+        * @param pair the hostkey to use
+        */
+
+       public void fromJavaKeyPair( KeyPair pair )
+       {
+               RSAPrivateCrtKey        priv;
+               BigInteger                      n,e,d,p,q,dmp1,dmq1,iqmp;
+               int                                     sizeIQMP;
+
+               priv=(RSAPrivateCrtKey) pair.getPrivate();
+               n=priv.getModulus();
+               e=priv.getPublicExponent();
+               d=priv.getPrivateExponent();
+               p=priv.getPrimeP();
+               q=priv.getPrimeQ();
+               dmp1=priv.getPrimeExponentP();
+               dmq1=priv.getPrimeExponentQ();
+               iqmp=priv.getCrtCoefficient();
+
+               sizeN=(n.bitLength()+7)/8;
+               sizeE=(e.bitLength()+7)/8;
+               sizeD=(d.bitLength()+7)/8;
+               sizeP=(p!=null ? (p.bitLength()+7)/8 : 0);
+               sizeQ=(q!=null ? (q.bitLength()+7)/8 : 0);
+               sizeDMPL=(dmp1!=null ? (dmp1.bitLength()+7)/8 : 0);
+               sizeDMQL=(dmq1!=null ? (dmq1.bitLength()+7)/8 : 0);
+               sizeIQMP=(iqmp!=null ? (iqmp.bitLength()+7)/8 : 0);
+
+               key=new 
byte[sizeN+sizeE+sizeD+sizeP+sizeQ+sizeDMPL+sizeDMQL+sizeIQMP];
+               Arrays.fill(key,(byte) 0);
+               Crypto.copyInt(n,key,0);
+               Crypto.copyInt(e,key,sizeN);
+               Crypto.copyInt(d,key,sizeN+sizeE);
+               if (p!=null) {
+                       Crypto.copyInt(p,key,sizeN+sizeE+sizeD);
+                       }
+               if (q!=null) {
+                       Crypto.copyInt(q,key,sizeN+sizeE+sizeD+sizeP);
+                       }
+               if (dmp1!=null) {
+                       Crypto.copyInt(dmp1,key,sizeN+sizeE+sizeD+sizeP+sizeQ);
+                       }
+               if (dmq1!=null) {
+                       
Crypto.copyInt(dmq1,key,sizeN+sizeE+sizeD+sizeP+sizeQ+sizeDMPL);
+                       }
+               if (iqmp!=null) {
+                       
Crypto.copyInt(iqmp,key,sizeN+sizeE+sizeD+sizeP+sizeQ+sizeDMPL+sizeDMQL);
+                       }
+
+               
length=sizeN+sizeE+sizeD+sizeP+sizeQ+sizeDMPL+sizeDMQL+sizeIQMP+16;
+       }
+
+       public org.gnu.freeway.util.crypto.PublicKey toPublicKey()
+       {
+               org.gnu.freeway.util.crypto.PublicKey   pub;
+
+               pub=new org.gnu.freeway.util.crypto.PublicKey();
+               pub.fromJavaPublicKey(toJavaPublicKey());
+               return pub;
+       }
+
+       /**
+        * Decrypt a given block with the hostkey.
+        *
+        * @param block the data to decrypt, encoded as returned by encrypt, 
not consumed
+        * @param result pointer to a location where the result can be stored
+        * @param max the maximum number of bits to store for the result, if
+        *        the decrypted block is bigger, an error is returned
+        * @return the size of the decrypted block, -1 on error
+        */
+
+       public int decrypt( EncryptedData block, byte[] result, int max )
+       {
+               Cipher  cipher;
+               byte[]  r;
+
+               if (block==null) {
+                       return -1;
+                       }
+
+               try {
+                       cipher=Cipher.getInstance("RSA/ECB/PKCS1PADDING");
+                       cipher.init(Cipher.DECRYPT_MODE,toJavaPrivateKey());
+                       r=cipher.doFinal(block.getData());
+
+                       if (r.length>max) {
+                               log(Level.WARNING,"Bad size of decrypted data 
(size "+r.length+", max. expected "+max+")");
+                               return -1;
+                               }
+
+                       System.arraycopy(r,0,result,0,r.length);
+                       return r.length;
+                       }
+               catch( GeneralSecurityException x ) {
+                       err("Block decryption failed !",x);
+                       return -1;
+                       }
+               catch( Throwable x ) {
+                       // catch *all* other exceptions, whatever they are
+                       err("Block decryption failed (unexpected exception) 
!",x);
+                       return -1;
+                       }
+       }
+
+       public org.gnu.freeway.util.crypto.Signature sign( String str )
+       {
+               return sign(str.getBytes());
+       }
+
+       public org.gnu.freeway.util.crypto.Signature sign( byte[] b )
+       {
+               return sign(b,0,b.length);
+       }
+
+       /**
+        * Sign a given data block. ALWAYS use only on data we entirely 
generated.
+        *
+        * @param block         the data to sign, first SIZE bytes give 
length/what to sign
+        * @param offset        offset
+        * @param len   how many bytes are there to sign ?
+        * @return                      true on success, false on failure
+        */
+
+       public org.gnu.freeway.util.crypto.Signature sign( byte[] block, int 
offset, int len )
+       {
+               byte[]                                                          
b;
+               java.security.Signature                         rsa;
+               org.gnu.freeway.util.crypto.Signature   sig;
+
+               if (block==null || len==0) {
+                       return null;
+                       }
+
+               sig=null;
+               try {
+                       
rsa=java.security.Signature.getInstance("RIPEMD160WITHRSA");
+                       rsa.initSign(toJavaPrivateKey());
+                       rsa.update(block,offset,len);
+                       b=rsa.sign();
+                       if (b.length!=RSA_ENC_LEN) {
+                               log(Level.SEVERE,"Signature length has 
unexpected value : "+b.length+" !");
+                               return null;
+                               }
+
+                       sig=new org.gnu.freeway.util.crypto.Signature(b);
+                       }
+               catch( Throwable x ) {
+                       err("Failed to sign "+len+" bytes of data !",x);
+                       return null;
+                       }
+
+               if (extraChecks) {
+                       if (!toPublicKey().verify(sig,block,0,len)) {
+                               log(Level.SEVERE,"generated signature does not 
pass verification !");
+                               return null;
+                               }
+                       }
+               return sig;
+       }
+
+       public int getByteSize()
+       {
+               return length;
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               buf.putShort((short) (length-2));
+               buf.putShort((short) sizeN);
+               buf.putShort((short) sizeE);
+               buf.putShort((short) sizeD);
+               buf.putShort((short) sizeP);
+               buf.putShort((short) sizeQ);
+               buf.putShort((short) sizeDMPL);
+               buf.putShort((short) sizeDMQL);
+               buf.put(key);
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               length=(buf.getShort() & 0x0000ffff)+2;
+               err.reportIf(length<SIZE,"Invalid length : "+length);
+
+               sizeN=buf.getShort() & 0x0000ffff;
+               sizeE=buf.getShort() & 0x0000ffff;
+               sizeD=buf.getShort() & 0x0000ffff;
+               sizeP=buf.getShort() & 0x0000ffff;
+               sizeQ=buf.getShort() & 0x0000ffff;
+               sizeDMPL=buf.getShort() & 0x0000ffff;
+               sizeDMQL=buf.getShort() & 0x0000ffff;
+               
err.reportIf(length<sizeN+sizeE+sizeD+sizeP+sizeQ+sizeDMPL+sizeDMQL+SIZE,"Invalid
 length : "+length);
+
+               key=(length>SIZE ? new byte[length-SIZE] : NO_BYTES);
+               buf.get(key);
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       /**
+        * Create a new RSA host key.
+        * @return
+        */
+
+       public static PrivateKey create()
+       {
+               KeyPairGenerator        keyGen;
+               PrivateKey                              hk;
+               Logger  logger;
+
+
+               try {
+                       keyGen=KeyPairGenerator.getInstance("RSA");
+                       keyGen.initialize(new 
RSAKeyGenParameterSpec(BITS_LENGTH,BigInteger.valueOf(65535)),Crypto.getRandom());
+
+                       hk=new PrivateKey();
+                       hk.fromJavaKeyPair(keyGen.generateKeyPair());
+                       }
+               catch( GeneralSecurityException x ) {
+                       logger=Logger.getLogger(PrivateKey.class.getName());
+                       logger.log(Level.SEVERE,"Failed to generate RSA key 
pair !",x);
+                       hk=null;
+                       }
+               catch( Throwable x ) {
+                       // catch *all* other exceptions, whatever they are
+                       logger=Logger.getLogger(PrivateKey.class.getName());
+                       logger.log(Level.SEVERE,"Failed to generate RSA key 
pair (unexpected exception) !",x);
+                       hk=null;
+                       }
+               return hk;
+       }
+
+       public static boolean isExtraChecksEnabled()
+       {
+               return extraChecks;
+       }
+
+       public static void setExtraChecksEnabled( boolean flag )
+       {
+               extraChecks=flag;
+       }
+}

Added: freeway/src/org/gnu/freeway/util/crypto/PublicKey.java
===================================================================
--- freeway/src/org/gnu/freeway/util/crypto/PublicKey.java      2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/util/crypto/PublicKey.java      2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,241 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util.crypto;
+
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.net.*;
+
+import java.math.*;
+import java.nio.*;
+import java.security.*;
+import java.security.interfaces.*;
+import java.security.spec.*;
+import java.util.*;
+import java.util.logging.*;
+import javax.crypto.*;
+
+/**
+ *
+ */
+
+public class PublicKey extends LoggedObject implements Persistent
+{
+       /** Length of an RSA KEY (d,e,len), 2048 bit (=256 octests) key d, 2 
byte e */
+       public static final int RSA_KEY_LEN     =       258;
+
+       public static final int SIZE            =       RSA_KEY_LEN+6;
+
+       private int             sizeN;
+       private byte[]  key;
+
+
+       public PublicKey()
+       {
+               super(true);
+               sizeN=0;
+               key=new byte[RSA_KEY_LEN];
+       }
+
+       public String toString()
+       {
+               return "(RSA) Public key [sizeN="+sizeN+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int hashCode()
+       {
+               return key[0]>>8^key[5];
+       }
+
+       public boolean equals( Object obj )
+       {
+               PublicKey       pub;
+
+               if (!(obj instanceof PublicKey)) {
+                       return false;
+                       }
+               pub=(PublicKey) obj;
+               return (pub.sizeN==sizeN && Arrays.equals(key,pub.key));
+       }
+
+       public BigInteger getModulus()
+       {
+               return Crypto.makeInt(key,0,sizeN);
+       }
+
+       public BigInteger getExponent()
+       {
+               return Crypto.makeInt(key,sizeN,key.length-sizeN);
+       }
+
+       public java.security.PublicKey toJavaPublicKey()
+       {
+               KeyFactory      factory;
+               BigInteger      n,e;
+
+               n=getModulus();
+               e=getExponent();
+
+               try {
+                       factory=KeyFactory.getInstance("RSA");
+                       return factory.generatePublic(new 
RSAPublicKeySpec(n,e));
+                       }
+               catch( GeneralSecurityException x ) {
+                       err("Could not generate RSA public key from 
(n="+n+",e="+e+") !",x);
+                       }
+               catch( Throwable x ) {
+                       // catch *all* other exceptions, whatever they are
+                       err("Could not generate RSA public key from 
(n="+n+",e="+e+") (unexpected exception) !",x);
+                       }
+               return null;
+       }
+
+       /**
+        * Extract the public key of the host.
+        * @param pub the hostkey to extract into the result.
+        */
+
+       public void fromJavaPublicKey( java.security.PublicKey pub )
+       {
+               RSAPublicKey            rpub;
+               BigInteger              n,e;
+               int                             sizeE;
+
+               rpub=(RSAPublicKey) pub;
+               n=rpub.getModulus();
+               e=rpub.getPublicExponent();
+
+               sizeN=(n.bitLength()+7)/8;
+               sizeE=(e.bitLength()+7)/8;
+
+               assert(sizeN+sizeE==RSA_KEY_LEN) : "sizeN ("+sizeN+") + sizeE 
("+sizeE+") != RSA_KEY_LEN ("+RSA_KEY_LEN+")";
+
+               Crypto.copyInt(n,key,0);
+               Crypto.copyInt(e,key,sizeN);
+       }
+
+       public EncryptedData encrypt( String str )
+       {
+               byte[]  b;
+
+               b=str.getBytes();
+               return encrypt(b,0,b.length);
+       }
+
+       public EncryptedData encrypt( byte[] block )
+       {
+               return encrypt(block,0,block.length);
+       }
+
+       /**
+        * Encrypt a block with the public key of another host that uses the 
same cypher.
+        *
+        * @param block the block to encrypt
+        * @param offset
+        * @param length the size of block
+        * @return false on error, true if ok
+        */
+
+       public EncryptedData encrypt( byte[] block, int offset, int length )
+       {
+               byte[]                  r;
+               Cipher                  cipher;
+               EncryptedData   target;
+
+               target=null;
+               try {
+                       cipher=Cipher.getInstance("RSA/ECB/PKCS1PADDING");
+                       cipher.init(Cipher.ENCRYPT_MODE,toJavaPublicKey());
+                       r=cipher.doFinal(block,offset,length);
+
+                       if (r.length!=PrivateKey.RSA_ENC_LEN) {
+                               log(Level.WARNING,"Encrypted result with an 
unexpected size : "+r.length+" != "+PrivateKey.RSA_ENC_LEN+".");
+                               return null;
+                               }
+
+                       target=new EncryptedData(r);
+                       }
+               catch( GeneralSecurityException x ) {
+                       err("Block encryption failed !",x);
+                       }
+               catch( Throwable x ) {
+                       // catch *all* other exceptions, whatever they are
+                       err("Block encryption failed (unexpected exception) 
!",x);
+                       }
+               return target;
+       }
+
+       public boolean verify( org.gnu.freeway.util.crypto.Signature sig, 
String str )
+       {
+               return verify(sig,str.getBytes());
+       }
+
+       public boolean verify( org.gnu.freeway.util.crypto.Signature sig, 
byte[] b )
+       {
+               return verify(sig,b,0,b.length);
+       }
+
+       /**
+        * Verify signature.
+        *
+        * @param sig signature
+        * @param block the signed data
+        * @param offset the offset
+        * @param length the length of the block
+        * @return true if ok, false if invalid
+        */
+
+       public boolean verify( org.gnu.freeway.util.crypto.Signature sig, 
byte[] block, int offset, int length )
+       {
+               java.security.Signature s;
+
+               if (sig==null || block==null) {
+                       return false;   // hey, no data !?
+                       }
+
+               try {
+                       
s=java.security.Signature.getInstance("RIPEMD160withRSA");
+                       s.initVerify(toJavaPublicKey());
+                       s.update(block,offset,length);
+                       return s.verify(sig.getBytes());
+                       }
+               catch( GeneralSecurityException x ) {
+                       err("Could not verify signature !",x);
+                       }
+               catch( Throwable x ) {
+                       // catch *all* other exceptions, whatever they are
+                       err("Could not verify signature (unexpected exception) 
!",x);
+                       }
+               return false;
+       }
+
+       public int getByteSize()
+       {
+               return SIZE;
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               buf.putShort((short) (SIZE-2));
+               buf.putShort((short) sizeN);
+               buf.put(key);
+               buf.putShort((short) 0);
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               int     length,padding;
+
+               length=buf.getShort() & 0x0000ffff;
+               err.reportIf(length!=SIZE-2,"Bad length : "+length+" != 
"+(SIZE-2));
+
+               sizeN=buf.getShort() & 0x0000ffff;
+               buf.get(key);
+               padding=buf.getShort() & 0x0000ffff;
+               err.reportIf(padding!=0,"Padding is not null ("+padding+") !");
+       }
+}

Added: freeway/src/org/gnu/freeway/util/crypto/SessionKey.java
===================================================================
--- freeway/src/org/gnu/freeway/util/crypto/SessionKey.java     2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/util/crypto/SessionKey.java     2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,269 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util.crypto;
+
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+import java.security.*;
+import java.util.*;
+import java.util.logging.*;
+import javax.crypto.*;
+import javax.crypto.spec.*;
+
+/**
+ * type for session keys
+ * Symetric encryption services.
+ * sym encrypt/decrypt operations
+ */
+
+public class SessionKey extends LoggedObject implements Persistent
+{
+       /** size of blowfish key in bytes */
+       public static final int BF_KEYSIZE                              =       
16;
+
+       /** this integer is 64 bit, blowfish */
+       public static final int BLOWFISH_BLOCK_LENGTH   =       8;
+
+       /** length of the sessionkey in bytes (128 BIT sessionkey) */
+       public static final int SESSIONKEY_LEN  =       (128/8);
+       public static final int SIZE                    =       SESSIONKEY_LEN;
+       /** value for the IV in the streamcipher for the link-to-link 
encryption */
+       public static final byte[]      INITVALUE       =       
"GNUnet!!".getBytes();
+
+       private byte[]  data;
+
+
+       public SessionKey()
+       {
+               super(true);
+               data=new byte[SIZE];
+       }
+
+       public SessionKey( byte[] b )
+       {
+               this();
+
+               assert(b.length==SIZE);
+
+               System.arraycopy(b,0,data,0,b.length);
+       }
+
+       public String toString()
+       {
+               return "Session key";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int hashCode()
+       {
+               return data[0]>>8^data[5];
+       }
+
+       public boolean equals( Object obj )
+       {
+               return ((obj instanceof SessionKey) && 
Arrays.equals(data,((SessionKey) obj).data));
+       }
+
+       public byte[] encrypt( String str, byte[] iv )
+       {
+               byte[]  b;
+
+               b=str.getBytes();
+               return encrypt(b,0,b.length,iv);
+       }
+
+       //todo: 'iv' inutile puisque égal à INITVALUE
+       public byte[] encrypt( byte[] block, byte[] iv )
+       {
+               return encrypt(block,0,block.length,iv);
+       }
+
+       /**
+        * Encrypt a block with the public key of another host that uses the 
same cypher.
+        *
+        * @param block         the block to encrypt
+        * @param offset        offset
+        * @param length        the size of the block
+        * @param iv            the initialization vector to use, use INITVALUE 
for streams.
+        * @return                      the size of the encrypted block, -1 for 
errors
+        */
+
+       public byte[] encrypt( byte[] block, int offset, int length, byte[] iv )
+       {
+               Cipher  cipher;
+
+               assert(block!=null && offset>=0 && length>=0 && 
offset+length<=block.length && iv!=null);
+
+               try {
+                       cipher=Cipher.getInstance("Blowfish/CFB/NoPadding");
+                       cipher.init(Cipher.ENCRYPT_MODE,new 
SecretKeySpec(data,"Blowfish"),new IvParameterSpec(iv));
+                       return cipher.doFinal(block,offset,length);
+                       }
+               catch( GeneralSecurityException x ) {
+                       err("Block encryption failed !",x);
+                       }
+               catch( Throwable x ) {
+                       // catch *all* other exceptions, whatever they are
+                       err("Block encryption failed (unexpected exception) 
!",x);
+                       }
+               return null;
+       }
+
+       public ByteBuffer encrypt( ByteBuffer buf )
+       {
+               byte[]          b;
+               Cipher          cipher;
+               ByteBuffer      res;
+               int                     pos;
+
+               res=null;
+               try {
+                       cipher=Cipher.getInstance("Blowfish/CFB/NoPadding");
+                       cipher.init(Cipher.ENCRYPT_MODE,new 
SecretKeySpec(data,"Blowfish"),new IvParameterSpec(INITVALUE));
+
+                       pos=buf.position();
+                       buf.flip();
+                       try {
+                               b=new byte[pos];
+                               buf.get(b);
+
+                               b=cipher.doFinal(b);
+                               if (b.length==pos) {
+                                       res=ByteBuffer.allocateDirect(b.length);
+                                       res.put(b);
+                                       }
+                               else {
+                                       log(Level.SEVERE,"Failed to encrypt 
buffer (expected "+pos+" bytes, got "+b.length+") !");
+                                       }
+                               }
+                       finally {
+                               buf.clear();
+                               buf.position(pos);
+                               }
+                       }
+               catch( GeneralSecurityException x ) {
+                       err("Failed to encrypt buffer !",x);
+                       }
+               catch( Throwable x ) {
+                       // catch *all* other exceptions, whatever they are
+                       err("Failed to encrypt buffer (unexpected exception) 
!",x);
+                       }
+               return res;
+       }
+
+       public byte[] decrypt( byte[] block, byte[] iv )
+       {
+               return decrypt(block,0,block.length,iv);
+       }
+
+       /**
+        * Decrypt a given block with the sessionkey.
+        *
+        * @param block the data to decrypt, encoded as returned by encrypt
+        * @param offset offset
+        * @param length the size of the block to decrypt/how big is the block?
+        * @param iv the initialization vector to use, use INITVALUE for 
streams.
+        * @return -1 on failure, size of decrypted block on success
+        */
+
+       public byte[] decrypt( byte[] block, int offset, int length, byte[] iv )
+       {
+               Cipher  cipher;
+
+               assert(block!=null && offset>=0 && length>=0 && 
offset+length<=block.length && iv!=null);
+
+               try {
+                       cipher=Cipher.getInstance("Blowfish/CFB/NoPadding");
+                       cipher.init(Cipher.DECRYPT_MODE,new 
SecretKeySpec(data,"Blowfish"),new IvParameterSpec(iv));
+                       return cipher.doFinal(block,offset,length);
+                       }
+               catch( GeneralSecurityException x ) {
+                       err("Block decryption failed !",x);
+                       }
+               catch( Throwable x ) {
+                       // catch *all* other exceptions, whatever they are
+                       err("Block decryption failed (unexpected exception) 
!",x);
+                       }
+               return null;
+       }
+
+       public boolean decrypt( ByteBuffer buf, byte[] iv )
+       {
+               byte[]  b;
+               Cipher  cipher;
+               int             pos;
+
+               assert(iv!=null);
+
+               try {
+                       cipher=Cipher.getInstance("Blowfish/CFB/NoPadding");
+                       cipher.init(Cipher.DECRYPT_MODE,new 
SecretKeySpec(data,"Blowfish"),new IvParameterSpec(iv));
+
+                       pos=buf.position();
+                       buf.flip();
+                       try {
+                               b=new byte[pos];
+                               buf.get(b);
+                               b=cipher.doFinal(b);
+                               if (b.length!=pos) {
+                                       log(Level.SEVERE,"Failed to encrypt 
decrypt (expected "+pos+" bytes, got "+b.length+") !");
+                                       return false;
+                                       }
+                               buf.clear();
+                               buf.put(b);
+                               }
+                       finally {
+                               buf.clear();
+                               buf.position(pos);
+                               }
+                       return true;
+                       }
+               catch( GeneralSecurityException x ) {
+                       err("Failed to decrypt buffer !",x);
+                       }
+               catch( Throwable x ) {
+                       // catch *all* other exceptions, whatever they are
+                       err("Failed to decrypt buffer (unexpected exception) 
!",x);
+                       }
+               return false;
+       }
+
+       public int getByteSize()
+       {
+               return SIZE;
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               buf.put(data);
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               buf.get(data);
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       /**
+        * Create a new SessionKey (for Blowfish)
+        * Create a new Session key.
+        * @return
+        */
+
+       public static SessionKey create()
+       {
+               SessionKey      skey;
+
+               skey=new SessionKey();
+               Crypto.nextBytes(skey.data);
+               return skey;
+       }
+}

Added: freeway/src/org/gnu/freeway/util/crypto/Signature.java
===================================================================
--- freeway/src/org/gnu/freeway/util/crypto/Signature.java      2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/util/crypto/Signature.java      2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,73 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util.crypto;
+
+import org.gnu.freeway.util.net.*;
+
+import java.nio.*;
+import java.util.*;
+
+/**
+ *
+ */
+
+public class Signature extends Object implements Persistent
+{
+       public static final int SIZE    =       PrivateKey.RSA_ENC_LEN;
+
+       private byte[]  data;
+
+
+       public Signature()
+       {
+               super();
+               data=new byte[SIZE];
+       }
+
+       public Signature( byte[] b )
+       {
+               this();
+               assert(b.length==Signature.SIZE) : "Bad signature length !";
+               System.arraycopy(b,0,data,0,b.length);
+       }
+
+       public String toString()
+       {
+               return "Signature";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int hashCode()
+       {
+               return data[0]>>8^data[5];
+       }
+
+       public boolean equals( Object obj )
+       {
+               return ((obj instanceof Signature) && 
Arrays.equals(data,((Signature) obj).data));
+       }
+
+       public byte[] getBytes()
+       {
+               return data;
+       }
+
+       public int getByteSize()
+       {
+               return SIZE;
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               buf.get(data);
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               buf.put(data);
+       }
+}

Added: freeway/src/org/gnu/freeway/util/io/Connector.java
===================================================================
--- freeway/src/org/gnu/freeway/util/io/Connector.java  2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/util/io/Connector.java  2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,95 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util.io;
+
+import org.gnu.freeway.util.*;
+
+import java.io.*;
+
+/**
+ *
+ */
+
+public class Connector extends Task
+{
+       public static final int OUT     =       0;
+       public static final int ERR     =       1;
+
+       private int                                     type;
+       private InputStream                     input;
+       private ConnectorEndPoint       endPoint;
+
+
+       public Connector( int t, InputStream is )
+       {
+               super("CONNECT-"+t,null);
+               type=t;
+               input=is;
+               endPoint=null;
+       }
+
+       public ConnectorEndPoint getEndPoint()
+       {
+               return endPoint;
+       }
+
+       public void setEndPoint( ConnectorEndPoint end )
+       {
+               endPoint=end;
+       }
+
+       public boolean launch()
+       {
+               setAction(new AbstractAction() {
+                       public void perform() throws IOException
+                       {
+                               readForEver();
+                       }
+                       });
+               return super.launch();
+       }
+
+       public void readForEver() throws IOException
+       {
+               BufferedReader  rd;
+               String                  str;
+
+               rd=new BufferedReader(new InputStreamReader(input));
+               for (str=rd.readLine(); str!=null; str=rd.readLine()) {
+                       if (endPoint!=null) {
+                               endPoint.gotMessage(type,str);
+                               }
+                       }
+               rd.close();
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public static void connect( Process process, ConnectorEndPoint end )
+       {
+               connect(OUT,process.getInputStream(),end);
+               connect(ERR,process.getErrorStream(),end);
+       }
+
+       public static void connect( int type, InputStream is, ConnectorEndPoint 
end )
+       {
+               Connector       c;
+
+               c=new Connector(type,is);
+               c.setEndPoint(end);
+               c.launch();
+       }
+
+       public static ConnectorEndPoint createEndPoint()
+       {
+               return new ConnectorEndPoint() {
+                       public void gotMessage( int type, String str )
+                       {
+                               System.out.println(type+": "+str);
+                       }
+                       };
+       }
+}

Added: freeway/src/org/gnu/freeway/util/io/ConnectorEndPoint.java
===================================================================
--- freeway/src/org/gnu/freeway/util/io/ConnectorEndPoint.java  2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/util/io/ConnectorEndPoint.java  2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,14 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util.io;
+
+/**
+ *
+ */
+
+public interface ConnectorEndPoint
+{
+       public void gotMessage( int type, String str );
+}

Added: freeway/src/org/gnu/freeway/util/io/DirLocation.java
===================================================================
--- freeway/src/org/gnu/freeway/util/io/DirLocation.java        2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/util/io/DirLocation.java        2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,280 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util.io;
+
+import org.gnu.freeway.util.*;
+
+import java.io.*;
+import java.util.*;
+import java.util.logging.*;
+
+/**
+ *
+ */
+
+public class DirLocation extends Location
+{
+       public DirLocation( String path )
+       {
+               super(path);
+       }
+
+       public String toString()
+       {
+               return "Directory location [label="+getLabel()+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       /**
+        * Return the size of the directory in bytes.
+        *
+        * @return
+        */
+
+       public long getSize()
+       {
+               if (!exists()) {
+                       return 0;
+                       }
+
+               log(Level.INFO,"Computing size for directory 
\""+getPath()+"\".");
+
+               final long[]    _size = new long[0];
+
+               _size[0]=0;
+               traverse(new Traverser() {
+                       public boolean examine( Location node, int depth )
+                       {
+                               if (node instanceof LinkLocation) {
+                                       _size[0]+=((LinkLocation) 
node).getTarget().getSize();
+                                       }
+                               else {
+                                       _size[0]+=node.getSize();
+                                       }
+                               return true;
+                       }
+                       });
+               return _size[0];
+       }
+
+       /**
+        * Get the size of directory without counting symbolic links.
+        *
+        * @return
+        */
+
+       public long getSizeWithoutLinks()
+       {
+               final long[]    _size = new long[0];
+
+               _size[0]=0;
+               traverse(new Traverser() {
+                       public boolean examine( Location node, int depth )
+                       {
+                               _size[0]+=node.getSize();
+                               return true;
+                       }
+                       });
+               return _size[0];
+       }
+
+       public boolean exists()
+       {
+               return (file.exists() && !OSAccess.fileIsLink(file.getPath()) 
&& file.isDirectory());
+       }
+
+       public boolean create()
+       {
+               if (file.exists() && !file.isDirectory()) {
+                       file.delete();
+                       }
+               if (!file.exists()) {
+                       return file.mkdirs();
+                       }
+               return exists();
+       }
+
+       public boolean delete()
+       {
+               return deleteFrom(file);
+       }
+
+       protected boolean deleteFrom( File f )
+       {
+               File[]  files;
+               int                             i;
+
+               //todo: faire avec un traverser
+               files=f.listFiles();
+               for (i=0; i<files.length; i++) {
+                       if (files[i].isDirectory()) {
+                               deleteFrom(files[i]);
+                               }
+                       else {
+                               files[i].delete();
+                               }
+                       }
+               return f.delete();
+       }
+
+       public boolean deleteUnder()
+       {
+               File[]  files;
+               int                             i;
+
+               files=file.listFiles();
+               for (i=0; i<files.length; i++) {
+                       if (files[i].isDirectory()) {
+                               deleteFrom(files[i]);
+                               }
+                       else {
+                               files[i].delete();
+                               }
+                       }
+               return true;//fixme: !!!
+       }
+
+       public LinkLocation getLink( String str )
+       {
+               return new LinkLocation(file.getPath()+File.separator+str);
+       }
+
+       public FileLocation getFile( String str )
+       {
+               return new FileLocation(file.getPath()+File.separator+str);
+       }
+
+       public DirLocation getDirectory( String str )
+       {
+               return new DirLocation(file.getPath()+File.separator+str);
+       }
+
+       public int count()
+       {
+               TraverserContext        ctx;
+
+               ctx=new TraverserContext();
+               ctx.setMaxDepth(1);
+               return count(ctx);
+       }
+
+       public int count( TraverserContext ctx )
+       {
+               final int[]     _count = new int[] { 0 };
+
+               traverse(new Traverser() {
+                       public boolean examine( Location node, int depth )
+                       {
+                               _count[0]++;
+                               return true;
+                       }
+                       },ctx);
+               return _count[0];
+       }
+
+       /**
+        * Scan a directory recursively for files.
+        *
+        * @param tr The traverser which is invoked on each file found.
+        * @return True if traversal has completed, false if it has been 
stopped.
+        */
+
+       public boolean traverse( Traverser tr )
+       {
+               return traverse(tr,new TraverserContext());
+       }
+
+       /**
+        * Scan a directory recursively for files, filtered by a given context.
+        *
+        * @param tr            The traverser which is invoked on each file 
found.
+        * @param ctx   The context used to determine which file will be 
examined.
+        * @return              True if traversal has completed, false if it 
has been stopped.
+        */
+
+       public boolean traverse( Traverser tr, TraverserContext ctx )
+       {
+               return traverse(tr,ctx,0);
+       }
+
+       protected boolean traverse( Traverser tr, TraverserContext ctx, int 
depth )
+       {
+               File[]  files;
+               File            f;
+               String                  str;
+               int                             i;
+               boolean                 more;
+               DirLocation     dloc;
+               FileLocation    floc;
+               LinkLocation    lloc;
+
+               if (depth==ctx.getMaxDepth()) {
+                       return true;
+                       }
+
+               // mark all invalid files (set entry to null)
+               files=file.listFiles();
+               for (i=0; i<files.length; i++) {
+                       f=files[i];
+
+                       if (OSAccess.fileIsLink(f.getPath())) {
+                               }
+                       else if (f.isDirectory()) {
+                               str=f.getName();
+                               if (str.equals(".") || str.equals("..")) {
+                                       files[i]=null;
+                                       }
+                               }
+                       else if (!f.isFile()) {
+                               files[i]=null;
+                               }
+                       }
+
+               // order against directories position and name comparator
+               Arrays.sort(files,ctx.getContentComparator());
+
+               // skip null files
+               for (i=0; i<files.length && files[i]==null; i++) {}
+
+               // and traverse...
+               more=true;
+               while (i<files.length && more) {
+                       f=files[i++];
+
+                       if (OSAccess.fileIsLink(f.getPath())) {
+                               lloc=new LinkLocation(f.getPath());
+
+                               if (ctx.accept(lloc)) {
+                                       more=tr.examine(lloc,depth+1);
+                                       }
+                               }
+                       else if (f.isDirectory()) {
+                               dloc=new DirLocation(f.getPath());
+
+                               if (ctx.areDeepestFirst()) {
+                                       more=dloc.traverse(tr,ctx,depth+1);
+                                       }
+                               if (more && ctx.accept(dloc)) {
+                                       more=tr.examine(dloc,depth+1);
+                                       }
+                               if (more && !ctx.areDeepestFirst()) {
+                                       more=dloc.traverse(tr,ctx,depth+1);
+                                       }
+                               }
+                       else if (f.isFile()) {
+                               floc=new FileLocation(f.getPath());
+
+                               if (ctx.accept(floc)) {
+                                       more=tr.examine(floc,depth+1);
+                                       }
+                               }
+                       else {
+                               }
+                       }
+               return more;
+       }
+}

Added: freeway/src/org/gnu/freeway/util/io/FileLocation.java
===================================================================
--- freeway/src/org/gnu/freeway/util/io/FileLocation.java       2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/util/io/FileLocation.java       2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,211 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util.io;
+
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.crypto.*;
+
+import java.io.*;
+import java.nio.channels.*;
+import java.util.logging.*;
+
+/**
+ *
+ */
+
+public class FileLocation extends Location
+{
+       public FileLocation( String path )
+       {
+               super(path);
+       }
+
+       public String toString()
+       {
+               return "File location [label="+getLabel()+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       /**
+        * Return the size of the file in bytes.
+        *
+        * @return
+        */
+
+       public long getSize()
+       {
+               return (exists() ? file.length() : 0);
+       }
+
+       public HashCode160 getHash()
+       {
+               return (exists() ? HashCode160.create(this) : null);
+       }
+
+       public boolean exists()
+       {
+               return (file.exists() && !OSAccess.fileIsLink(file.getPath()) 
&& file.isFile());
+       }
+
+       public boolean create()
+       {
+               if (file.exists()) {
+                       if (OSAccess.fileIsLink(file.getPath())) {
+                               if (!file.delete()) {
+                                       return false;
+                                       }
+                               }
+                       else if (file.isDirectory()) {
+                               if (!new DirLocation(file.getPath()).delete()) {
+                                       return false;
+                                       }
+                               }
+                       }
+               else if (!getParent().create()) {
+                       return false;
+                       }
+
+               if (!file.exists()) {
+                       try {
+                               if (!file.createNewFile()) {
+                                       return false;
+                                       }
+                               }
+                       catch( IOException x ) {
+                               err("Could not create new file at 
\""+getLabel()+"\" !",x);
+                               return false;
+                               }
+                       }
+               return exists();
+       }
+
+       public boolean delete()
+       {
+               if (!file.exists()) {
+                       return true;
+                       }
+               if (!file.isDirectory()) {
+                       return file.delete();
+                       }
+               return new DirLocation(file.getPath()).delete();
+       }
+
+       public boolean duplicate( FileLocation f )
+       {
+               FileChannel     from,to;
+
+               if (!exists()) {
+                       log("Could not duplicate a file \""+getLabel()+"\" that 
does not exist !");
+                       return false;
+                       }
+
+               try {
+                       from=new 
RandomAccessFile(file.getPath(),"rw").getChannel();
+                       try {
+                               to=new 
RandomAccessFile(f.getPath(),"rw").getChannel();
+                               try {
+                                       from.transferTo(0,from.size(),to);
+                                       }
+                               finally {
+                                       to.close();
+                                       }
+                               }
+                       finally {
+                               from.close();
+                               }
+                       return true;
+                       }
+               catch( IOException x ) {
+                       err("Could not duplicate \""+getLabel()+"\" to 
\""+f.getLabel()+"\" !",x);
+                       return false;
+                       }
+       }
+
+       public MappedFile open()
+       {
+               return open(false);
+       }
+
+       public MappedFile openNew()
+       {
+               return open(true);
+       }
+
+       protected MappedFile open( boolean fresh )
+       {
+               MappedFile      m;
+
+               m=null;
+               if (fresh && !exists()) {
+                       if (!create()) {
+                               log(Level.WARNING,"Failed to create a new file 
at \""+getLabel()+"\" !");
+                               }
+                       }
+
+               if (exists()) {
+                       try {
+                               m=new MappedFile(this);
+                               if (fresh && !m.erase()) {
+                                       log(Level.WARNING,"Failed to erase 
content of file \""+getLabel()+"\" !");
+                                       }
+                               }
+                       catch( IOException x ) {
+                               err("Could not open file \""+getLabel()+"\" 
!",x);
+                               }
+                       }
+               return m;
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public static FileLocation temporary()
+       {
+               Logger  logger;
+
+               try {
+                       return new 
FileLocation(File.createTempFile("temp",null,null).getPath());
+                       }
+               catch( IOException x ) {
+                       logger=Logger.getLogger(FileLocation.class.getName());
+                       logger.log(Level.SEVERE,"Could not create temporary 
file !",x);
+                       }
+               return null;
+       }
+
+       public static FileLocation temporary( FileLocation f )
+       {
+               Logger  logger;
+               String  name,ext;
+               int             index;
+
+               name=f.getName();
+               ext="";
+
+               index=name.lastIndexOf('.');
+               if (index>=0) {
+                       ext=name.substring(index+1);
+                       name=name.substring(0,index);
+                       }
+
+               if (name.length()<3) {
+                       name+="___";
+                       }
+               if (ext.length()==0) {
+                       ext="tmp";
+                       }
+
+               try {
+                       return new 
FileLocation(File.createTempFile(name,"."+ext,null).getPath());
+                       }
+               catch( IOException x ) {
+                       logger=Logger.getLogger(FileLocation.class.getName());
+                       logger.log(Level.SEVERE,"Could not create temporary 
file !",x);
+                       }
+               return null;
+       }
+}

Added: freeway/src/org/gnu/freeway/util/io/IOUtils.java
===================================================================
--- freeway/src/org/gnu/freeway/util/io/IOUtils.java    2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/util/io/IOUtils.java    2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,99 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util.io;
+
+import org.gnu.freeway.util.*;
+
+import java.io.*;
+import java.text.*;
+import java.util.*;
+import java.util.regex.*;
+
+/**
+ *
+ */
+
+public class IOUtils extends Object
+{
+       public static Comparator newNameComparator( Locale loc )
+       {
+               final Collator  _collator = Collator.getInstance(loc);
+
+               return new Comparator() {
+                       public int compare( Object o1, Object o2 )
+                       {
+                               return _collator.compare(((File) 
o1).getName().toUpperCase(),((File) o2).getName().toUpperCase());
+                       }
+                       };
+       }
+
+       public static FileFilter newSuffixFilter( String str, boolean cs )
+       {
+               final String            _str = (cs ? str : str.toLowerCase());
+               final boolean   _cs = cs;
+
+               return new FileFilter() {
+                       public boolean accept( File file )
+                       {
+                               return (_cs ? file.getName().endsWith(_str) : 
file.getName().toLowerCase().endsWith(_str));
+                       }
+                       };
+       }
+
+       public static FileFilter newRegexFilter( String str )
+       {
+               final Matcher   _matcher = Pattern.compile(str).matcher("");
+
+               return new FileFilter() {
+                       public boolean accept( File file )
+                       {
+                               _matcher.reset(file.getName());
+                               return _matcher.matches();
+                       }
+                       };
+       }
+
+       /**
+        * Create a formatter which displays sizes rounded to the nearest well 
known computer size unit : B, KB, MB and GB.
+        *
+        * @return A newly created formatter.
+        */
+
+       public static Format newSizeFormatter()
+       {
+               final NumberFormat      _nform1 = NumberFormat.getInstance();
+               final NumberFormat      _nform2 = NumberFormat.getInstance();
+
+               _nform2.setMinimumFractionDigits(2);
+               _nform2.setMaximumFractionDigits(2);
+
+               return new AbstractFormatter() {
+                       protected String formatObject( Object obj ) throws 
Throwable
+                       {
+                               StringBuffer    buf;
+                               long                    n;
+
+                               buf=new StringBuffer();
+                               if (obj!=null && (obj instanceof Number)) {
+                                       n=((Number) obj).longValue();
+
+                                       if (n<1024L) {
+                                               _nform1.format(new 
Long(n),buf,new FieldPosition(0));
+                                               buf.append(" B");
+                                               }
+                                       else if (n<1048576L) {
+                                               _nform2.format(new 
Double((double) n/(double) 1024),buf,new FieldPosition(0));
+                                               buf.append(" KB");
+                                               }
+                                       else {
+                                               _nform2.format(new 
Double((double) n/(double) 1048576L),buf,new FieldPosition(0));
+                                               buf.append(" MB");
+                                               }
+                                       }
+                               return buf.toString();
+                       }
+                       };
+       }
+}

Added: freeway/src/org/gnu/freeway/util/io/LineDecoder.java
===================================================================
--- freeway/src/org/gnu/freeway/util/io/LineDecoder.java        2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/util/io/LineDecoder.java        2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,98 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util.io;
+
+import java.nio.*;
+
+/**
+ *
+ */
+
+public class LineDecoder extends Object
+{
+       private TextDecoder     decoder;
+       private char[]                  line;
+       private CharBuffer              buffer;
+       private boolean                 hasMore;
+       private int                             lastLength;
+
+
+       public LineDecoder( TextDecoder dec, char[] buf )
+       {
+               super();
+               decoder=dec;
+               line=buf;
+               buffer=CharBuffer.wrap(buf);
+               hasMore=true;
+               lastLength=-1;
+       }
+
+       public String toString()
+       {
+               return "Channel line decoder [decoder="+decoder+", 
lineMaxLength="+line.length+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int getLineMaxLength()
+       {
+               return line.length;
+       }
+
+       public char[] getLine()
+       {
+               return line;
+       }
+
+       public int decode()
+       {
+               int     len,imax,i;
+
+               if (lastLength>0) {
+                       buffer.limit(buffer.position());
+                       buffer.position(lastLength);
+                       buffer.compact();
+                       }
+
+               if (buffer.position()==0 && !hasMore) {
+                       return -1;
+                       }
+
+               readIfNeeded();
+
+               imax=buffer.position();
+               for (i=0; i<imax && line[i]!='\n'; i++) {}
+
+               if (i<imax) {
+                       len=((i>0 && line[i-1]=='\r') ? i-1 : i);
+                       if (i+1<imax && line[i+1]=='\r') {
+                               i++;
+                               }
+                       lastLength=i+1;
+                       }
+               else {
+                       len=i;
+                       lastLength=i;
+                       }
+               return len;
+       }
+
+       protected void readIfNeeded()
+       {
+               int     imax,i;
+
+               imax=buffer.position();
+               for (i=0; i<imax && line[i]!='\n'; i++) {}
+
+               // test is made on (imax-1) because of possibly pending '\r' 
after '\n' (depending on platforms)
+               while (i>=imax-1 && hasMore) {
+                       hasMore=decoder.decodeInto(buffer);
+
+                       imax=buffer.position();
+                       for (i=0; i<imax && line[i]!='\n'; i++) {}
+                       }
+       }
+}

Added: freeway/src/org/gnu/freeway/util/io/LinkLocation.java
===================================================================
--- freeway/src/org/gnu/freeway/util/io/LinkLocation.java       2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/util/io/LinkLocation.java       2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,124 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util.io;
+
+import org.gnu.freeway.util.*;
+
+import java.io.*;
+
+/**
+ *
+ */
+
+public class LinkLocation extends Location
+{
+       private Location        target;
+
+
+       public LinkLocation( String path )
+       {
+               super(path);
+               target=null;
+       }
+
+       public String toString()
+       {
+               return "Link location [label="+getLabel()+", 
target="+target+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public long getSize()
+       {
+               return 0;
+       }
+
+       public boolean exists()
+       {
+               Location                loc;
+
+               if (!OSAccess.fileIsLink(file.getPath())) {
+                       return false;
+                       }
+
+               loc=storedTarget();
+               if (loc==null) {
+                       return false;
+                       }
+
+               if (target==null) {
+                       target=loc;
+                       return true;
+                       }
+
+               return loc.equals(target);
+       }
+
+       public boolean create()
+       {
+               if (exists()) {
+                       return true;
+                       }
+
+               // trash any previous entry so that symlink() on existing won't 
cause retry attempts to fail
+               if (!delete()) {
+                       return false;
+                       }
+               return OSAccess.fileCreateLink(file.getPath(),target.getPath());
+       }
+
+       public boolean delete()
+       {
+               if (!file.exists()) {
+                       return true;
+                       }
+               if (file.isFile()) {
+                       // file or symbolic link to a file are considered same 
for JVM
+                       return new FileLocation(file.getPath()).delete();
+                       }
+               // directory or symbolic link to a directory are considered 
same for JVM
+               return new DirLocation(file.getPath()).delete();
+       }
+
+       public Location getTarget()
+       {
+               if (!exists()) {
+                       return null;
+                       }
+
+               if (target==null) {
+                       target=storedTarget();
+                       }
+               return target;
+       }
+
+       protected Location storedTarget()
+       {
+               File            f;
+               String  str;
+
+               str=OSAccess.fileGetLinkTarget(file.getPath());
+               if (str==null) {
+                       return null;
+                       }
+
+               f=new File(str);
+               if (!f.exists()) {
+                       return null;
+                       }
+               return (f.isFile() ? (Location) new FileLocation(str) : 
(Location) new DirLocation(str));
+       }
+
+       public boolean setTarget( Location loc )
+       {
+               if (!loc.exists()) {
+                       log("Could not link to a location 
\""+loc.getLabel()+"\" that does not exist !");
+                       return false;
+                       }
+               target=loc;
+               return true;
+       }
+}

Added: freeway/src/org/gnu/freeway/util/io/Location.java
===================================================================
--- freeway/src/org/gnu/freeway/util/io/Location.java   2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/util/io/Location.java   2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,164 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util.io;
+
+import org.gnu.freeway.util.*;
+
+import java.io.*;
+import java.net.*;
+import java.util.logging.*;
+
+/**
+ * Abstraction for a disk object (file, directory or symbolic link).
+ */
+
+public abstract class Location extends LoggedObject
+{
+       /** Underlying file. */
+       protected File  file;
+
+
+       protected Location( File f )
+       {
+               super(true);
+               file=f;
+       }
+
+       protected Location( String path )
+       {
+               super(true);
+               init(path);
+       }
+
+       public String toString()
+       {
+               return "Location [label="+getLabel()+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int hashCode()
+       {
+               return file.hashCode();
+       }
+
+       public boolean equals( Object obj )
+       {
+               if (obj==null) {
+                       return false;
+                       }
+               return obj.getClass().equals(getClass()) && ((Location) 
obj).file.equals(file);
+       }
+
+       protected void init( String path )
+       {
+               if (path==null) {
+                       path="";
+                       }
+               else if (path.startsWith("~")) {
+                       path=System.getProperty("user.home")+path.substring(1);
+                       }
+               file=new File(path).getAbsoluteFile();
+       }
+
+       /**
+        * Return the size of the object denoted by this location.
+        *
+        * @return The size in bytes.
+        */
+
+       public abstract long getSize();
+
+       public String getName()
+       {
+               return file.getName();
+       }
+
+       public String getPath()
+       {
+               return file.getPath();
+       }
+
+       public String getPath( int depth )
+       {
+               return (depth>1 ? 
getParent().getPath(depth-1)+File.separator+getName() : getName());
+       }
+
+       public String getLabel()
+       {
+               String  path,home;
+
+               path=file.getPath();
+               home=System.getProperty("user.home");
+               if (path.startsWith(home)) {
+                       path="~"+path.substring(home.length());
+                       }
+               return path;
+       }
+
+       public URL asURL()
+       {
+               try {
+                       return file.toURL();
+                       }
+               catch( MalformedURLException x ) {
+                       err("Can't create URL with path \""+file.getPath()+"\" 
!",x);
+                       return null;
+                       }
+       }
+
+       public DirLocation getParent()
+       {
+               String  str;
+
+               str=file.getParent();
+               return (str!=null ? new DirLocation(str) : null);
+       }
+
+       /**
+        * Check for object existence and for object correctness.
+        *
+        * @return True if the object exists and is of type denoted by this 
location, false otherwise.
+        */
+
+       public abstract boolean exists();
+
+       /**
+        * Create the object denoted by this location, if it doesn't already 
exist or it hasn't correct type.
+        *
+        * @return True if succeedeed, false otherwise.
+        */
+
+       public abstract boolean create();
+
+       /**
+        * Move the object denoted by this location to a new location.
+        *
+        * @param str
+        * @return
+        */
+
+       public boolean move( String str )
+       {
+               File            f;
+
+               f=new File(str).getAbsoluteFile();
+               if (!file.renameTo(f)) {
+                       log(Level.SEVERE,"Failed to move \""+getLabel()+"\" to 
\""+f.getPath()+"\" !");
+                       return false;
+                       }
+               file=f;
+               return true;
+       }
+
+       /**
+        * Try to delete the object pointed by this location. Note that if the 
object does not exist at all, it will return true.
+        *
+        * @return True if the object does not exist any longer (in any form, 
file, link or directory), false otherwise.
+        */
+
+       public abstract boolean delete();
+}

Added: freeway/src/org/gnu/freeway/util/io/MappedFile.java
===================================================================
--- freeway/src/org/gnu/freeway/util/io/MappedFile.java 2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/util/io/MappedFile.java 2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,512 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util.io;
+
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.net.*;
+
+import java.io.*;
+import java.net.*;
+import java.nio.*;
+import java.nio.channels.*;
+import java.nio.charset.*;
+import java.util.*;
+import java.util.logging.*;
+
+/**
+ *
+ */
+
+public class MappedFile extends LoggedObject
+{
+       private FileLocation    location;
+       private FileChannel     channel;
+       private FileLock                lock;
+
+
+       public MappedFile( FileLocation loc ) throws IOException
+       {
+               super(true);
+               location=loc;
+               channel=new RandomAccessFile(loc.getPath(),"rw").getChannel();
+               lock=null;
+       }
+
+       public String toString()
+       {
+               return "Mapped file [location="+location+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public FileLocation getLocation()
+       {
+               return location;
+       }
+
+       /**
+        * Acquire a lock on this file. This method <em>blocks</em> until 
succeedeed or an error occurred.
+        *
+        * @return True if succeedeed, false otherwise.
+        */
+
+       public boolean lock()
+       {
+               if (channel==null) {
+                       log(Level.WARNING,"File \""+location.getLabel()+"\" is 
closed, could not lock it !");
+                       return false;
+                       }
+               if (lock!=null) {
+                       log(Level.WARNING,"File \""+location.getLabel()+"\" is 
already locked !");
+                       return false;
+                       }
+
+               try {
+                       lock=channel.lock();
+                       }
+               catch( IOException x ) {
+                       err("Could not lock file \""+location.getLabel()+"\" 
!",x);
+                       return false;
+                       }
+               return true;
+       }
+
+       public boolean unlock()
+       {
+               if (channel==null) {
+                       log(Level.WARNING,"File \""+location.getLabel()+"\" is 
closed, could not unlock it !");
+                       return false;
+                       }
+               if (lock==null) {
+                       log(Level.WARNING,"File \""+location.getLabel()+"\" is 
not locked !");
+                       return false;
+                       }
+
+               try {
+                       lock.release();
+                       }
+               catch( IOException x ) {
+                       err("Could not unlock file \""+location.getLabel()+"\" 
!",x);
+                       return false;
+                       }
+               return true;
+       }
+
+       public int asInt( int def )
+       {
+               if (location.getSize()!=4) {
+                       log(Level.WARNING,"Bad size for file 
\""+location.getLabel()+"\" ("+location.getSize()+" instead of 4) !");
+                       return def;
+                       }
+               if (!seekStart()) {
+                       return def;
+                       }
+               return readInt(def);
+       }
+
+       public long asLong( long def )
+       {
+               if (location.getSize()!=8) {
+                       log(Level.WARNING,"Bad size for file 
\""+location.getLabel()+"\" ("+location.getSize()+" instead of 8) !");
+                       return def;
+                       }
+               if (!seekStart()) {
+                       return def;
+                       }
+               return readLong(def);
+       }
+
+       public String asString( String def )
+       {
+               return asString(Charset.forName("US-ASCII"),"");
+       }
+
+       public String asString( Charset cs, String def )
+       {
+               ByteBuffer      buf;
+
+               buf=readBuffer();
+               return (buf!=null ? cs.decode(buf).toString() : def);
+       }
+
+       public TextDecoder asText( Charset cs )
+       {
+               if (channel==null) {
+                       log(Level.WARNING,"File \""+location.getLabel()+"\" is 
closed, could not read bytes !");
+                       return null;
+                       }
+               return new TextDecoder(cs,channel);
+       }
+
+       public Persistent asPersistent( Class c )
+       {
+               Persistent      p;
+
+               if (!seekStart()) {
+                       return null;
+                       }
+               p=readPersistent(c);
+               if (p==null) {
+                       return null;
+                       }
+               if (location.getSize()!=p.getByteSize()) {
+                       log(Level.WARNING,"Bad size for file 
\""+location.getLabel()+"\" ("+location.getSize()+" insted of 
"+p.getByteSize()+") !");
+                       return null;
+                       }
+               return p;
+       }
+
+       public int readInt( int def )
+       {
+               ByteBuffer      buf;
+
+               buf=ByteBuffer.allocateDirect(4);
+               if (!readBuffer(buf)) {
+                       log(Level.WARNING,"Not enough bytes (less than 4) at 
\""+location.getLabel()+"\" to read integer !");
+                       return def;
+                       }
+               buf.flip();
+               return buf.getInt();
+       }
+
+       public long readLong( long def )
+       {
+               ByteBuffer      buf;
+
+               buf=ByteBuffer.allocateDirect(8);
+               if (!readBuffer(buf)) {
+                       log(Level.WARNING,"Not enough bytes (less than 8) at 
\""+location.getLabel()+"\" to read long integer !");
+                       return def;
+                       }
+               buf.flip();
+               return buf.getLong();
+       }
+
+       public Persistent readPersistent( Class c )
+       {
+               ByteBuffer      buf;
+
+               buf=readBuffer();
+               return (buf!=null ? PersistentHelper.readFully(c,buf) : null);
+       }
+
+       public byte[] readBytes()
+       {
+               long            length;
+
+               if (channel==null) {
+                       log(Level.WARNING,"File \""+location.getLabel()+"\" is 
closed, could not read bytes !");
+                       return null;
+                       }
+
+               try {
+                       length=channel.size()-channel.position();
+                       }
+               catch( IOException x ) {
+                       err("Unable to read bytes from 
\""+location.getLabel()+"\" !",x);
+                       return null;
+                       }
+               if (length>Integer.MAX_VALUE) {
+                       log(Level.WARNING,"Could not read more than 2^32 bytes 
at once on file \""+location.getLabel()+"\" !");
+                       return null;
+                       }
+               return readBytes((int) length);
+       }
+
+       public byte[] readBytes( int length )
+       {
+               byte[]  b;
+
+               b=new byte[length];
+               return (readBuffer(ByteBuffer.wrap(b)) ? b : null);
+       }
+
+       public boolean readBytes( byte[] b )
+       {
+               return readBuffer(ByteBuffer.wrap(b));
+       }
+
+       public boolean readBytes( byte[] b, int offset, int length )
+       {
+               return readBuffer(ByteBuffer.wrap(b,offset,length));
+       }
+
+       /**
+        * Read the content of this file into a byte buffer, starting at the 
current position.
+        *
+        * @return The buffer containing data on success, null on failure.
+        */
+
+       public ByteBuffer readBuffer()
+       {
+               ByteBuffer      buf;
+               long                    length;
+
+               if (channel==null) {
+                       log(Level.WARNING,"File \""+location.getLabel()+"\" is 
closed, could not read bytes !");
+                       return null;
+                       }
+
+               try {
+                       length=channel.size()-channel.position();
+                       }
+               catch( IOException x ) {
+                       err("Unable to read bytes from 
\""+location.getLabel()+"\" !",x);
+                       return null;
+                       }
+               if (length>Integer.MAX_VALUE) {
+                       log(Level.WARNING,"Could not read more than 2^32 bytes 
at once on file \""+location.getLabel()+"\" !");
+                       return null;
+                       }
+
+               buf=ByteBuffer.allocateDirect((int) length);
+               if (!readBuffer(buf)) {
+                       return null;
+                       }
+               buf.flip();
+               return buf;
+       }
+
+       public boolean readBuffer( ByteBuffer buf )
+       {
+               int     len,r;
+
+               if (channel==null) {
+                       log(Level.WARNING,"File \""+location.getLabel()+"\" is 
closed, could not read bytes !");
+                       return false;
+                       }
+
+               try {
+                       len=buf.remaining();
+                       r=channel.read(buf);
+                       if (r==len) {
+                               return true;
+                               }
+
+                       if (r>=0) {
+                               log(Level.WARNING,"Could not read all bytes 
from \""+location.getLabel()+"\" (only "+r+" out of "+len+") !");
+                               }
+                       else {
+                               log(Level.WARNING,"Could not read all bytes 
from \""+location.getLabel()+"\" (end of stream, "+len+" requested) !");
+                               }
+                       }
+               catch( IOException x ) {
+                       err("Could not read bytes from 
\""+getLocation().getLabel()+"\" !",x);
+                       }
+               return false;
+       }
+
+       public boolean writeInt( int n )
+       {
+               ByteBuffer      buf;
+
+               buf=ByteBuffer.allocateDirect(4);
+               buf.putInt(n);
+               buf.flip();
+               return writeBuffer(buf);
+       }
+
+       public boolean writeLong( long n )
+       {
+               ByteBuffer      buf;
+
+               buf=ByteBuffer.allocateDirect(8);
+               buf.putLong(n);
+               buf.flip();
+               return writeBuffer(buf);
+       }
+
+       public boolean writeString( String str )
+       {
+               return writeString(str,Charset.forName("US-ASCII"));
+       }
+
+       public boolean writeString( String str, Charset cs )
+       {
+               return writeBuffer(cs.encode(str));
+       }
+
+       public boolean writePersistent( Persistent p )
+       {
+               ByteBuffer      buf;
+
+               buf=ByteBuffer.allocate(p.getByteSize());
+               if (!PersistentHelper.writeFully(p,buf)) {
+                       return false;
+                       }
+               buf.flip();
+               return writeBuffer(buf);
+       }
+
+       public boolean writeURL( URL url )
+       {
+               byte[]          b;
+               InputStream     is;
+               int                     len;
+
+               try {
+                       is=url.openStream();
+                       try {
+                               b=new byte[8192];
+                               for (len=is.read(b); len>=0; len=is.read(b)) {
+                                       if 
(!writeBuffer(ByteBuffer.wrap(b,0,len))) {
+                                               log(Level.WARNING,"Could not 
write bytes from \""+url+"\" to \""+location.getLabel()+"\" !");
+                                               return false;
+                                               }
+                                       }
+                               }
+                       finally {
+                               is.close();
+                               }
+                       return true;
+                       }
+               catch( IOException x ) {
+                       err("Could not write bytes from \""+url+"\" to 
\""+location.getLabel()+"\" !",x);
+                       }
+               return false;
+       }
+
+       public boolean writeBytes( byte[] b )
+       {
+               return writeBytes(b,0,b.length);
+       }
+
+       public boolean writeBytes( byte[] b, int offset, int length )
+       {
+               return writeBuffer(ByteBuffer.wrap(b,offset,length));
+       }
+
+       /**
+        * Write content of the buffer at current position.
+        *
+        * @param buf   The buffer to write.
+        * @return              True if all bytes have been written and no 
underlying I/O errors have occured, false otherwise.
+        */
+
+       public boolean writeBuffer( ByteBuffer buf )
+       {
+               int     len,w;
+
+               if (channel==null) {
+                       log(Level.WARNING,"File \""+location.getLabel()+"\" is 
closed, could not write bytes !");
+                       return false;
+                       }
+
+               try {
+                       len=buf.remaining();
+                       w=channel.write(buf);
+                       if (w==len) {
+                               return true;
+                               }
+
+                       log(Level.WARNING,"Could not write all bytes to 
\""+location.getLabel()+"\" (only "+w+" out of "+len+") !");
+                       }
+               catch( IOException x ) {
+                       err("Could not write bytes to 
\""+location.getLabel()+"\" !",x);
+                       }
+               return false;
+       }
+
+       public boolean seek( long offset )
+       {
+               byte[]  buf;
+               long            size;
+               int             len;
+
+               if (channel==null) {
+                       log(Level.WARNING,"File \""+location.getLabel()+"\" is 
closed, could not set position at "+offset+" !");
+                       return false;
+                       }
+
+               try {
+                       size=channel.size();
+                       if (offset>size) {
+                               buf=new byte[16384];
+                               Arrays.fill(buf,(byte) 0);
+
+                               channel.position(size);
+                               while (offset>size) {
+                                       len=(int) 
Math.min(buf.length,offset-size);
+                                       
channel.write(ByteBuffer.wrap(buf,0,len));
+                                       size+=len;
+                                       }
+                               }
+
+                       channel.position(offset);
+                       if (channel.position()!=offset) {
+                               log(Level.SEVERE,"Failed to set position at 
"+offset+" for file \""+location.getLabel()+"\", got "+channel.position()+" !");
+                               return false;
+                               }
+                       }
+               catch( IOException x ) {
+                       err("Could not set position at "+offset+" for file 
\""+location.getLabel()+"\" !",x);
+                       return false;
+                       }
+               return true;
+       }
+
+       public boolean seekStart()
+       {
+               return seek(0);
+       }
+
+       public boolean seekEnd()
+       {
+               try {
+                       return seek(channel.size());
+                       }
+               catch( IOException x ) {
+                       err("Could not set position at end for file 
\""+location.getLabel()+"\" !",x);
+                       return false;
+                       }
+       }
+
+       public boolean erase()
+       {
+               if (channel==null) {
+                       log(Level.WARNING,"File \""+location.getLabel()+"\" is 
closed, could not erase content !");
+                       return false;
+                       }
+
+               try {
+                       channel.position(0);
+                       channel.truncate(0);
+                       }
+               catch( IOException x ) {
+                       err("Could not erase content at 
\""+location.getLabel()+"\" !",x);
+                       return false;
+                       }
+               return true;
+       }
+
+       public boolean close()
+       {
+               if (channel==null) {
+                       log(Level.WARNING,"File \""+location.getLabel()+"\" is 
already closed !");
+                       return false;
+                       }
+
+               try {
+                       try {
+                               if (lock!=null) {
+                                       lock.release();
+                                       }
+                               }
+                       finally {
+                               channel.close();
+                               }
+                       return true;
+                       }
+               catch( IOException x ) {
+                       err("Could not close file \""+location.getLabel()+"\" 
!",x);
+                       }
+               finally {
+                       lock=null;
+                       channel=null;
+                       }
+               return false;
+       }
+}

Added: freeway/src/org/gnu/freeway/util/io/TextDecoder.java
===================================================================
--- freeway/src/org/gnu/freeway/util/io/TextDecoder.java        2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/util/io/TextDecoder.java        2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,110 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util.io;
+
+import org.gnu.freeway.util.*;
+
+import java.io.*;
+import java.nio.*;
+import java.nio.channels.*;
+import java.nio.charset.*;
+
+/**
+ * Decode bytes read on a given channel according to a specified charset.
+ *
+ * <p>Internal buffer contract (before and after method call):
+ *      limit : number of read bytes not having been decoded yet
+ *   position : set to 0
+ * After each decoding op, buffer is compacted (to allow future bytes to be 
read) and limit/position set as described.
+ */
+
+public class TextDecoder extends LoggedObject
+{
+       private static final int        BUFFER_SIZE     =       8192;
+
+       private CharsetDecoder          decoder;
+       private ReadableByteChannel     channel;
+       private ByteBuffer                      buffer;
+       private CoderResult                     lastResult;
+       private boolean                         hasMore;
+
+
+       public TextDecoder( Charset cs, ReadableByteChannel c )
+       {
+               super(true);
+               decoder=cs.newDecoder();
+               channel=c;
+               buffer=null;
+               lastResult=null;
+               hasMore=true;
+
+               setReplacement(null);
+       }
+
+       public String toString()
+       {
+               return "Text decoder [channel="+channel+", 
characterSet="+getCharacterSet()+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public Charset getCharacterSet()
+       {
+               return decoder.charset();
+       }
+
+       public String getReplacement()
+       {
+               return decoder.replacement();
+       }
+
+       public void setReplacement( String str )
+       {
+               if (str!=null && str.length()>0) {
+                       decoder.onMalformedInput(CodingErrorAction.REPLACE);
+                       
decoder.onUnmappableCharacter(CodingErrorAction.REPLACE);
+                       decoder.replaceWith(str);
+                       }
+               else {
+                       decoder.onMalformedInput(CodingErrorAction.IGNORE);
+                       decoder.onUnmappableCharacter(CodingErrorAction.IGNORE);
+                       }
+       }
+
+       public boolean decodeInto( CharBuffer to )
+       {
+               int     pos;
+
+               if (buffer==null) {
+                       buffer=ByteBuffer.allocateDirect(BUFFER_SIZE);
+                       buffer.limit(0);
+                       decoder.reset();
+                       lastResult=CoderResult.UNDERFLOW;
+                       hasMore=true;
+                       }
+
+               if (lastResult==CoderResult.UNDERFLOW && hasMore) {
+                       try {
+                               pos=buffer.limit();
+                               buffer.limit(buffer.capacity());
+                               buffer.position(pos);
+
+                               hasMore=(channel.read(buffer)>0);
+                               buffer.flip();
+                               }
+                       catch( IOException x ) {
+                               err("Failed to read bytes !",x);
+                               hasMore=false;
+                               }
+                       }
+
+               pos=to.position();
+               lastResult=decoder.decode(buffer,to,!hasMore);
+               buffer.compact();
+               buffer.flip();
+               return to.position()>pos;
+       }
+}

Added: freeway/src/org/gnu/freeway/util/io/Traverser.java
===================================================================
--- freeway/src/org/gnu/freeway/util/io/Traverser.java  2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/util/io/Traverser.java  2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,14 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util.io;
+
+/**
+ *
+ */
+
+public interface Traverser
+{
+       public boolean examine( Location node, int depth );
+}

Added: freeway/src/org/gnu/freeway/util/io/TraverserContext.java
===================================================================
--- freeway/src/org/gnu/freeway/util/io/TraverserContext.java   2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/util/io/TraverserContext.java   2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,186 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util.io;
+
+import java.io.*;
+import java.util.*;
+
+/**
+ *
+ */
+
+public class TraverserContext extends Object
+{
+       /** Directories are listed first. */
+       public static final int DIR_BEFORE      =       0;
+
+       /** Directories are listed mixed with files (no special ordering). */
+       public static final int DIR_MIXED       =       1;
+
+       /** Directories are listed last. */
+       public static final int DIR_AFTER       =       2;
+
+       /** Begins traversal with deepest nodes or not ? */
+       private boolean         deepestFirst;
+
+       /** Sub-directories ordering used when traversing. */
+       private int                     ordering;
+
+       /** Sub-files and sub-directories comparator used when traversing. */
+       private Comparator      comparator;
+
+       private boolean         filesOnly;
+       private boolean         dirsOnly;
+
+       /** Object used to filter incoming files. */
+       private FileFilter      filter;
+
+       /** The maximum traversal depth in this directory subtree (should be 
greater than zero). */
+       private int                     maxDepth;
+
+       private Comparator      globalComp;
+
+
+       public TraverserContext()
+       {
+               super();
+               deepestFirst=false;
+               ordering=DIR_BEFORE;
+               comparator=IOUtils.newNameComparator(Locale.getDefault());
+               filesOnly=false;
+               dirsOnly=false;
+               filter=null;
+               maxDepth=Integer.MAX_VALUE;
+
+               globalComp=new Comparator() {
+                       public int compare( Object o1, Object o2 )
+                       {
+                               File            f1,f2;
+                               int             res;
+
+                               if (o1==null || o2==null) {
+                                       // null files at the beginning
+                                       return (o1!=null ? -1 : (o2!=null ? 1 : 
0));
+                                       }
+
+                               f1=(File) o1;
+                               f2=(File) o2;
+                               if (ordering!=DIR_MIXED && 
f1.isDirectory()!=f2.isDirectory()) {
+                                       res=(f1.isDirectory() ? -1 : 1);
+                                       return (ordering==DIR_BEFORE ? res : 
-res);
+                                       }
+                               return (comparator!=null ? 
comparator.compare(f1,f2) : 0);
+                       }
+                       };
+       }
+
+       public String toString()
+       {
+               return "Traverser context [ordering="+ordering+", 
deepestFirst="+deepestFirst+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int getMaxDepth()
+       {
+               return maxDepth;
+       }
+
+       public void setMaxDepth( int d )
+       {
+               assert(d>0);
+
+               maxDepth=d;
+       }
+
+       public FileFilter getFilter()
+       {
+               return filter;
+       }
+
+       public void setFilter( FileFilter f )
+       {
+               filter=f;
+       }
+
+
+       public boolean isFilesOnly()
+       {
+               return filesOnly;
+       }
+
+       public void setFilesOnly( boolean flag )
+       {
+               filesOnly=flag;
+       }
+
+       public boolean isDirsOnly()
+       {
+               return dirsOnly;
+       }
+
+       public void setDirsOnly( boolean flag )
+       {
+               dirsOnly=flag;
+       }
+
+
+
+       public boolean areDeepestFirst()
+       {
+               return deepestFirst;
+       }
+
+       public void setDeepestFirst( boolean flag )
+       {
+               deepestFirst=flag;
+       }
+
+       public int getOrdering()
+       {
+               return ordering;
+       }
+
+       public void setOrdering( int order )
+       {
+               assert(order==DIR_BEFORE || order==DIR_MIXED || 
order==DIR_AFTER);
+
+               ordering=order;
+       }
+
+       public Comparator getComparator()
+       {
+               return comparator;
+       }
+
+       public void setComparator( Comparator c )
+       {
+               comparator=c;
+       }
+
+
+       public boolean accept( DirLocation loc )
+       {
+               return (!filesOnly && (filter==null || filter.accept(new 
File(loc.getPath()))));
+       }
+
+       public boolean accept( FileLocation loc )
+       {
+               return (!dirsOnly && (filter==null || filter.accept(new 
File(loc.getPath()))));
+       }
+
+       public boolean accept( LinkLocation loc )
+       {
+               return (!dirsOnly && (filter==null || filter.accept(new 
File(loc.getPath()))));
+       }
+
+
+       public Comparator getContentComparator()
+       {
+               return globalComp;
+       }
+
+}

Added: freeway/src/org/gnu/freeway/util/io/storage.c
===================================================================
--- freeway/src/org/gnu/freeway/util/io/storage.c       2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/util/io/storage.c       2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,663 @@
+/*
+     This file is part of GNUnet.
+     (C) 2001, 2002 Christian Grothoff (and other contributing authors)
+
+     GNUnet is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published
+     by the Free Software Foundation; either version 2, or (at your
+     option) any later version.
+
+     GNUnet is distributed in the hope that it will be useful, but
+     WITHOUT ANY WARRANTY; without even the implied warranty of
+     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+     General Public License for more details.
+
+     You should have received a copy of the GNU General Public License
+     along with GNUnet; see the file COPYING.  If not, write to the
+     Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+     Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file util/storage.c
+ * @brief IO convenience methods
+ * @author Christian Grothoff
+ **/
+
+#include "gnunet_util.h"
+#include "platform.h"
+
+#if LINUX || CYGWIN
+#include <sys/vfs.h>
+#else
+#ifdef SOMEBSD
+#include <sys/param.h>
+#include <sys/mount.h>
+#else
+#ifdef OSX
+#include <sys/param.h>
+#include <sys/mount.h>
+#else
+#ifdef SOLARIS
+#include <sys/types.h>
+#include <sys/statvfs.h>
+#else
+#ifdef MINGW
+#define                _IFMT           0170000 /* type of file */
+#define                _IFLNK          0120000 /* symbolic link */
+#define        S_ISLNK(m)      (((m)&_IFMT) == _IFLNK)
+#else
+#error FIXME: need to port statfs (how much space is left on the drive?)
+#endif
+#endif
+#endif
+#endif
+#endif
+
+#ifndef SOMEBSD
+ #ifndef WINDOWS
+  #ifndef OSX
+   #include <wordexp.h>
+  #endif
+ #endif
+#endif
+
+
+/* FIXME: Currently this function does not return errors */
+static void getSizeRec(char * filename,
+                      char * dirname,
+                      unsigned long long * size) {
+  struct stat buf;  
+  char * fn;
+  
+  if (filename == NULL)
+    return;
+  if (dirname != NULL) {
+    fn = MALLOC(strlen(filename) + strlen(dirname) + 2);
+    fn[0] = '\0';
+    if (strlen(dirname) > 0) {
+      strcat(fn, dirname);      
+      if (dirname[strlen(dirname)-1] != DIR_SEPARATOR)
+       strcat(fn, "/"); /* add tailing / if needed */
+    }
+    /* Windows paths don't start with / */
+#ifndef MINGW
+    else
+      strcat(fn, "/");
+#endif
+    if (filename[0] == DIR_SEPARATOR) /* if filename starts with a "/", don't 
copy it */
+      strcat(fn, &filename[1]);
+    else
+      strcat(fn, filename);
+  } else
+    fn = STRDUP(filename);
+
+  if (0 != STAT(fn, &buf)) {
+    LOG(LOG_EVERYTHING,
+       "EVERYTHING: Can not stat %s (%s)\n",
+       fn, 
+       STRERROR(errno));
+    FREE(fn);
+    return;
+  }
+  *size += buf.st_size;
+  if ( (S_ISDIR(buf.st_mode)) &&
+       (!S_ISLNK(buf.st_mode)) ) {
+    scanDirectory(fn,
+                 (DirectoryEntryCallback)&getSizeRec,
+                 size);
+  }  
+  FREE(fn);
+}
+
+/* FIXME: Currently this function does not return errors */
+static void getSizeWithoutSymlinksRec(char * filename,
+                                     char * dirname,
+                                     unsigned long long * size) {
+  struct stat buf;  
+  char * fn;
+  
+  if (filename == NULL)
+    return;
+  if (dirname != NULL) {
+    fn = MALLOC(strlen(filename) + strlen(dirname) + 2);
+    fn[0] = '\0';
+    if (strlen(dirname) > 0) {
+      strcat(fn, dirname);      
+      if (dirname[strlen(dirname)-1] != DIR_SEPARATOR)
+       strcat(fn, "/"); /* add tailing / if needed */
+    }
+    /* Windows paths don't start with / */
+#ifndef MINGW
+    else
+      strcat(fn, "/");
+#endif
+    if (filename[0] == DIR_SEPARATOR) /* if filename starts with a "/", don't 
copy it */
+      strcat(fn, &filename[1]);
+    else
+      strcat(fn, filename);
+  } else
+    fn = STRDUP(filename);
+
+  if (0 != STAT(fn, &buf)) {
+    LOG(LOG_EVERYTHING,
+       "EVERYTHING: Can not stat %s (%s)\n",
+       fn, 
+       STRERROR(errno));
+    FREE(fn);
+    return;
+  }
+  if (! S_ISLNK(buf.st_mode))
+    *size += buf.st_size;
+  if ( (S_ISDIR(buf.st_mode)) &&
+       (!S_ISLNK(buf.st_mode)) ) {
+    scanDirectory(fn,
+                 (DirectoryEntryCallback)&getSizeRec,
+                 size);
+  }  
+  FREE(fn);
+}
+
+/**
+ * Get the number of blocks that are left on the partition that
+ * contains the given file (for normal users).
+ *
+ * @param part a file on the partition to check
+ * @return -1 on errors, otherwise the number of free blocks
+ **/
+long getBlocksLeftOnDrive(const char * part) {
+#ifdef SOLARIS
+  struct statvfs buf;
+
+  if (0 == statvfs(part, &buf)) {
+    return buf.f_bavail;
+  } else {
+    LOG(LOG_ERROR,
+       "ERROR: statfs failed: %s\n",
+       STRERROR(errno));
+    return -1;
+  }
+#elif MINGW
+  DWORD dwDummy, dwBlocks;
+  char szDrive[4];
+  
+  memcpy(szDrive, part, 3);
+  szDrive[3] = 0; 
+  if(!GetDiskFreeSpace(szDrive, &dwDummy, &dwDummy, &dwBlocks, &dwDummy))
+  {
+    LOG(LOG_ERROR,
+        "ERROR: GetDiskFreeSpace failed for drive %s: %u\n",
+        szDrive, GetLastError());
+        
+    return -1;
+  }
+  else
+  {
+    return dwBlocks;
+  }
+#else
+  struct statfs s;
+  if (0 == statfs(part, &s)) {
+    return s.f_bavail;
+  } else {
+    LOG(LOG_ERROR,
+       "ERROR: statfs failed: %s\n",
+       STRERROR(errno));
+    return -1;
+  }
+#endif
+}
+
+/**
+ * Get the size of the file (or directory)
+ * of the given file (in bytes).
+ * FIXME: Currently this function does not return errors
+ **/
+unsigned long long getFileSize(char * filename) {
+  unsigned long long size;
+
+  size = 0;
+  getSizeRec(filename, "", &size);
+  return size;
+}
+
+/**
+ * Get the size of the file (or directory) without 
+ * counting symlinks.
+ * FIXME: Currently this function does not return errors
+ **/
+unsigned long long getFileSizeWithoutSymlinks(char * filename) {
+  unsigned long long size;
+
+  size = 0;
+  getSizeWithoutSymlinksRec(filename, "", &size);
+  return size;
+}
+
+
+/**
+ * Convert string to value ('755' for chmod-call)
+ **/
+static int atoo(char *s) {
+  int n = 0;
+
+  while ( ('0' <= *s) && (*s < '8') ) {
+    n <<= 3;
+    n += *s++ - '0';
+  }
+  return n;
+}
+
+/**
+ * Test if fil is a directory.
+ * @returns YES if yes, NO if not
+ **/
+int isDirectory(const char * fil) {
+  struct stat filestat;
+  int ret;
+
+  ret = STAT(fil, &filestat);
+  if (ret != 0) {
+    LOG(LOG_EVERYTHING,
+       "EVERYTHING: Can not stat %s (%s).\n",
+       fil,
+       STRERROR(errno));
+    return NO;
+  }
+  if (S_ISDIR(filestat.st_mode))
+    return YES;
+  else
+    return NO;
+}
+
+/**
+ * Assert that fil corresponds to a filename
+ * (of a file that exists and that is not a directory).
+ * @returns 1 if yes, 0 if not (will print an error
+ * message in that case, too).
+ **/
+int assertIsFile(const char * fil) {
+  struct stat filestat;
+  int ret;
+
+  ret = STAT(fil, &filestat);
+  if (ret != 0) {
+    LOG(LOG_EVERYTHING,
+       "EVERYTHING: Can not stat %s (%s).\n",
+       fil,
+       STRERROR(errno));
+    return 0;
+  }
+  if (!S_ISREG(filestat.st_mode)) {
+    LOG(LOG_WARNING,
+       "WARNING: %s is not a regular file.\n",
+       fil);
+    return 0;
+  }  
+  if ( ACCESS(fil, R_OK) < 0 ) {
+    LOG(LOG_WARNING,
+       "WARNING: access failed (%s).\n",
+       STRERROR(errno));
+    return 0;
+  }
+  return 1;
+}
+
+/**
+ * Complete filename (a la shell) from abbrevition.
+ * @param fil the name of the file, may contain ~/ or 
+ *        be relative to the current directory
+ * @returns the full file name, 
+ *          NULL is returned on error
+ **/
+char * expandFileName(const char * fil) {
+  char buffer[512];
+  char * fn;
+#ifndef MINGW
+  char * fm;
+  const char *fil_ptr;
+#else
+  long lRet;
+#endif
+
+  if (fil == NULL)
+    return NULL;
+
+#ifndef MINGW
+  if (fil[0] == DIR_SEPARATOR) {
+    /* absolute path, just copy */
+    return(STRDUP(fil));
+  }
+  else if (fil[0] == '~') {
+    fm = getenv("HOME");
+    if (fm == NULL) {
+      /* keep it symbolic to show error to user! */
+      fm = "$HOME";
+    }
+
+    /* do not copy '~' */
+    fil_ptr = fil + 1;
+
+       /* skip over dir seperator to be consistent */    
+    if (fil_ptr[0] == DIR_SEPARATOR) {
+       fil_ptr++;
+    }
+  } else {
+    fil_ptr = fil;
+    if (getcwd(buffer, 512) != NULL)
+      fm = buffer;
+    else
+      fm = "$PWD";
+  }
+
+  fn = MALLOC(strlen(fm) + 1 + strlen(fil_ptr) + 1);
+
+  sprintf(fn, "%s/%s", fm, fil_ptr);
+#else
+  fn = MALLOC(MAX_PATH + 1);
+
+  if ((lRet = conv_to_win_path(fil, buffer)) != ERROR_SUCCESS)
+  {
+       SetErrnoFromWinError(lRet);
+
+    return NULL;
+  }
+  /* is the path relative? */
+  if ((strncmp(buffer + 1, ":\\", 2) != 0) && 
+      (strncmp(buffer, "\\\\", 2) != 0))
+  {
+    char szCurDir[MAX_PATH + 1];
+    lRet = GetCurrentDirectory(MAX_PATH + 1, szCurDir);
+    if (lRet + strlen(fn) + 1 > (_MAX_PATH + 1))
+    {
+      SetErrnoFromWinError(ERROR_BUFFER_OVERFLOW);
+      
+      return NULL;
+    }
+    sprintf(fn, "%s\\%s", szCurDir, buffer);
+  }
+  else
+  {
+    strcpy(fn, buffer);
+  }
+#endif
+  return fn;
+}
+
+/**
+ * Implementation of "mkdir -p"
+ * @param dir the directory to create
+ * @returns OK on success, SYSERR on failure
+ **/
+int mkdirp(char * dir) {
+  char * rdir;
+  int len;
+  int pos;
+  int ret = OK;
+
+  rdir = expandFileName(dir); /* expand directory */
+  len = strlen(rdir);
+#ifndef MINGW
+  pos = 1; /* skip heading '/' */
+#else
+
+  /* Local or Network path? */
+  if (strncmp(rdir, "\\\\", 2) == 0)
+  {
+    pos = 2;
+    while (rdir[pos])
+    {
+      if (rdir[pos] == '\\')
+      {
+        pos ++;
+        
+        break;
+      }
+      pos ++;
+    }
+  }
+  else
+  {
+    pos = 3;  /* strlen("C:\\") */
+  }
+#endif
+  while (pos <= len) {
+    if ( (rdir[pos] == DIR_SEPARATOR) || 
+        (pos == len) ) {
+      rdir[pos] = '\0';
+      if (! isDirectory(dir))
+#ifndef MINGW
+       if (0 != mkdir(rdir,
+                      S_IRUSR | S_IWUSR | 
+                      S_IXUSR | S_IRGRP | 
+                      S_IXGRP | S_IROTH | 
+                      S_IXOTH)) { /* 755 */
+#else
+       if (0 != mkdir(rdir)) {    
+#endif
+         if (errno != EEXIST) {
+           LOG(LOG_ERROR,
+               "ERROR: could not mkdir %s: %s\n",
+               rdir,
+               STRERROR(errno));
+           ret = SYSERR;
+         }
+       }
+      rdir[pos] = DIR_SEPARATOR;       
+    }      
+    pos++;
+  }   
+  FREE(rdir);
+  return ret;
+}
+
+/**
+ * Read the contents of a binary file into a buffer.
+ * @param fileName the name of the file, not freed,
+ *        must already be expanded!
+ * @param len the maximum number of bytes to read
+ * @param result the buffer to write the result to
+ * @return the number of bytes read on success, -1 on failure
+ **/ 
+int readFile(char * fileName,
+            int  len,
+            void * result) {
+  /* open file, must exist, open read only */
+  int handle;  
+  int size;
+
+  if ((fileName == NULL) || (result == NULL))
+    return -1;
+  handle = OPEN(fileName,O_RDONLY,S_IRUSR);
+  if (handle < 0)
+    return -1; 
+  size = READ(handle, result, len);
+  CLOSE(handle);
+  return size;  
+}
+
+/**
+ * Write a buffer to a file.
+ * @param fileName the name of the file, NOT freed!
+ * @param buffer the data to write
+ * @param n number of bytes to write
+ * @param mode permissions to set on the file
+ **/ 
+void writeFile(char * fileName, 
+              void * buffer,
+              int n,
+              char *mode) {
+  int handle;
+  /* open file, open with 600, create if not 
+     present, otherwise overwrite */  
+  if ((fileName == NULL) || (buffer == NULL))
+    return;
+  handle = OPEN(fileName,
+               O_CREAT|O_WRONLY,S_IRUSR|S_IWUSR);
+  /* write the buffer take length from the beginning */
+  if (n != WRITE(handle, buffer, n)) 
+    LOG(LOG_WARNING,
+       "WARNING: Writing %d bytes to file %s failed!\n",
+       n, 
+       fileName);
+  CHMOD(fileName, atoo(mode));
+  CLOSE(handle);
+}
+
+/**
+ * Build a filename from directory and filename, completing like the shell does
+ * @param dir the name of the directory, may contain ~/ or other shell stuff. 
Will 
+ *        NOT be freed!
+ * @param fil the name of the file, will NOT be deallocated anymore!
+ * @param result where to store the full file name (must be large enough!)
+ **/
+void buildFileName(char * dir,
+                  HexName * fil,
+                  char * result) {
+  if ((dir == NULL) || (fil == NULL) || (result == NULL)) 
+    errexit("buildFileName called with NULL in arguments");
+  strcpy(result, dir);
+  strcat(result, (char*)fil);
+}
+
+/**
+ * Scan a directory for files. The name of the directory
+ * must be expanded first (!).
+ * @param dirName the name of the directory
+ * @param callback the method to call for each file,
+ *        can be NULL, in that case, we only count
+ * @param data argument to pass to callback
+ * @return the number of files found, -1 on error
+ **/
+int scanDirectory(char * dirName,
+                 DirectoryEntryCallback callback,
+                 void * data) {
+  DIR * dinfo;
+  struct dirent *finfo;
+  struct stat istat;
+  int count = 0;
+ 
+  if (dirName == NULL) 
+    return -1; 
+  if (0 != STAT(dirName, &istat)) {
+    LOG(LOG_WARNING,
+       "WARNING: Could not stat %s (%s)\n",
+       dirName, 
+       STRERROR(errno));
+    return -1;
+  }
+  if (!S_ISDIR(istat.st_mode)) {
+    LOG(LOG_ERROR,
+       "ERROR: scanDirectory must be invoked on a directory (%s)!\n",
+       dirName);
+    return -1;
+  }
+  errno = 0;
+  dinfo = OPENDIR(dirName);
+  if ((errno == EACCES) || (dinfo == NULL)) {
+    LOG(LOG_WARNING,
+       "WARNING: scanDirectory: %s (%s)\n",
+       STRERROR(errno),
+       (char*)dirName);
+   return -1;
+  }
+  while ((finfo = readdir(dinfo)) != NULL) {
+    if (finfo->d_name[0] == '.')
+      continue;
+    if (callback != NULL)
+      callback(finfo->d_name,
+              dirName,
+              data);
+    count++;
+  }
+  closedir(dinfo);
+  return count;
+}
+
+/**
+ * Callback for rm_minus_rf.
+ **/
+static void rmHelper(char * fil,
+                    char * dir,
+                    int * ok) {
+  char * fn;
+  fn = MALLOC(strlen(dir) + strlen(fil) + 2);
+  sprintf(fn, "%s/%s", dir, fil);
+  if (SYSERR == rm_minus_rf(fn))
+    *ok = SYSERR;
+  FREE(fn);
+}
+
+/**
+ * Remove all files in a directory (rm -rf). Call with
+ * caution.
+ *
+ *
+ * @param fileName the file to remove
+ * @return OK on success, SYSERR on error
+ **/
+int rm_minus_rf(char * fileName) {
+  if (UNLINK(fileName) == 0)
+    return OK;
+  if ( (errno == EISDIR) || 
+       /* EISDIR is not sufficient in all cases, e.g.
+         sticky /tmp directory may result in EPERM on BSD.
+         So we also explicitly check "isDirectory" */
+       (YES == isDirectory(fileName)) ) {
+    int ok;
+
+    ok = OK;
+    scanDirectory(fileName,
+                 (DirectoryEntryCallback)&rmHelper,
+                 &ok);
+    if (ok == OK)
+      if (0 != RMDIR(fileName)) {
+       LOG(LOG_WARNING,
+           "WARNING: could not remove %s: %s\n",
+           fileName, 
+           STRERROR(errno));
+       ok = SYSERR;
+      }
+    return ok;
+  } else {
+    LOG(LOG_WARNING,
+       "WARNING: could not remove %s: %s\n",
+       fileName,
+       STRERROR(errno));
+    return SYSERR;
+  }
+}
+
+void close_(int fd,
+           char * filename,
+           int linenumber) {
+#ifdef MINGW
+  /* Windows sockets have to be closed using closesocket() */
+  if (closesocket(fd) != 0) {
+#endif
+    if (0 != close(fd)) {
+#ifdef MINGW
+      /* Close Windows handle */
+      if (! CloseHandle((HANDLE) fd)) {
+#endif
+        LOG(LOG_INFO,
+               "INFO: error closing file descriptor in %s:%d (%s)\n",
+               filename,
+               linenumber,
+               STRERROR(errno));
+      }
+#ifdef MINGW
+    } else {
+      /* discard blocking mode */
+      unsigned int uiIndex;
+      WaitForSingleObject(hSocksLock, INFINITE);
+      for(uiIndex = 0; uiIndex < uiSockCount; uiIndex++)
+        if (pSocks[uiIndex].s == fd)
+          pSocks[uiIndex].s = -1;
+      ReleaseMutex(hSocksLock);
+    }
+  }
+#endif
+}
+
+/* end of storage.c */

Added: freeway/src/org/gnu/freeway/util/net/CSHandler.java
===================================================================
--- freeway/src/org/gnu/freeway/util/net/CSHandler.java 2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/util/net/CSHandler.java 2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,22 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util.net;
+
+/**
+ * Handler for messages coming from clients.
+ */
+
+public interface CSHandler
+{
+       /**
+        * Handle received message.
+        *
+        * @param session       The session where the message comes from.
+        * @param message       The message to handle.
+        * @return                      True to indicate the message has been 
handled, false otherwise.
+        */
+
+       public boolean handle( CSSession session, CSMessage message );
+}

Added: freeway/src/org/gnu/freeway/util/net/CSMessage.java
===================================================================
--- freeway/src/org/gnu/freeway/util/net/CSMessage.java 2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/util/net/CSMessage.java 2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,284 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util.net;
+
+import java.util.*;
+
+/**
+ * Client-Server message.
+ */
+
+public abstract class CSMessage extends Object implements Persistent
+{
+       /** Maximum size of a message. */
+       public static final int MAX_MESSAGE_SIZE                        =       
65536;
+
+       /** core: return value for remote calls */
+       public static final int IS_RESULT                               =       
0;
+
+       /** core: client to gnunetd: to how many nodes are we connected ? */
+       public static final int IS_CLIENT_COUNT                 =       1;
+
+       /** core: client to gnunetd: how much traffic do we have at the moment? 
*/
+       public static final int IS_TRAFFIC_QUERY                        =       
2;
+
+       /** core: gnunetd to client: traffic statistics */
+       public static final int IS_TRAFFIC_INFO                 =       3;
+
+       /** core: client to gnunetd: request statistics */
+       public static final int IS_GET_STATISTICS               =       4;
+
+       /** core: gnunetd to client: statistics */
+       public static final int IS_STATISTICS                   =       5;
+
+       /** core: client to gnunetd: is client server message supported */
+       public static final int IS_CS_MESSAGE_SUPPORTED =       6;
+
+       /** core: client to gnunetd: is p2p message supported */
+       public static final int IS_P2P_MESSAGE_SUPPORTED        =       7;
+
+       /** afs : client to gnunetd: send queries */
+       public static final int IS_QUERY                                        
=       8;
+
+       /** afs : gnunetd to client: here is your answer (3-hash-content) */
+       public static final int IS_RESULT_3HASH                 =       9;
+
+       /** afs : gnunetd to client: here is your answer (CHK-content) */
+       public static final int IS_RESULT_CHK                   =       10;
+
+       /** afs : client to gnunetd: insert CHK content (no index) */
+       public static final int IS_INSERT_CHK                   =       11;
+
+       /** afs : client to gnunetd: insert 3HASH content (no index) */
+       public static final int IS_INSERT_3HASH                 =       12;
+
+       /** afs : client to gnunetd: index content */
+       public static final int IS_INDEX_BLOCK                  =       13;
+
+       /** afs : client to gnunetd: get an index for a file */
+       public static final int IS_INDEX_FILE                   =       14;
+
+       /** afs : client to gnunetd: index super-block */
+       public static final int IS_INDEX_SUPER                  =       15;
+
+       /** afs : client to gnunetd: delete CHK content (no index) */
+       public static final int IS_DELETE_CHK                   =       16;
+
+       /** afs : client to gnunetd: delete 3HASH content (no index) Not used 
so far! */
+       public static final int IS_DELETE_3HASH                 =       17;
+
+       /** afs : client to gnunetd: unindex content (remove) */
+       public static final int IS_UNINDEX_BLOCK                        =       
18;
+
+       /** afs : client to gnunetd: remove an file from the indexed list */
+       public static final int IS_UNINDEX_FILE                 =       19;
+
+       /** afs : client to gnunetd: unindex super-block */
+       public static final int IS_UNINDEX_SUPER                        =       
20;
+
+       /** afs : client to gnunetd: issue namespace query */
+       public static final int IS_NSQUERY                              =       
21;
+
+       /** afs : client to gnunetd: store SBlock */
+       public static final int IS_INSERT_SBLOCK                        =       
22;
+
+       /** afs : gnunetd to client: SBlock found */
+       public static final int IS_RESULT_SBLOCK                        =       
23;
+
+       /** afs : client to gnunetd: bits of file to upload (indexing) */
+       public static final int IS_UPLOAD_FILE                  =       24;
+
+       /** afs : client to gnunetd: try using a link for the file */
+       public static final int IS_LINK_FILE                            =       
25;
+
+       /** afs : client to gnunetd: what is the average priority of entries in 
the routing table ? */
+       public static final int IS_GET_AVG_PRIORITY             =       26;
+
+       /** chat : */
+       public static final int IS_CHAT_MESSAGE                 =       32;
+
+       /** trace : */
+       public static final int IS_TRACE_PROBE                  =       36;
+
+       /** trace : */
+       public static final int IS_TRACE_REPLY                  =       37;
+
+       /** benchmark : */
+       public static final int IS_BENCH_REQUEST                        =       
40;
+
+       /** benchmark : */
+       public static final int IS_BENCH_REPLY                  =       41;
+
+       /** testbed : */
+       public static final int IS_TESTBED_REQUEST              =       50;
+
+       /** testbed : */
+       public static final int IS_TESTBED_REPLY                        =       
51;
+
+       /** core: client to gnunetd: shutdown */
+       public static final int IS_SHUTDOWN                             =       
64;
+
+       /** core: client to gnunetd: get configuration option */
+       public static final int IS_GET_OPTION_REQUEST           =       65;
+
+       /** core: gnunetd to client: option value */
+       public static final int IS_GET_OPTION_REPLY             =       66;
+
+       /** */
+       public static final int IS_GET_HOST_INFO                        =       
67;
+
+       /** */
+       public static final int IS_HOST_INFO                            =       
68;
+
+       /** dht: client to CS: ask ID for API     */
+       public static final int IS_DHT_REQUEST_API_ID           =       70;
+
+       /** dht: client to CS: create new table   */
+       public static final int IS_DHT_REQUEST_CREATE           =       71;
+
+       /** dht: client to CS: join table         */
+       public static final int IS_DHT_REQUEST_JOIN             =       72;
+
+       /** dht: client to CS: leave table        */
+       public static final int IS_DHT_REQUEST_LEAVE            =       73;
+
+       /** dht: client to CS: insert to table    */
+       public static final int IS_DHT_REQUEST_INSERT           =       74;
+
+       /** dht: client to CS: fetch from table   */
+       public static final int IS_DHT_REQUEST_FETCH            =       75;
+
+       /** dht: client to CS: list tables        */
+       public static final int IS_DHT_REQUEST_TABLES           =       76;
+
+       /** dht: client to CS: list inserted data */
+       public static final int IS_DHT_REQUEST_INSERTED =       77;
+
+       /** dht: client to CS: drop inserted data */
+       public static final int IS_DHT_REQUEST_DROP             =       78;
+
+       /** dht: client to CS: operation status   */
+       public static final int IS_DHT_REQUEST_STATUS           =       79;
+
+       /** dht: CS to client: ack or denial      */
+       public static final int IS_DHT_REPLY_STANDARD           =       80;
+
+       /** dht: CS to client: operation failed   */
+       public static final int IS_DHT_REPLY_FAILURE            =       81;
+
+       /** dht: CS to client: operation results  */
+       public static final int IS_DHT_REPLY_RESULTS            =       82;
+
+       /** dht: CS to client: operation status   */
+       public static final int IS_DHT_REPLY_STATUS             =       83;
+
+       /** */
+       public static final int IS_MAX                                  =       
100;
+
+       /** */
+       private static String[] names;
+
+       static {
+               initNames();
+               }
+
+       /** The type of the message. */
+       private int     type;
+
+
+       protected CSMessage( int t )
+       {
+               super();
+               type=t;
+       }
+
+       public String toString()
+       {
+               return "Client server message [type="+type+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int getType()
+       {
+               return type;
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       protected static void initNames()
+       {
+               names=new String[IS_MAX];
+               Arrays.fill(names,null);
+
+               names[IS_RESULT]="IS_RESULT";
+               names[IS_CLIENT_COUNT]="IS_CLIENT_COUNT";
+               names[IS_TRAFFIC_QUERY]="IS_TRAFFIC_QUERY";
+               names[IS_TRAFFIC_INFO]="IS_TRAFFIC_INFO";
+               names[IS_GET_STATISTICS]="IS_GET_STATISTICS";
+               names[IS_STATISTICS]="IS_STATISTICS";
+               names[IS_CS_MESSAGE_SUPPORTED]="IS_CS_MESSAGE_SUPPORTED";
+               names[IS_P2P_MESSAGE_SUPPORTED]="IS_P2P_MESSAGE_SUPPORTED";
+               names[IS_QUERY]="IS_QUERY";
+               names[IS_RESULT_3HASH]="IS_RESULT_3HASH";
+               names[IS_RESULT_CHK]="IS_RESULT_CHK";
+               names[IS_INSERT_CHK]="IS_INSERT_CHK";
+               names[IS_INSERT_3HASH]="IS_INSERT_3HASH";
+               names[IS_INDEX_BLOCK]="IS_INDEX_BLOCK";
+               names[IS_INDEX_FILE]="IS_INDEX_FILE";
+               names[IS_INDEX_SUPER]="IS_INDEX_SUPER";
+               names[IS_DELETE_CHK]="IS_DELETE_CHK";
+               names[IS_DELETE_3HASH]="IS_DELETE_3HASH";
+               names[IS_UNINDEX_BLOCK]="IS_UNINDEX_BLOCK";
+               names[IS_UNINDEX_FILE]="IS_UNINDEX_FILE";
+               names[IS_UNINDEX_SUPER]="IS_UNINDEX_SUPER";
+               names[IS_NSQUERY]="IS_NSQUERY";
+               names[IS_INSERT_SBLOCK]="IS_INSERT_SBLOCK";
+               names[IS_RESULT_SBLOCK]="IS_RESULT_SBLOCK";
+               names[IS_UPLOAD_FILE    ]="IS_UPLOAD_FILE";
+               names[IS_LINK_FILE]="IS_UPLOAD_FILE";
+               names[IS_GET_AVG_PRIORITY]="IS_GET_AVG_PRIORITY";
+               names[IS_CHAT_MESSAGE]="IS_CHAT_MESSAGE";
+               names[IS_TRACE_PROBE]="IS_TRACE_PROBE";
+               names[IS_TRACE_REPLY]="IS_TRACE_REPLY";
+               names[IS_BENCH_REQUEST]="IS_BENCH_REQUEST";
+               names[IS_BENCH_REPLY]="IS_BENCH_REPLY";
+               names[IS_TESTBED_REQUEST]="IS_TESTBED_REQUEST";
+               names[IS_TESTBED_REPLY]="IS_TESTBED_REPLY";
+               names[IS_SHUTDOWN]="IS_SHUTDOWN";
+               names[IS_GET_OPTION_REQUEST]="IS_GET_OPTION_REQUEST";
+               names[IS_GET_OPTION_REPLY]="IS_GET_OPTION_REPLY";
+               names[IS_GET_HOST_INFO]="IS_GET_HOST_INFO";
+               names[IS_HOST_INFO]="IS_HOST_INFO";
+               names[IS_DHT_REQUEST_API_ID]="IS_DHT_REQUEST_API_ID";
+               names[IS_DHT_REQUEST_CREATE]="IS_DHT_REQUEST_CREATE";
+               names[IS_DHT_REQUEST_JOIN]="IS_DHT_REQUEST_JOIN";
+               names[IS_DHT_REQUEST_LEAVE]="IS_DHT_REQUEST_LEAVE";
+               names[IS_DHT_REQUEST_INSERT]="IS_DHT_REQUEST_INSERT";
+               names[IS_DHT_REQUEST_FETCH]="IS_DHT_REQUEST_FETCH";
+               names[IS_DHT_REQUEST_TABLES]="IS_DHT_REQUEST_TABLES";
+               names[IS_DHT_REQUEST_INSERTED]="IS_DHT_REQUEST_INSERTED";
+               names[IS_DHT_REQUEST_DROP]="IS_DHT_REQUEST_DROP";
+               names[IS_DHT_REQUEST_STATUS]="IS_DHT_REQUEST_STATUS";
+               names[IS_DHT_REPLY_STANDARD]="IS_DHT_REPLY_STANDARD";
+               names[IS_DHT_REPLY_FAILURE]="IS_DHT_REPLY_FAILURE";
+               names[IS_DHT_REPLY_RESULTS]="IS_DHT_REPLY_RESULTS";
+               names[IS_DHT_REPLY_STATUS]="IS_DHT_REPLY_STATUS";
+       }
+
+       /**
+        * Return a descriptive name for a client server message type.
+        *
+        * @param type
+        * @return
+        */
+
+       public static String nameFor( int type )
+       {
+               return ((type>=0 && type<names.length) ? names[type] : null);
+       }
+}

Added: freeway/src/org/gnu/freeway/util/net/CSResult.java
===================================================================
--- freeway/src/org/gnu/freeway/util/net/CSResult.java  2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/util/net/CSResult.java  2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,82 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util.net;
+
+import java.nio.*;
+
+/**
+ * CS communication: simple return value
+ */
+
+public class CSResult extends CSMessage
+{
+       public static final CSResult    OKAY            =       new CSResult(1);
+       public static final CSResult    ERR             =       new 
CSResult(-1);
+
+       /** The returned value. */
+       private int     value;
+
+
+       public CSResult()
+       {
+               super(IS_RESULT);
+               value=0;
+       }
+
+       public CSResult( boolean ret )
+       {
+               this();
+               value=(ret ? 1 : -1);
+       }
+
+       public CSResult( int ret )
+       {
+               this();
+               value=ret;
+       }
+
+       public String toString()
+       {
+               return "C/S result message [value="+value+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public boolean isOkay()
+       {
+               return value>0;
+       }
+
+       public int getResult()
+       {
+               return value;
+       }
+
+       public int getByteSize()
+       {
+               return 8;
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               int     size,type;
+
+               size=buf.getShort() & 0x0000ffff;
+               err.reportIf(size!=8,"bad size !");
+
+               type=buf.getShort() & 0x0000ffff;
+               err.reportIf(type!=IS_RESULT,"bad type !");
+
+               value=buf.getInt();
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               buf.putShort((short) 8);
+               buf.putShort((short) IS_RESULT);
+               buf.putInt(value);
+       }
+}

Added: freeway/src/org/gnu/freeway/util/net/CSServer.java
===================================================================
--- freeway/src/org/gnu/freeway/util/net/CSServer.java  2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/util/net/CSServer.java  2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,27 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util.net;
+
+/**
+ *
+ */
+
+public interface CSServer
+{
+       public String getLabel();
+
+       public boolean isLaunched();
+       public boolean launch( int port );
+       public boolean shutdown();
+
+       /**
+        * Add manually a session to the pool of listened sessions.
+        *
+        * @param s     The session to add.
+        * @return      True if okay (enough ressources), false otherwise.
+        */
+
+       public boolean register( CSSession s );
+}

Added: freeway/src/org/gnu/freeway/util/net/CSSession.java
===================================================================
--- freeway/src/org/gnu/freeway/util/net/CSSession.java 2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/util/net/CSSession.java 2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,39 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util.net;
+
+import java.net.*;
+import java.nio.channels.*;
+
+/**
+ *
+ */
+
+public interface CSSession
+{
+       public String getLabel();
+
+       public int getOps();
+       public SelectionKey registerOps( Selector sel, int ops );
+
+       public boolean isConnected();
+       public boolean connect( InetAddress ip, int port, boolean careful );
+       public boolean connect( SocketChannel channel, boolean careful );
+       public boolean disconnect();
+
+       public boolean isBlocking();
+       public void setBlocking( boolean flag );
+
+       public int doReceive();
+       public boolean hasReceived();
+       public Persistent receive( Class c );
+       public Persistent receive( PersistentDecoder decoder );
+
+       public boolean send( Persistent p );
+       public boolean sendAndCheck( Persistent p );
+       public boolean flushAndSend( Persistent p );
+       public boolean hasToSend();
+       public int doSend();
+}

Added: freeway/src/org/gnu/freeway/util/net/CSSessionHandler.java
===================================================================
--- freeway/src/org/gnu/freeway/util/net/CSSessionHandler.java  2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/util/net/CSSessionHandler.java  2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,19 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util.net;
+
+import java.nio.channels.*;
+
+/**
+ *
+ */
+
+public interface CSSessionHandler
+{
+       public CSSession handleAccept( SocketChannel socket );
+       public boolean handleRead( CSSession s, int len );
+       public boolean handleWrite( CSSession s, int len );
+       public void handleDestroy( CSSession s );
+}

Added: freeway/src/org/gnu/freeway/util/net/CSUnknown.java
===================================================================
--- freeway/src/org/gnu/freeway/util/net/CSUnknown.java 2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/util/net/CSUnknown.java 2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,109 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util.net;
+
+import org.gnu.freeway.util.*;
+
+import java.nio.*;
+import java.util.*;
+
+/**
+ * Unknown client/server message.
+ */
+
+public class CSUnknown extends CSMessage
+{
+       public static final int SIZE            =       4;
+
+       /** Size of the message. */
+       private int             size;
+
+       /** Type of the message. */
+       private int             type;
+
+       /** Raw data (unknown format). */
+       private byte[]  data;
+
+
+       protected CSUnknown()
+       {
+               super(0);
+               size=SIZE;
+               type=0;
+               data=new byte[] {};
+       }
+
+       public CSUnknown( int tp )
+       {
+               this();
+               type=tp;
+       }
+
+       public String toString()
+       {
+               return "Unknown message [type="+type+", 
data="+Utils.toHex(data)+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int hashCode()
+       {
+               return type;
+       }
+
+       public boolean equals( Object o )
+       {
+               if (!(o instanceof CSUnknown)) {
+                       return false;
+                       }
+               return Arrays.equals(((CSUnknown) o).data,data);
+       }
+
+       public int getType()
+       {
+               return type;
+       }
+
+       public byte[] getData()
+       {
+               return data;
+       }
+
+       public void setData( byte[] b )
+       {
+               setData(b,0,b.length);
+       }
+
+       public void setData( byte[] b, int off, int len )
+       {
+               data=new byte[len];
+               System.arraycopy(b,off,data,0,len);
+               size=SIZE+len;
+       }
+
+       public int getByteSize()
+       {
+               return size;
+       }
+
+       public void readBytes( ByteBuffer buf, ErrorReporter err )
+       {
+               size=buf.getShort() & 0x0000ffff;
+               err.reportIf(size<SIZE,"Bad size (got "+size+", expected at 
least "+SIZE+") !");
+
+               type=buf.getShort() & 0x0000ffff;       // not defined since 
content is not known
+
+               data=new byte[size-SIZE];
+               buf.get(data);
+       }
+
+       public void writeBytes( ByteBuffer buf )
+       {
+               buf.putShort((short) size);
+               buf.putShort((short) type);
+               buf.put(data);
+       }
+}

Added: freeway/src/org/gnu/freeway/util/net/ErrorReporter.java
===================================================================
--- freeway/src/org/gnu/freeway/util/net/ErrorReporter.java     2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/util/net/ErrorReporter.java     2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,46 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util.net;
+
+import java.util.*;
+
+/**
+ *
+ */
+
+public class ErrorReporter extends Object
+{
+       private List    errors;
+
+
+       public ErrorReporter()
+       {
+               super();
+               errors=new ArrayList();
+       }
+
+       public String[] getErrors()
+       {
+               return (String[]) errors.toArray(new String[errors.size()]);
+       }
+
+       public void reset()
+       {
+               errors.clear();
+       }
+
+       public void reportIf( boolean cnd, String mess )
+       {
+               if (cnd) {
+                       report(mess);
+                       }
+       }
+
+       public void report( String mess )
+       {
+               errors.add(mess);
+               throw new IllegalStateException(mess);
+       }
+}

Added: freeway/src/org/gnu/freeway/util/net/NetUtils.java
===================================================================
--- freeway/src/org/gnu/freeway/util/net/NetUtils.java  2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/util/net/NetUtils.java  2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,177 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util.net;
+
+import org.gnu.freeway.util.*;
+
+import java.io.*;
+import java.net.*;
+import java.nio.channels.*;
+import java.util.*;
+import java.util.logging.*;
+
+/**
+ *
+ */
+
+public class NetUtils extends Object
+{
+       public static boolean connectWithTimeout( SocketChannel channel, 
InetAddress ip, int port, long timeout )
+       {
+               Logger  logger;
+               boolean reached,was;
+
+               logger=Logger.getLogger(NetUtils.class.getClass().getName());
+               logger.log(Level.FINEST,"Trying to connect to 
"+ip.getHostAddress()+":"+port+" (timeout set to 
"+Scheduler.toMillis(timeout)+" ms)...");
+
+               reached=false;
+               try {
+                       was=channel.isBlocking();
+                       channel.configureBlocking(false);
+                       channel.connect(new InetSocketAddress(ip,port));
+                       reached=reachWithTimeout(channel,timeout);
+                       if (was && reached) {
+                               channel.configureBlocking(true);
+                               }
+                       }
+               catch( IOException x ) {
+                       logger.log(Level.SEVERE,"Got exception !",x);
+                       }
+
+               if (!reached) {
+                       logger.log(Level.SEVERE,"Could not connect to 
"+ip.getHostAddress()+":"+port+" within "+Scheduler.toMillis(timeout)+" ms !");
+                       }
+               return reached;
+       }
+
+       /**
+        * Check if a non-blocking channel is reachable (connectable and 
writable) within a given timeout.
+        *
+        * @param channel               The socket channel to test.
+        * @param timeout               Timeout value in cron units.
+        * @return                              True if reachable, false 
otherwise.
+        * @throws IOException  If an error (other than timeout or connection 
problem) occured.
+        */
+
+       public static boolean reachWithTimeout( SocketChannel channel, long 
timeout ) throws IOException
+       {
+               Selector                selector;
+               SelectionKey    key;
+               Iterator                iter;
+               long                    limit,t;
+               boolean         done,reached;
+
+               reached=false;
+
+               selector=Selector.open();
+               try {
+                       channel.register(selector,(SelectionKey.OP_CONNECT | 
SelectionKey.OP_WRITE));
+
+                       done=false;
+                       limit=Scheduler.now()+Scheduler.millis(timeout);
+                       while (Scheduler.now()<limit && !done) {
+                               t=limit-Scheduler.now();
+                               if (t<=0) {
+                                       break;
+                                       }
+
+                               if (selector.select(t)==0) {
+                                       continue;
+                                       }
+
+                               iter=selector.selectedKeys().iterator();
+                               while (iter.hasNext()) {
+                                       key=(SelectionKey) iter.next();
+                                       iter.remove();
+
+                                       if (!key.isValid()) {
+                                               continue;
+                                               }
+                                       if (key.isConnectable()) {
+                                               try {
+                                                       channel.finishConnect();
+                                                       }
+                                               catch( ConnectException x ) {
+                                                       // connection refused 
(mostly)
+                                                       done=true;
+                                                       }
+                                               }
+                                       if (!done && key.isWritable()) {
+                                               reached=true;
+                                               done=true;
+                                               }
+                                       }
+                               }
+                       }
+               finally {
+                       selector.close();
+                       }
+               return reached;
+       }
+
+       public static String labelFor( SocketChannel c )
+       {
+               Socket                  sock;
+               StringBuffer    buf;
+
+               buf=new StringBuffer();
+               try {
+                       buf.append("{ ");
+                       if (c!=null) {
+                               sock=c.socket();
+                               if (sock.isBound()) {
+                                       
buf.append(sock.getLocalAddress().getHostAddress());
+                                       buf.append(":");
+                                       buf.append(sock.getLocalPort());
+                                       }
+                               else {
+                                       buf.append("(not bound)");
+                                       }
+                               buf.append(" -> ");
+                               if (sock.isConnected()) {
+                                       
buf.append(sock.getInetAddress().getHostAddress());
+                                       buf.append(":");
+                                       buf.append(sock.getPort());
+                                       }
+                               else {
+                                       buf.append("(not connected)");
+                                       }
+                               }
+                       else {
+                               buf.append("(closed)");
+                               }
+                       buf.append(" }");
+                       }
+               catch( Throwable x ) {
+                       // underlying I/O may throw non I/O exceptions...
+                       buf.setLength(0);
+                       buf.append("{ (err) }");
+                       }
+               return buf.toString();
+       }
+
+       public static String labelForOps( int ops )
+       {
+               StringBuffer            buf;
+
+               buf=new StringBuffer();
+               if ((ops & SelectionKey.OP_CONNECT)!=0) {
+                       buf.append(" connect");
+                       }
+               if ((ops & SelectionKey.OP_ACCEPT)!=0) {
+                       buf.append(" accept");
+                       }
+               if ((ops & SelectionKey.OP_READ)!=0) {
+                       buf.append(" read");
+                       }
+               if ((ops & SelectionKey.OP_WRITE)!=0) {
+                       buf.append(" write");
+                       }
+               if (buf.length()>0 && buf.charAt(0)==' ') {
+                       buf.deleteCharAt(0);
+                       }
+               return buf.toString();
+       }
+}

Added: freeway/src/org/gnu/freeway/util/net/P2PHandler.java
===================================================================
--- freeway/src/org/gnu/freeway/util/net/P2PHandler.java        2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/util/net/P2PHandler.java        2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,26 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util.net;
+
+import org.gnu.freeway.transport.*;
+
+/**
+ * Handler for some message type.
+ */
+
+public interface P2PHandler
+{
+       /**
+        * Handle the received message. Return true if the processing of 
incoming messages should be continued, false otherwise.
+        *
+        * @param session       The session the message belongs to.
+        * @param msg           The received message, decrypted if needed.
+        * @param encrypted     True if the message was encrypted, false 
otherwise.
+        * @return                      True to continue processing, false 
otherwise.
+        *
+        */
+
+       public boolean handle( Session session, P2PMessage msg, boolean 
encrypted );
+}

Added: freeway/src/org/gnu/freeway/util/net/P2PMessage.java
===================================================================
--- freeway/src/org/gnu/freeway/util/net/P2PMessage.java        2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/util/net/P2PMessage.java        2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,155 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util.net;
+
+import java.util.*;
+
+/**
+ * Peer-to-Peer message.
+ */
+
+public abstract class P2PMessage extends Object implements Persistent
+{
+       /** Maximum size of a message. */
+       public static final int MAX_MESSAGE_SIZE        =       65536;
+
+       /** core : Announcement of public key. */
+       public static final int IS_HELO                 =       0;
+
+       /** core : Session key exchange, session key is encrypted with host's 
private key. */
+       public static final int IS_SESSION_KEY  =       1;
+
+       /** core : Ping. */
+       public static final int IS_PING                 =       2;
+
+       /** core : Pong, response to a ping. */
+       public static final int IS_PONG                 =       3;
+
+       /** core : Sequence number (discard packet if sequence number is not 
higher than previously received number) */
+       public static final int IS_SEQUENCE             =       5;
+
+       /** core : Noise, used to fill packets to sizes >1k. */
+       public static final int IS_NOISE                        =       6;
+
+       /** core : Termination of connection (other host is nice and tells us, 
there is NO requirement to do so!) */
+       public static final int IS_HANGUP               =       7;
+
+       /** core : Advertise capability (or limitation). */
+       public static final int IS_CAPABILITY   =       8;
+
+       /** afs : Query for content. */
+       public static final int IS_QUERY                        =       16;
+
+       /** afs : receive content */
+       public static final int IS_3HASH_RESULT =       17;
+
+       /** afs : receive CHK content */
+       public static final int IS_CHK_RESULT   =       18;
+
+       /** afs : Request namespace entry */
+       public static final int IS_NSQUERY              =       19;
+
+       /** afs : Received namespace entry */
+       public static final int IS_SBLOCK_RESULT        =       20;
+
+       /** chat message */
+       public static final int IS_CHAT_MESSAGE =       32;
+
+       /** trace kit message : */
+       public static final int IS_TRACE_PROBE  =       36;
+
+       /** trace kit message : */
+       public static final int IS_TRACE_REPLY  =       37;
+
+       /** benchmark message: send back reply asap */
+       public static final int IS_BENCH_REQUEST        =       40;
+
+       /** benchmark message: */
+       public static final int IS_BENCH_REPLY  =       41;
+
+       /** rpc/dht : */
+       public static final int IS_RPC_DHT_REQ  =       42;
+
+       /** rpc/dht : */
+       public static final int IS_RPC_DHT_RES  =       43;
+
+       /** rpc/dht : */
+       public static final int IS_RPC_DHT_ACK  =       44;
+
+       /** */
+       public static final int IS_MAX                  =       60;
+
+       /** */
+       private static String[] names;
+
+       static {
+               initNames();
+               }
+
+       /** type of the request */
+       private int     type;
+
+
+       protected P2PMessage( int t )
+       {
+               super();
+               type=t;
+       }
+
+       public String toString()
+       {
+               return "Peer to peer message [type="+type+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int getType()
+       {
+               return type;
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       protected static void initNames()
+       {
+               names=new String[IS_MAX];
+               Arrays.fill(names,null);
+
+               names[IS_HELO]="IS_HELO";
+               names[IS_SESSION_KEY]="IS_SESSION_KEY";
+               names[IS_PING]="IS_PING";
+               names[IS_PONG]="IS_PONG";
+               names[IS_SEQUENCE]="IS_SEQUENCE";
+               names[IS_NOISE]="IS_NOISE";
+               names[IS_HANGUP]="IS_HANGUP";
+               names[IS_CAPABILITY]="IS_CAPABILITY";
+               names[IS_QUERY]="IS_QUERY";
+               names[IS_3HASH_RESULT]="IS_3HASH_RESULT";
+               names[IS_CHK_RESULT]="IS_CHK_RESULT";
+               names[IS_NSQUERY]="IS_NSQUERY";
+               names[IS_SBLOCK_RESULT]="IS_SBLOCK_RESULT";
+               names[IS_CHAT_MESSAGE]="IS_CHAT_MESSAGE";
+               names[IS_TRACE_PROBE]="IS_TRACE_PROBE";
+               names[IS_TRACE_REPLY]="IS_TRACE_REPLY";
+               names[IS_BENCH_REQUEST]="IS_BENCH_REQUEST";
+               names[IS_BENCH_REPLY]="IS_BENCH_REPLY";
+               names[IS_RPC_DHT_REQ]="IS_RPC_DHT_REQ";
+               names[IS_RPC_DHT_RES]="IS_RPC_DHT_RES";
+               names[IS_RPC_DHT_ACK]="IS_RPC_DHT_ACK";
+       }
+
+       /**
+        * Return a descriptive name for a p2p message type
+        * @param type
+        * @return
+        */
+
+       public static String nameFor( int type )
+       {
+               return ((type>=0 && type<names.length) ? names[type] : null);
+       }
+}

Added: freeway/src/org/gnu/freeway/util/net/Persistent.java
===================================================================
--- freeway/src/org/gnu/freeway/util/net/Persistent.java        2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/util/net/Persistent.java        2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,18 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util.net;
+
+import java.nio.*;
+
+/**
+ *
+ */
+
+public interface Persistent
+{
+       public int getByteSize();
+       public void writeBytes( ByteBuffer buf );
+       public void readBytes( ByteBuffer buf, ErrorReporter err );
+}

Added: freeway/src/org/gnu/freeway/util/net/PersistentDecoder.java
===================================================================
--- freeway/src/org/gnu/freeway/util/net/PersistentDecoder.java 2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/util/net/PersistentDecoder.java 2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,266 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util.net;
+
+import org.gnu.freeway.util.*;
+
+import java.nio.*;
+import java.util.*;
+import java.util.logging.*;
+
+/**
+ *
+ */
+
+public class PersistentDecoder extends LoggedObject
+{
+       public static final int ID_LIMIT        =       2048;
+
+       private Class[] classes;
+       private int             defaultID;
+       private int             corruptedID;
+
+
+       public PersistentDecoder()
+       {
+               super(true);
+               classes=new Class[ID_LIMIT];
+               defaultID=-1;
+               corruptedID=-1;
+       }
+
+       public String toString()
+       {
+               return "Persistent decoder [classes="+Arrays.asList(classes)+", 
defaultID="+defaultID+", corruptedID="+corruptedID+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public synchronized int[] getIDs()
+       {
+               int[]   ids;
+               int             i,n;
+
+               for (i=n=0; i<classes.length; i++) {
+                       if (classes[i]!=null) {
+                               n++;
+                               }
+                       }
+
+               ids=new int[n];
+               for (i=n=0; i<classes.length; i++) {
+                       if (classes[i]!=null) {
+                               ids[n++]=i;
+                               }
+                       }
+               return ids;
+       }
+
+       public synchronized Class get( int id )
+       {
+               if (id<0 || id>=classes.length) {
+                       log(Level.WARNING,"Given ID exceeds limits ("+id+").");
+                       return null;
+                       }
+               return classes[id];
+       }
+
+       public synchronized boolean add( int id, Class c )
+       {
+               if (!Persistent.class.isAssignableFrom(c)) {
+                       log(Level.WARNING,"Instances of "+c.getName()+" are not 
persistent.");
+                       return false;
+                       }
+
+               if (id<0 || id>=classes.length) {
+                       log(Level.WARNING,"Given ID exceeds limits ("+id+").");
+                       return false;
+                       }
+               if (classes[id]!=null) {
+                       log(Level.WARNING,"Slot #"+id+" is busy.");
+                       return false;
+                       }
+
+               classes[id]=c;
+               return true;
+       }
+
+       public synchronized boolean remove( int id )
+       {
+               if (id<0 || id>=classes.length) {
+                       log(Level.WARNING,"Given ID exceeds limits ("+id+").");
+                       return false;
+                       }
+               if (classes[id]==null) {
+                       log(Level.WARNING,"Slot #"+id+" is empty.");
+                       return false;
+                       }
+
+               if (defaultID==id) {
+                       defaultID=-1;
+                       }
+               if (corruptedID==id) {
+                       corruptedID=-1;
+                       }
+               classes[id]=null;
+               return true;
+       }
+
+       public synchronized void clear()
+       {
+               Arrays.fill(classes,null);
+               defaultID=-1;
+               corruptedID=-1;
+       }
+
+       public synchronized boolean setDefault( int id )
+       {
+               if (id<0 || id>=classes.length) {
+                       log(Level.WARNING,"Given ID exceeds limits ("+id+").");
+                       return false;
+                       }
+               if (classes[id]==null) {
+                       log(Level.WARNING,"Slot #"+id+" is empty.");
+                       return false;
+                       }
+
+               defaultID=id;
+               return true;
+       }
+
+       public synchronized boolean setCorrupted( int id )
+       {
+               if (id<0 || id>=classes.length) {
+                       log(Level.WARNING,"Given ID exceeds limits ("+id+").");
+                       return false;
+                       }
+               if (classes[id]==null) {
+                       log(Level.WARNING,"Slot #"+id+" is empty.");
+                       return false;
+                       }
+
+               corruptedID=id;
+               return true;
+       }
+
+       public synchronized boolean merge( PersistentDecoder dec )
+       {
+               int             i;
+               boolean res;
+
+               synchronized(dec) {
+                       res=true;
+                       for (i=0; i<dec.classes.length; i++) {
+                               if (dec.classes[i]!=null) {
+                                       if (classes[i]!=null && 
!classes[i].equals(dec.classes[i])) {
+                                               log(Level.WARNING,"Slot #"+i+" 
mismatch : "+classes[i].getName()+" != "+dec.classes[i].getName()+".");
+                                               res=false;
+                                               }
+                                       else {
+                                               classes[i]=dec.classes[i];
+                                               }
+                                       }
+                               }
+                       }
+               return res;
+       }
+
+       public synchronized Persistent decode( ByteBuffer buf )
+       {
+               Persistent      p;
+               int                     pos,size,origID,id;
+
+               assert(neededToDecode(buf,buf.position(),buf.limit())==0);
+
+               pos=buf.position();
+               size=buf.getShort(pos) & 0x0000ffff;
+               id=origID=buf.getShort(pos+2) & 0x0000ffff;
+
+               p=null;
+               try {
+                       id=((id<classes.length && classes[id]!=null) ? id : 
defaultID);
+                       if (id>=0) {
+                               p=decode(buf,pos,size,id);
+                               if (p==null && corruptedID>=0) {
+                                       buf.position(pos);
+                                       p=decode(buf,pos,size,corruptedID);
+                                       }
+                               }
+
+                       if (p==null) {
+                               log(Level.WARNING,"Unable to decode data (id 
#"+origID+"), skip "+size+" bytes.");
+                               }
+                       }
+               finally {
+                       buf.position(pos+size);
+                       }
+               return p;
+       }
+
+       protected Persistent decode( ByteBuffer buf, int pos, int size, int id )
+       {
+               Persistent      p;
+
+               p=null;
+               if (classes[id]!=null) {
+                       try {
+                               p=(Persistent) classes[id].newInstance();
+                               p.readBytes(buf,new ErrorReporter());
+                               if (buf.position()==pos+size && 
p.getByteSize()==size) {
+                                       log(Level.FINEST,"Decoded "+size+" 
bytes with id #"+id+" : "+p+".");
+                                       }
+                               else {
+                                       log(Level.SEVERE,"Wrong advertised size 
(delta: "+(pos+size-buf.position())+", id #"+id+", 
\""+classes[id].getName()+"\") !");
+                                       p=null;
+                                       }
+                               }
+                       catch( BufferUnderflowException x ) {
+                               err("Buffer underflow (id #"+id+", 
\""+classes[id].getName()+"\") !",x);
+                               p=null;
+                               }
+                       catch( IllegalStateException x ) {
+                               err("Corrupted data (msg: "+x.getMessage()+", 
id #"+id+", \""+classes[id].getName()+"\") !",x);
+                               p=null;
+                               }
+                       catch( Throwable x ) {
+                               err("Unexpected error (id #"+id+", 
\""+classes[id].getName()+"\") !",x);
+                               p=null;
+                               }
+                       }
+               return p;
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       /**
+        * Returns the number of bytes needed to decode the first message 
present at position <code>from</code>.
+        *
+        * @param buf
+        * @param from
+        * @param to
+        * @return
+        */
+
+       public static int neededToDecode( ByteBuffer buf, int from, int to )
+       {
+               int     req;
+
+               req=requestedToDecode(buf,from,to);
+               if (from+req>to) {
+                       return from+req-to;
+                       }
+               return 0;
+       }
+
+       public static int requestedToDecode( ByteBuffer buf, int from, int to )
+       {
+               if ((to-from)<2) {
+                       return 2;
+                       }
+               return buf.getShort(from) & 0x0000ffff;
+       }
+}

Added: freeway/src/org/gnu/freeway/util/net/PersistentHelper.java
===================================================================
--- freeway/src/org/gnu/freeway/util/net/PersistentHelper.java  2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/util/net/PersistentHelper.java  2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,186 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util.net;
+
+import java.nio.*;
+import java.util.*;
+import java.util.logging.*;
+
+/**
+ *
+ */
+
+public class PersistentHelper extends Object
+{
+       private static Logger   logger;
+
+       static {
+               logger=Logger.getLogger(PersistentHelper.class.getName());
+               }
+
+
+       public static Persistent read( Class c, ByteBuffer buf )
+       {
+               Persistent      p;
+               int                     pos;
+
+               p=null;
+               if (Persistent.class.isAssignableFrom(c)) {
+                       pos=buf.position();
+                       try {
+                               p=(Persistent) c.newInstance();
+                               p.readBytes(buf,new ErrorReporter());
+
+                               if (buf.position()!=pos+p.getByteSize()) {
+                                       logger.log(Level.SEVERE,"Advertised 
size does not match reality ("+c.getName()+") !");
+                                       buf.position(pos);
+                                       p=null;
+                                       }
+                               }
+                       catch( BufferUnderflowException x ) {
+                               logger.log(Level.SEVERE,"Buffer underflow 
("+c.getName()+") !",x);
+                               buf.position(pos);
+                               p=null;
+                               }
+                       catch( IllegalStateException x ) {
+                               logger.log(Level.SEVERE,"Corrupted data 
("+x.getMessage()+") !",x);
+                               buf.position(pos);
+                               p=null;
+                               }
+                       catch( InstantiationException x ) {
+                               logger.log(Level.SEVERE,"Could not create 
instance of "+c.getName()+".",x);
+                               }
+                       catch( IllegalAccessException x ) {
+                               logger.log(Level.SEVERE,"Could not create 
instance of "+c.getName()+".",x);
+                               }
+                       }
+               else {
+                       logger.log(Level.SEVERE,"Instances of "+c.getName()+" 
are not persistent.");
+                       }
+               return p;
+       }
+
+       public static Persistent readFully( Class c, ByteBuffer buf )
+       {
+               Persistent      p;
+
+               p=read(c,buf);
+               if (p!=null && buf.remaining()>0) {
+                       logger.log(Level.SEVERE,"Buffer larger than expected 
("+c.getName()+").");
+                       p=null;
+                       }
+               return p;
+       }
+
+       public static boolean write( Persistent p, ByteBuffer buf )
+       {
+               int             pos;
+               boolean success;
+
+               success=false;
+               if (buf.remaining()>=p.getByteSize()) {
+                       pos=buf.position();
+                       try {
+                               p.writeBytes(buf);
+                               if (buf.position()==pos+p.getByteSize()) {
+                                       success=true;
+                                       }
+                               else {
+                                       logger.log(Level.SEVERE,"Advertised 
size does not match reality ("+p.getClass().getName()+") !");
+                                       buf.position(pos);
+                                       }
+                               }
+                       catch( BufferOverflowException x ) {
+                               logger.log(Level.SEVERE,"Buffer overflow 
("+p.getClass().getName()+") !",x);
+                               buf.position(pos);
+                               }
+                       }
+               return success;
+       }
+
+       public static boolean writeFully( Persistent p, ByteBuffer buf )
+       {
+               boolean res;
+
+               res=write(p,buf);
+               if (res && buf.remaining()>0) {
+                       logger.log(Level.SEVERE,"Buffer larger than expected 
("+p.getClass().getName()+").");
+                       res=false;
+                       }
+               return res;
+       }
+
+       public static Persistent copy( Persistent p )
+       {
+               ByteBuffer      buf;
+
+               if (p!=null) {
+                       buf=ByteBuffer.allocate(p.getByteSize());
+                       if (writeFully(p,buf)) {
+                               buf.flip();
+                               p=readFully(p.getClass(),buf);
+                               }
+                       else {
+                               p=null;
+                               }
+                       }
+               return p;
+       }
+
+       public static ByteBuffer toBuffer( Persistent p )
+       {
+               ByteBuffer      buf;
+
+               buf=ByteBuffer.allocateDirect(p.getByteSize());
+               if (writeFully(p,buf)) {
+                       buf.flip();
+                       }
+               else {
+                       buf=null;
+                       }
+               return buf;
+       }
+
+       public static byte[] toBytes( Persistent p )
+       {
+               byte[]  b;
+
+               b=new byte[p.getByteSize()];
+               if (!writeFully(p,ByteBuffer.wrap(b))) {
+                       b=null;
+                       }
+               return b;
+       }
+
+       public static byte[] toBytes( List list )
+       {
+               return toBytes((Persistent[]) list.toArray(new 
Persistent[list.size()]));
+       }
+
+       public static byte[] toBytes( Persistent[] p )
+       {
+               byte[]          b;
+               ByteBuffer      buf;
+               int                     len,i;
+
+               len=0;
+               for (i=0; i<p.length; i++) {
+                       len+=p[i].getByteSize();
+                       }
+
+               b=new byte[len];
+               buf=ByteBuffer.wrap(b);
+               for (i=0; i<p.length; i++) {
+                       if (!write(p[i],buf)) {
+                               return null;
+                               }
+                       }
+               if (buf.remaining()>0) {
+                       logger.log(Level.SEVERE,"Buffer larger than expected.");
+                       return null;
+                       }
+               return b;
+       }
+}

Added: freeway/src/org/gnu/freeway/util/net/PersistentReader.java
===================================================================
--- freeway/src/org/gnu/freeway/util/net/PersistentReader.java  2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/util/net/PersistentReader.java  2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,209 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util.net;
+
+import org.gnu.freeway.util.*;
+
+import java.io.*;
+import java.nio.*;
+import java.nio.channels.*;
+import java.util.*;
+import java.util.logging.*;
+
+/**
+ * A reader for persistent data.
+ */
+
+public class PersistentReader extends LoggedObject
+{
+       private static final int                DEFAULT_SIZE    =       32768;
+       private static final int                MAX_SIZE                =       
DEFAULT_SIZE*8;
+       private static final boolean    DUMP                    =       false;
+
+       private ByteBuffer      buffer;
+       private LinkedList      queue;
+
+
+       public PersistentReader()
+       {
+               this(DEFAULT_SIZE);
+       }
+
+       public PersistentReader( int size )
+       {
+               this(ByteBuffer.allocateDirect(size));
+       }
+
+       public PersistentReader( ByteBuffer src )
+       {
+               super(true);
+               buffer=src;
+               queue=new LinkedList();
+       }
+
+       public String toString()
+       {
+               return "Persistent reader";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public boolean canConsume()
+       {
+               return (queue.size()>0 || 
PersistentDecoder.neededToDecode(buffer,0,buffer.position())==0);
+       }
+
+       public Persistent consume( Class c )
+       {
+               Persistent      p;
+               int                     needed;
+
+               if (queue.size()==0 && buffer.position()>0) {
+                       
needed=PersistentDecoder.neededToDecode(buffer,0,buffer.position());
+                       if (needed==0) {
+                               buffer.flip();
+                               try {
+                                       p=PersistentHelper.read(c,buffer);
+                                       if (p!=null) {
+                                               queue.add(p);
+                                               }
+                                       }
+                               finally {
+                                       buffer.compact();
+                                       }
+                               }
+                       else {
+                               grow(needed);
+                               }
+                       }
+
+               p=(queue.size()>0 ? (Persistent) queue.removeFirst() : null);
+               return (c.isInstance(p) ? p : null);
+       }
+
+       public Persistent consume( PersistentDecoder dec )
+       {
+               Persistent      p;
+               int                     needed;
+
+               if (queue.size()==0 && buffer.position()>0) {
+                       
needed=PersistentDecoder.neededToDecode(buffer,0,buffer.position());
+                       if (needed==0) {
+                               buffer.flip();
+                               try {
+                                       p=dec.decode(buffer);
+                                       if (p!=null) {
+                                               queue.add(p);
+                                               }
+                                       }
+                               finally {
+                                       buffer.compact();
+                                       }
+                               }
+                       else {
+                               grow(needed);
+                               }
+                       }
+
+               p=(queue.size()>0 ? (Persistent) queue.removeFirst() : null);
+               return p;
+       }
+
+       protected void growIfNeeded()
+       {
+               int     needed;
+
+               
needed=PersistentDecoder.neededToDecode(buffer,0,buffer.position());
+               if (needed>0) {
+                       grow(needed);
+                       }
+       }
+
+       protected void grow( int needed )
+       {
+               needed+=buffer.position();
+               if (needed>buffer.capacity()) {
+                       if (needed<MAX_SIZE) {
+                               buffer=Utils.resize(buffer,needed);
+                               }
+                       else {
+                               log(Level.WARNING,"Data to be read exceeds max. 
buffer size ("+needed+">="+MAX_SIZE+"), discard it.");
+                               //fixme: implement discard of n bytes...
+                               }
+                       }
+       }
+
+       public int getNotConsumed()
+       {
+               Persistent      p;
+               int                     size,i;
+
+               size=0;
+               for (i=0; i<queue.size(); i++) {
+                       p=(Persistent) queue.get(i);
+                       size+=p.getByteSize();
+                       }
+               size+=buffer.position();
+               return size;
+       }
+
+       public int feed( byte[] buf )
+       {
+               return feed(buf,0,buf.length);
+       }
+
+       public int feed( byte[] buf, int offset, int length )
+       {
+               int     ret;
+
+               growIfNeeded();
+
+               ret=Math.min(length,buffer.remaining());
+               if (ret>0) {
+                       buffer.put(buf,offset,ret);
+                       }
+               return ret;
+       }
+
+       public int feed( ReadableByteChannel c )
+       {
+               int     ret,pos;
+
+               growIfNeeded();
+
+               ret=0;
+               try {
+                       pos=buffer.position();
+
+                       ret=c.read(buffer);
+                       if (ret==-1) {
+                               return ret;
+                               }
+
+                       if (ret>0 && DUMP) {
+                               Utils.dump(buffer,pos,pos+ret,"DUMP READ");
+                               }
+                       }
+               catch( IOException x ) {
+                       err("Could not read bytes !",x);
+                       ret=-1;
+                       }
+               return ret;
+       }
+
+       public void clear()
+       {
+               int     n;
+
+               n=getNotConsumed();
+               if (n>0) {
+                       log(Level.WARNING,"Have not consumed "+n+" bytes, 
discard them.");
+                       }
+
+               queue.clear();
+               buffer.clear();
+       }
+}

Added: freeway/src/org/gnu/freeway/util/net/PersistentSocket.java
===================================================================
--- freeway/src/org/gnu/freeway/util/net/PersistentSocket.java  2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/util/net/PersistentSocket.java  2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,350 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util.net;
+
+import org.gnu.freeway.util.*;
+
+import java.io.*;
+import java.net.*;
+import java.nio.channels.*;
+import java.util.logging.*;
+
+/**
+ *
+ */
+
+public class PersistentSocket extends LoggedObject
+{
+       /** Connection timeout in scheduler unit. */
+       private static final long               CONNECT_TIMEOUT =       
Scheduler.SECS_5;
+
+       /** Read timeout in scheduler unit. */
+       private static final long               READ_TIMEOUT            =       
Scheduler.SECS_3;
+
+       /** */
+       private String                  label;
+
+       /** The TCP socket channel. */
+       private SocketChannel           channel;
+
+       /** */
+       private boolean                 blocking;
+
+       /** Read buffer. */
+       private PersistentReader        reader;
+
+       /** Write buffer. */
+       private PersistentWriter        writer;
+
+
+       public PersistentSocket()
+       {
+               this(4096);
+       }
+
+       public PersistentSocket( int size )
+       {
+               super(true);
+               label=NetUtils.labelFor(null);
+               channel=null;
+               blocking=false;
+               reader=new PersistentReader(size);
+               writer=new PersistentWriter(size);
+       }
+
+       public String toString()
+       {
+               return "Persistent socket [label="+label+", 
blocking="+blocking+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public String getLabel()
+       {
+               return label;
+       }
+
+       public SocketChannel getChannel()
+       {
+               return channel;
+       }
+
+       public boolean open( InetAddress ip, int port, boolean careful )
+       {
+               if (channel!=null) {
+                       log(Level.WARNING,"Channel is already connected.");
+                       return false;
+                       }
+
+               try {
+                       channel=SocketChannel.open();
+                       channel.socket().setSoTimeout((int) 
Scheduler.toMillis(READ_TIMEOUT));
+
+                       if (!careful) {
+                               // send closed status immediately, do not 
attempt to recover pending data
+                               // => got IOException instead of -1 when 
reading at the other side...
+                               channel.socket().setSoLinger(true,0);
+                               }
+
+                       channel.configureBlocking(true);
+
+                       // we call select() first with a timeout of 
CONNECT_TIMEOUT to avoid blocking on a later write indefinitely;
+                       // this is mostly needed for gnunet-testbed to keep 
working if an advertised testbed-client is behind
+                       // a firewall and unreachable.  But it is also nice if 
a local firewall decides to just drop the TCP handshake...
+                       if 
(!NetUtils.connectWithTimeout(channel,ip,port,CONNECT_TIMEOUT)) {
+                               try {
+                                       channel.close();
+                                       }
+                               catch( IOException xx ) {
+                                       err("Failed to close channel 
("+channel+") !",xx);
+                                       }
+                               return false;
+                               }
+
+                       label=NetUtils.labelFor(channel);
+                       blocking=true;
+                       debug(Level.INFO,label+" Connected.");
+                       return true;
+                       }
+               catch( ConnectException x ) {
+                       // do not log full stack trace for common problem
+                       log(Level.SEVERE,"Unable to connect to 
"+ip.getHostAddress()+":"+port+" ("+x.getMessage()+") !");
+                       channel=null;
+                       }
+               catch( IOException x ) {
+                       // log full stack trace
+                       err("Could not setup channel to 
"+ip.getHostAddress()+":"+port+" !",x);
+                       if (channel!=null) {
+                               try {
+                                       channel.close();
+                                       }
+                               catch( IOException xx ) {
+                                       err("Failed to close channel 
("+channel+") !",xx);
+                                       }
+                               channel=null;
+                               }
+                       }
+               return false;
+       }
+
+       protected boolean open( SocketChannel c, boolean careful )
+       {
+               if (channel!=null) {
+                       log(Level.WARNING,"Channel is already connected.");
+                       return false;
+                       }
+
+               try {
+                       channel=c;
+                       channel.socket().setSoTimeout((int) 
Scheduler.toMillis(READ_TIMEOUT));
+
+                       // do *not* set SO_LINGER for local clients app
+                       if (!careful) {
+                               // send closed status immediately, do not 
attempt to recover pending data
+                               // => got IOException instead of -1 when 
reading at the other side...
+                               channel.socket().setSoLinger(true,0);
+                               }
+                       channel.configureBlocking(false);
+
+                       label=NetUtils.labelFor(channel);
+                       blocking=false;
+                       debug(Level.INFO,label+" Connected.");
+                       return true;
+                       }
+               catch( IOException x ) {
+                       err("Could not connect to channel ("+channel+") !",x);
+                       if (channel!=null) {
+                               try {
+                                       channel.close();
+                                       }
+                               catch( IOException xx ) {
+                                       err("Failed to close channel 
("+channel+") !",xx);
+                                       }
+                               channel=null;
+                               }
+                       }
+               return false;
+       }
+
+       public boolean close()
+       {
+               if (channel==null) {
+                       log(Level.WARNING,"Channel is not connected.");
+                       return false;
+                       }
+
+               if (channel.isOpen()) {
+                       try {
+                               try {
+                                       // needed when another thread blocked 
on a read operation (in blocking mode)
+                                       channel.socket().shutdownOutput();
+                                       }
+                               catch( IOException xx ) {
+                                       err("Could not shutdown output !",xx);
+                                       }
+
+                               try {
+                                       channel.socket().shutdownInput();
+                                       }
+                               catch( IOException xx ) {
+                                       err("Could not shutdown input !",xx);
+                                       }
+
+                               channel.close();
+                               debug(Level.FINE,label+" Closed.");
+                               }
+                       catch( IOException x ) {
+                               err(label+" Got error while shutting down !",x);
+                               }
+                       }
+               channel=null;
+               label=NetUtils.labelFor(null);
+
+               reader.clear();
+               writer.clear();
+               return true;
+       }
+
+       public boolean isClosed()
+       {
+               return channel==null;
+       }
+
+       /**
+        * Check whether the socket is blocking
+        *
+        * @return true if blocking, false non-blocking
+        */
+
+       public boolean isBlocking()
+       {
+               return blocking;
+       }
+
+       /**
+        * Depending on <code>flag</code>, enable or disable the nonblocking 
mode of socket.
+        *
+        * @param flag
+        * @return              Upon successful completion, it returns true, 
otherwise false.
+        */
+
+       public boolean setBlocking( boolean flag )
+       {
+               if (blocking==flag) {
+                       return true;
+                       }
+
+               blocking=flag;
+
+               if (channel!=null) {
+                       try {
+                               channel.configureBlocking(flag);
+                               }
+                       catch( IOException x ) {
+                               err(label+" Could not configure blocking !",x);
+                               return false;
+                               }
+                       }
+               return true;
+       }
+
+       /**
+        * Read data from channel to buffer.
+        *
+        * @return The number of bytes read, or -1 if failed.
+        */
+
+       public int doRead()
+       {
+               int     ret;
+
+               if (channel==null) {
+                       log(Level.WARNING,"Channel is not connected.");
+                       return -1;
+                       }
+
+               ret=reader.feed(channel);
+               if (ret>0) {
+                       debug(label+" Have read "+ret+" bytes 
("+reader.getNotConsumed()+" not consumed).");
+                       }
+               else if (ret==-1) {
+                       debug(Level.INFO,label+" Socket has been closed.");
+                       }
+               return ret;
+       }
+
+       public boolean shouldDequeue()
+       {
+               if (channel==null) {
+                       log(Level.WARNING,"Channel is not connected.");
+                       return false;
+                       }
+
+               return reader.canConsume();
+       }
+
+       public Persistent dequeue( Class c )
+       {
+               if (channel==null) {
+                       log(Level.WARNING,"Channel is not connected.");
+                       return null;
+                       }
+
+               return reader.consume(c);
+       }
+
+       public Persistent dequeue( PersistentDecoder decoder )
+       {
+               if (channel==null) {
+                       log(Level.WARNING,"Channel is not connected.");
+                       return null;
+                       }
+
+               return reader.consume(decoder);
+       }
+
+       public void enqueue( Persistent p )
+       {
+               if (channel==null) {
+                       log(Level.WARNING,"Channel is not connected.");
+                       return;
+                       }
+
+               writer.enqueue(p);
+       }
+
+       public boolean shouldWrite()
+       {
+               if (channel==null) {
+                       log(Level.WARNING,"Channel is not connected.");
+                       return false;
+                       }
+
+               return writer.getNotWritten()>0;
+       }
+
+       /**
+        * Write data in buffer to channel.
+        *
+        * @return The number of bytes written, or -1 if failed.
+        */
+
+       public int doWrite()
+       {
+               int     ret;
+
+               if (channel==null) {
+                       log(Level.WARNING,"Channel is not connected.");
+                       return -1;
+                       }
+
+               ret=writer.writeTo(channel);
+               if (ret>0) {
+                       debug(label+" Have written "+ret+" bytes 
("+writer.getNotWritten()+" remaining).");
+                       }
+               return ret;
+       }
+}

Added: freeway/src/org/gnu/freeway/util/net/PersistentWriter.java
===================================================================
--- freeway/src/org/gnu/freeway/util/net/PersistentWriter.java  2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/util/net/PersistentWriter.java  2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,171 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util.net;
+
+import org.gnu.freeway.util.*;
+
+import java.io.*;
+import java.nio.*;
+import java.nio.channels.*;
+import java.util.*;
+import java.util.logging.*;
+
+/**
+ * A writer for persistent data.
+ */
+
+public class PersistentWriter extends LoggedObject
+{
+       private static final int                DEFAULT_SIZE    =       32768;
+       private static final int                MAX_SIZE                =       
DEFAULT_SIZE*8;
+       private static final boolean    DUMP                    =       false;
+
+       private ByteBuffer      buffer;
+       private LinkedList      queue;
+
+
+       public PersistentWriter()
+       {
+               this(DEFAULT_SIZE);
+       }
+
+       public PersistentWriter( int size )
+       {
+               super(true);
+               buffer=ByteBuffer.allocateDirect(size);
+               queue=new LinkedList();
+       }
+
+       public String toString()
+       {
+               return "Persistent writer";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public void enqueue( Persistent p )
+       {
+               queue.add(p);
+       }
+
+       public boolean needToBeWritten()
+       {
+               return getNotWritten()>0;
+       }
+
+       public int getNotWritten()
+       {
+               Persistent      p;
+               int                     size,i;
+
+               size=0;
+               for (i=0; i<queue.size(); i++) {
+                       p=(Persistent) queue.get(i);
+                       size+=p.getByteSize();
+                       }
+               size+=buffer.position();
+               return size;
+       }
+
+       public int writeTo( WritableByteChannel c )
+       {
+               int     ret,pos;
+
+               encodeQueued();
+               if (buffer.position()==0) {
+                       log(Level.WARNING,"No messages are pending !");
+                       return 0;
+                       }
+
+               ret=0;
+               buffer.flip();
+               try {
+                       pos=buffer.position();
+
+                       ret=c.write(buffer);
+                       if (ret>0) {
+                               if (DUMP) {
+                                       Utils.dump(buffer,pos,pos+ret,"DUMP 
WRITE");
+                                       }
+                               }
+                       }
+               catch( IOException x ) {
+                       err("Could not write bytes !",x);
+                       ret=-1;
+                       }
+               finally {
+                       buffer.compact();
+                       }
+               return ret;
+       }
+
+       public void clear()
+       {
+               if (getNotWritten()>0) {
+                       log(Level.WARNING,"Have not written "+getNotWritten()+" 
bytes, discard them.");
+                       }
+
+               buffer.clear();
+               queue.clear();
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       protected void encodeQueued()
+       {
+               Persistent      p;
+               int                     needed;
+
+               needed=0;
+               while (queue.size()>0 && needed==0) {
+                       p=(Persistent) queue.getFirst();
+                       if (p.getByteSize()<=buffer.remaining()) {
+                               encode(p);
+                               queue.removeFirst();
+                               }
+                       else {
+                               needed=p.getByteSize()-buffer.remaining();
+                               }
+                       }
+
+               needed+=buffer.position();
+               if (needed>buffer.capacity()) {
+                       if (needed<MAX_SIZE) {
+                               buffer=Utils.resize(buffer,needed);
+                               }
+                       else {
+                               log(Level.WARNING,"Data to be written exceeds 
max. buffer size ("+needed+">="+MAX_SIZE+"), discard it.");
+                               queue.removeFirst();
+                               }
+                       }
+       }
+
+       protected boolean encode( Persistent p )
+       {
+               int     pos;
+
+               pos=buffer.position();
+               buffer.limit(pos+p.getByteSize());      // ensure no possible 
overflow...
+               try {
+                       p.writeBytes(buffer);
+                       if (buffer.position()-pos!=p.getByteSize()) {   // 
check if advertised size was correct
+                               log(Level.WARNING,"Bad advertised size, skip 
data.");
+                               buffer.position(pos);
+                               return false;
+                               }
+                       }
+               catch( BufferOverflowException x ) {
+                       err("Buffer overflow, skip data !",x);
+                       buffer.position(pos);
+                       return false;
+                       }
+               finally {
+                       buffer.limit(buffer.capacity());
+                       }
+               return true;
+       }
+}

Added: freeway/src/org/gnu/freeway/util/net/TCPServer.java
===================================================================
--- freeway/src/org/gnu/freeway/util/net/TCPServer.java 2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/util/net/TCPServer.java 2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,648 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util.net;
+
+import org.gnu.freeway.util.*;
+
+import java.io.*;
+import java.net.*;
+import java.nio.channels.*;
+import java.util.*;
+import java.util.logging.*;
+
+/**
+ *
+ */
+
+public class TCPServer extends LoggedObject implements CSServer
+{
+       /** */
+       private static final long               SELECT_TIMEOUT          =       
Scheduler.SECS_3;
+
+       /** Maximum of pending unhandled connections. */
+       private static final int                MAX_QUEUED_REQUESTS     =       
5;
+
+       /** Maximum number of concurrent allowed sessions. */
+       private static final int                MAX_SESSIONS                    
=       64;
+
+       /** Name of this server (for debugging purpose only). */
+       private String                          label;
+
+       /** Selector of the server thread */
+       private Selector                                selector;
+
+       /** The TCP socket that we listen on for new inbound connections. */
+       private ServerSocketChannel     server;
+
+       /** Thread for listening for new connections. */
+       private MasterTask                      listenTask;
+
+       /** Thread for accepting new connections. */
+       private SlaveTask                       acceptTask;
+
+       /** Thread for reading on all open sockets. */
+       private SlaveTask                       readTask;
+
+       /** Thread for writing on all open sockets. */
+       private SlaveTask                       writeTask;
+
+       /** Should the listen thread exit ? */
+       private boolean                         running;
+
+       /** Array of currently active TCP sessions. */
+       private CSSession[]                     sessions;
+
+       /** */
+       private int                                     sessionCount;
+
+       /** Sessions' current operations. */
+       private int[]                           sessionsOps;
+
+       /** */
+       private boolean                         acceptingOp;
+
+       /** Sessions lock. */
+       private Object                          internal;
+
+       /** */
+       private CSSessionHandler                handler;
+
+
+       public TCPServer( String str, CSSessionHandler h )
+       {
+               super(true);
+               label=str;
+               selector=null;
+               server=null;
+               listenTask=new MasterTask("TCP-LISTEN("+str+")",new 
EvalAction(this,"performListen"));
+               acceptTask=listenTask.create("TCP-ACCEPT("+str+")",new 
EvalAction(this,"performAccept"));
+               readTask=listenTask.create("TCP-READ("+str+")",new 
EvalAction(this,"performRead"));
+               writeTask=listenTask.create("TCP-WRITE("+str+")",new 
EvalAction(this,"performWrite"));
+               running=false;
+               sessions=new CSSession[0];
+               sessionCount=0;
+               sessionsOps=new int[0];
+               acceptingOp=false;
+               internal=new Object();
+               handler=h;
+       }
+
+       public String toString()
+       {
+               return "Abstract TCP server";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public String getLabel()
+       {
+               return label;
+       }
+
+       public boolean isLaunched()
+       {
+               return running;
+       }
+
+       public boolean launch( int port )
+       {
+               int     secs;
+
+               log(Level.INFO,label+" Launching TCP server...");
+
+               // open selector
+               try {
+                       selector=Selector.open();
+                       }
+               catch( IOException x ) {
+                       err("Could not create selector !",x);
+                       return false;
+                       }
+
+               // create server socket
+               secs=5;
+               while (server==null && secs<60) {
+                       try {
+                               server=ServerSocketChannel.open();
+                               server.configureBlocking(false);
+                               server.socket().setReuseAddress(true);
+                               server.socket().bind(new 
InetSocketAddress(port),MAX_QUEUED_REQUESTS);
+                               log(Level.INFO,label+" TCP server bound to port 
"+port+".");
+                               }
+                       catch( IOException x ) {
+                               err("Failed to open socket at port "+port+". 
Trying again in "+secs+" seconds...",x);
+
+                               Scheduler.sleep(Scheduler.seconds(secs));
+                               secs+=5;        // slow progression...
+
+                               if (server!=null) {
+                                       try {
+                                               server.close();
+                                               }
+                                       catch( IOException xx ) {
+                                               }
+                                       server=null;
+                                       }
+                               }
+                       }
+
+               if (server==null) {
+                       log(Level.SEVERE,label+" Could not create socket, 
abort.");
+                       try {
+                               selector.close();
+                               }
+                       catch( IOException x ) {
+                               }
+                       selector=null;
+                       return false;
+                       }
+
+               // start listening thread
+               running=true;
+               listenTask.launch();
+               return true;
+       }
+
+       public boolean shutdown()
+       {
+               int     i;
+
+               // signal listening thread
+               running=false;
+               selector.wakeup();
+
+               // stop listening thread
+               listenTask.shutdown();
+
+               try {
+                       server.close();
+                       }
+               catch( IOException x ) {
+                       err("Failed to close socket !",x);
+                       return false;
+                       }
+               finally {
+                       server=null;
+                       try {
+                               selector.close();
+                               }
+                       catch( IOException x ) {
+                               err("Failed to close selector !",x);
+                               return false;
+                               }
+                       finally {
+                               selector=null;
+                               }
+                       }
+               log(Level.INFO,label+" TCP server stopped.");
+
+               synchronized(internal) {
+                       for (i=0; i<sessions.length; i++) {
+                               if (sessions[i]!=null) {
+                                       log(Level.WARNING,label+" Session still 
alive : "+sessions[i].getLabel());
+                                       destroySession(i);
+                                       }
+                               }
+                       }
+               return true;
+       }
+
+       public void wakeUp()
+       {
+               selector.wakeup();
+       }
+
+       /**
+        * Add session to the pool of listened sessions. If it can't be added, 
session will be disconnected and false returned.
+        *
+        * @param s
+        * @return
+        * @see CSSession#disconnect()
+        */
+
+       public boolean register( CSSession s )
+       {
+               if (addSession(s)>=0) {
+                       // signal the thread that is blocked in a select call 
that the set of sockets to listen to has changed
+                       selector.wakeup();
+                       return true;
+                       }
+               return false;
+       }
+
+       /**
+        * Listen for incoming connections.
+        * Main method for the thread listening on the tcp socket and all tcp 
connections.
+        * Whenever a message is received, it is processed by the handler.
+        * This thread waits for activity on any of the TCP connections and 
processes deferred (async) writes and buffers reads
+        * until an entire message has been received.
+        *
+        * @throws IOException
+        */
+
+       public void performListen() throws IOException
+       {
+               SelectionKey    key;
+               Iterator                iter;
+               int                     mergedOps,ops,ret,i;
+
+               acceptingOp=true;
+
+               while (running) {
+                       synchronized(internal) {
+                               server.register(selector,(acceptingOp ? 
SelectionKey.OP_ACCEPT : 0));
+
+                               for (i=0; i<sessions.length; i++) {
+                                       if (sessions[i]!=null) {
+                                               if (sessions[i].isConnected()) 
{        // always check because impl. may disconnect after timeout...
+                                                       
key=sessions[i].registerOps(selector,(sessions[i].getOps() & ~sessionsOps[i]));
+                                                       if (key!=null) {
+                                                               key.attach(new 
Integer(i));
+                                                               }
+                                                       else {
+                                                               
destroySession(i);
+                                                               }
+                                                       }
+                                               else {
+                                                       // clean up (depends on 
session implementation : timeout detected, other side closed connection...)
+                                                       destroySession(i);
+                                                       }
+                                               }
+                                       }
+                               }
+
+                       // should wake up regularly (to clean up sessions...)
+                       ret=selector.select(Scheduler.toMillis(SELECT_TIMEOUT));
+                       if (ret==0) {
+                               continue;
+                               }
+
+                       synchronized(internal) {
+                               mergedOps=0;
+
+                               iter=selector.selectedKeys().iterator();
+                               while (iter.hasNext()) {
+                                       key=(SelectionKey) iter.next();
+                                       iter.remove();
+
+                                       if (key.isValid()) {
+                                               ops=key.readyOps();
+                                               mergedOps|=ops;
+                                               if ((ops & 
SelectionKey.OP_ACCEPT)==0) {        // read or write op
+                                                       i=((Number) 
key.attachment()).intValue();
+                                                       sessionsOps[i]|=ops;
+                                                       }
+                                               }
+                                       }
+
+                               debug(label+" Selected #"+ret+" sockets with 
merged ops { "+NetUtils.labelForOps(mergedOps)+" }.");
+
+                               // signal appropriate tasks
+                               if ((mergedOps & SelectionKey.OP_ACCEPT)!=0) {
+                                       acceptingOp=false;
+                                       acceptTask.signal();
+                                       }
+                               if ((mergedOps & SelectionKey.OP_READ)!=0) {
+                                       readTask.signal();
+                                       }
+                               if ((mergedOps & SelectionKey.OP_WRITE)!=0) {
+                                       writeTask.signal();
+                                       }
+                               }
+                       }
+
+               // shutdown... close all sessions
+               synchronized(internal) {
+                       for (i=0; i<sessions.length; i++) {
+                               if (sessions[i]!=null) {
+                                       destroySession(i);
+                                       }
+                               }
+                       }
+       }
+
+       public void performAccept()
+       {
+               CSSession               s;
+               SocketChannel   c;
+
+               try {
+                       for (c=server.accept(); c!=null; c=server.accept()) {
+                               s=handler.handleAccept(c);
+                               if (s!=null) {
+                                       if (addSession(s)<0) {
+                                               s.disconnect();
+                                               }
+                                       }
+                               else {
+                                       try {
+                                               c.close();
+                                               }
+                                       catch( IOException xx ) {
+                                               err("Failed to close channel 
!",xx);
+                                               }
+                                       }
+                               }
+                       }
+               catch( IOException x ) {
+                       err("Failed to accept new connection !",x);
+                       }
+
+               synchronized(internal) {
+                       acceptingOp=true;
+                       }
+
+               selector.wakeup();
+       }
+
+       public void performRead()
+       {
+               CSSession       s;
+               int                     len;
+
+               do {
+                       s=firstSessionWithOp(SelectionKey.OP_READ);
+                       if (s!=null) {
+                               len=s.doReceive();
+                               if (len>0 && handler.handleRead(s,len)) {
+                                       clearSessionOp(s,SelectionKey.OP_READ);
+                                       }
+                               else {
+                                       debug(s.getLabel()+" End of stream.");
+                                       destroySession(s);
+                                       }
+                               }
+                       }
+               while (s!=null);
+
+               // signal the thread that is blocked in a select call that the 
set of sockets to listen to has changed
+               selector.wakeup();
+       }
+
+       public void performWrite()
+       {
+               CSSession       s;
+               int                     len;
+
+               do {
+                       s=firstSessionWithOp(SelectionKey.OP_WRITE);
+                       if (s!=null) {
+                               len=s.doSend();
+                               if (len>0 && handler.handleWrite(s,len)) {
+                                       clearSessionOp(s,SelectionKey.OP_WRITE);
+                                       }
+                               else {
+                                       debug(s.getLabel()+" End of stream.");
+                                       destroySession(s);
+                                       }
+                               }
+                       }
+               while (s!=null);
+
+               // signal the thread that is blocked in a select call that the 
set of sockets to listen to has changed
+               selector.wakeup();
+       }
+
+       /**
+        * Add a new session to the array watched by the select thread. Grows 
the array if needed.
+        *
+        * @param s     Session to add.
+        * @return      Index of added session, or -1 on error.
+        */
+
+       protected int addSession( CSSession s )
+       {
+               CSSession[]     tmp;
+               int[]           tmp2;
+               int                     i;
+
+               synchronized(internal) {
+                       if (sessionCount==MAX_SESSIONS) {
+                               log(Level.WARNING,"Too many sessions 
("+MAX_SESSIONS+"), ignore connection.");
+                               return -1;
+                               }
+
+                       for (i=0; i<sessions.length && sessions[i]!=null; i++) 
{}
+                       if (i==sessions.length) {
+                               tmp=new CSSession[sessions.length+16];
+                               Arrays.fill(tmp,null);
+                               
System.arraycopy(sessions,0,tmp,0,sessions.length);
+                               sessions=tmp;
+
+                               tmp2=new int[sessionsOps.length+16];
+                               Arrays.fill(tmp2,0);
+                               
System.arraycopy(sessionsOps,0,tmp2,0,sessionsOps.length);
+                               sessionsOps=tmp2;
+                               }
+
+                       sessions[i]=s;
+                       sessionsOps[i]=0;
+                       sessionCount++;
+                       debug("Add session at slot #"+i+" 
"+Utils.gauge(sessionCount,sessions.length)+".");
+                       return i;
+                       }
+       }
+
+       protected CSSession firstSessionWithOp( int op )
+       {
+               int     i;
+
+               synchronized(internal) {
+                       for (i=0; i<sessionsOps.length && (sessionsOps[i] & 
op)==0; i++) {}
+                       return (i<sessionsOps.length ? sessions[i] : null);
+                       }
+       }
+
+       protected boolean clearSessionOp( CSSession s, int op )
+       {
+               int     i;
+
+               assert(s!=null);
+
+               synchronized(internal) {
+                       for (i=0; i<sessions.length && sessions[i]!=s; i++) {}
+                       if (i==sessions.length) {
+                               log(Level.WARNING,label+" Session not found : 
"+s.getLabel()+".");
+                               return false;
+                               }
+
+                       sessionsOps[i]&=~op;
+                       return true;
+                       }
+       }
+
+       protected boolean destroySession( CSSession s )
+       {
+               int     i;
+
+               assert(s!=null);
+
+               synchronized(internal) {
+                       for (i=0; i<sessions.length && sessions[i]!=s; i++) {}
+                       if (i==sessions.length) {
+                               log(Level.WARNING,label+" Session not found : 
"+s.getLabel()+".");
+                               return false;
+                               }
+
+                       return destroySession(i);
+                       }
+       }
+
+       /**
+        * The client has disconnected. Close the socket, free the buffers, 
unlink session from the linked list.
+        * Remove a session, either the other side closed the connection or we 
have otherwise reason to believe
+        * that it should better be killed.
+        *
+        * @param index index to the session handle
+        * @return
+        */
+
+       protected boolean destroySession( int index )
+       {
+               assert(index>=0);
+
+               synchronized(internal) {
+                       if (index>=sessions.length || sessions[index]==null) {
+                               log(Level.WARNING,label+" No session at slot 
"+index+".");
+                               return false;
+                               }
+
+                       if (sessions[index].isConnected()) {
+                               sessions[index].disconnect();
+                               }
+
+                       handler.handleDestroy(sessions[index]);
+
+                       sessions[index]=null;
+                       sessionsOps[index]=0;
+                       sessionCount--;
+                       debug("Destroyed session at slot #"+index+" 
"+Utils.gauge(sessionCount,sessions.length)+".");
+                       }
+               return true;
+       }
+}
+
+
+/*
+       public void add( int id, Class c, CommandHandler h )
+       {
+               decoder.add(id,c);
+               setHandler(c,h);
+       }
+
+       public void setDefault( int id, Class c, CommandHandler h )
+       {
+               decoder.add(id,c);
+               setHandler(c,h);
+               decoder.setDefault(id);
+       }
+
+       public void setCorrupted( int id, Class c, CommandHandler h )
+       {
+               decoder.add(id,c);
+               setHandler(c,h);
+               decoder.setCorrupted(id);
+       }
+
+       public boolean hasHandler( Class c )
+       {
+               synchronized(handlers) {
+                       return handlers.get(c)!=null;
+                       }
+       }
+
+       public CommandHandler getHandler( Class c )
+       {
+               synchronized(handlers) {
+                       return (CommandHandler) handlers.get(c);
+                       }
+       }
+
+       public boolean setHandler( Class c, CommandHandler h )
+       {
+               synchronized(handlers) {
+                       if (handlers.get(c)!=null) {
+                               log(Level.WARNING,"Could not assign handler, 
class "+c.getName()+" is already registered.");
+                               return false;
+                               }
+                       handlers.put(c,h);
+                       return true;
+                       }
+       }
+
+       public boolean removeHandler( Class c, CommandHandler h )
+       {
+               synchronized(handlers) {
+                       if (handlers.get(c)==null) {
+                               log(Level.WARNING,"Could not remove handler, 
class "+c.getName()+" is not registered.");
+                               return false;
+                               }
+                       handlers.remove(c);
+                       return true;
+                       }
+       }
+
+
+deriver de TCP Server:
+       public void start()
+       {
+               add(ProxyCommand.HELLO_ID,ProxyHello.class,this);
+               add(ProxyCommand.CONNECT_ID,ProxyConnect.class,this);
+               add(ProxyCommand.SETBLOCKING_ID,ProxySetBlocking.class,this);
+
+               setDefault(ProxyCommand.UNKNOWN_ID,ProxyUnknown.class,this);
+               
setCorrupted(ProxyCommand.CORRUPTED_ID,ProxyCorrupted.class,this);
+
+               super.start();
+       }
+
+       public void stop()
+       {
+               super.stop();
+       }
+
+       public TCP Session createSession( PersistentDecoder decoder )
+       {
+               return new ProxySession(decoder);
+       }
+
+       public boolean handle( TCP Session session, Persistent p )
+       {
+               ProxySession    s;
+               ProxyToken      token;
+               int     id;
+
+               id=((ProxyCommand) p).getID();
+
+               s=(ProxySession) session;
+               if (!s.isWelcomed()) {
+                       if (id!=ProxyCommand.HELLO_ID) {
+                               log("No hello received, close session.");
+                               return false;
+                               }
+                       s.welcome();
+
+                       token=new ProxyToken(s.getToken());
+                       token.setAddress("",0);
+                       sendToClient(s,token);
+                       return true;
+                       }
+
+               switch (id) {
+                       case ProxyCommand.CONNECT_ID:
+                               break;
+
+                       case ProxyCommand.SETBLOCKING_ID:
+                               break;
+
+                       default:
+                               log("Unknown message : "+p);
+                               return false;
+                       }
+               return true;
+       }
+
+*/

Added: freeway/src/org/gnu/freeway/util/net/TCPSession.java
===================================================================
--- freeway/src/org/gnu/freeway/util/net/TCPSession.java        2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/util/net/TCPSession.java        2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,410 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util.net;
+
+import org.gnu.freeway.util.*;
+
+import java.net.*;
+import java.nio.channels.*;
+import java.util.logging.*;
+
+/**
+ * Per-client data structure (kept in linked list).  Also: the opaque
+ * handle for client connections passed by the core to the CSHandlers.
+ * Opaque handle for client connections passed by
+ * the core to the CSHandlers.
+ *
+ * A connection to a freeway client application. To be used in non-blocking 
mode.
+ *
+ * Struct to refer to a GNUnet TCP connection.
+ * This is more than just a socket because if the server
+ * drops the connection, the client automatically tries
+ * to reconnect (and for that needs connection information).
+ *
+ * Code for synchronized access to TCP streams
+ *
+ * Generic TCP code for reliable, mostly blocking, record-oriented TCP
+ * connections. GNUnet uses the "tcpio" code for trusted client-server
+ * (e.g. gnunet-gtk to gnunetd via loopback) communications.  Note
+ * that an unblocking write is also provided since if both client and
+ * server use blocking IO, both may block on a write and cause a
+ * mutual inter-process deadlock.
+ *
+ * Since we do not want other peers (!) to be able to block a peer by
+ * not reading from the TCP stream, the peer-to-peer TCP transport
+ * uses unreliable, buffered, non-blocking, record-oriented TCP code
+ * with a select call to reduce the number of threads which is
+ * provided in transports/tcp.c.
+ * Generic TCP code. This module is used to receive or send records
+ * (!) from a TCP stream. The code automatically attempts to
+ * re-connect if the other side closes the connection.<br>
+ *
+ * The code can be used on the server- or the client side, just in
+ * case of the server the reconnect can of course not be used. The TCP
+ * stream is broken into records of maximum length MAX_BUFFER_SIZE,
+ * each preceeded by a 16 bits integer (not signed) giving the length of the
+ * following record.<p>
+ */
+
+public class TCPSession extends LoggedObject implements CSSession
+{
+       /** */
+       private TCPServer                       server;
+
+       /** Socket to communicate with the other side. */
+       private PersistentSocket                socket;
+
+       /** */
+       private String                          label;
+
+       /** Lock used to synchronized read operations. */
+       protected Object                                readLock;
+
+       /** Lock used to synchronized write operations. */
+       protected Object                                writeLock;
+
+
+       public TCPSession()
+       {
+               this(null);
+       }
+
+       public TCPSession( TCPServer s )
+       {
+               super(true);
+               server=s;
+               socket=new PersistentSocket();
+               socket.setDebug(false);
+               label=socket.getLabel();
+               readLock=new Object();
+               writeLock=new Object();
+       }
+
+       public String toString()
+       {
+               return "Client/server session";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public String getLabel()
+       {
+               return label;
+       }
+
+       public int getOps()
+       {
+               synchronized(readLock) {
+                       synchronized(writeLock) {
+                               return (socket.shouldWrite() ? 
(SelectionKey.OP_READ | SelectionKey.OP_WRITE) : SelectionKey.OP_READ);
+                               }
+                       }
+       }
+
+       public SelectionKey registerOps( Selector sel, int ops )
+       {
+               SelectionKey            key;
+
+               synchronized(readLock) {
+                       synchronized(writeLock) {
+                               key=socket.getChannel().keyFor(sel);
+                               if (key==null) {
+                                       try {
+                                               
key=socket.getChannel().register(sel,0);
+                                               }
+                                       catch( ClosedChannelException x ) {
+                                               err(label+" Failed to register 
on selector !",x);
+                                               return null;
+                                               }
+                                       }
+                               key.interestOps(ops);
+                               return key;
+                               }
+                       }
+       }
+
+       public boolean isConnected()
+       {
+               synchronized(readLock) {
+                       synchronized(writeLock) {
+                               return !socket.isClosed();
+                               }
+                       }
+       }
+
+       /**
+        * Connect this session to the specified ip and port in *blocking mode*.
+        * Used when connecting to a server at {ip,port}.
+        *
+        * @param ip                    IP of the host to connect to.
+        * @param port          The port number.
+        * @param careful       Should we treat socket with respect (SO_LINGER 
not set) ?
+        * @return                      True if successful, false on failure.
+        */
+
+       public boolean connect( InetAddress ip, int port, boolean careful )
+       {
+               boolean res;
+
+               synchronized(readLock) {
+                       synchronized(writeLock) {
+                               res=socket.open(ip,port,careful);
+                               if (res) {
+                                       label=socket.getLabel();
+                                       debug(label+" Connected.");
+                                       }
+                               return res;
+                               }
+                       }
+       }
+
+       /**
+        * Connect this session to the specified channel in *non blocking* mode.
+        * Used when connecting to a client from a server.
+        *
+        * @param channel       The open client socket.
+        * @param careful       Should we treat socket with respect (SO_LINGER 
not set) ?
+        * @return                      True if successful, false on failure.
+        */
+
+       public boolean connect( SocketChannel channel, boolean careful )
+       {
+               boolean res;
+
+               synchronized(readLock) {
+                       synchronized(writeLock) {
+                               res=socket.open(channel,careful);
+                               if (res) {
+                                       label=socket.getLabel();
+                                       debug(label+" Connected.");
+                                       }
+                               return res;
+                               }
+                       }
+       }
+
+       /**
+        * Close the session.
+        *
+        * @return      True if succeedeed, false otherwise.
+        */
+
+       public boolean disconnect()
+       {
+               boolean res;
+
+               synchronized(readLock) {
+                       synchronized(writeLock) {
+                               if (socket.isClosed()) {
+                                       log(Level.WARNING,"Session is already 
closed.");
+                                       return false;
+                                       }
+
+                               res=socket.close();
+                               if (res) {
+                                       debug(label+" Disconnected.");
+                                       }
+                               return res;
+                               }
+                       }
+       }
+
+       public boolean isBlocking()
+       {
+               synchronized(readLock) {
+                       synchronized(writeLock) {
+                               return socket.isBlocking();
+                               }
+                       }
+       }
+
+       public void setBlocking( boolean flag )
+       {
+               synchronized(readLock) {
+                       synchronized(writeLock) {
+                               socket.setBlocking(flag);
+                               }
+                       }
+       }
+
+       /**
+        * Buffer data received from the other side.
+        *
+        * @return      True if at least one byte has been received, false if 
the socket was closed by the other side or if an error occured.
+        */
+
+       public int doReceive()
+       {
+               int     len;
+
+               synchronized(readLock) {
+                       len=socket.doRead();
+                       if (len>0) {
+                               debug(label+" Have read "+len+" bytes.");
+                               }
+                       return len;
+                       }
+       }
+
+       public boolean hasReceived()
+       {
+               synchronized(readLock) {
+                       return socket.shouldDequeue();
+                       }
+       }
+
+       /**
+        * @param c
+        * @return
+        */
+
+       public Persistent receive( Class c )
+       {
+               Persistent      p;
+
+               synchronized(readLock) {
+                       if (!socket.shouldDequeue() && socket.isBlocking()) {
+                               doReceive();
+                               }
+
+                       p=socket.dequeue(c);
+                       if (p!=null) {
+                               debug(label+" Received message : "+p+".");
+                               }
+                       return p;
+                       }
+       }
+
+       /**
+        * Decode buffered data. If in blocking mode and no messages are 
buffered, an attempt is made to read fresh data.
+        *
+        * @param decoder       Decoder used to transform transmitted data into 
messages.
+        * @return                      Any decoded data if available, null 
otherwise.
+        */
+
+       public Persistent receive( PersistentDecoder decoder )
+       {
+               Persistent      p;
+
+               synchronized(readLock) {
+                       if (!socket.shouldDequeue() && socket.isBlocking()) {
+                               doReceive();
+                               }
+
+                       p=socket.dequeue(decoder);
+                       if (p!=null) {
+                               debug(label+" Received message : "+p+".");
+                               }
+                       return p;
+                       }
+       }
+
+       /**
+        * Add data to the buffer, and if blocking, start transferring buffered 
data.
+        *
+        * <div>When in blocking mode, try to also send buffered data to the 
other side. Returns true if, at least, one byte
+        * has been transmitted. Please note that it does *not* imply that any 
part of the data <code>p</code> has been transmitted,
+        * since other data may had been buffered previously (transfer is 
initiated but may be incomplete).</div>
+        *
+        * <div>In non-blocking mode, returns true. The actual transfer happens 
asynchronously.</div>
+        *
+        * @param p     The data to write (duplicated, because may be buffered 
and stored a certain amount of time...).
+        * @return      True if in non-blocking mode, or if at least one byte 
of buffered data has been transmitted, false otherwise.
+        */
+
+       public boolean send( Persistent p )
+       {
+               p=PersistentHelper.copy(p);
+
+               synchronized(writeLock) {
+                       socket.enqueue(p);
+                       debug(label+" Sent message : "+p+".");
+
+                       if (server!=null) {
+                               server.wakeUp();
+                               }
+
+                       return (socket.isBlocking() ? doSend()>0 : true);
+                       }
+       }
+
+       public boolean sendAndCheck( Persistent p )
+       {
+               CSResult                res;
+
+               synchronized(writeLock) {
+                       if (!send(p)) {
+                               return false;
+                               }
+
+                       res=(CSResult) receive(CSResult.class);
+                       return (res!=null && res.isOkay());
+                       }
+       }
+
+       /**
+        * Flush buffered data, buffer given data <code>p</code> and try to 
initiate transfer of this data.
+        * Note that it is possible that only a part of the message is sent.
+        *
+        * Returning true here means that at least a small part of the message 
has been transmitted,
+        * though it may be transmitted entirely a bit later.
+        *
+        * @param p     The data to write (duplicated, because may be buffered 
and stored a certain amount of time...).
+        * @return      False if an I/O error occurred, or if it did not 
transmit any byte of the message. Return true otherwise.
+        */
+
+       public boolean flushAndSend( Persistent p )
+       {
+               boolean empty;
+
+               p=PersistentHelper.copy(p);
+
+               synchronized(writeLock) {
+                       doSend();
+
+                       empty=!socket.shouldWrite();
+
+                       socket.enqueue(p);
+                       debug(label+" Sent message : "+p+".");
+
+                       if (server!=null) {
+                               server.wakeUp();
+                               }
+
+                       return (empty ? doSend()>0 : false);
+                       }
+       }
+
+       public boolean hasToSend()
+       {
+               synchronized(writeLock) {
+                       return socket.shouldWrite();
+                       }
+       }
+
+       /**
+        * Send buffered data, if any.
+        *
+        * @return      True if at least one byte has been transmitted, false 
otherwise
+        *                      (an error occured, the other side is not ready, 
or there is no data in buffer).
+        */
+
+       public int doSend()
+       {
+               int     len;
+
+               synchronized(writeLock) {
+                       len=0;
+                       if (socket.shouldWrite()) {
+                               len=socket.doWrite();
+                               if (len>0) {
+                                       debug(label+" Have written "+len+" 
bytes.");
+                                       }
+                               }
+                       return len;
+                       }
+       }
+}

Added: freeway/src/org/gnu/freeway/util/ui/AbstractAdapter.java
===================================================================
--- freeway/src/org/gnu/freeway/util/ui/AbstractAdapter.java    2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/util/ui/AbstractAdapter.java    2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,77 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util.ui;
+
+import org.gnu.freeway.util.*;
+
+/**
+ * Utility class which implements basic mechanisms of an adapter.
+ * Subclasses need to override <code>getFacetCount</code> and
+ * <code>fillFacets</code> methods.
+ *
+ * @see #getFacetCount()
+ * @see #fillFacets(Object,Object[])
+ */
+
+public abstract class AbstractAdapter extends LoggedObject implements Adapter
+{
+       private Object  object;
+       private Object[]        facets;
+       private int             facetCount;
+
+
+       public AbstractAdapter( int count )
+       {
+               super(true);
+               object=null;
+               facets=null;
+               facetCount=count;
+       }
+
+       public Object getObject()
+       {
+               return object;
+       }
+
+       public void setObject( Object obj )
+       {
+               object=obj;
+               facets=null;
+       }
+
+       public int getFacetCount()
+       {
+               return facetCount;
+       }
+
+       public Object getFacet( int index )
+       {
+               if (facets==null) {
+                       makeFacets();
+                       }
+               return ((index>=0 && index<facets.length) ? facets[index] : 
null);
+       }
+
+       public Object[] getFacets()
+       {
+               if (facets==null) {
+                       makeFacets();
+                       }
+               return facets;
+       }
+
+       protected void makeFacets()
+       {
+               try {
+                       facets=new Object[getFacetCount()];
+                       fillFacets(object,facets);
+                       }
+               catch( Throwable x ) {
+                       err("Could not extract facets !",x);
+                       }
+       }
+
+       protected abstract void fillFacets( Object obj, Object[] facetz ) 
throws Throwable;
+}

Added: freeway/src/org/gnu/freeway/util/ui/Adapter.java
===================================================================
--- freeway/src/org/gnu/freeway/util/ui/Adapter.java    2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/util/ui/Adapter.java    2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,19 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util.ui;
+
+/**
+ * Provides a specific view on an object.
+ */
+
+public interface Adapter
+{
+       public Object getObject();
+       public void setObject( Object obj );
+
+       public int getFacetCount();
+       public Object getFacet( int index );
+       public Object[] getFacets();
+}

Added: freeway/src/org/gnu/freeway/util/ui/CompoundComparator.java
===================================================================
--- freeway/src/org/gnu/freeway/util/ui/CompoundComparator.java 2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/util/ui/CompoundComparator.java 2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,198 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util.ui;
+
+import java.util.*;
+
+/**
+ *
+ */
+
+public class CompoundComparator extends Object implements Comparator
+{
+       public static final int ORDER_NONE                      =       0;
+       public static final int ORDER_ASCENDANT         =       1;
+       public static final int ORDER_DESCENDANT        =       2;
+
+       private Adapter                 adapter;
+       private Comparator[]    comparators;
+       private int[]                   orders;
+       private int[]                   sequence;
+
+
+       public CompoundComparator( Adapter a )
+       {
+               this(a,null);
+       }
+
+       public CompoundComparator( Adapter a, Comparator[] c )
+       {
+               super();
+               adapter=a;
+               comparators=new Comparator[a.getFacetCount()];
+               Arrays.fill(comparators,null);
+               orders=new int[a.getFacetCount()];
+               Arrays.fill(orders,ORDER_NONE);
+               sequence=new int[a.getFacetCount()];
+               Arrays.fill(sequence,-1);
+
+               setComparators(c);
+       }
+
+       public String toString()
+       {
+               return "Compound comparator";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public Adapter getAdapter()
+       {
+               return adapter;
+       }
+
+       public Comparator[] getComparators()
+       {
+               return comparators;
+       }
+
+       public void setComparators( Comparator[] c )
+       {
+               if (c!=null) {
+                       
System.arraycopy(c,0,comparators,0,Math.min(c.length,comparators.length));
+                       }
+               else {
+                       Arrays.fill(comparators,new DefaultComparator());
+                       }
+       }
+
+       public boolean isSorting()
+       {
+               return sequence[0]>=0;
+       }
+
+       public void resetSorting()
+       {
+               Arrays.fill(orders,ORDER_NONE);
+               Arrays.fill(sequence,-1);
+       }
+
+       /**
+        * Cycle the sorting states.
+        * @param index
+        * @param reset
+        * @param up
+        */
+
+       public void cycleSorting( int index, boolean reset, boolean up )
+       {
+               int     i;
+
+               if (reset) {
+                       i=orders[index];
+                       resetSorting();
+                       orders[index]=i;
+                       }
+
+               if (up) {
+                       switch (orders[index]) {
+                               case ORDER_NONE: orders[index]=ORDER_ASCENDANT; 
break;
+                               case ORDER_ASCENDANT: 
orders[index]=ORDER_DESCENDANT; break;
+                               case ORDER_DESCENDANT: 
orders[index]=ORDER_NONE; break;
+                               }
+                       }
+               else {
+                       switch (orders[index]) {
+                               case ORDER_NONE: 
orders[index]=ORDER_DESCENDANT; break;
+                               case ORDER_DESCENDANT: 
orders[index]=ORDER_ASCENDANT; break;
+                               case ORDER_ASCENDANT: orders[index]=ORDER_NONE; 
break;
+                               }
+                       }
+
+               for (i=0; i<sequence.length && sequence[i]!=index; i++) {}
+               if (i<sequence.length) {
+                       
System.arraycopy(sequence,i+1,sequence,i,sequence.length-i-1);
+                       sequence[sequence.length-1]=-1;
+                       }
+               if (orders[index]!=ORDER_NONE) {
+                       for (i=0; sequence[i]>=0; i++) {}
+                       sequence[i]=index;
+                       }
+
+//             System.out.println("ORDERS : "+debugOrders());
+//             System.out.println("SEQUENCE : "+debugPositions());
+       }
+
+       protected String debugOrders()
+       {
+               StringBuffer    buf;
+               int                             i;
+
+               buf=new StringBuffer();
+               for (i=0; i<orders.length; i++) {
+                       switch (orders[i]) {
+                               case ORDER_ASCENDANT: buf.append("<"); break;
+                               case ORDER_NONE: buf.append("-"); break;
+                               case ORDER_DESCENDANT: buf.append(">"); break;
+                               }
+                       buf.append(" ");
+                       }
+               return buf.toString();
+       }
+
+       protected String debugPositions()
+       {
+               StringBuffer    buf;
+               int                             i;
+
+               buf=new StringBuffer();
+               for (i=0; i<sequence.length && sequence[i]>=0; i++) {
+                       buf.append(sequence[i]);
+                       buf.append(" ");
+                       }
+               return buf.toString();
+       }
+
+       public int getSortOrder( int index )
+       {
+               return orders[index];
+       }
+
+       public int getSortPosition( int index )
+       {
+               int     i;
+
+               if (orders[index]==ORDER_NONE) {
+                       return -1;
+                       }
+
+               for (i=0; sequence[i]!=index; i++) {}
+               return i;
+       }
+
+       public int compare( Object o1, Object o2 )
+       {
+               Object[]        f1,f2;
+               int                     i,j,n;
+
+               adapter.setObject(o1);
+               f1=adapter.getFacets();
+
+               adapter.setObject(o2);
+               f2=adapter.getFacets();
+
+               for (i=n=0; i<sequence.length && sequence[i]>=0 && n==0; i++) {
+                       j=sequence[i];
+                       if (comparators[j]!=null) {
+                               n=comparators[j].compare(f1[j],f2[j]);
+                               if (orders[j]==ORDER_DESCENDANT) {
+                                       n=-n;
+                                       }
+                               }
+                       }
+               return n;
+       }
+}

Added: freeway/src/org/gnu/freeway/util/ui/Controller.java
===================================================================
--- freeway/src/org/gnu/freeway/util/ui/Controller.java 2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/util/ui/Controller.java 2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,16 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util.ui;
+
+import org.gnu.freeway.*;
+
+/**
+ *
+ */
+
+public interface Controller
+{
+       public Application getApplication();
+}

Added: freeway/src/org/gnu/freeway/util/ui/DefaultComparator.java
===================================================================
--- freeway/src/org/gnu/freeway/util/ui/DefaultComparator.java  2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/util/ui/DefaultComparator.java  2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,47 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util.ui;
+
+import java.util.*;
+
+/**
+ *
+ */
+
+public class DefaultComparator extends Object implements Comparator
+{
+       public DefaultComparator()
+       {
+               super();
+       }
+
+       public int compare( Object o1, Object o2 )
+       {
+               Object[]        p1,p2;
+               int                     imax,i;
+
+               if (o1==null || o2==null) {
+                       return (o1!=null ? 1 : (o2!=null ? -1 : 0));
+                       }
+               if ((o1 instanceof Boolean) && (o2 instanceof Boolean)) {
+                       return (o1.equals(o2) ? 0 : (((Boolean) 
o1).booleanValue() ? 1 : -1));
+                       }
+               if ((o1 instanceof String) && (o2 instanceof String)) {
+                       return ((String) o1).compareToIgnoreCase((String) o2);
+                       }
+               if ((o1 instanceof Comparable) && (o2 instanceof Comparable)) {
+                       return ((Comparable) o1).compareTo(o2);
+                       }
+               if ((o1 instanceof Object[]) && (o2 instanceof Object[])) {
+                       p1=(Object[]) o1;
+                       p2=(Object[]) o2;
+
+                       imax=Math.min(p1.length,p2.length);
+                       for (i=0; i<imax && compare(p1[i],p2[i])==0; i++) {}
+                       return (i<imax ? compare(p1[i],p2[i]) : 0);
+                       }
+               return 0;
+       }
+}

Added: freeway/src/org/gnu/freeway/util/ui/GBar.java
===================================================================
--- freeway/src/org/gnu/freeway/util/ui/GBar.java       2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/util/ui/GBar.java       2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,88 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util.ui;
+
+import java.awt.*;
+import javax.swing.*;
+
+/**
+ *
+ */
+
+public class GBar extends JPanel
+{
+       public GBar()
+       {
+               super(new FlowLayout(FlowLayout.CENTER,3,0));
+               setOpaque(false);
+       }
+
+       public void addSeparator()
+       {
+       }
+
+       public void add( String label, JComponent c )
+       {
+               JLabel  l;
+               int             index;
+
+               index=label.indexOf('_');
+               if (index>=0 && index<label.length()-1) {
+                       l=new 
JLabel(label.substring(0,index)+label.substring(index+1),SwingConstants.TRAILING);
+                       l.setDisplayedMnemonic(label.charAt(index+1));
+                       }
+               else {
+                       l=new JLabel(label,SwingConstants.TRAILING);
+                       }
+               l.setLabelFor(c);
+               add(l);
+               add(c);
+       }
+
+       public JButton add( Action a )
+       {
+               JButton b;
+               String  str;
+
+               b=new JButton(a);
+//             b.setBorderPainted(false);
+//b.setBorder(BorderFactory.createEtchedBorder());
+//b.setBorder(BorderFactory.createEmptyBorder(1,1,1,1));
+//b.setBorder(BorderFactory.createBevelBorder(javax.swing.border.BevelBorder.LOWERED));
+               str=(String) a.getValue(Action.LONG_DESCRIPTION);
+               if (str!=null && str.length()>0) {
+                       b.setToolTipText(str);
+                       }
+//             b.setSize(150,32);
+
+//             b.setPreferredSize(new Dimension(100,32));
+
+               //              b.setMinimumSize(new Dimension(150,32));
+//             b.setMaximumSize(new Dimension(150,32));
+/*
+       button.setActionCommand(actionCommand);
+       button.setToolTipText(toolTipText);
+       button.addActionListener(this);
+
+       if (imageURL != null) {                      //image found
+               button.setIcon(new ImageIcon(imageURL, altText));
+       } else {                                     //no image found
+               button.setText(altText);
+               System.err.println("Resource not found: " + imgLocation);
+       } */
+
+               add(b);
+               return b;
+//             return super.add(a);
+       }
+
+/*
+
+putValue(javax.swing.Action.ACTION_COMMAND_KEY,v.get("id",""));
+putValue(javax.swing.Action.,new ImageIcon(url));
+putValue(javax.swing.Action.ACCELERATOR_KEY,KeyStroke.getKeyStroke(v.text("")));
+putValue(javax.swing.Action.MNEMONIC_KEY,new 
Integer(str.toUpperCase().charAt(0)));
+       */
+}

Added: freeway/src/org/gnu/freeway/util/ui/GColumn.java
===================================================================
--- freeway/src/org/gnu/freeway/util/ui/GColumn.java    2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/util/ui/GColumn.java    2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,64 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util.ui;
+
+import javax.swing.*;
+
+/**
+ *
+ */
+
+public class GColumn extends Object implements SwingConstants
+{
+       private String  title;
+       private int             width;
+       private int             alignment;
+       private boolean sortable;
+
+
+       public GColumn( String str, int w )
+       {
+               this(str,w,CENTER,false);
+       }
+
+       public GColumn( String str, int w, int a, boolean s )
+       {
+               super();
+               title=str;
+               width=w;
+               alignment=a;
+               sortable=s;
+       }
+
+       public String getTitle()
+       {
+               return title;
+       }
+
+       public int getWidth()
+       {
+               return width;
+       }
+
+       public int getAlignment()
+       {
+               return alignment;
+       }
+
+       public void setAlignment( int align )
+       {
+               alignment=align;
+       }
+
+       public boolean isSortable()
+       {
+               return sortable;
+       }
+
+       public void setSortable( boolean flag )
+       {
+               sortable=flag;
+       }
+}

Added: freeway/src/org/gnu/freeway/util/ui/GConsole.java
===================================================================
--- freeway/src/org/gnu/freeway/util/ui/GConsole.java   2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/util/ui/GConsole.java   2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,277 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util.ui;
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+
+/**
+ *
+ */
+
+public class GConsole extends GScrollPane
+{
+       private static final int[]      FONT_SIZES              =       { 9, 
10, 11, 12, 13 };
+       private static final int[]      BUFFER_SIZES    =       { 64, 80, 128, 
256, 512, 1024, 4096, 32768 };
+
+       private JTextArea       area;
+       private Font            usedFont;
+       private int[]           lengths;
+       private int                     offset;
+       private int                     count;
+       private boolean         autoScroll;
+
+
+       public GConsole()
+       {
+               super();
+               setup();
+               lengths=new int[256];
+               offset=0;
+               count=0;
+               autoScroll=true;
+       }
+
+       public String toString()
+       {
+               return "Console";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       protected void setup()
+       {
+               usedFont=new Font("Monospaced",Font.PLAIN,10);
+
+               area=new JTextArea();
+               area.setEditable(false);
+               area.setLineWrap(false);
+               area.setMargin(new Insets(5,5,5,5));
+               area.setFont(usedFont);
+
+               UIHelper.addPopupToComponent(createPopup(),area);
+
+               setVerticalScrollBarPolicy(VERTICAL_SCROLLBAR_AS_NEEDED);
+               setHorizontalScrollBarPolicy(HORIZONTAL_SCROLLBAR_NEVER);
+               setViewportView(area);
+
+               
getViewport().setScrollMode(JViewport.BACKINGSTORE_SCROLL_MODE);        // less 
flicks than with BLIT_SCROLL_MODE
+       }
+
+       public int getFontSize()
+       {
+               return usedFont.getSize();
+       }
+
+       public void setFontSize( int size )
+       {
+               usedFont=usedFont.deriveFont((float) size);
+               area.setFont(usedFont);
+       }
+
+       public int getBufferSize()
+       {
+               synchronized(lengths) {
+                       return lengths.length;
+                       }
+       }
+
+       public void setBufferSize( int bufsize )
+       {
+               int[]   old;
+               int             total,i;
+
+               synchronized(lengths) {
+                       total=0;
+                       while (bufsize<count) {
+                               total+=lengths[offset++];
+                               if (offset==lengths.length) {
+                                       offset=0;
+                                       }
+                               count--;
+                               }
+                       if (total>0) {
+                               area.replaceRange("",0,total);
+                               }
+
+                       old=lengths;
+
+                       lengths=new int[bufsize];
+                       for (i=0; i<count; i++) {
+                               lengths[i]=old[(offset+i) % old.length];
+                               }
+                       offset=0;
+                       }
+       }
+
+       public void clear()
+       {
+               SwingUtilities.invokeLater(new Runnable() {
+                       public void run()
+                       {
+                               area.setText("");
+                               offset=0;
+                               count=0;
+                       }
+                       });
+       }
+
+       public void println( String str )
+       {
+               JViewport       vport;
+
+               area.append(str);
+               area.append("\n");
+
+               synchronized(lengths) {
+                       if (count<lengths.length) {
+                               lengths[(offset+count) % 
lengths.length]=str.length()+1;
+                               count++;
+                               }
+                       else {
+                               area.replaceRange("",0,lengths[offset]);
+                               lengths[offset++]=str.length()+1;
+                               if (offset==lengths.length) {
+                                       offset=0;
+                                       }
+                               }
+                       }
+
+               if (autoScroll) {
+                       vport=getViewport();
+                       vport.scrollRectToVisible(new 
Rectangle(0,area.getHeight(),1,1));
+                       }
+       }
+
+       protected JPopupMenu createPopup()
+       {
+               JPopupMenu      popup;
+               JMenu   menu;
+               int             i;
+
+               popup=new JPopupMenu();
+
+               menu=new JMenu("Font size");
+               for (i=0; i<FONT_SIZES.length; i++) {
+                       final int       _i = i;
+                       menu.add(new 
AbstractAction(String.valueOf(FONT_SIZES[i])) {
+                               public void actionPerformed( ActionEvent evt )
+                               {
+                                       setFontSize(FONT_SIZES[_i]);
+                               }
+                               });
+                       }
+               popup.add(menu);
+
+               menu=new JMenu("Rows");
+               for (i=0; i<BUFFER_SIZES.length; i++) {
+                       final int       _i = i;
+                       menu.add(new 
AbstractAction(String.valueOf(BUFFER_SIZES[i])) {
+                               public void actionPerformed( ActionEvent evt )
+                               {
+                                       setBufferSize(BUFFER_SIZES[_i]);
+                               }
+                               });
+                       }
+               popup.add(menu);
+               popup.addSeparator();
+               popup.add(new AbstractAction("Clear") {
+                       public void actionPerformed( ActionEvent evt )
+                       {
+                               clear();
+                       }
+                       });
+               return popup;
+       }
+
+       public JComponent createControls()
+       {
+               JComboBox       combo;
+               JButton         button;
+               JCheckBox       check;
+               JLabel          label;
+               Box                     box;
+               int                     i;
+
+               box=new Box(BoxLayout.X_AXIS);
+
+               label=new JLabel("Font size : ");
+               label.setFont(label.getFont().deriveFont((float) 
(label.getFont().getSize()-2)));
+               label.setAlignmentY((float) 0.5);
+               box.add(label);
+
+               combo=new JComboBox();
+               for (i=0; i<FONT_SIZES.length; i++) {
+                       combo.addItem(new Integer(FONT_SIZES[i]));
+                       }
+               combo.addActionListener(new ActionListener() {
+                       public void actionPerformed( ActionEvent evt )
+                       {
+                               setFontSize(((Number) ((JComboBox) 
evt.getSource()).getSelectedItem()).intValue());
+                       }
+                       });
+               combo.setFont(combo.getFont().deriveFont((float) 
(combo.getFont().getSize()-2)));
+               combo.setMaximumSize(combo.getPreferredSize());
+               combo.setSelectedItem(new Integer(getFontSize()));
+               combo.setAlignmentY((float) 0.5);
+               box.add(combo);
+
+               box.add(Box.createRigidArea(new Dimension(10,0)));
+
+               label=new JLabel("Buffer size : ");
+               label.setFont(label.getFont().deriveFont((float) 
(label.getFont().getSize()-2)));
+               label.setAlignmentY((float) 0.5);
+               box.add(label);
+
+               combo=new JComboBox();
+               for (i=0; i<BUFFER_SIZES.length; i++) {
+                       combo.addItem(new Integer(BUFFER_SIZES[i]));
+                       }
+               combo.addActionListener(new ActionListener() {
+                       public void actionPerformed( ActionEvent evt )
+                       {
+                               setBufferSize(((Number) ((JComboBox) 
evt.getSource()).getSelectedItem()).intValue());
+                       }
+                       });
+               combo.setFont(combo.getFont().deriveFont((float) 
(combo.getFont().getSize()-2)));
+               combo.setMaximumSize(combo.getPreferredSize());
+               combo.setSelectedItem(new Integer(getBufferSize()));
+               combo.setAlignmentY((float) 0.5);
+               box.add(combo);
+
+               box.add(Box.createRigidArea(new Dimension(10,0)));
+
+               button=new JButton("Clear");
+               button.setFont(button.getFont().deriveFont((float) 
(button.getFont().getSize()-2)));
+               button.addActionListener(new ActionListener() {
+                       public void actionPerformed( ActionEvent evt )
+                       {
+                               clear();
+                       }
+                       });
+               button.setAlignmentY((float) 0.5);
+               box.add(button);
+
+               box.add(Box.createRigidArea(new Dimension(10,0)));
+
+               label=new JLabel("Autoscroll : ");
+               label.setFont(label.getFont().deriveFont((float) 
(label.getFont().getSize()-2)));
+               label.setAlignmentY((float) 0.5);
+               box.add(label);
+
+               check=new JCheckBox();
+               check.setSelected(autoScroll);
+               check.addActionListener(new ActionListener() {
+                       public void actionPerformed( ActionEvent evt )
+                       {
+                               autoScroll=((JCheckBox) 
evt.getSource()).isSelected();
+                       }
+                       });
+               box.add(check);
+               return box;
+       }
+}

Added: freeway/src/org/gnu/freeway/util/ui/GDialog.java
===================================================================
--- freeway/src/org/gnu/freeway/util/ui/GDialog.java    2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/util/ui/GDialog.java    2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,154 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util.ui;
+
+import java.util.logging.*;
+import javax.swing.*;
+
+/**
+ *
+ */
+
+public abstract class GDialog extends JDialog implements SwingConstants
+{
+       private Controller      controller;
+       private String          id;
+       private JComponent      content;
+       private Logger          logger;
+
+
+       protected GDialog( Controller ctr, String str )
+       {
+               super((JFrame) null,true);
+               init(ctr,str);
+       }
+
+       protected GDialog( GFrame frame, String str )
+       {
+               super(frame,true);
+               init(frame.getController(),str);
+       }
+
+       protected GDialog( GDialog dialog, String str )
+       {
+               super(dialog,true);
+               init(dialog.getController(),str);
+       }
+
+       protected void init( Controller ctr, String str )
+       {
+               UIHelper.restoreLAF(ctr.getApplication());
+               controller=ctr;
+
+               id=str;
+               content=null;
+               logger=Logger.getLogger(getClass().getName());
+
+               setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
+
+               controller.getApplication().addCritical(new 
org.gnu.freeway.util.AbstractAction() {
+                       public void perform()
+                       {
+                               if (isVisible()) {
+                                       
UIHelper.saveDims(controller.getApplication(),GDialog.this,id);
+                                       }
+                       }
+                       });
+       }
+
+       public String toString()
+       {
+               return "Dialog";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public GFrame getFrame()
+       {
+               return (GFrame) getOwner();
+       }
+
+       public Controller getController()
+       {
+               return controller;
+       }
+
+       public String getID()
+       {
+               return id;
+       }
+
+       public JComponent getContent()
+       {
+               return content;
+       }
+
+       public void setContent( JComponent c )
+       {
+               content=c;
+       }
+
+       public JComponent createContent()
+       {
+               return null;
+       }
+
+       public void updateContent()
+       {
+       }
+
+       public void leaveContent()
+       {
+       }
+
+       public void display()
+       {
+               if (!isVisible()) {
+                       if (content==null) {
+                               content=createContent();
+                               if (content==null) {
+                                       logger.log(Level.INFO,"No content to 
display !");
+                                       return;
+                                       }
+                               }
+
+                       if (!content.equals(getContentPane())) {
+                               setContentPane(content);
+                               }
+
+                       updateContent();
+                       pack();
+
+                       
UIHelper.restoreDims(controller.getApplication(),this,id);
+                       setVisible(true);
+                       }
+               toFront();
+       }
+
+       public void close()
+       {
+               if (isVisible()) {
+                       setVisible(false);
+                       UIHelper.saveDims(controller.getApplication(),this,id);
+
+                       dispose();
+
+                       leaveContent();
+                       }
+       }
+
+       public void showMessage( String title, String str )
+       {
+               //todo: avec icon de la frame
+               
JOptionPane.showMessageDialog(this,str,title,JOptionPane.INFORMATION_MESSAGE,null);
+       }
+
+       public boolean askMessage( String title, String str )
+       {
+               //todo: avec icon de la frame
+               return 
JOptionPane.showConfirmDialog(this,str,title,JOptionPane.YES_NO_OPTION,JOptionPane.QUESTION_MESSAGE,null)==JOptionPane.YES_OPTION;
+       }
+}

Added: freeway/src/org/gnu/freeway/util/ui/GFile.java
===================================================================
--- freeway/src/org/gnu/freeway/util/ui/GFile.java      2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/util/ui/GFile.java      2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,195 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util.ui;
+
+import org.gnu.freeway.util.io.*;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.io.*;
+import javax.swing.*;
+
+/**
+ *
+ */
+
+public class GFile extends Box implements ActionListener, FocusListener
+{
+       public static final int SELECT_FILE             =       0;
+       public static final int SELECT_DIRECTORY        =       1;
+
+       private int                     selectType;
+       private String          selectTitle;
+       private DirLocation     selectedBase;
+       private Location                selectedLocation;
+
+       private JTextField      field;
+       private JButton         button;
+
+
+       public GFile()
+       {
+               super(BoxLayout.X_AXIS);
+               selectType=SELECT_FILE;
+               selectTitle="Choose a file";
+               selectedBase=null;
+               selectedLocation=null;
+
+               setup();
+       }
+
+       public String toString()
+       {
+               return "File entry field";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int getSelectType()
+       {
+               return selectType;
+       }
+
+       public String getSelectTitle()
+       {
+               return selectTitle;
+       }
+
+       public void selectFile( String str )
+       {
+               selectFile(str,null);
+       }
+
+       public void selectFile( String str, DirLocation base )
+       {
+               selectType=SELECT_FILE;
+               selectTitle=str;
+               selectedBase=base;
+               selectedLocation=(selectedLocation!=null ? new 
FileLocation(selectedLocation.getPath()) : null);
+       }
+
+       public void selectDirectory( String str )
+       {
+               selectDirectory(str,null);
+       }
+
+       public void selectDirectory( String str, DirLocation base )
+       {
+               selectType=SELECT_DIRECTORY;
+               selectTitle=str;
+               selectedBase=base;
+               selectedLocation=(selectedLocation!=null ? new 
DirLocation(selectedLocation.getPath()) : null);
+       }
+
+       public DirLocation getSelectedBase()
+       {
+               return selectedBase;
+       }
+
+       public Location getSelectedLocation()
+       {
+               return selectedLocation;
+       }
+
+       protected String getSelectedLocationLabel()
+       {
+               return (selectedLocation!=null ? selectedLocation.getLabel() : 
"");
+       }
+
+       public void setSelectedLocation( String str )
+       {
+               Location                loc;
+
+               loc=null;
+               if (str!=null && str.length()>0) {
+                       if (selectType==SELECT_FILE) {
+                               loc=new FileLocation(str);
+                               }
+                       else {
+                               loc=new DirLocation(str);
+                               }
+                       }
+               setSelectedLocation(loc);
+       }
+
+       public void setSelectedLocation( Location f )
+       {
+               if (f!=null) {
+                       selectedLocation=f;
+                       selectedBase=f.getParent();
+                       }
+               else {
+                       selectedLocation=null;
+                       }
+
+               field.setText(getSelectedLocationLabel());
+               field.selectAll();
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       protected void setup()
+       {
+               field=new JTextField();
+               field.addActionListener(this);
+               field.addFocusListener(this);
+               field.setMaximumSize(new 
Dimension(Integer.MAX_VALUE,field.getPreferredSize().height));
+               field.setAlignmentY((float) 0.5);
+               field.setText(getSelectedLocationLabel());
+
+               button=new JButton("Choose...");
+               button.addActionListener(this);
+               button.setAlignmentY((float) 0.5);
+
+               add(field);
+               add(Box.createHorizontalStrut(5));
+               add(button);
+       }
+
+       public void actionPerformed( ActionEvent evt )
+       {
+               JFileChooser    dialog;
+               String                  str;
+
+               if (evt.getSource()==field) {
+                       str=field.getText();
+                       setSelectedLocation(str);
+                       }
+               else {
+                       dialog=new JFileChooser();
+                       dialog.setDialogTitle(selectTitle);
+                       dialog.setFileSelectionMode(selectType==SELECT_FILE ? 
JFileChooser.FILES_ONLY : JFileChooser.DIRECTORIES_ONLY);
+                       dialog.setFileHidingEnabled(false);
+                       dialog.setMultiSelectionEnabled(false);
+
+                       if (selectedBase!=null) {
+                               dialog.setCurrentDirectory(new 
File(selectedBase.getPath()));
+                               }
+                       if (selectedLocation!=null) {
+                               dialog.setSelectedFile(new 
File(selectedLocation.getPath()));
+                               }
+
+                       if 
(dialog.showOpenDialog(this)==JFileChooser.APPROVE_OPTION) {
+                               
setSelectedLocation(dialog.getSelectedFile().getPath());
+                               }
+                       }
+       }
+
+       public void focusGained( FocusEvent evt )
+       {
+               if (!evt.isTemporary()) {
+                       field.selectAll();
+                       }
+       }
+
+       public void focusLost( FocusEvent evt )
+       {
+               if (!evt.isTemporary()) {
+                       field.postActionEvent();
+                       }
+       }
+}

Added: freeway/src/org/gnu/freeway/util/ui/GForm.java
===================================================================
--- freeway/src/org/gnu/freeway/util/ui/GForm.java      2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/util/ui/GForm.java      2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,80 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util.ui;
+
+import java.awt.*;
+import javax.swing.*;
+
+/**
+ *
+ */
+
+public class GForm extends JPanel implements SwingConstants
+{
+       private GridBagConstraints      c;
+
+
+       public GForm()
+       {
+               this(7,3);
+       }
+
+       public GForm( int hgap, int vgap )
+       {
+               super(new GridBagLayout());
+               c=new 
GridBagConstraints(0,0,1,1,0,0,GridBagConstraints.NORTH,GridBagConstraints.HORIZONTAL,new
 Insets(vgap,hgap,0,0),0,0);
+               setOpaque(false);
+       }
+
+       public void addWidget( String str, JComponent comp )
+       {
+               addWidget(str,comp,0.5);
+       }
+
+       public void addWidget( String str, JComponent comp, double labelVAlign )
+       {
+               GridBagLayout   gridbag;
+               JLabel                  label;
+               int                             index,anchor;
+
+               gridbag=(GridBagLayout) getLayout();
+
+               if (labelVAlign<0.1) {
+                       anchor=GridBagConstraints.NORTH;
+                       }
+               else if (labelVAlign>0.9) {
+                       anchor=GridBagConstraints.SOUTH;
+                       }
+               else {
+                       anchor=GridBagConstraints.CENTER;
+                       }
+
+               index=str.indexOf('_');
+               if (index>=0 && index<str.length()-1) {
+                       label=new 
JLabel(str.substring(0,index)+str.substring(index+1),TRAILING);
+                       label.setDisplayedMnemonic(str.charAt(index+1));
+                       }
+               else {
+                       label=new JLabel(str,TRAILING);
+                       }
+               label.setLabelFor(comp);
+
+               c.gridx=0;
+               c.gridy++;
+               c.weightx=0;
+               c.anchor=anchor;
+               c.fill=GridBagConstraints.HORIZONTAL;
+               gridbag.setConstraints(label,c);
+
+               c.gridx=1;
+               c.weightx=1;
+               c.anchor=anchor;
+               c.fill=GridBagConstraints.BOTH;
+               gridbag.setConstraints(comp,c);
+
+               add(label);
+               add(comp);
+       }
+}

Added: freeway/src/org/gnu/freeway/util/ui/GFrame.java
===================================================================
--- freeway/src/org/gnu/freeway/util/ui/GFrame.java     2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/util/ui/GFrame.java     2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,133 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util.ui;
+
+import java.util.logging.*;
+import javax.swing.*;
+
+/**
+ *
+ */
+
+public class GFrame extends JFrame
+{
+       private Controller      controller;
+       private String          id;
+       private JComponent      content;
+       private Logger          logger;
+
+
+       public GFrame( Controller c, String str )
+       {
+               super();
+               UIHelper.restoreLAF(c.getApplication());
+
+               controller=c;
+               id=str;
+               content=null;
+               logger=Logger.getLogger(getClass().getName());
+
+               setUndecorated(false);
+               setResizable(true);
+               setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
+
+               controller.getApplication().addCritical(new 
org.gnu.freeway.util.AbstractAction() {
+                       public void perform()
+                       {
+                               if (isVisible()) {
+                                       
UIHelper.saveDims(controller.getApplication(),GFrame.this,id);
+                                       }
+                       }
+                       });
+       }
+
+       public String toString()
+       {
+               return "Frame";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public Controller getController()
+       {
+               return controller;
+       }
+
+       public String getID()
+       {
+               return id;
+       }
+
+       public JComponent getContent()
+       {
+               return content;
+       }
+
+       public void setContent( JComponent c )
+       {
+               content=c;
+       }
+
+       public JComponent createContent()
+       {
+               return null;
+       }
+
+       public void updateContent()
+       {
+       }
+
+       public void leaveContent()
+       {
+       }
+
+       public void display()
+       {
+               if (!isVisible()) {
+                       if (content==null) {
+                               content=createContent();
+                               if (content==null) {
+                                       logger.log(Level.INFO,"No content to 
display !");
+                                       return;
+                                       }
+                               }
+
+                       if (!content.equals(getContentPane())) {
+                               setContentPane(content);
+                               }
+
+                       updateContent();
+                       pack();
+
+                       
UIHelper.restoreDims(controller.getApplication(),this,id);
+                       setVisible(true);
+                       }
+               toFront();
+       }
+
+       public void close()
+       {
+               if (isVisible()) {
+                       setVisible(false);
+                       UIHelper.saveDims(controller.getApplication(),this,id);
+
+                       dispose();
+
+                       leaveContent();
+                       }
+       }
+
+       public void showMessage( String str )
+       {
+               //todo: avec icon de la frame
+               
JOptionPane.showMessageDialog(this,str,"Message",JOptionPane.INFORMATION_MESSAGE,null);
+       }
+
+       public boolean askMessage( String str )
+       {
+               return 
(JOptionPane.showConfirmDialog(this,str,"Question",JOptionPane.YES_NO_OPTION,JOptionPane.QUESTION_MESSAGE,null)==JOptionPane.YES_OPTION);
+       }
+}

Added: freeway/src/org/gnu/freeway/util/ui/GProgress.java
===================================================================
--- freeway/src/org/gnu/freeway/util/ui/GProgress.java  2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/util/ui/GProgress.java  2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,129 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util.ui;
+
+import org.gnu.freeway.util.*;
+
+import EDU.oswego.cs.dl.util.concurrent.*;
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+
+/**
+ *
+ */
+
+public abstract class GProgress extends GDialog
+{
+       private Semaphore               opened;
+       private String                  infos;
+
+       private GText                   text;
+       private JProgressBar    bar;
+       private JLabel                  label;
+
+
+       public GProgress( GFrame frame, String id )
+       {
+               super(frame,id);
+               init();
+       }
+
+       public GProgress( GDialog dialog, String id )
+       {
+               super(dialog,id);
+               init();
+       }
+
+       protected void init()
+       {
+               opened=new Semaphore(0);
+               infos="";
+
+               addWindowListener(new WindowAdapter() {
+                       public void windowOpened( WindowEvent evt )
+                       {
+                               opened.release();
+                       }
+                       });
+       }
+
+       public JComponent createContent()
+       {
+               GStack  stack;
+               JPanel  content;
+
+               text=new GText();
+
+               bar=new JProgressBar();
+               bar.setValue(0);
+               bar.setStringPainted(false);
+
+               label=new JLabel("");
+
+               stack=new GStack();
+               
stack.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEtchedBorder(),BorderFactory.createEmptyBorder(5,5,5,5)));
+               stack.setBackground(Color.WHITE);
+               stack.setOpaque(true);
+
+               stack.addWidget(text);
+               stack.addWidget(bar);
+               stack.addWidget(label);
+
+               content=new JPanel(new BorderLayout());
+               content.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
+               content.add(stack,BorderLayout.CENTER);
+               return content;
+       }
+
+       public void updateContent()
+       {
+               text.setText(infos);
+       }
+
+       public void launch( String str )
+       {
+               Task    task;
+
+               infos=str;
+
+               task=new Task("PROGRESS",new 
org.gnu.freeway.util.AbstractAction() {
+                       public void perform() throws InterruptedException
+                       {
+                               opened.acquire();
+                               GProgress.this.perform();
+                               SwingUtilities.invokeLater(new Runnable() {
+                                       public void run()
+                                       {
+                                               close();
+                                       }
+                                       });
+                       }
+                       });
+
+               task.launch();
+               display();
+               task.join();
+       }
+
+       public void progress( int at, int total, String txt )
+       {
+               final int               _at = at;
+               final int               _total = total;
+               final String            _txt = txt;
+
+               SwingUtilities.invokeLater(new Runnable() {
+                       public void run()
+                       {
+                               bar.setMaximum(_total);
+                               bar.setValue(_at);
+                               label.setText(_txt);
+                       }
+                       });
+       }
+
+       public abstract void perform();
+}

Added: freeway/src/org/gnu/freeway/util/ui/GScrollPane.java
===================================================================
--- freeway/src/org/gnu/freeway/util/ui/GScrollPane.java        2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/util/ui/GScrollPane.java        2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,40 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util.ui;
+
+import java.awt.*;
+import javax.swing.*;
+
+/**
+ *
+ */
+
+public class GScrollPane extends JScrollPane
+{
+       public GScrollPane()
+       {
+               super();
+       }
+
+       /**
+        * If this scrollpane has no ancestors that are aware of viewport 
changes, return default (true).
+        * Otherwise, return false to let viewport start validating at first 
reclaiming ancestor.
+        * @return
+        */
+
+       public boolean isValidateRoot()
+       {
+               Component       c;
+
+               // allow for container to be the 'root' to be validated
+               for (c=getParent(); c!=null; c=c.getParent()) {
+                       if ((c instanceof JComponent) && ((JComponent) 
c).isValidateRoot()) {
+                               return false;
+                               }
+                       }
+               // return default (true !)
+               return super.isValidateRoot();
+       }
+}

Added: freeway/src/org/gnu/freeway/util/ui/GSplash.java
===================================================================
--- freeway/src/org/gnu/freeway/util/ui/GSplash.java    2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/util/ui/GSplash.java    2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,109 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util.ui;
+
+import org.gnu.freeway.util.*;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.awt.image.*;
+import java.io.*;
+import java.net.*;
+import javax.imageio.*;
+import javax.swing.*;
+
+/**
+ *
+ */
+
+public class GSplash extends JFrame
+{
+       private Task                    task;
+       private BufferedImage   image;
+       private Point                   click;
+
+
+       public GSplash( URL url, int secs ) throws IOException
+       {
+               this(url,waitTask(secs));
+       }
+
+       public GSplash( URL url, Task t ) throws IOException
+       {
+               super();
+               task=t;
+               image=ImageIO.read(url);
+               click=new Point(0,0);
+
+               setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
+               setUndecorated(true);
+
+               addMouseMotionListener(new MouseMotionAdapter() {
+                       public void mouseDragged( MouseEvent e )
+                       {
+                               Point   p;
+
+                               p=getLocation();
+                               
setLocation(p.x+e.getX()-click.x,p.y+e.getY()-click.y);
+                       }
+                       });
+
+               addMouseListener(new MouseAdapter() {
+                       public void mousePressed( MouseEvent e )
+                       {
+                               click.x=e.getX();
+                               click.y=e.getY();
+                       }
+                       });
+       }
+
+       public String toString()
+       {
+               return "Splash";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public void splash()
+       {
+               Dimension       sz;
+
+               sz=getToolkit().getScreenSize();
+
+               pack();
+               
setBounds((sz.width-image.getWidth())/2,(sz.height-image.getHeight())/2,image.getWidth(),image.getHeight());
+               setVisible(true);
+
+               task.launch();
+               task.join();
+
+               setVisible(false);
+               dispose();
+       }
+
+       public void paint( Graphics g )
+       {
+               Graphics2D      g2;
+
+               g2=(Graphics2D) g;
+               g2.drawImage(image,0,0,this);
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       protected static Task waitTask( int secs )
+       {
+               final int       _secs = secs;
+
+               return new Task("WAIT",new 
org.gnu.freeway.util.AbstractAction() {
+                       public void perform()
+                       {
+                               Scheduler.sleep(Scheduler.seconds(_secs));
+                       }
+                       });
+       }
+}

Added: freeway/src/org/gnu/freeway/util/ui/GStack.java
===================================================================
--- freeway/src/org/gnu/freeway/util/ui/GStack.java     2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/util/ui/GStack.java     2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,72 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util.ui;
+
+import java.awt.*;
+import javax.swing.*;
+import javax.swing.text.*;
+
+/**
+ *
+ */
+
+public class GStack extends JPanel
+{
+       public GStack()
+       {
+               super();
+               setLayout(new BoxLayout(this,BoxLayout.PAGE_AXIS));
+               add(Box.createVerticalGlue());
+               setOpaque(false);
+       }
+
+       public void addWidget( JComponent c )
+       {
+               int     count;
+
+               c.setAlignmentX(SwingConstants.CENTER);
+
+               count=getComponentCount();
+               add(c,count-1);
+               if (count>1) {
+                       add(Box.createVerticalStrut(5),count-1);
+                       }
+       }
+
+       /**
+        * Return true to tell children to be revalidated when they need to 
revalidate.
+        * @return
+        */
+
+       public boolean isValidateRoot()
+       {
+               return true;
+       }
+
+       /**
+        * Before doing layout, ajust maximum dimensions of some components to 
maximum width and preferred height.
+        */
+
+       public void doLayout()
+       {
+               Component       c;
+               Dimension       size;
+               int                     i;
+
+               for (i=0; i<getComponentCount(); i++) {
+                       c=getComponent(i);
+
+                       if ((c instanceof JTextComponent) || (c instanceof 
JPanel) || (c instanceof JScrollPane) || (c instanceof Box)) {
+                               size=c.getPreferredSize();
+                               if (size!=null) {
+                                       size.width=Integer.MAX_VALUE;
+                                       ((JComponent) c).setMaximumSize(size);
+                                       }
+                               }
+                       }
+
+               super.doLayout();
+       }
+}

Added: freeway/src/org/gnu/freeway/util/ui/GStatus.java
===================================================================
--- freeway/src/org/gnu/freeway/util/ui/GStatus.java    2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/util/ui/GStatus.java    2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,147 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util.ui;
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+
+/**
+ *
+ */
+
+public class GStatus extends JLabel
+{
+       private MouseAdapter    mouseAdapter;
+       private String                  text;
+       private boolean                 isTemporary;
+       private int                             defHeight;
+
+
+       public GStatus()
+       {
+               super();
+               mouseAdapter=null;
+               text="";
+               isTemporary=false;
+
+               defHeight=getDefaultHeight()+6;
+               setBorder(BorderFactory.createEmptyBorder(0,4,0,4));
+       }
+
+       public String toString()
+       {
+               return "Status";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public Dimension getPreferredSize()
+       {
+               Dimension       size;
+
+               size=super.getPreferredSize();
+               size.height=defHeight;
+               return size;
+       }
+
+       public String getText()
+       {
+               return text;
+       }
+
+       public void setText( String str )
+       {
+               text=(str!=null ? str.trim() : "");
+               if (!isTemporary) {
+                       super.setText(text);
+                       }
+       }
+
+       public void clearTemporaryText()
+       {
+               isTemporary=false;
+               super.setText(text);
+       }
+
+       public void setTemporaryText( String str )
+       {
+               str=(str!=null ? str.trim() : "");
+               super.setText(str);
+               isTemporary=true;
+       }
+
+       public void observe( MenuElement me )
+       {
+               MenuElement[]   elems;
+               int                             i;
+
+               if (me.getComponent() instanceof AbstractButton) {
+                       if (mouseAdapter==null) {
+                               mouseAdapter=createHandler();
+                               }
+                       me.getComponent().addMouseListener(mouseAdapter);
+                       }
+
+               elems=me.getSubElements();
+               for (i=0; i<elems.length; i++) {
+                       observe(elems[i]);
+                       }
+       }
+
+       public void observe( JComponent c )
+       {
+               Component[]     comps;
+               int                     i;
+
+               if (c instanceof AbstractButton) {
+                       if (mouseAdapter==null) {
+                               mouseAdapter=createHandler();
+                               }
+                       c.addMouseListener(mouseAdapter);
+                       }
+
+               comps=c.getComponents();
+               for (i=0; i<comps.length; i++) {
+                       if (comps[i] instanceof JComponent) {
+                               observe((JComponent) comps[i]);
+                               }
+                       }
+       }
+
+       protected MouseAdapter createHandler()
+       {
+               return new MouseAdapter() {
+                       public void mouseEntered( MouseEvent evt )
+                       {
+//                             Action  ac;
+//                             Object  src;
+
+//                             src=evt.getSource();
+//System.out.println("mouse entered: "+src);
+/*                             if (src instanceof AbstractButton) {
+                                       ac=((AbstractButton) src).getAction();
+                                       if (ac!=null && 
ac.getValue(Action.LONG_DESCRIPTION)!=null) {
+                                               setTemporaryText((String) 
ac.getValue(Action.LONG_DESCRIPTION));
+                                               }
+                                       }*/
+                       }
+
+                       public void mouseExited( MouseEvent evt )
+                       {
+                               clearTemporaryText();
+                       }
+                       };
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public static int getDefaultHeight()
+       {
+               return new JLabel("A").getPreferredSize().height;
+       }
+}

Added: freeway/src/org/gnu/freeway/util/ui/GStyledText.java
===================================================================
--- freeway/src/org/gnu/freeway/util/ui/GStyledText.java        2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/util/ui/GStyledText.java        2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,254 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util.ui;
+
+import java.awt.*;
+import java.util.*;
+import java.util.logging.*;
+import java.util.regex.*;
+import javax.swing.*;
+import javax.swing.text.*;
+
+/**
+ *
+ */
+
+public class GStyledText extends GScrollPane
+{
+       private Matcher                                 tagMatcher;
+       private ArrayList                               tags;
+       private boolean                                 paraStart;
+       private Logger                                  logger;
+
+       private DefaultStyledDocument           styledDoc;
+       private StyleContext                            styleContext;
+       private Style                                   rootStyle;
+       private JTextPane                               textPanel;
+
+
+       public GStyledText()
+       {
+               this(false);
+       }
+
+       public GStyledText( boolean borders )
+       {
+               super();
+               tagMatcher=Pattern.compile("\\<(\\/?)(\\w+)\\>").matcher("");
+               tags=new ArrayList();
+               paraStart=true;
+               logger=Logger.getLogger(getClass().getName());
+
+               setup(borders);
+       }
+
+       public String toString()
+       {
+               return "Styled text";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       protected void setup( boolean borders )
+       {
+               Font    font;
+
+               styledDoc=new DefaultStyledDocument();
+               textPanel=new JTextPane(styledDoc);
+               textPanel.setCaretPosition(0);
+               textPanel.setEditable(false);
+
+               setVerticalScrollBarPolicy(VERTICAL_SCROLLBAR_AS_NEEDED);
+               setHorizontalScrollBarPolicy(HORIZONTAL_SCROLLBAR_NEVER);
+               setViewportView(textPanel);
+
+               if (borders) {
+                       textPanel.setMargin(new Insets(5,5,5,5));
+                       }
+               else {
+                       setBorder(null);
+                       setOpaque(false);
+                       textPanel.setOpaque(false);
+                       getViewport().setOpaque(false);
+                       }
+
+               styleContext=new StyleContext();
+
+               
rootStyle=styleContext.addStyle(StyleContext.DEFAULT_STYLE,null);
+
+               font=new JLabel().getFont();
+               StyleConstants.setFontFamily(rootStyle,font.getFamily());
+               StyleConstants.setFontSize(rootStyle,font.getSize());
+               StyleConstants.setBold(rootStyle,font.isBold());
+               StyleConstants.setItalic(rootStyle,font.isItalic());
+       }
+
+       public Style style()
+       {
+               return rootStyle;
+       }
+
+       public Style style( String str )
+       {
+               Style   style;
+
+               style=styleContext.getStyle(str);
+               return (style!=null ? style : styleContext.addStyle(str,null));
+       }
+
+       protected Style compoundStyle()
+       {
+               Style                           style,s;
+               MutableAttributeSet     attrs;
+               StringBuffer            buf;
+               String                          str;
+               int                                     i;
+
+               buf=new StringBuffer("S");
+               for (i=0; i<tags.size(); i++) {
+                       buf.append(":");
+                       buf.append((String) tags.get(i));
+                       }
+               str=buf.toString();
+
+               style=styleContext.getStyle(str);
+               if (style==null) {
+                       style=styleContext.addStyle(str,rootStyle);
+
+                       for (i=0; i<tags.size(); i++) {
+                               s=styleContext.getStyle((String) tags.get(i));
+                               if (s!=null) {
+                                       attrs=(MutableAttributeSet) 
s.copyAttributes();
+                                       
attrs.removeAttribute(AttributeSet.NameAttribute);
+                                       
attrs.removeAttribute(AttributeSet.ResolveAttribute);
+                                       style.addAttributes(attrs);
+                                       }
+                               }
+                       }
+               return style;
+       }
+
+       public GStyledText print( String str )
+       {
+               Style   style;
+               String  tag;
+               int             from;
+               boolean closing;
+
+               tagMatcher.reset(str);
+
+               for (from=0; tagMatcher.find(from); from=tagMatcher.end()) {
+                       tag=tagMatcher.group(2);
+                       closing=(tagMatcher.group(1)!=null && 
tagMatcher.group(1).equals("/"));
+                       if (closing && (tags.size()==0 || 
!tags.get(tags.size()-1).equals(tag))) {
+                               logger.log(Level.WARNING,"Invalid closing tag 
</"+tag+"> at position "+tagMatcher.start()+" !");
+                               return this;
+                               }
+
+                       style=compoundStyle();
+                       if (closing) {
+                               tags.remove(tags.size()-1);
+                               }
+                       else {
+                               tags.add(tag);
+                               }
+
+                       append(str.substring(from,tagMatcher.start()),style);
+                       }
+
+               append(str.substring(from),compoundStyle());
+               return this;
+       }
+
+       protected void append( String str, Style style )
+       {
+               if (str.length()>0) {
+                       try {
+                               if (paraStart) {
+                                       
styledDoc.setParagraphAttributes(styledDoc.getLength(),0,style,true);
+                                       paraStart=false;
+                                       }
+                               
styledDoc.insertString(styledDoc.getLength(),str,style);
+                               if (str.indexOf("\n")>=0) {
+                                       paraStart=true;
+                                       }
+                               }
+                       catch( BadLocationException x ) {
+                               logger.log(Level.SEVERE,"Unable to add text 
!",x);
+                               }
+                       }
+       }
+
+       public GStyledText print( Icon i )
+       {
+               Style   style;
+
+               try {
+                       style=styleContext.addStyle(null,null);
+                       StyleConstants.setIcon(style,i);
+                       styledDoc.insertString(styledDoc.getLength()," ",style);
+                       }
+               catch( BadLocationException x ) {
+                       logger.log(Level.SEVERE,"Unable to add text !",x);
+                       }
+               return this;
+       }
+
+       public GStyledText print( JComponent c )
+       {
+               Style   style;
+
+               try {
+                       c.setAlignmentY((float) 0.75);
+                       c.setOpaque(false);
+
+                       style=styleContext.addStyle(null,null);
+                       StyleConstants.setComponent(style,c);
+                       styledDoc.insertString(styledDoc.getLength()," ",style);
+                       }
+               catch( BadLocationException x ) {
+                       logger.log(Level.SEVERE,"Unable to add text !",x);
+                       }
+               return this;
+       }
+
+       public GStyledText println()
+       {
+               print("\n");
+               return this;
+       }
+
+       public GStyledText println( String str )
+       {
+               print(str);
+               print("\n");
+               return this;
+       }
+
+       public GStyledText println( Icon i )
+       {
+               print(i);
+               print("\n");
+               return this;
+       }
+
+       public GStyledText println( JComponent c )
+       {
+               print(c);
+               print("\n");
+               return this;
+       }
+
+       public void clear()
+       {
+               try {
+                       styledDoc.remove(0,styledDoc.getLength());
+                       }
+               catch( BadLocationException x ) {
+                       logger.log(Level.SEVERE,"Unable to clear text !",x);
+                       }
+       }
+}

Added: freeway/src/org/gnu/freeway/util/ui/GSwap.java
===================================================================
--- freeway/src/org/gnu/freeway/util/ui/GSwap.java      2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/util/ui/GSwap.java      2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,47 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util.ui;
+
+import java.awt.*;
+import javax.swing.*;
+
+/**
+ *
+ */
+
+public class GSwap extends JPanel
+{
+       private int     num;
+
+
+       public GSwap( JComponent comp )
+       {
+               this(comp,0);
+       }
+
+       public GSwap( JComponent comp, int gap )
+       {
+               super(new CardLayout(gap,gap));
+               add(comp,"0");
+               add(new JPanel(),"1");
+               num=0;
+       }
+
+       public void swap( JComponent comp )
+       {
+               comp.setSize(getComponent(0).getSize());
+
+               if ((num & 1)==0) {
+                       remove(1);
+                       add(comp,"1");
+                       }
+               else {
+                       remove(0);
+                       add(comp,"0",0);
+                       }
+               num++;
+               ((CardLayout) getLayout()).next(this);
+       }
+}

Added: freeway/src/org/gnu/freeway/util/ui/GTable.java
===================================================================
--- freeway/src/org/gnu/freeway/util/ui/GTable.java     2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/util/ui/GTable.java     2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,272 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util.ui;
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import javax.swing.table.*;
+
+/**
+ *
+ */
+
+public class GTable extends JTable implements SwingConstants
+{
+       private GColumn[]                       columns;
+       private CompoundComparator      comparator;
+       private SortedListModel         sortedModel;
+       private SortedSelectionModel    sortedSelection;
+       private TableCellRenderer               renderer;
+
+
+       protected GTable()
+       {
+               super();
+               columns=null;
+               comparator=null;
+               sortedModel=null;
+               sortedSelection=null;
+               renderer=new CellRenderer(getDefaultRenderer(Object.class));
+
+               
setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
+               setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
+               setAutoCreateColumnsFromModel(true);
+               setColumnSelectionAllowed(false);
+               getTableHeader().addMouseListener(new MouseHandler());
+               getTableHeader().setDefaultRenderer(new 
HeaderRenderer(getTableHeader().getDefaultRenderer()));
+       }
+
+       public GTable( Adapter a )
+       {
+               this();
+               comparator=new CompoundComparator(a);
+               setUnsortedModels(new DefaultListModel(),new 
DefaultListSelectionModel());
+       }
+
+       public GTable( Adapter a, ListModel lm )
+       {
+               this();
+               comparator=new CompoundComparator(a);
+               setUnsortedModels(lm,new DefaultListSelectionModel());
+       }
+
+       public GTable( Adapter a, ListModel lm, ListSelectionModel sm )
+       {
+               this();
+               comparator=new CompoundComparator(a);
+               setUnsortedModels(lm,sm);
+       }
+
+       public String toString()
+       {
+               return "Table";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public ListModel getUnsortedModel()
+       {
+               return (sortedModel!=null ? sortedModel.getModel() : null);
+       }
+
+       public ListSelectionModel getUnsortedSelectionModel()
+       {
+               return (sortedSelection!=null ? 
sortedSelection.getSelectionModel() : null);
+       }
+
+       public void setUnsortedModels( ListModel lm, ListSelectionModel sm )
+       {
+               sortedModel=new SortedListModel(comparator,lm,sm);
+               sortedSelection=new SortedSelectionModel(sortedModel,sm);
+
+               setModel(new 
ListToTableModel(sortedModel,comparator.getAdapter()) {
+                       public String getColumnName( int column )
+                       {
+                               return ((columns!=null && column>=0 && 
column<columns.length) ? columns[column].getTitle() : "");
+                       }
+                       });
+               setSelectionModel(sortedSelection);
+       }
+
+       public GColumn[] getColumns()
+       {
+               return columns;
+       }
+
+       public void setColumns( GColumn[] cols )
+       {
+               TableColumn     column;
+               int                     tw,w,i;
+
+               columns=cols;
+
+               ((ListToTableModel) getModel()).fireTableStructureChanged();
+
+               for (i=tw=0; i<columns.length; i++) {
+                       column=getColumnModel().getColumn(i);
+                       tw+=columns[i].getWidth();
+                       }
+
+               w=getPreferredSize().width;
+               for (i=0; i<columns.length; i++) {
+                       column=getColumnModel().getColumn(i);
+                       column.setPreferredWidth((int) (((double) 
columns[i].getWidth()*(double) w/tw)+0.5));
+                       }
+       }
+
+       public TableCellRenderer getCellRenderer( int row, int column )
+       {
+               return renderer;
+       }
+
+       public void setSelectedRows( int[] rows )
+       {
+               int     i;
+
+               for (i=0; i<rows.length; i++) {
+                       sortedSelection.addSelectionInterval(rows[i],rows[i]);
+                       }
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       protected class MouseHandler extends MouseAdapter
+       {
+               public void mouseClicked( MouseEvent evt )
+               {
+                       int     index;
+
+                       
index=convertColumnIndexToModel(getColumnModel().getColumnIndexAtX(evt.getX()));
+                       if (evt.getClickCount()==1 && index>=0 && 
columns[index].isSortable()) {
+                               
comparator.cycleSorting(index,!evt.isControlDown(),!evt.isShiftDown());
+
+                               sortedModel.sort();
+
+                               getTableHeader().repaint();
+                               }
+               }
+       }
+
+       protected class CellRenderer extends Object implements TableCellRenderer
+       {
+               private TableCellRenderer               renderer2;
+
+
+               public CellRenderer( TableCellRenderer r )
+               {
+                       super();
+                       renderer2=r;
+               }
+
+               public Component getTableCellRendererComponent( JTable table, 
Object obj, boolean selected, boolean focused, int row, int column )
+               {
+                       JLabel  label;
+                       int             index;
+
+                       index=convertColumnIndexToModel(column);
+
+                       label=(JLabel) 
renderer2.getTableCellRendererComponent(table,obj,selected,focused,row,column);
+                       
label.setHorizontalAlignment(columns[index].getAlignment());
+                       return label;
+               }
+       }
+
+       protected class HeaderRenderer extends Object implements 
TableCellRenderer
+       {
+               private TableCellRenderer               renderer2;
+               private Color                           c1;
+               private Color                           c2;
+               private int                                     height;
+
+
+               public HeaderRenderer( TableCellRenderer r )
+               {
+                       super();
+                       renderer2=r;
+                       c1=Color.BLACK;
+                       c2=new Color(0x00666666);
+                       height=new JLabel("A").getPreferredSize().height;
+               }
+
+               public Component getTableCellRendererComponent( JTable table, 
Object obj, boolean selected, boolean focused, int row, int column )
+               {
+                       JLabel  label;
+                       int             order,index,i;
+
+                       index=convertColumnIndexToModel(column);
+
+                       label=(JLabel) 
renderer2.getTableCellRendererComponent(table,obj,selected,focused,row,column);
+                       label.setForeground(columns[index].isSortable() ? c1 : 
c2);
+
+                       order=comparator.getSortOrder(index);
+                       if (order==CompoundComparator.ORDER_NONE) {
+                               label.setHorizontalTextPosition(CENTER);
+                               label.setIcon(null);
+                               }
+                       else {
+                               i=comparator.getSortPosition(index);
+
+                               label.setHorizontalTextPosition(LEFT);
+                               label.setIcon(new 
Arrow(height,i,order==CompoundComparator.ORDER_DESCENDANT));
+                               }
+                       return label;
+               }
+       }
+
+       public static class Arrow extends Object implements Icon
+       {
+               private int             size;
+               private int             index;
+               private boolean descendant;
+               private Color   color;
+
+
+               public Arrow( int sz, int i, boolean desc )
+               {
+                       super();
+                       size=sz;
+                       index=i;
+                       descendant=desc;
+                       color=UIManager.getColor("Table.selectionBackground");
+               }
+
+               public int getIconWidth()
+               {
+                       return size;
+               }
+
+               public int getIconHeight()
+               {
+                       return size;
+               }
+
+               public void paintIcon( Component c, Graphics g, int x, int y )
+               {
+                       int[]   px,py;
+                       int             sz;
+
+                       // in a compound sort, make each succesive triangle 
smaller than the previous one
+                       sz=(int) ((size*Math.pow(0.8,index))/2);
+
+                       if (descendant) {
+                               px=new int[] { x, x+sz/2, x+sz };
+                               py=new int[] { y+(size-sz)/2, y+(size+sz)/2, 
y+(size-sz)/2 };
+                               }
+                       else {
+                               px=new int[] { x, x+sz/2, x+sz };
+                               py=new int[] { y+(size+sz)/2, y+(size-sz)/2, 
y+(size+sz)/2 };
+                               }
+
+                       ((Graphics2D) 
g).setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
+                       g.setColor(color);
+                       g.fillPolygon(px,py,3);
+                       g.setColor(color.darker());
+                       g.drawPolygon(px,py,3);
+               }
+       }
+}

Added: freeway/src/org/gnu/freeway/util/ui/GText.java
===================================================================
--- freeway/src/org/gnu/freeway/util/ui/GText.java      2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/util/ui/GText.java      2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,74 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util.ui;
+
+import java.awt.*;
+import javax.swing.*;
+
+/**
+ *
+ */
+
+public class GText extends GScrollPane
+{
+       private JTextArea       area;
+
+
+       public GText()
+       {
+               this(false);
+       }
+
+       public GText( boolean borders )
+       {
+               super();
+               setup(borders);
+       }
+
+       public String toString()
+       {
+               return "Text";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       protected void setup( boolean borders )
+       {
+               area=new JTextArea();
+               area.setEditable(false);
+               area.setLineWrap(true);
+               area.setWrapStyleWord(true);
+
+               setVerticalScrollBarPolicy(VERTICAL_SCROLLBAR_AS_NEEDED);
+               setHorizontalScrollBarPolicy(HORIZONTAL_SCROLLBAR_NEVER);
+               setViewportView(area);
+
+               if (borders) {
+                       area.setMargin(new Insets(5,5,5,5));
+                       }
+               else {
+                       setBorder(null);
+                       setOpaque(false);
+                       area.setOpaque(false);
+                       getViewport().setOpaque(false);
+                       }
+       }
+
+       public String getText()
+       {
+               return area.getText();
+       }
+
+       public void setText( String str )
+       {
+               area.setText(str);
+       }
+
+       public void appendText( String str )
+       {
+               area.append(str);
+       }
+}

Added: freeway/src/org/gnu/freeway/util/ui/GWizard.java
===================================================================
--- freeway/src/org/gnu/freeway/util/ui/GWizard.java    2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/util/ui/GWizard.java    2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,206 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util.ui;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.net.*;
+import java.util.*;
+import javax.swing.*;
+
+/**
+ *
+ */
+
+public abstract class GWizard extends GDialog
+{
+       private ArrayList       pages;
+       private int                     currentPage;
+       private UIResources     resources;
+
+       private JPanel          content;
+       private JLabel          stepLabel;
+       private JLabel          iconLabel;
+       private GSwap           swapPanel;
+       private JPanel          buttonBar;
+       private JButton         prevButton;
+       private JButton         nextButton;
+       private JButton         execButton;
+
+
+       protected GWizard( Controller ctr, String id )
+       {
+               super(ctr,id);
+               initWizard();
+       }
+
+       protected GWizard( GFrame frame, String id )
+       {
+               super(frame,id);
+               initWizard();
+       }
+
+       protected void initWizard()
+       {
+               pages=new ArrayList();
+               currentPage=0;
+               resources=new 
UIResources("system/wizard.xml",getController().getApplication().getPreferences().getSystemCache());
+               resources.setGlobalTarget(this);
+       }
+
+       public String toString()
+       {
+               return "Wizard";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public abstract GWizardPage[] getSteps();
+
+       public abstract UIResources getResources();
+
+       public abstract void doTask();
+
+       public JComponent createContent()
+       {
+               GWizardPage[]   p;
+               int                             i;
+
+               addWindowListener(new WindowAdapter() {
+                       public void windowClosing( WindowEvent evt )
+                       {
+                               UIAction        a;
+
+                               a=resolvedAction("quit.action");
+                               if (a!=null) {
+                                       a.actionPerformed(null);
+                                       }
+                       }
+                       });
+
+               stepLabel=new JLabel("A");
+               stepLabel.setHorizontalAlignment(LEADING);
+               stepLabel.setPreferredSize(new 
Dimension(Integer.MAX_VALUE,stepLabel.getPreferredSize().height+7));
+
+               iconLabel=new JLabel(resolvedIcon("application.icon"));
+               iconLabel.setHorizontalAlignment(CENTER);
+               iconLabel.setVerticalAlignment(CENTER);
+
+               prevButton=new JButton(resources.getAction("previous.action"));
+               nextButton=new JButton(resources.getAction("next.action"));
+               execButton=new JButton(resources.getAction("exec.action"));
+
+               buttonBar=new JPanel();
+               buttonBar.setLayout(new 
BoxLayout(buttonBar,BoxLayout.LINE_AXIS));
+               buttonBar.add(Box.createHorizontalGlue());
+               buttonBar.add(prevButton);
+               buttonBar.add(Box.createRigidArea(new Dimension(10,0)));
+               buttonBar.add(nextButton);
+               buttonBar.add(Box.createRigidArea(new Dimension(10,0)));
+               buttonBar.add(execButton);
+
+               swapPanel=new GSwap(new JPanel(),5);
+               swapPanel.setBorder(BorderFactory.createEtchedBorder());
+               swapPanel.setBackground(Color.WHITE);
+
+               content=new JPanel(new TileLayout(2,1));
+               content.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
+               content.add(iconLabel,TileLayout.WEST);
+               content.add(stepLabel,TileLayout.NORTH);
+               content.add(buttonBar,TileLayout.SOUTH);
+               content.add(swapPanel);
+
+               p=getSteps();
+               for (i=0; i<p.length; i++) {
+                       pages.add(p[i]);
+                       }
+
+               current().createViewIfNot();
+               current().updateView();
+
+               refresh();
+               return content;
+       }
+
+       protected UIAction resolvedAction( String str )
+       {
+               UIResources     res;
+               UIAction        a;
+
+               res=getResources();
+               a=(res!=null ? res.getAction(str) : null);
+               return (a!=null ? a : resources.getAction(str));
+       }
+
+       protected Icon resolvedIcon( String str )
+       {
+               UIResources     res;
+               URL                     url;
+
+               res=getResources();
+               url=(res!=null ? res.getImageURL(str) : null);
+               if (url==null) {
+                       url=resources.getImageURL(str);
+                       }
+               return (url!=null ? new ImageIcon(url) : null);
+       }
+
+       public void onNext()
+       {
+               String  str;
+
+               if (currentPage<pages.size()-1) {
+                       current().createViewIfNot();
+
+                       str=current().canLeaveView();
+                       if (str!=null) {
+                               showMessage("Page not completed",str);
+                               return;
+                               }
+
+                       current().leaveView();
+                       currentPage++;
+                       current().createViewIfNot();
+                       current().updateView();
+                       }
+
+               refresh();
+       }
+
+       public void onPrevious()
+       {
+               if (currentPage>0) {
+                       current().createViewIfNot();
+                       current().leaveView();
+                       currentPage--;
+                       current().createViewIfNot();
+                       current().updateView();
+                       }
+
+               refresh();
+       }
+
+       public void onExec()
+       {
+               current().leaveView();
+
+               doTask();
+       }
+
+       public GWizardPage current()
+       {
+               return (GWizardPage) pages.get(currentPage);
+       }
+
+       protected void refresh()
+       {
+               stepLabel.setText(current().getTitle()+" (page 
"+(currentPage+1)+" out of "+pages.size()+")");
+               swapPanel.swap(current().getView());
+               
resources.getAction("next.action").setEnabled(currentPage<pages.size()-1);
+               
resources.getAction("previous.action").setEnabled(currentPage>0);
+               
resources.getAction("exec.action").setEnabled(currentPage==pages.size()-1);
+       }
+}

Added: freeway/src/org/gnu/freeway/util/ui/GWizardPage.java
===================================================================
--- freeway/src/org/gnu/freeway/util/ui/GWizardPage.java        2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/util/ui/GWizardPage.java        2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,61 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util.ui;
+
+import javax.swing.*;
+
+/**
+ *
+ */
+
+public class GWizardPage extends Object
+{
+       private String          title;
+       private JComponent      component;
+
+
+       public GWizardPage( String str )
+       {
+               super();
+               title=str;
+               component=null;
+       }
+
+       public String getTitle()
+       {
+               return title;
+       }
+
+       public void createViewIfNot()
+       {
+               if (component==null) {
+                       component=createView();
+                       }
+       }
+
+       public JComponent getView()
+       {
+               createViewIfNot();
+               return component;
+       }
+
+       public JComponent createView()
+       {
+               return null;
+       }
+
+       public void updateView()
+       {
+       }
+
+       public String canLeaveView()
+       {
+               return null;
+       }
+
+       public void leaveView()
+       {
+       }
+}

Added: freeway/src/org/gnu/freeway/util/ui/ListToTableModel.java
===================================================================
--- freeway/src/org/gnu/freeway/util/ui/ListToTableModel.java   2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/util/ui/ListToTableModel.java   2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,85 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util.ui;
+
+import javax.swing.*;
+import javax.swing.event.*;
+import javax.swing.table.*;
+
+/**
+ *
+ */
+
+public class ListToTableModel extends AbstractTableModel implements 
ListDataListener
+{
+       private ListModel       model;
+       private Adapter         adapter;
+
+
+       public ListToTableModel( ListModel m, Adapter a )
+       {
+               super();
+               model=m;
+               model.addListDataListener(this);
+               adapter=a;
+       }
+
+       public String toString()
+       {
+               return "List to table model [model="+model+", 
adapter="+adapter+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public ListModel getModel()
+       {
+               return model;
+       }
+
+       public Adapter getAdapter()
+       {
+               return adapter;
+       }
+
+       public void intervalAdded( ListDataEvent evt )
+       {
+               fireTableRowsInserted(evt.getIndex0(),evt.getIndex1());
+       }
+
+       public void intervalRemoved( ListDataEvent evt )
+       {
+               fireTableRowsDeleted(evt.getIndex0(),evt.getIndex1());
+       }
+
+       public void contentsChanged( ListDataEvent evt )
+       {
+               fireTableRowsUpdated(evt.getIndex0(),evt.getIndex1());
+       }
+
+       public int getColumnCount()
+       {
+               return adapter.getFacetCount();
+       }
+
+       public int getRowCount()
+       {
+               return model.getSize();
+       }
+
+       public Object getValueAt( int row, int column )
+       {
+               Object  obj;
+
+               obj=((row>=0 && row<model.getSize()) ? model.getElementAt(row) 
: null);
+               adapter.setObject(obj);
+               return ((column>=0 && column<adapter.getFacetCount()) ? 
adapter.getFacet(column) : null);
+       }
+
+       public boolean isCellEditable( int row, int column )
+       {
+               return false;
+       }
+}

Added: freeway/src/org/gnu/freeway/util/ui/SVGRenderer.java
===================================================================
--- freeway/src/org/gnu/freeway/util/ui/SVGRenderer.java        2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/util/ui/SVGRenderer.java        2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,85 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util.ui;
+
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.io.*;
+
+import java.io.*;
+import java.net.*;
+
+import org.apache.batik.transcoder.*;
+import org.apache.batik.transcoder.image.*;
+
+/**
+ *
+ */
+
+public class SVGRenderer extends LoggedObject
+{
+       private URL     from;
+
+
+       public SVGRenderer( URL url )
+       {
+               super(true);
+               from=url;
+       }
+
+       public boolean renderToPNG( FileLocation to, int w, int h )
+       {
+               ImageTranscoder trans;
+
+               trans=new PNGTranscoder();
+               trans.addTranscodingHint(SVGAbstractTranscoder.KEY_WIDTH,new 
Float(w));
+               trans.addTranscodingHint(SVGAbstractTranscoder.KEY_HEIGHT,new 
Float(h));
+               return render(trans,to);
+       }
+
+       public boolean renderToJPEG( FileLocation to, int w, int h, float 
quality )
+       {
+               ImageTranscoder trans;
+
+               trans=new JPEGTranscoder();
+               trans.addTranscodingHint(SVGAbstractTranscoder.KEY_WIDTH,new 
Float(w));
+               trans.addTranscodingHint(SVGAbstractTranscoder.KEY_HEIGHT,new 
Float(h));
+               trans.addTranscodingHint(JPEGTranscoder.KEY_QUALITY,new 
Float(quality));
+               return render(trans,to);
+       }
+
+       public boolean renderToTIFF( FileLocation to, int w, int h )
+       {
+               ImageTranscoder trans;
+
+               trans=new TIFFTranscoder();
+               trans.addTranscodingHint(SVGAbstractTranscoder.KEY_WIDTH,new 
Float(w));
+               trans.addTranscodingHint(SVGAbstractTranscoder.KEY_HEIGHT,new 
Float(h));
+               
trans.addTranscodingHint(TIFFTranscoder.KEY_FORCE_TRANSPARENT_WHITE,Boolean.TRUE);
+               return render(trans,to);
+       }
+
+       protected boolean render( ImageTranscoder trans, FileLocation to )
+       {
+               OutputStream    os;
+
+               try {
+                       os=new BufferedOutputStream(new 
FileOutputStream(to.getPath()));
+                       try {
+                               trans.transcode(new 
TranscoderInput(from.toString()),new TranscoderOutput(os));
+                               return true;
+                               }
+                       finally {
+                               os.close();
+                               }
+                       }
+               catch( IOException x ) {
+                       err("Could not render SVG !",x);
+                       }
+               catch( TranscoderException x ) {
+                       err("Could not render SVG !",x);
+                       }
+               return false;
+       }
+}

Added: freeway/src/org/gnu/freeway/util/ui/SortedListModel.java
===================================================================
--- freeway/src/org/gnu/freeway/util/ui/SortedListModel.java    2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/util/ui/SortedListModel.java    2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,234 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util.ui;
+
+import java.util.*;
+import javax.swing.*;
+import javax.swing.event.*;
+
+/**
+ *
+ */
+
+public class SortedListModel extends AbstractListModel implements 
ListDataListener
+{
+       private Comparator                      comparator;
+       private ListModel                       model;
+       private ListSelectionModel      selModel;
+
+       private int                                     rowCount;
+       private int[]                           toSorted;
+
+
+       public SortedListModel( Comparator c, ListModel lm, ListSelectionModel 
sm )
+       {
+               super();
+               comparator=c;
+               model=null;
+               selModel=null;
+               rowCount=0;
+               toSorted=null;
+
+               setModels(lm,sm);
+       }
+
+       public String toString()
+       {
+               return "Sorted list model [comparator="+comparator+", 
model="+model+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int getSize()
+       {
+               return rowCount;
+       }
+
+       public Object getElementAt( int index )
+       {
+               return model.getElementAt(toUnsorted(index));
+       }
+
+       public Comparator getComparator()
+       {
+               return comparator;
+       }
+
+       public ListModel getModel()
+       {
+               return model;
+       }
+
+       public void setModels( ListModel lm, ListSelectionModel sm )
+       {
+               int     i;
+
+               if (model!=null) {
+                       model.removeListDataListener(this);
+                       }
+               model=lm;
+               model.addListDataListener(this);
+               selModel=sm;
+
+               rowCount=model.getSize();
+               toSorted=new int[rowCount];
+               for (i=0; i<rowCount; i++) {
+                       toSorted[i]=i;
+                       }
+       }
+
+       public int toSorted( int index )
+       {
+               return toSorted[index];
+       }
+
+       public int toUnsorted( int index )
+       {
+               int     i;
+
+               for (i=0; toSorted[i]!=index; i++) {}
+               return i;
+       }
+
+       public void sort()
+       {
+               BitSet  sel;
+
+               sel=saveSel();
+               sort(rowCount);
+               fireContentsChanged(this,0,rowCount-1);
+               setSel(sel);
+       }
+
+       public void intervalAdded( ListDataEvent evt )
+       {
+               int[]   old;
+               BitSet  sel;
+               int             delta;
+
+               sel=saveSel();
+
+               delta=evt.getIndex1()-evt.getIndex0()+1;
+//             System.out.println("> interval to be added  : 
["+evt.getIndex0()+","+evt.getIndex1()+"] / "+rowCount+" + "+delta);
+
+               if (rowCount+delta>toSorted.length) {
+                       old=toSorted;
+                       toSorted=new int[rowCount+delta+4];
+                       System.arraycopy(old,0,toSorted,0,old.length);
+                       }
+
+               sort(rowCount+delta);
+               fireContentsChanged(this,0,rowCount-1);
+               rowCount+=delta;
+               fireIntervalAdded(this,rowCount-delta,rowCount-1);
+
+               setSel(sel);
+       }
+
+       public void intervalRemoved( ListDataEvent evt )
+       {
+               BitSet  sel;
+               int             delta,i;
+
+               sel=saveSel();
+               for (i=evt.getIndex0(); i<=evt.getIndex1(); i++) {
+                       sel.clear(i);
+                       }
+
+               delta=evt.getIndex1()-evt.getIndex0()+1;
+//             System.out.println("> interval to be removed  : 
["+evt.getIndex0()+","+evt.getIndex1()+"] / "+rowCount+" - "+delta);
+
+               rowCount-=delta;
+               sort(rowCount);
+               fireIntervalRemoved(this,rowCount,rowCount+delta-1);
+               fireContentsChanged(this,0,rowCount-1);
+
+               setSel(sel);
+       }
+
+       public void contentsChanged( ListDataEvent evt )
+       {
+               BitSet  sel;
+               int             /*delta,*/i;
+
+               sel=saveSel();
+               for (i=evt.getIndex0(); i<=evt.getIndex1(); i++) {
+                       sel.clear(i);
+                       }
+
+//             delta=evt.getIndex1()-evt.getIndex0()+1;
+//             System.out.println("> content to be changed  : 
["+evt.getIndex0()+","+evt.getIndex1()+"] / "+rowCount+" = "+delta);
+
+               sort(rowCount);
+               fireContentsChanged(this,0,rowCount-1);
+
+               setSel(sel);
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public void sort( int num )
+       {
+               Integer[]       indexes;
+               int                     i;
+
+               indexes=new Integer[num];
+               for (i=0; i<indexes.length; i++) {
+                       indexes[i]=new Integer(i);
+                       }
+
+               Arrays.sort(indexes,new Comparator() {
+                       public int compare( Object o1, Object o2 )
+                       {
+                               o1=model.getElementAt(((Integer) 
o1).intValue());
+                               o2=model.getElementAt(((Integer) 
o2).intValue());
+                               return comparator.compare(o1,o2);
+                       }
+                       });
+
+               for (i=0; i<indexes.length; i++) {
+                       toSorted[indexes[i].intValue()]=i;
+                       }
+
+//             debugSort(num);
+       }
+
+       protected void debugSort( int num )
+       {
+               int     i;
+
+               System.out.println("> sort result : ");
+               for (i=0; i<num; i++) {
+                       System.out.println("  "+i+" : "+model.getElementAt(i)+" 
: "+toSorted[i]);
+                       }
+               System.out.println();
+       }
+
+       protected BitSet saveSel()
+       {
+               BitSet  sel;
+               int             i;
+
+               sel=new BitSet();
+               for (i=selModel.getMinSelectionIndex(); 
i<=selModel.getMaxSelectionIndex(); i++) {
+                       if (selModel.isSelectedIndex(i)) {
+                               sel.set(i);
+                               }
+                       }
+               selModel.clearSelection();
+               return sel;
+       }
+
+       protected void setSel( BitSet sel )
+       {
+               int     i;
+
+               for (i=sel.nextSetBit(0); i>=0; i=sel.nextSetBit(i+1)) {
+                       selModel.addSelectionInterval(i,i);
+                       }
+       }
+}

Added: freeway/src/org/gnu/freeway/util/ui/SortedSelectionModel.java
===================================================================
--- freeway/src/org/gnu/freeway/util/ui/SortedSelectionModel.java       
2005-01-31 23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/util/ui/SortedSelectionModel.java       
2005-02-01 01:07:27 UTC (rev 137)
@@ -0,0 +1,171 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util.ui;
+
+import javax.swing.*;
+import javax.swing.event.*;
+
+/**
+ *
+ */
+
+public class SortedSelectionModel extends DefaultListSelectionModel implements 
ListSelectionListener
+{
+       private SortedListModel         sortedModel;
+       private ListSelectionModel      model;
+       private boolean                         ignore;
+
+
+       public SortedSelectionModel( SortedListModel s, ListSelectionModel m )
+       {
+               super();
+               sortedModel=s;
+               model=m;
+
+               setSelectionMode(m.getSelectionMode());
+               
m.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
+               m.addListSelectionListener(this);
+
+               ignore=false;
+       }
+
+       public String toString()
+       {
+               return "Sorted selection model";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public ListSelectionModel getSelectionModel()
+       {
+               return model;
+       }
+
+       public void valueChanged( ListSelectionEvent evt )
+       {
+               int     i,j;
+
+               if (!ignore && !evt.getValueIsAdjusting()) {
+                       ignore=true;
+//                     System.out.println("> received : "+debugEvent(evt));
+
+                       for (i=evt.getFirstIndex(); i<=evt.getLastIndex(); i++) 
{
+                               j=sortedModel.toSorted(i);
+                               if (model.isSelectedIndex(i)) {
+                                       addSelectionInterval(j,j);
+                                       }
+                               else {
+                                       removeSelectionInterval(j,j);
+                                       }
+                               }
+
+                       ignore=false;
+                       }
+       }
+
+       protected void update()
+       {
+               int     i,j;
+
+               if (!ignore) {
+                       ignore=true;
+//                     System.out.println("> update : "+debugSelection());
+
+                       model.clearSelection();
+                       for (i=getMinSelectionIndex(); 
i<=getMaxSelectionIndex(); i++) {
+                               if (isSelectedIndex(i)) {
+                                       j=sortedModel.toUnsorted(i);
+                                       model.addSelectionInterval(j,j);
+                                       }
+                               }
+
+                       ignore=false;
+                       }
+       }
+
+       public void setSelectionMode( int mode )
+       {
+               super.setSelectionMode(mode);
+               update();
+       }
+
+       public void addSelectionInterval( int index0, int index1 )
+       {
+               super.addSelectionInterval(index0,index1);
+               update();
+       }
+
+       public void removeSelectionInterval( int index0, int index1 )
+       {
+               super.removeSelectionInterval(index0,index1);
+               update();
+       }
+
+       public void setSelectionInterval( int index0, int index1 )
+       {
+               super.setSelectionInterval(index0,index1);
+               update();
+       }
+
+       public void clearSelection()
+       {
+               super.clearSelection();
+               update();
+       }
+
+       public void setAnchorSelectionIndex( int index )
+       {
+               super.setAnchorSelectionIndex(index);
+               update();
+       }
+
+       public void setLeadSelectionIndex( int index )
+       {
+               super.setLeadSelectionIndex(index);
+               update();
+       }
+
+       public void insertIndexInterval( int index, int length, boolean before )
+       {
+               super.insertIndexInterval(index,length,before);
+               update();
+       }
+
+       public void removeIndexInterval( int index0, int index1 )
+       {
+               super.removeIndexInterval(index0,index1);
+               update();
+       }
+
+       protected String debugSelection()
+       {
+               StringBuffer    buf;
+               int                             i;
+
+               buf=new StringBuffer("[ ");
+               for (i=getMinSelectionIndex(); i<=getMaxSelectionIndex(); i++) {
+                       if (isSelectedIndex(i)) {
+                               buf.append(i+" ");
+                               }
+                       }
+               buf.append("]");
+               return buf.toString();
+       }
+
+       protected String debugEvent( ListSelectionEvent evt )
+       {
+               StringBuffer    buf;
+               int                             i;
+
+               buf=new StringBuffer("[ ");
+               for (i=evt.getFirstIndex(); i<=evt.getLastIndex(); i++) {
+                       buf.append(model.isSelectedIndex(i) ? "+" : "-");
+                       buf.append(sortedModel.toSorted(i)+" ");
+                       }
+               buf.append("]");
+               return buf.toString();
+       }
+}

Added: freeway/src/org/gnu/freeway/util/ui/TileLayout.java
===================================================================
--- freeway/src/org/gnu/freeway/util/ui/TileLayout.java 2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/util/ui/TileLayout.java 2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,276 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util.ui;
+
+import java.awt.*;
+import java.util.*;
+
+/**
+ *
+ */
+
+public class TileLayout extends Object implements LayoutManager2
+{
+       public static final String              NORTH                   =       
"North";
+       public static final String              SOUTH                   =       
"South";
+       public static final String              EAST                    =       
"East";
+       public static final String              WEST                    =       
"West";
+       public static final String              CENTER                  =       
"Center";
+
+       protected static final String[] ALL                             =       
{ NORTH, SOUTH, EAST, WEST, CENTER };
+       protected static final int              NORTH_INDEX             =       
0;
+       protected static final int              SOUTH_INDEX             =       
1;
+       protected static final int              EAST_INDEX              =       
2;
+       protected static final int              WEST_INDEX              =       
3;
+       protected static final int              CENTER_INDEX    =       4;
+
+       protected int           hgap;
+       protected int           vgap;
+       protected ArrayList     components;
+       protected ArrayList     positions;
+
+
+       public TileLayout()
+       {
+               super();
+               hgap=0;
+               vgap=0;
+               components=new ArrayList();
+               positions=new ArrayList();
+       }
+
+       public TileLayout( int h, int v )
+       {
+               this();
+               setHorizontalGap(h);
+               setVerticalGap(v);
+       }
+
+       public String toString()
+       {
+               return "Tile layout [hgap="+hgap+", vgap="+vgap+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public int getHorizontalGap()
+       {
+               return hgap;
+       }
+
+       public void setHorizontalGap( int value )
+       {
+               assert(value>=0);
+               hgap=value;
+       }
+
+       public int getVerticalGap()
+       {
+               return vgap;
+       }
+
+       public void setVerticalGap( int value )
+       {
+               assert(value>=0);
+               vgap=value;
+       }
+
+       public void addLayoutComponent( String string, Component component )
+       {
+               addLayoutComponent(component,string);
+       }
+
+       public void addLayoutComponent( Component component, Object constraints 
)
+       {
+               int     i;
+
+               if (constraints==null) {
+                       constraints=CENTER;
+                       }
+
+               for (i=0; i<ALL.length && !ALL[i].equals(constraints); i++) {}
+               if (i==ALL.length) {
+                       throw new IllegalArgumentException("Cannot add to 
layout, unknown constraint : "+constraints);
+                       }
+
+               synchronized(component.getTreeLock()) {
+                       components.add(component);
+                       positions.add(new Integer(i));
+                       }
+       }
+
+       public void removeLayoutComponent( Component component )
+       {
+               int     i;
+
+               synchronized(component.getTreeLock()) {
+                       i=components.indexOf(component);
+                       if (i>=0) {
+                               components.remove(i);
+                               positions.remove(i);
+                               }
+                       }
+       }
+
+       public float getLayoutAlignmentX( Container container )
+       {
+               return (float) 0.5;
+       }
+
+       public float getLayoutAlignmentY( Container container )
+       {
+               return (float) 0.5;
+       }
+
+       public Dimension minimumLayoutSize( Container container )
+       {
+               Component       component;
+               Dimension       dim,d;
+               Insets          insets;
+               int                     i;
+
+               dim=new Dimension(0,0);
+
+               synchronized(container.getTreeLock()) {
+                       for (i=components.size(); i>0; i--) {
+                               component=(Component) components.get(i-1);
+
+                               if (component.isVisible()) {
+                                       d=component.getMinimumSize();
+
+                                       switch (((Integer) 
positions.get(i-1)).intValue()) {
+                                               case NORTH_INDEX:
+                                               case SOUTH_INDEX:
+                                                       
dim.width=Math.max(d.width,dim.width);
+                                                       
dim.height+=d.height+vgap;
+                                                       break;
+
+                                               case EAST_INDEX:
+                                               case WEST_INDEX:
+                                                       dim.width+=d.width+hgap;
+                                                       
dim.height=Math.max(d.height,dim.height);
+                                                       break;
+
+                                               default:
+                                                       dim.width+=d.width;
+                                                       dim.height+=d.height;
+                                                       break;
+                                               }
+                                       }
+                               }
+
+                       insets=container.getInsets();
+                       dim.width+=insets.left+insets.right;
+                       dim.height+=insets.top+insets.bottom;
+                       }
+               return dim;
+       }
+
+       public Dimension maximumLayoutSize( Container container )
+       {
+               return new Dimension(Integer.MAX_VALUE,Integer.MAX_VALUE);
+       }
+
+       public Dimension preferredLayoutSize( Container container )
+       {
+               Component       component;
+               Dimension       dim,d;
+               Insets          insets;
+               int                     i;
+
+               dim=new Dimension(0,0);
+
+               synchronized(container.getTreeLock()) {
+                       for (i=components.size(); i>0; i--) {
+                               component=(Component) components.get(i-1);
+
+                               if (component.isVisible()) {
+                                       d=component.getPreferredSize();
+
+                                       switch (((Integer) 
positions.get(i-1)).intValue()) {
+                                               case NORTH_INDEX:
+                                               case SOUTH_INDEX:
+                                                       
dim.width=Math.max(d.width,dim.width);
+                                                       
dim.height+=d.height+vgap;
+                                                       break;
+
+                                               case EAST_INDEX:
+                                               case WEST_INDEX:
+                                                       dim.width+=d.width+hgap;
+                                                       
dim.height=Math.max(d.height,dim.height);
+                                                       break;
+
+                                               default:
+                                                       dim.width+=d.width;
+                                                       dim.height+=d.height;
+                                                       break;
+                                               }
+                                       }
+                               }
+
+                       insets=container.getInsets();
+                       dim.width+=insets.left+insets.right;
+                       dim.height+=insets.top+insets.bottom;
+                       }
+               return dim;
+       }
+
+       public void invalidateLayout( Container container )
+       {
+       }
+
+       public void layoutContainer( Container container )
+       {
+               Component       component;
+               Dimension       d;
+               Insets          insets;
+               int                     top,bottom,left,right,imax,i;
+
+               synchronized(container.getTreeLock()) {
+                       insets=container.getInsets();
+
+                       top=insets.top;
+                       bottom=container.getSize().height-insets.bottom;
+                       left=insets.left;
+                       right=container.getSize().width-insets.right;
+
+                       imax=components.size();
+                       for (i=0; i<imax; i++) {
+                               component=(Component) components.get(i);
+
+                               if (component.isVisible()) {
+                                       d=component.getPreferredSize();
+
+                                       switch (((Integer) 
positions.get(i)).intValue()) {
+                                               case NORTH_INDEX:
+                                                       
component.setBounds(left,top,right-left,d.height);
+                                                       top+=d.height+vgap;
+                                                       break;
+
+                                               case SOUTH_INDEX:
+                                                       
component.setBounds(left,bottom-d.height,right-left,d.height);
+                                                       bottom-=d.height+vgap;
+                                                       break;
+
+                                               case EAST_INDEX:
+                                                       
component.setBounds(right-d.width,top,d.width,bottom-top);
+                                                       right-=d.width+hgap;
+                                                       break;
+
+                                               case WEST_INDEX:
+                                                       
component.setBounds(left,top,d.width,bottom-top);
+                                                       left+=d.width+hgap;
+                                                       break;
+
+                                               default:
+                                                       
component.setBounds(left,top,right-left,bottom-top);
+                                                       break;
+                                               }
+                                       }
+                               }
+                       }
+       }
+}

Added: freeway/src/org/gnu/freeway/util/ui/UIAction.java
===================================================================
--- freeway/src/org/gnu/freeway/util/ui/UIAction.java   2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/util/ui/UIAction.java   2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,138 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util.ui;
+
+import org.gnu.freeway.util.*;
+
+import java.awt.event.*;
+import java.lang.reflect.*;
+import java.net.*;
+import javax.swing.*;
+
+/**
+ *
+ */
+
+public class UIAction extends javax.swing.AbstractAction
+{
+       private String  method;
+       private Object  target;
+
+
+       public UIAction( UIResources res, Voyager v )
+       {
+               super();
+               method=null;
+               target=null;
+               init(res,v);
+       }
+
+       public String toString()
+       {
+               return "UI action [method="+method+", target="+target+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       protected void init( UIResources res, Voyager v )
+       {
+               URL             url;
+               String  str;
+
+               method=v.get("method","");
+
+               putValue(javax.swing.Action.ACTION_COMMAND_KEY,v.get("id",""));
+
+               v.selectTag("label");
+               if (v.next()) {
+                       putValue(javax.swing.Action.NAME,v.text(""));
+                       }
+               v.up();
+
+               v.selectTag("description");
+               if (v.next()) {
+                       
putValue(javax.swing.Action.LONG_DESCRIPTION,v.text(""));
+                       }
+               v.up();
+
+               v.selectTag("icon");
+               if (v.next()) {
+                       str=v.get("refid","");
+                       if (str.length()>0) {
+                               url=res.getImageURL(str);
+                               if (url!=null) {
+                                       
putValue(javax.swing.Action.SMALL_ICON,new ImageIcon(url));
+                                       }
+                               }
+                       else {
+                               
url=getClass().getClassLoader().getResource(v.text(""));
+                               if (url!=null) {
+                                       
putValue(javax.swing.Action.SMALL_ICON,new ImageIcon(url));
+                                       }
+                               }
+                       }
+               v.up();
+
+               v.selectTag("shortcut");
+               if (v.next()) {
+                       
putValue(javax.swing.Action.ACCELERATOR_KEY,KeyStroke.getKeyStroke(v.text("")));
+                       }
+               v.up();
+
+               v.selectTag("accelerator");
+               if (v.next()) {
+                       str=v.text("");
+                       if (str.length()==1) {
+                               putValue(javax.swing.Action.MNEMONIC_KEY,new 
Integer(str.toUpperCase().charAt(0)));
+                               }
+                       }
+               v.up();
+       }
+
+       public String getID()
+       {
+               return (String) getValue(javax.swing.Action.ACTION_COMMAND_KEY);
+       }
+
+       public String getMethod()
+       {
+               return method;
+       }
+
+       public Object getTarget()
+       {
+               return target;
+       }
+
+       public void setTarget( Object obj )
+       {
+               target=obj;
+       }
+
+       public void actionPerformed( ActionEvent evt )
+       {
+               Method          meth;
+               Throwable       xx;
+
+               if (target!=null && method!=null && method.length()>0) {
+                       try {
+                               meth=target.getClass().getMethod(method,null);
+                               meth.invoke(target,null);
+                               }
+                       catch( NoSuchMethodException x ) {
+                               System.err.println("No such method : "+method);
+                               }
+                       catch( InvocationTargetException x ) {
+                               xx=(x.getCause()!=null ? x.getCause() : x);
+                               System.err.println("Exception on target : 
"+xx.getMessage());
+                               xx.printStackTrace(System.err);
+                               }
+                       catch( IllegalAccessException x ) {
+                               System.err.println("No access for method : 
"+method);
+                               }
+                       }
+       }
+}

Added: freeway/src/org/gnu/freeway/util/ui/UIHelper.java
===================================================================
--- freeway/src/org/gnu/freeway/util/ui/UIHelper.java   2005-01-31 23:47:23 UTC 
(rev 136)
+++ freeway/src/org/gnu/freeway/util/ui/UIHelper.java   2005-02-01 01:07:27 UTC 
(rev 137)
@@ -0,0 +1,197 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util.ui;
+
+import org.gnu.freeway.*;
+import org.gnu.freeway.util.*;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.util.*;
+import java.util.logging.*;
+import javax.swing.*;
+import javax.swing.table.*;
+
+/**
+ *
+ */
+
+public class UIHelper extends Object
+{
+       private static Logger   logger;
+       private static boolean  lafOnce;
+
+       static {
+               logger=Logger.getLogger(UIHelper.class.getName());
+               lafOnce=false;
+
+               javax.swing.JFrame.setDefaultLookAndFeelDecorated(false);
+               java.awt.Toolkit.getDefaultToolkit().setDynamicLayout(true);
+               //fixme: how to set anti-aliasing *globally* ???
+               }
+
+
+       public static int selectMatching( JTable table, int column, String str )
+       {
+               TableModel      model;
+               Object          obj;
+               int                     hits,i;
+
+               str=str.toUpperCase();
+
+               table.getSelectionModel().clearSelection();
+
+               model=table.getModel();
+               for (i=hits=0; i<model.getRowCount(); i++) {
+                       obj=model.getValueAt(i,column);
+                       if (obj!=null && 
obj.toString().toUpperCase().indexOf(str)>=0) {
+                               
table.getSelectionModel().addSelectionInterval(i,i);
+                               hits++;
+                               }
+                       }
+               return hits;
+       }
+
+       /**
+        * A general right-button popup menu callback
+        * @param popup
+        * @param c
+        */
+
+       public static void addPopupToComponent( JPopupMenu popup, JComponent c )
+       {
+               final JPopupMenu        _popup = popup;
+               final JComponent        _c = c;
+
+               c.addMouseListener(new MouseAdapter() {
+                       public void mousePressed( MouseEvent evt )
+                       {
+                               checkPopup(evt);
+                       }
+
+                       public void mouseClicked( MouseEvent evt )
+                       {
+                               checkPopup(evt);
+                       }
+
+                       public void mouseReleased( MouseEvent evt )
+                       {
+                               checkPopup(evt);
+                       }
+
+                       protected void checkPopup( MouseEvent evt )
+                       {
+                               if (evt.isPopupTrigger()) {
+                                       _popup.show(_c,evt.getX(),evt.getY());
+                                       }
+                       }
+                       });
+       }
+
+       public static void restoreDims( Application app, Window w, String id )
+       {
+               Prefs           prefs;
+               Dimension       size;
+               int                     width,height,x,y;
+
+               prefs=app.getPreferences();
+
+               x=prefs.getGlobalInt(app.getName(),id+".window.x",-1);
+               y=prefs.getGlobalInt(app.getName(),id+".window.y",-1);
+               width=prefs.getGlobalInt(app.getName(),id+".window.width",-1);
+               height=prefs.getGlobalInt(app.getName(),id+".window.height",-1);
+
+               if (x<0 || y<0 || width<16 || height<16) {
+                       size=Toolkit.getDefaultToolkit().getScreenSize();
+
+                       width=size.width/4;
+                       height=size.height/4;
+                       x=(size.width-width)/2;
+                       y=(size.height-height)/2;
+                       }
+               w.setBounds(x,y,width,height);
+       }
+
+       public static void saveDims( Application app, Window w, String id )
+       {
+               Prefs   prefs;
+
+               prefs=app.getPreferences();
+               prefs.setGlobalInt(app.getName(),id+".window.x",w.getX());
+               prefs.setGlobalInt(app.getName(),id+".window.y",w.getY());
+               
prefs.setGlobalInt(app.getName(),id+".window.width",w.getWidth());
+               
prefs.setGlobalInt(app.getName(),id+".window.height",w.getHeight());
+       }
+
+       public static JMenu createLAFMenu( Application app )
+       {
+               UIManager.LookAndFeelInfo[]     infos;
+               JMenu                                           menu;
+               JMenuItem                                       item;
+               int                                                     i;
+
+               menu=new JMenu("Look & feel");
+               menu.setMnemonic('o');
+
+               try {
+                       infos=UIManager.getInstalledLookAndFeels();
+                       Arrays.sort(infos,new Comparator() {
+                               public int compare( Object o1, Object o2 )
+                               {
+                                       return ((UIManager.LookAndFeelInfo) 
o1).getName().compareToIgnoreCase(((UIManager.LookAndFeelInfo) o2).getName());
+                               }
+                               });
+                       for (i=0; i<infos.length; i++) {
+                               final String    _str = infos[i].getClassName();
+                               final Prefs             _prefs = 
app.getPreferences();
+                               final String    _name = app.getName();
+
+                               item=new JMenuItem(infos[i].getName());
+                               item.addActionListener(new ActionListener() {
+                                       public void actionPerformed( 
ActionEvent evt )
+                                       {
+                                               setLAF(_str);
+                                               
_prefs.setGlobalString(_name,"laf.classname",_str);
+                                       }
+                                       });
+                               menu.add(item);
+                               }
+                       }
+               catch( Throwable x ) {
+                       }
+               return menu;
+       }
+
+       public static synchronized void restoreLAF( Application app )
+       {
+               String  str;
+
+               if (!lafOnce) {
+                       
str=app.getPreferences().getGlobalString(app.getName(),"laf.classname","");
+                       if (str.length()>0) {
+                               setLAF(str);
+                               }
+                       lafOnce=true;
+                       }
+       }
+
+       public static void setLAF( String classname )
+       {
+               Frame[] frames;
+               int             i;
+
+               try {
+                       UIManager.setLookAndFeel((LookAndFeel) 
Class.forName(classname).newInstance());
+
+                       frames=Frame.getFrames();
+                       for (i=0; i<frames.length; i++) {
+                               SwingUtilities.updateComponentTreeUI(frames[i]);
+                               }
+                       }
+               catch( Throwable x ) {
+                       logger.log(Level.SEVERE,"Could not set l&f 
("+classname+") !",x);
+                       }
+       }
+}

Added: freeway/src/org/gnu/freeway/util/ui/UIResources.java
===================================================================
--- freeway/src/org/gnu/freeway/util/ui/UIResources.java        2005-01-31 
23:47:23 UTC (rev 136)
+++ freeway/src/org/gnu/freeway/util/ui/UIResources.java        2005-02-01 
01:07:27 UTC (rev 137)
@@ -0,0 +1,438 @@
+/**
+ * @PROJECT_INFO@
+ */
+
+package org.gnu.freeway.util.ui;
+
+import org.gnu.freeway.util.*;
+import org.gnu.freeway.util.io.*;
+
+import java.awt.*;
+import java.io.*;
+import java.net.*;
+import java.nio.charset.*;
+import java.util.*;
+import java.util.logging.*;
+import java.util.regex.*;
+import javax.swing.*;
+
+/**
+ *
+ */
+
+public class UIResources extends LoggedObject
+{
+       private static final int        PNG             =       0;
+       private static final int        JPEG    =       1;
+       private static final int        TIFF    =       2;
+
+       private static Matcher  imageMatcher;
+       private static Map              imageExtensions;
+
+       static {
+               
imageMatcher=Pattern.compile("(.*)/([0-9]+)x([0-9]+)/([^/\\.]+)\\.([a-zA-Z]+)(\\?bg=([0-9a-f]{6}))?").matcher("");
+
+               imageExtensions=new HashMap();
+               imageExtensions.put("png",new Integer(PNG));
+               imageExtensions.put("jpg",new Integer(JPEG));
+               imageExtensions.put("jpeg",new Integer(JPEG));
+               imageExtensions.put("jpe",new Integer(JPEG));
+               imageExtensions.put("tiff",new Integer(TIFF));
+               }
+
+       private UIResources     parent;
+       private String          name;
+       private Object          target;
+       private DirLocation     cache;
+
+       private Map                     actions;
+       private Map                     menus;
+       private Map                     images;
+       private boolean         loaded;
+
+
+       protected UIResources( String str )
+       {
+               super(true);
+               parent=null;
+               name=str;
+               target=null;
+               cache=null;
+
+               actions=new HashMap();
+               menus=new HashMap();
+               images=new HashMap();
+               loaded=false;
+       }
+
+       public UIResources( String str, File f )
+       {
+               this(str);
+               cache=new DirLocation(f.getPath());
+       }
+
+       public UIResources( UIResources p, String str )
+       {
+               this(str);
+               parent=p;
+               cache=p.cache;
+       }
+
+       public String toString()
+       {
+               return "UI resources [name="+name+"]";
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       public synchronized void setGlobalTarget( Object obj )
+       {
+               Iterator        iter;
+               UIAction        a;
+
+               target=obj;
+
+               if (loaded) {
+                       iter=actions.values().iterator();
+                       while (iter.hasNext()) {
+                               a=(UIAction) iter.next();
+                               a.setTarget(target);
+                               }
+                       }
+       }
+
+       public synchronized URL getImageURL( String str )
+       {
+               ImageEntry      ie;
+
+               loadIfNot();
+
+               ie=(ImageEntry) images.get(str);
+               return (ie!=null ? ie.getURL() : (parent!=null ? 
parent.getImageURL(str) : null));
+       }
+
+       public synchronized UIAction getAction( String str )
+       {
+               UIAction        a;
+
+               loadIfNot();
+
+               a=(UIAction) actions.get(str);
+               return ((a!=null || parent==null) ? a : parent.getAction(str));
+       }
+
+       /**
+        * Create a pulldown menu.
+        * @param str
+        * @return
+        */
+
+       public synchronized JMenuBar getMenuBar( String str )
+       {
+               MenuEntry       me;
+
+               loadIfNot();
+
+               me=(MenuEntry) menus.get(str);
+               return (me!=null ? me.createBar() : (parent!=null ? 
parent.getMenuBar(str) : null));
+       }
+
+       /**
+        * Create a popup menu.
+        * @param str
+        * @return
+        */
+
+       public synchronized JPopupMenu getPopupMenu( String str )
+       {
+               MenuEntry       me;
+
+               loadIfNot();
+
+               me=(MenuEntry) menus.get(str);
+               return (me!=null ? me.createPopup() : (parent!=null ? 
parent.getPopupMenu(str) : null));
+       }
+
+       protected void loadIfNot()
+       {
+               if (!loaded) {
+                       loaded=true;
+
+                       load();
+                       }
+       }
+
+       protected void load()
+       {
+               Voyager         v;
+               ImageEntry      ie;
+               MenuEntry       me;
+               UIAction        a;
+
+               log(Level.INFO,"Load resource file : "+name);
+
+               
v=Voyager.create(getClass().getClassLoader().getResource(name),Charset.forName("UTF-8"));
+               if (v!=null) {
+                       v.select("//address@hidden'enUS']/image");
+                       while (v.next()) {
+                               ie=new ImageEntry();
+                               if (ie.parse(v)) {
+                                       images.put(v.get("id",""),ie);
+                                       }
+                               }
+                       v.up();
+
+                       v.select("//address@hidden'enUS']/action");
+                       while (v.next()) {
+                               a=new UIAction(this,v);
+                               a.setEnabled(false);
+                               if (target!=null) {
+                                       a.setTarget(target);
+                                       }
+
+                               actions.put(a.getID(),a);
+                               }
+                       v.up();
+
+                       v.select("//address@hidden'enUS']/menu");
+                       while (v.next()) {
+                               me=new MenuEntry();
+                               me.parse(v);
+                               menus.put(v.get("id",""),me);
+                               }
+                       v.up();
+                       }
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       protected class ImageEntry extends Object
+       {
+               protected String                realPath;
+               protected String                cachedPath;
+               protected int           format;
+               protected int           width;
+               protected int           height;
+               protected Color         color;  //todo: a utiliser
+               protected boolean       rendered;
+
+
+               protected ImageEntry()
+               {
+                       super();
+                       realPath=null;
+                       cachedPath=null;
+                       format=0;
+                       width=0;
+                       height=0;
+                       color=null;
+                       rendered=false;
+               }
+
+               protected boolean parse( Voyager v )
+               {
+                       URL             url;
+                       String  str,ext;
+
+                       str=v.get("src","");
+
+                       url=getClass().getClassLoader().getResource(str);
+                       if (url!=null) {
+                               realPath=str;
+                               return true;
+                               }
+
+                       synchronized(imageMatcher) {
+                               imageMatcher.reset(str);
+                               if (!imageMatcher.matches()) {
+                                       return false;
+                                       }
+
+                               ext=imageMatcher.group(5).toLowerCase();
+                               if (!imageExtensions.containsKey(ext)) {
+                                       // bad extension
+                                       return false;
+                                       }
+
+                               
str=imageMatcher.group(1)+"/"+imageMatcher.group(4)+".svg";
+                               
url=getClass().getClassLoader().getResource(str);
+                               if (url==null) {
+                                       // no SVG source avalaible
+                                       return false;
+                                       }
+
+                               realPath=str;
+
+                               str=imageMatcher.group(7);
+                               color=(str!=null ? new 
Color(Integer.parseInt(str,16)) : new Color(0,0,0));
+
+                               width=Integer.parseInt(imageMatcher.group(2));
+                               height=Integer.parseInt(imageMatcher.group(3));
+                               
cachedPath=imageMatcher.group(1)+"/"+width+"x"+height+"/"+imageMatcher.group(4)+"["+Utils.toHex(color.getRGB())+"]."+imageMatcher.group(5);
+                               format=((Number) 
imageExtensions.get(ext)).intValue();
+                               }
+                       return true;
+               }
+
+               protected URL getURL()
+               {
+                       URL                     from;
+                       FileLocation    to;
+
+                       if (cachedPath==null) {
+                               return 
getClass().getClassLoader().getResource(realPath);
+                               }
+
+                       from=getClass().getClassLoader().getResource(realPath);
+
+                       to=cache.getFile(cachedPath);
+                       if (!to.exists()) {
+                               render(from,to);
+                               }
+                       return to.asURL();
+               }
+
+               protected void render( URL from, FileLocation to )
+               {
+                       SVGRenderer     renderer;
+
+                       if (rendered) {
+                               return;
+                               }
+                       rendered=true;
+                       log(Level.INFO,"About to render "+from+" -> 
"+to.getLabel());
+
+                       to.create();
+
+                       renderer=new SVGRenderer(from);
+                       switch (format) {
+                               case PNG: 
renderer.renderToPNG(to,width,height); break;
+                               case JPEG: 
renderer.renderToJPEG(to,width,height,(float) 0.9); break;
+                               case TIFF: 
renderer.renderToTIFF(to,width,height); break;
+                               }
+               }
+       }
+
+
+       
////////////////////////////////////////////////////////////////////////////////////////////////
+
+       protected class MenuEntry extends Object
+       {
+               protected String        label;
+               protected char          acc;
+               protected ArrayList     children;
+
+               protected MenuEntry()
+               {
+                       super();
+                       label="";
+                       acc=(char) 0;
+                       children=new ArrayList();
+               }
+
+               protected JMenu create()
+               {
+                       JMenu   menu;
+                       Object  obj;
+                       int             i;
+
+                       menu=new JMenu(label);
+                       if (acc!=0) {
+                               menu.setMnemonic(acc);
+                               }
+
+                       for (i=0; i<children.size(); i++) {
+                               obj=children.get(i);
+
+                               if (obj instanceof UIAction) {
+                                       menu.add((UIAction) obj);
+                                       }
+                               else if (obj instanceof MenuEntry) {
+                                       menu.add(((MenuEntry) obj).create());
+                                       }
+                               else {
+                                       menu.addSeparator();
+                                       }
+                               }
+                       return menu;
+               }
+
+               protected JPopupMenu createPopup()
+               {
+                       JPopupMenu      popup;
+                       Object  obj;
+                       int             i;
+
+                       popup=new JPopupMenu(label);
+
+                       for (i=0; i<children.size(); i++) {
+                               obj=children.get(i);
+
+                               if (obj instanceof UIAction) {
+                                       popup.add((UIAction) obj);
+                                       }
+                               else if (obj instanceof MenuEntry) {
+                                       popup.add(((MenuEntry) obj).create());
+                                       }
+                               else {
+                                       popup.addSeparator();
+                                       }
+                               }
+                       return popup;
+               }
+
+               protected JMenuBar createBar()
+               {
+                       JMenuBar        bar;
+                       Object  obj;
+                       int             i;
+
+                       bar=new JMenuBar();
+                       for (i=0; i<children.size(); i++) {
+                               obj=children.get(i);
+
+                               if (obj instanceof MenuEntry) {
+                                       bar.add(((MenuEntry) obj).create());
+                                       }
+                               }
+                       return bar;
+               }
+
+               protected void parse( Voyager v )
+               {
+                       UIAction        a;
+                       String          str;
+                       MenuEntry       me;
+
+                       v.select();
+                       while (v.next()) {
+                               if (v.is("label")) {
+                                       label=v.text("");
+                                       }
+                               else if (v.is("accelerator")) {
+                                       str=v.text("");
+                                       if (str.length()==1) {
+                                               acc=str.toUpperCase().charAt(0);
+                                               }
+                                       }
+                               else if (v.is("action")) {
+                                       a=(UIAction) 
actions.get(v.get("refid",""));
+                                       if (a!=null) {
+                                               children.add(a);
+                                               }
+                                       }
+                               else if (v.is("separator")) {
+                                       children.add(null);
+                                       }
+                               else if (v.is("menu")) {
+                                       me=new MenuEntry();
+                                       me.parse(v);
+                                       children.add(me);
+                                       }
+                               }
+                       v.up();
+               }
+       }
+}





reply via email to

[Prev in Thread] Current Thread [Next in Thread]