From 8603250d73f587456e47db8df6eadaaa93c8ebb1 Mon Sep 17 00:00:00 2001
From: jrandom <jrandom>
Date: Wed, 21 Jul 2004 06:25:44 +0000
Subject: [PATCH] updated the readme to reference the current specs and
 implementations removed the old out of date jython and python code

---
 apps/sam/code.leo                   | 3957 ---------------------------
 apps/sam/doc/README                 |   35 +-
 apps/sam/jython/README              |   54 -
 apps/sam/jython/build.xml           |   36 -
 apps/sam/jython/src/i2psam.py       | 2579 -----------------
 apps/sam/python/src/i2psamclient.py | 1334 ---------
 6 files changed, 8 insertions(+), 7987 deletions(-)
 delete mode 100644 apps/sam/code.leo
 delete mode 100644 apps/sam/jython/README
 delete mode 100644 apps/sam/jython/build.xml
 delete mode 100644 apps/sam/jython/src/i2psam.py
 delete mode 100644 apps/sam/python/src/i2psamclient.py

diff --git a/apps/sam/code.leo b/apps/sam/code.leo
deleted file mode 100644
index 8869595259..0000000000
--- a/apps/sam/code.leo
+++ /dev/null
@@ -1,3957 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<leo_file>
-<leo_header file_format="2" tnodes="0" max_tnode_index="217" clone_windows="0"/>
-<globals body_outline_ratio="0.35262008733624456">
-	<global_window_position top="70" left="219" height="649" width="978"/>
-	<global_log_window_position top="0" left="0" height="0" width="0"/>
-</globals>
-<preferences>
-</preferences>
-<find_panel_settings>
-	<find_string></find_string>
-	<change_string></change_string>
-</find_panel_settings>
-<vnodes>
-<v t="davidmcnab.041004143447" a="E"><vh>I2P SAM Server and Client</vh>
-<v t="davidmcnab.041004144338" a="E" tnodeList="davidmcnab.041004144338,davidmcnab.041004144338.1,davidmcnab.041004144338.2,davidmcnab.041004144338.4,davidmcnab.041004144338.5,davidmcnab.041004144338.6,davidmcnab.041004144338.8,davidmcnab.041004144338.9,davidmcnab.041004144338.10,davidmcnab.041004144338.11,davidmcnab.041004144338.12,davidmcnab.041004144338.13,davidmcnab.041004144338.14,davidmcnab.041004144338.15,davidmcnab.041004144338.17,davidmcnab.041004144338.18,davidmcnab.041004144338.19,davidmcnab.041004144338.20,davidmcnab.041004144338.21,davidmcnab.041004144338.22,davidmcnab.041004144338.23,davidmcnab.041004144338.24,davidmcnab.041004144338.26,davidmcnab.041004144338.27,davidmcnab.041004144338.29,davidmcnab.041004144338.30,davidmcnab.041004144338.31,davidmcnab.041004144338.32,davidmcnab.041004144338.33,davidmcnab.041004144338.34,davidmcnab.041004144338.35,davidmcnab.041004144338.36,davidmcnab.041004144338.37,davidmcnab.041004144338.38,davidmcnab.041004144338.39,davidmcnab.041004144338.40,davidmcnab.041004144338.41,davidmcnab.041004144338.42,davidmcnab.041004144338.43,davidmcnab.041004144338.44,davidmcnab.041004144338.45,davidmcnab.041004144338.46,davidmcnab.041004144338.47,davidmcnab.041004144338.49,davidmcnab.041004144338.50,davidmcnab.041004144338.51,davidmcnab.041004144338.52,davidmcnab.041004144338.53,davidmcnab.041004144338.54,davidmcnab.041004144338.55,davidmcnab.041004144338.56,davidmcnab.041004144338.57,davidmcnab.041004144338.58,davidmcnab.041004144338.59,davidmcnab.041004144338.60,davidmcnab.041004144338.62,davidmcnab.041004144338.63,davidmcnab.041004144338.64,davidmcnab.041004144338.65,davidmcnab.041004144338.66,davidmcnab.041004144338.67,davidmcnab.041004144338.68,davidmcnab.041004144338.69,davidmcnab.041004144338.70,davidmcnab.041004144338.71,davidmcnab.041004144338.72,davidmcnab.041004144338.73,davidmcnab.041004144338.74,davidmcnab.041004144338.75,davidmcnab.041004144338.76,davidmcnab.041004144338.77,davidmcnab.041004144338.78,davidmcnab.041004144338.79,davidmcnab.041004144338.80,davidmcnab.041004144338.81,davidmcnab.041004144338.82,davidmcnab.041004144338.83,davidmcnab.041004144338.84,davidmcnab.041304205426,davidmcnab.041004144338.85,davidmcnab.041004144338.86,davidmcnab.041004144338.87,davidmcnab.041004144338.88,davidmcnab.041004144338.89,davidmcnab.041004144338.90,davidmcnab.041004144338.92,davidmcnab.041004144338.93,davidmcnab.041004144338.94,davidmcnab.041004144338.95,davidmcnab.041004144338.96,davidmcnab.041004144338.97,davidmcnab.041004144338.98,davidmcnab.041004144338.99,davidmcnab.041004144338.100,davidmcnab.041004144338.101,davidmcnab.041004144338.105,davidmcnab.041004144338.106,davidmcnab.041004144338.107,davidmcnab.041004144338.108,davidmcnab.041004144338.102,davidmcnab.041004144338.103,davidmcnab.041004144338.109"><vh>@file jython/src/i2psam.py</vh>
-<v t="davidmcnab.041004144338.1" a="M"><vh>imports</vh></v>
-<v t="davidmcnab.041004144338.2" a="V"><vh>globals</vh></v>
-<v t="davidmcnab.041004144338.3" a="E"><vh>I2CP Interface Classes</vh>
-<v t="davidmcnab.041004144338.4"><vh>class JavaWrapper</vh></v>
-<v t="davidmcnab.041004144338.5" a="E"><vh>class I2PDestination</vh>
-<v t="davidmcnab.041004144338.6"><vh>__init__</vh></v>
-<v t="davidmcnab.041004144338.7" a="E"><vh>Exporting Methods</vh>
-<v t="davidmcnab.041004144338.8"><vh>toBin</vh></v>
-<v t="davidmcnab.041004144338.9"><vh>toBinFile</vh></v>
-<v t="davidmcnab.041004144338.10"><vh>toBinPrivate</vh></v>
-<v t="davidmcnab.041004144338.11"><vh>toBinFilePrivate</vh></v>
-<v t="davidmcnab.041004144338.12"><vh>toBase64</vh></v>
-<v t="davidmcnab.041004144338.13"><vh>toBase64Private</vh></v>
-<v t="davidmcnab.041004144338.14"><vh>toBase64File</vh></v>
-<v t="davidmcnab.041004144338.15"><vh>toBase64FilePrivate</vh></v>
-</v>
-<v t="davidmcnab.041004144338.16" a="E"><vh>Importing Methods</vh>
-<v t="davidmcnab.041004144338.17"><vh>fromBin</vh></v>
-<v t="davidmcnab.041004144338.18"><vh>fromBinFile</vh></v>
-<v t="davidmcnab.041004144338.19"><vh>fromBinPrivate</vh></v>
-<v t="davidmcnab.041004144338.20"><vh>fromBinFilePrivate</vh></v>
-<v t="davidmcnab.041004144338.21"><vh>fromBase64</vh></v>
-<v t="davidmcnab.041004144338.22"><vh>fromBase64File</vh></v>
-<v t="davidmcnab.041004144338.23"><vh>fromBase64Private</vh></v>
-<v t="davidmcnab.041004144338.24"><vh>fromBase64PrivateFile</vh></v>
-</v>
-<v t="davidmcnab.041004144338.25" a="E"><vh>Signature Methods</vh>
-<v t="davidmcnab.041004144338.26"><vh>sign</vh></v>
-<v t="davidmcnab.041004144338.27"><vh>verify</vh></v>
-</v>
-<v t="davidmcnab.041004144338.28" a="E"><vh>Sanity Methods</vh>
-<v t="davidmcnab.041004144338.29"><vh>hasPrivate</vh></v>
-</v>
-</v>
-<v t="davidmcnab.041004144338.30" a="E"><vh>class I2PClient</vh>
-<v t="davidmcnab.041004144338.31" a="E"><vh>__init__</vh></v>
-<v t="davidmcnab.041004144338.32"><vh>createDestination</vh></v>
-<v t="davidmcnab.041004144338.33"><vh>createSession</vh></v>
-</v>
-<v t="davidmcnab.041004144338.34" a="E"><vh>class I2PSession</vh>
-<v t="davidmcnab.041004144338.35"><vh>attributes</vh></v>
-<v t="davidmcnab.041004144338.36"><vh>__init__</vh></v>
-<v t="davidmcnab.041004144338.37"><vh>sendMessage</vh></v>
-<v t="davidmcnab.041004144338.38"><vh>numMessages</vh></v>
-<v t="davidmcnab.041004144338.39"><vh>getMessage</vh></v>
-<v t="davidmcnab.041004144338.40"><vh>setSessionListener</vh></v>
-<v t="davidmcnab.041004144338.41"><vh>destroySession</vh></v>
-<v t="davidmcnab.041004144338.42" a="E"><vh>CALLBACKS</vh>
-<v t="davidmcnab.041004144338.43"><vh>on_message</vh></v>
-<v t="davidmcnab.041004144338.44"><vh>on_abuse</vh></v>
-<v t="davidmcnab.041004144338.45"><vh>on_disconnected</vh></v>
-<v t="davidmcnab.041004144338.46"><vh>on_error</vh></v>
-</v>
-</v>
-<v t="davidmcnab.041004144338.47"><vh>class I2PSessionListener</vh></v>
-</v>
-<v t="davidmcnab.041004144338.48" a="E"><vh>Streaming Interface Classes</vh>
-<v t="davidmcnab.041004144338.49" a="E"><vh>class I2PSocket</vh>
-<v t="davidmcnab.041004144338.50"><vh>attributes</vh></v>
-<v t="davidmcnab.041004144338.51"><vh>__init__</vh></v>
-<v t="davidmcnab.041004144338.52"><vh>bind</vh></v>
-<v t="davidmcnab.041004144338.53"><vh>listen</vh></v>
-<v t="davidmcnab.041004144338.54"><vh>accept</vh></v>
-<v t="davidmcnab.041004144338.55"><vh>connect</vh></v>
-<v t="davidmcnab.041004144338.56"><vh>recv</vh></v>
-<v t="davidmcnab.041004144338.57"><vh>send</vh></v>
-<v t="davidmcnab.041004144338.58"><vh>available</vh></v>
-<v t="davidmcnab.041004144338.59"><vh>close</vh></v>
-<v t="davidmcnab.041004144338.60"><vh>_createSockmgr</vh></v>
-</v>
-</v>
-<v t="davidmcnab.041004144338.61" a="E"><vh>I2P SAM Server</vh>
-<v t="davidmcnab.041004144338.62" a="E"><vh>class I2PSamServer</vh>
-<v t="davidmcnab.041004144338.63"><vh>attributes</vh></v>
-<v t="davidmcnab.041004144338.64"><vh>__init__</vh></v>
-<v t="davidmcnab.041004144338.65"><vh>run</vh></v>
-<v t="davidmcnab.041004144338.66"><vh>finish_request</vh></v>
-<v t="davidmcnab.041004144338.67"><vh>samAllocId</vh></v>
-</v>
-<v t="davidmcnab.041004144338.68" a="E"><vh>class I2PSamClientHandler</vh>
-<v t="davidmcnab.041004144338.69"><vh>handle</vh></v>
-<v t="davidmcnab.041004144338.70"><vh>on_genkeys</vh></v>
-<v t="davidmcnab.041004144338.71"><vh>on_createsession</vh></v>
-<v t="davidmcnab.041004144338.72"><vh>on_destroysession</vh></v>
-<v t="davidmcnab.041004144338.73"><vh>on_send</vh></v>
-<v t="davidmcnab.041004144338.74"><vh>on_receive</vh></v>
-<v t="davidmcnab.041004144338.75"><vh>on_HELLO</vh></v>
-<v t="davidmcnab.041004144338.76"><vh>on_SESSION</vh></v>
-<v t="davidmcnab.041004144338.77"><vh>on_SESSION_CREATE</vh></v>
-<v t="davidmcnab.041004144338.78"><vh>on_STREAM</vh></v>
-<v t="davidmcnab.041004144338.79"><vh>on_DATAGRAM</vh></v>
-<v t="davidmcnab.041004144338.80"><vh>on_RAW</vh></v>
-<v t="davidmcnab.041004144338.81"><vh>on_NAMING</vh></v>
-<v t="davidmcnab.041004144338.82"><vh>on_DEST</vh></v>
-<v t="davidmcnab.041004144338.83"><vh>on_message</vh></v>
-<v t="davidmcnab.041004144338.84"><vh>threadSocketListener</vh></v>
-<v t="davidmcnab.041304205426"><vh>threadSocketReceiver</vh></v>
-<v t="davidmcnab.041004144338.85"><vh>samParse</vh></v>
-<v t="davidmcnab.041004144338.86"><vh>samSend</vh></v>
-<v t="davidmcnab.041004144338.87"><vh>samCreateArgsList</vh></v>
-<v t="davidmcnab.041004144338.88"><vh>_sendbytes</vh></v>
-<v t="davidmcnab.041004144338.89"><vh>_recvbytes</vh></v>
-</v>
-</v>
-<v t="davidmcnab.041004144338.90"><vh>Exceptions</vh></v>
-<v t="davidmcnab.041004144338.91" a="E"><vh>Functions</vh>
-<v t="davidmcnab.041004144338.92"><vh>shahash</vh></v>
-<v t="davidmcnab.041004144338.93"><vh>base64enc</vh></v>
-<v t="davidmcnab.041004144338.94"><vh>base64dec</vh></v>
-<v t="davidmcnab.041004144338.95"><vh>str2bytearray</vh></v>
-<v t="davidmcnab.041004144338.96"><vh>bytearray2str</vh></v>
-<v t="davidmcnab.041004144338.97"><vh>byteoutstream2str</vh></v>
-<v t="davidmcnab.041004144338.98"><vh>dict2props</vh></v>
-<v t="davidmcnab.041004144338.99"><vh>takeKey</vh></v>
-<v t="davidmcnab.041004144338.100"><vh>log</vh></v>
-<v t="davidmcnab.041004144338.101"><vh>logException</vh></v>
-<v t="davidmcnab.041004144338.104" a="E"><vh>Tests</vh>
-<v t="davidmcnab.041004144338.105" tnodeList="davidmcnab.041004144338.105"><vh>testdests</vh></v>
-<v t="davidmcnab.041004144338.106"><vh>testsigs</vh></v>
-<v t="davidmcnab.041004144338.107"><vh>testsession</vh></v>
-<v t="davidmcnab.041004144338.108"><vh>testsocket</vh></v>
-</v>
-<v t="davidmcnab.041004144338.102"><vh>usage</vh></v>
-<v t="davidmcnab.041004144338.103"><vh>main</vh></v>
-</v>
-<v t="davidmcnab.041004144338.109"><vh>MAINLINE</vh></v>
-</v>
-<v t="davidmcnab.041004144551" a="E" tnodeList="davidmcnab.041004144551,davidmcnab.041004144551.1,davidmcnab.041004144551.2,davidmcnab.041004144551.3,davidmcnab.041004144551.4,davidmcnab.041004144551.5,davidmcnab.041004144551.6,davidmcnab.041004144551.7,davidmcnab.041004144551.8,davidmcnab.041004144551.9,davidmcnab.041004144551.10,davidmcnab.041004144551.12,davidmcnab.041004144551.13,davidmcnab.041004144551.14,davidmcnab.041004144551.15,davidmcnab.041004144551.16,davidmcnab.041004144551.17,davidmcnab.041004144551.18,davidmcnab.041004144551.19,davidmcnab.041004144551.20,davidmcnab.041204020513,davidmcnab.041204204235,davidmcnab.041204044735,davidmcnab.041204050339,davidmcnab.041004144551.21,davidmcnab.041004144551.22,davidmcnab.041004144551.23,davidmcnab.041004144551.24,davidmcnab.041004144551.26,davidmcnab.041004144551.27,davidmcnab.041004144551.28,davidmcnab.041004144551.29,davidmcnab.041004144551.30,davidmcnab.041004144551.31,davidmcnab.041004144551.32,davidmcnab.041004144551.33,davidmcnab.041204042212,davidmcnab.041004144551.35,davidmcnab.041004144551.36,davidmcnab.041004144551.37,davidmcnab.041004144551.38,davidmcnab.041204042212.1,davidmcnab.041204042212.2,davidmcnab.041204044735.1,davidmcnab.041204050339.1,davidmcnab.041304235615,davidmcnab.041204050339.2,davidmcnab.041204050511,davidmcnab.041204044135,davidmcnab.041004144551.39,davidmcnab.041004144551.40,davidmcnab.041004144551.41,davidmcnab.041004144551.42,davidmcnab.041004144551.43,davidmcnab.041004144551.45,davidmcnab.041004144551.46,davidmcnab.041004144551.47,davidmcnab.041004144551.48,davidmcnab.041004144551.49,davidmcnab.041004144551.50,davidmcnab.041204203651,davidmcnab.041004144551.51,davidmcnab.041004144551.52"><vh>@file python/src/i2psamclient.py</vh>
-<v t="davidmcnab.041004144551.1"><vh>imports</vh></v>
-<v t="davidmcnab.041004144551.2"><vh>globals</vh></v>
-<v t="davidmcnab.041004144551.3"><vh>exceptions</vh></v>
-<v t="davidmcnab.041004144551.4" a="E"><vh>class I2PSamClient</vh>
-<v t="davidmcnab.041004144551.5"><vh>attributes</vh></v>
-<v t="davidmcnab.041004144551.6"><vh>__init__</vh></v>
-<v t="davidmcnab.041004144551.7"><vh>createSession</vh></v>
-<v t="davidmcnab.041004144551.8"><vh>destroySession</vh></v>
-<v t="davidmcnab.041004144551.9"><vh>send</vh></v>
-<v t="davidmcnab.041004144551.10"><vh>receive</vh></v>
-<v t="davidmcnab.041004144551.11" a="E"><vh>SAM methods</vh>
-<v t="davidmcnab.041004144551.12"><vh>samHello</vh></v>
-<v t="davidmcnab.041004144551.13"><vh>samSessionCreate</vh></v>
-<v t="davidmcnab.041004144551.14"><vh>samDestGenerate</vh></v>
-<v t="davidmcnab.041004144551.15"><vh>samRawSend</vh></v>
-<v t="davidmcnab.041004144551.16"><vh>samRawCheck</vh></v>
-<v t="davidmcnab.041004144551.17"><vh>samRawReceive</vh></v>
-<v t="davidmcnab.041004144551.18"><vh>samDatagramSend</vh></v>
-<v t="davidmcnab.041004144551.19"><vh>samDatagramCheck</vh></v>
-<v t="davidmcnab.041004144551.20"><vh>samDatagramReceive</vh></v>
-<v t="davidmcnab.041204020513"><vh>samStreamConnect</vh></v>
-<v t="davidmcnab.041204204235"><vh>samStreamAccept</vh></v>
-<v t="davidmcnab.041204044735"><vh>samStreamSend</vh></v>
-<v t="davidmcnab.041204050339"><vh>samStreamClose</vh></v>
-<v t="davidmcnab.041004144551.21"><vh>samNamingLookup</vh></v>
-<v t="davidmcnab.041004144551.22"><vh>samParse</vh></v>
-<v t="davidmcnab.041004144551.23"><vh>samSend</vh></v>
-<v t="davidmcnab.041004144551.24"><vh>samCreateArgsList</vh></v>
-</v>
-<v t="davidmcnab.041004144551.25" a="E"><vh>Receiver Side</vh>
-<v t="davidmcnab.041004144551.26"><vh>threadRx</vh></v>
-<v t="davidmcnab.041004144551.27"><vh>on_HELLO</vh></v>
-<v t="davidmcnab.041004144551.28"><vh>on_SESSION</vh></v>
-<v t="davidmcnab.041004144551.29"><vh>on_STREAM</vh></v>
-<v t="davidmcnab.041004144551.30"><vh>on_DATAGRAM</vh></v>
-<v t="davidmcnab.041004144551.31"><vh>on_RAW</vh></v>
-<v t="davidmcnab.041004144551.32"><vh>on_NAMING</vh></v>
-<v t="davidmcnab.041004144551.33"><vh>on_DEST</vh></v>
-</v>
-<v t="davidmcnab.041004144551.34" a="E"><vh>Utility Methods</vh>
-<v t="davidmcnab.041204042212"><vh>samAllocId</vh></v>
-<v t="davidmcnab.041004144551.35"><vh>_recvline</vh></v>
-<v t="davidmcnab.041004144551.36"><vh>_recvbytes</vh></v>
-<v t="davidmcnab.041004144551.37"><vh>_sendbytes</vh></v>
-<v t="davidmcnab.041004144551.38"><vh>_sendline</vh></v>
-</v>
-</v>
-<v t="davidmcnab.041204042212.1" a="E"><vh>class I2PSAMStream</vh>
-<v t="davidmcnab.041204042212.2"><vh>__init__</vh></v>
-<v t="davidmcnab.041204044735.1"><vh>send</vh></v>
-<v t="davidmcnab.041204050339.1"><vh>recv</vh></v>
-<v t="davidmcnab.041304235615"><vh>readline</vh></v>
-<v t="davidmcnab.041204050339.2"><vh>close</vh></v>
-<v t="davidmcnab.041204050511"><vh>__del__</vh></v>
-<v t="davidmcnab.041204044135"><vh>_notifyIncomingData</vh></v>
-</v>
-<v t="davidmcnab.041004144551.39" a="E"><vh>class I2PRemoteSession</vh>
-<v t="davidmcnab.041004144551.40"><vh>__init__</vh></v>
-<v t="davidmcnab.041004144551.41"><vh>send</vh></v>
-<v t="davidmcnab.041004144551.42"><vh>recv</vh></v>
-<v t="davidmcnab.041004144551.43"><vh>destroy</vh></v>
-</v>
-<v t="davidmcnab.041004144551.44" a="E"><vh>Functions</vh>
-<v t="davidmcnab.041004144551.45"><vh>log</vh></v>
-<v t="davidmcnab.041004144551.46"><vh>logException</vh></v>
-<v t="davidmcnab.041004144551.47"><vh>demoNAMING</vh></v>
-<v t="davidmcnab.041004144551.48"><vh>demoRAW</vh></v>
-<v t="davidmcnab.041004144551.49"><vh>demoDATAGRAM</vh></v>
-<v t="davidmcnab.041004144551.50"><vh>demoSTREAM</vh></v>
-<v t="davidmcnab.041204203651"><vh>demoSTREAM_thread</vh></v>
-<v t="davidmcnab.041004144551.51"><vh>demo</vh></v>
-</v>
-<v t="davidmcnab.041004144551.52"><vh>MAINLINE</vh></v>
-</v>
-</v>
-</vnodes>
-<tnodes>
-<t tx="davidmcnab.041004143447"></t>
-<t tx="davidmcnab.041004144338">@first #!/usr/bin/env jython
-r"""
-Implements I2P SAM Server. (refer U{http://drupal.i2p.net/node/view/144})
-
-Also contains useful classes for jython programs,
-which wrap the I2P java classes into more python-compatible
-paradigms.
-
-If you run this module (or the i2psam.jar file created from it)
-without arguments, it'll run an I2P SAM server bridge, listening
-on port 7656.
-
-The file i2psamclient.py contains python client classes and a
-demo program.
-
-Latest vers of this file is available from U{http://www.freenet.org.nz/i2p/i2psam.py}
-Latest epydoc-generated doco at U{http://www.freenet.org.nz/i2p/i2pjyDoc}
-
-The i2psam.jar file is built from this module with the following
-command (requires jython and java 1.4.x+ to be installed)::
-
-  CLASSPATH=/path/to/i2p.jar:/path/to/mstreaming.jar \
-          jythonc -jar i2psam.jar --all -A net.invisiblenet i2psam.py
-
-"""
-
-@others
-
-
-</t>
-<t tx="davidmcnab.041004144338.1"># python imports
-import sys, os, time, Queue, thread, threading, StringIO, traceback, getopt
-from SocketServer import ThreadingTCPServer, StreamRequestHandler
-
-# java imports
-import java
-
-# i2p-specific imports
-import net.i2p
-import net.i2p.client # to shut up epydoc
-#import net.i2p.client.I2PClient
-#import net.i2p.client.I2PClientFactory
-#import net.i2p.client.I2PSessionListener
-import net.i2p.client.naming
-import net.i2p.client.streaming
-import net.i2p.crypto
-import net.i2p.data
-
-# handy shorthand refs
-i2p = net.i2p
-jI2PClient = i2p.client.I2PClient
-
-# import my own helper hack module
-#import I2PHelper
-
-</t>
-<t tx="davidmcnab.041004144338.2">clientFactory = i2p.client.I2PClientFactory
-
-#i2phelper = I2PHelper()
-
-PROP_RELIABILITY_BEST_EFFORT = i2p.client.I2PClient.PROP_RELIABILITY_BEST_EFFORT
-PROP_RELIABILITY_GUARANTEED = i2p.client.I2PClient.PROP_RELIABILITY_GUARANTEED
-
-version = "0.1.0"
-
-# host/port that our socketserver listens on
-i2psamhost = "127.0.0.1"
-i2psamport = 7656
-
-# host/port that I2P's I2CP listens on
-i2cpHost = "127.0.0.1"
-i2cpPort = 7654
-
-#print "i2cpPort=%s" % repr(i2cpPort)
-
-# ------------------------------------------
-# logging settings
-
-# 1=v.quiet, 2=normal, 3=verbose, 4=debug, 5=painful
-verbosity = 2
-
-# change to a filename to log there instead
-logfile = sys.stdout
-
-# when set to 1, and when logfile != sys.stdout, log msgs are written
-# both to logfile and console stdout
-log2console = 1
-
-# don't touch this!
-loglock = threading.Lock()
-
-
-</t>
-<t tx="davidmcnab.041004144338.3"></t>
-<t tx="davidmcnab.041004144338.4">class JavaWrapper:
-    """
-    Wraps a java object as attribute '_item', and forwards
-    __getattr__ to it.
-    
-    All the classes here derive from this
-    """
-    def __init__(self, item):
-        self._item = item
-    
-    def __getattr__(self, attr):
-        return getattr(self._item, attr)
-    
-
-</t>
-<t tx="davidmcnab.041004144338.5">class I2PDestination(JavaWrapper):
-    """
-    Wraps java I2P destination objects, with a big difference - these
-    objects store the private parts.
-    """
-    @others
-
-</t>
-<t tx="davidmcnab.041004144338.6">def __init__(self, **kw):
-    """
-    Versatile constructor
-    
-    Keywords (choose only one option):
-        - (none) - create a whole new dest
-        - dest, private - wrap an existing I2P java dest with private stream
-          (private is a byte array)
-        - bin - reconstitute a public-only dest from a binary string
-        - binfile - reconstitute public-only from a binary file
-        - binprivate - reconsistitute private dest from binary string
-        - binfileprivate - reconsistitute private dest from binary file pathname
-        - base64 - reconstitute public-only from base64 string
-        - base64file - reconstitute public-only from file containing base64
-        - base64private - reconstitute private from string containing base64
-        - base64fileprivate - reconstitute private from file containing base64
-
-    also:
-        - client - a java net.i2p.client.I2PClient object
-          (avoids need for temporary client object when creating new dests)
-    """
-    dest = i2p.data.Destination()
-    JavaWrapper.__init__(self, dest)
-    self._private = None
-
-    if kw.has_key('dest'):
-        self._item = kw['dest']
-        if kw.has_key('private'):
-            self._private = kw['private']
-
-    elif kw.has_key('bin'):
-        self.fromBin(kw['bin'])
-
-    elif kw.has_key('binfile'):
-        self.fromBinFilePrivate(kw['binfile'])
-
-    elif kw.has_key('binprivate'):
-        self.fromBinPrivate(kw['binprivate'])
-
-    elif kw.has_key('binfileprivate'):
-        self.fromBinFilePrivate(kw['binfileprivate'])
-
-    elif kw.has_key('base64'):
-        self.fromBase64(kw['base64'])
-
-    elif kw.has_key('base64file'):
-        self.fromBase64File(kw['base64file'])
-    
-    elif kw.has_key('base64private'):
-        self.fromBase64Private(kw['base64private'])
-
-    elif kw.has_key('base64fileprivate'):
-        self.fromBase64FilePrivate(kw['base64fileprivate'])
-
-    else:
-        # create a whole new one, with a temporary client object (if needed)
-        if kw.has_key('client'):
-            client = kw['client']
-        else:
-            client = clientFactory.createClient()
-        bytestream = java.io.ByteArrayOutputStream()
-        self._item = client.createDestination(bytestream)
-        self._private = bytestream.toByteArray()
-
-</t>
-<t tx="davidmcnab.041004144338.7"></t>
-<t tx="davidmcnab.041004144338.8">def toBin(self):
-    """
-    Returns a binary string of dest
-    """
-    return bytearray2str(self.toByteArray())
-
-</t>
-<t tx="davidmcnab.041004144338.9">def toBinFile(self, path):
-    """
-    Writes out public binary to a file
-    """
-    f = open(path, "wb")
-    f.write(self.toBin())
-    f.flush()
-    f.close()
-
-</t>
-<t tx="davidmcnab.041004144338.10">def toBinPrivate(self):
-    """
-    Returns the private key string as binary
-    """
-    if self._private == None:
-        raise NoPrivateKey
-    return bytearray2str(self._private)
-
-</t>
-<t tx="davidmcnab.041004144338.11">def toBinFilePrivate(self, path):
-    """
-    Writes out a binary file with the dest info
-    """
-    f = open(path, "wb")
-    f.write(self.toBinPrivate())
-    f.flush()
-    f.close()
-
-</t>
-<t tx="davidmcnab.041004144338.12">def toBase64(self):
-    """
-    Returns base64 string of public part
-    """
-    return self._item.toBase64()
-
-</t>
-<t tx="davidmcnab.041004144338.13">def toBase64Private(self):
-    """
-    Exports dest as base64, including private stuff
-    """
-    if self._private == None:
-        raise NoPrivateKey
-    return i2p.data.Base64.encode(self._private)
-
-</t>
-<t tx="davidmcnab.041004144338.14">def toBase64File(self, path):
-    """
-    Exports dest to file as base64
-    """
-    f = open(path, "wb")
-    f.write(self.toBase64())
-    f.flush()
-    f.close()
-
-</t>
-<t tx="davidmcnab.041004144338.15">def toBase64FilePrivate(self, path):
-    """
-    Writes out a base64 file with the private dest info
-    """
-    f = open(path, "wb")
-    f.write(self.toBase64Private())
-    f.flush()
-    f.close()
-
-</t>
-<t tx="davidmcnab.041004144338.16"></t>
-<t tx="davidmcnab.041004144338.17">def fromBin(self, bin):
-    """
-    Loads this dest from a binary string
-    """
-    self._item.fromByteArray(str2bytearray(bin))
-    self._private = None
-
-</t>
-<t tx="davidmcnab.041004144338.18">def fromBinFile(self, path):
-    """
-    Loads public part from file containing binary
-    """
-    f = open(path, "rb")
-    self.fromBin(f.read())
-    f.close()
-
-</t>
-<t tx="davidmcnab.041004144338.19">def fromBinPrivate(self, s):
-    """
-    Loads this dest object from a base64 private key string
-    """
-    bytes = str2bytearray(s)
-    self._private = bytes
-    stream = java.io.ByteArrayInputStream(bytes)
-    self._item.readBytes(stream)
-
-</t>
-<t tx="davidmcnab.041004144338.20">def fromBinFilePrivate(self, path):
-    """
-    Loads this dest object, given the pathname of a file containing
-    a binary destkey
-    """
-    self.fromBinPrivate(open(path, "rb").read())
-
-</t>
-<t tx="davidmcnab.041004144338.21">def fromBase64(self, b64):
-    """
-    Loads this dest from a base64 string
-    """
-    self._item.fromBase64(b64)
-    self._private = None
-
-</t>
-<t tx="davidmcnab.041004144338.22">def fromBase64File(self, path):
-    """
-    Loads public part from file containing base64
-    """
-    f = open(path, "rb")
-    self.fromBase64(f.read())
-    f.close()
-
-</t>
-<t tx="davidmcnab.041004144338.23">def fromBase64Private(self, s):
-    """
-    Loads this dest object from a base64 private key string
-    """
-    bytes = i2p.data.Base64.decode(s)
-    self._private = bytes
-    stream = java.io.ByteArrayInputStream(bytes)
-    self._item.readBytes(stream)
-
-</t>
-<t tx="davidmcnab.041004144338.24">def fromBase64FilePrivate(self, path):
-    """
-    Loads this dest from a base64 file containing private key
-    """
-    self.fromBase64Private(open(path, "rb").read())
-
-</t>
-<t tx="davidmcnab.041004144338.25"></t>
-<t tx="davidmcnab.041004144338.26">def sign(self, s):
-    """
-    Signs a string using this dest's priv key
-    """
-    # get byte stream
-    bytes = str2bytearray(s)
-
-    # stream up our private bytes
-    stream = java.io.ByteArrayInputStream(self._private)
-
-    # temporary dest object
-    d = i2p.data.Destination()
-
-    # suck the public part off the stream
-    d.readBytes(stream)
-
-    # temporary private key object
-    privkey = i2p.data.PrivateKey()
-    privkey.readBytes(stream)
-    
-    # now we should just have the signing key portion left in the stream
-    signingkey = i2p.data.SigningPrivateKey()
-    signingkey.readBytes(stream)
-    
-    # create DSA engine
-    dsa = i2p.crypto.DSAEngine()
-    
-    sig = dsa.sign(bytes, signingkey)
-
-    rawsig = bytearray2str(sig.getData())
-
-    return rawsig
-
-</t>
-<t tx="davidmcnab.041004144338.27">def verify(self, s, sig):
-    """
-    Verifies a string against this dest, to test if it was actually
-    signed by whoever has the dest privkey
-    """
-    # get byte stream from data
-    databytes = str2bytearray(s)
-
-    # get signature stream from sig
-    sigstream = java.io.ByteArrayInputStream(str2bytearray(sig))
-
-    # make a signature object
-    signature = i2p.data.Signature()
-    signature.readBytes(sigstream)
-
-    # get signature verify key
-    pubkey = self.getSigningPublicKey()
-
-	#log(4, "databytes=%s, pubkey=%s" % (repr(databytes), repr(pubkey)))
-    
-    # now get a verification
-    dsa = i2p.crypto.DSAEngine()
-    result = dsa.verifySignature(signature, databytes, pubkey)
-
-    return result
-
-
-
-</t>
-<t tx="davidmcnab.041004144338.28"></t>
-<t tx="davidmcnab.041004144338.29">def hasPrivate(self):
-    """
-    Returns True if this dest has private parts, False if not
-    """
-
-    if self._private:
-        return 1
-    else:
-        return 0
-</t>
-<t tx="davidmcnab.041004144338.30">class I2PClient(JavaWrapper):
-    """
-    jython-comfortable wrapper for java I2P client class
-    """
-    @others
-
-</t>
-<t tx="davidmcnab.041004144338.31">def __init__(self, **kw):
-    """
-    I2PClient constructor
-    
-    No args or keywords as yet
-    """
-    client = clientFactory.createClient()
-    JavaWrapper.__init__(self, client)
-
-</t>
-<t tx="davidmcnab.041004144338.32">def createDestination(self, **kw):
-    """
-    Creates a destination, either a new one, or from a bin or base64 file
-    
-    Keywords:
-        - see L{I2PDestination} constructor
-    """
-    return I2PDestination(**kw)
-
-</t>
-<t tx="davidmcnab.041004144338.33">def createSession(self, dest, sessionClass=None, **kw):
-    """
-    Create a session
-
-    Arguments:
-        - dest - an L{I2PDestination} object which MUST contain a private portion
-        - sessionClass - if given, this should be a subclass
-          of I2PSession. This allows you to implement your own handlers.
-
-    Keywords:
-        - session options (refer javadocs)
-    """
-    if sessionClass is None:
-        sessionClass = I2PSession
-
-    if not dest.hasPrivate():
-        raise NoPrivateKey("Dest object has no private key")
-
-    #print kw
-    #session = self._item.createSession(destStream, dict2props(kw))
-    session = sessionClass(client=self, dest=dest, **kw)
-    return session
-    #return sessionClass(session=session)
-
-</t>
-<t tx="davidmcnab.041004144338.34">class I2PSession(JavaWrapper):
-    """
-    Wraps an I2P client session
-
-    You can subclass this, overriding the on_* handler callbacks,
-    and pass it as an argument to I2PClient.createSession
-
-    In the default 'on_message' callback, message retrieval is
-    synchronous - inbound messages get written to an internal queue,
-    which you can checked with numMessages() and retrieved from via
-    getMessage(). You may override on_message() if you
-    want to handle incoming messages asynchronously yourself.
-
-    Note - as far as I can tell, this class should be thread-safe.
-    """
-    @others
-</t>
-<t tx="davidmcnab.041004144338.35">host = i2cpHost
-port = i2cpPort
-</t>
-<t tx="davidmcnab.041004144338.36">def __init__(self, **kw):
-    """
-    I2PSession constructor
-
-    Keywords:
-        - either:
-            - session - a java i2p session object
-        - or:
-            - client - an L{I2PClient} object
-            - dest - an L{I2PDestination} object
-    Also:
-        - listener - an L{I2PSessionListener} object.
-
-    Router-level options:
-        - reliability - one of 'guaranteed' and 'besteffort' (default 'besteffort')
-        - host - host on which router is running
-        - port - port on which router is listening
-    """
-    #
-    # grab options destined for java class
-    #
-    options = {}
-
-    reliability = takeKey(kw, 'reliability', 'besteffort')
-    if reliability == 'guaranteed':
-        reliability = jI2PClient.PROP_RELIABILITY_GUARANTEED
-    else:
-        reliability = jI2PClient.PROP_RELIABILITY_BEST_EFFORT
-    options[jI2PClient.PROP_RELIABILITY] = reliability
-
-    host = takeKey(kw, 'host', self.host)
-    options[jI2PClient.PROP_TCP_HOST] = host
-
-    port = takeKey(kw, 'port', self.port)
-    options[jI2PClient.PROP_TCP_PORT] = str(port)
-
-    if kw.has_key('reliability'):
-        reliability = kw['reliability']
-
-    if kw.has_key('listener'):
-        listener = kw['listener']
-        del kw['listener']
-    else:
-        listener = I2PSessionListener()
-
-    #print options
-
-    #
-    # other keywords handled locally
-    #
-    if kw.has_key('session'):
-        session = kw['session']
-        del kw['session']
-        JavaWrapper.__init__(self, session)
-    elif kw.has_key('client') and kw.has_key('dest'):
-        client = kw['client']
-        dest = kw['dest']
-        del kw['client']
-        del kw['dest']
-        destStream = java.io.ByteArrayInputStream(dest._private)
-        session = self._item = client._item.createSession(destStream, dict2props(options))
-        #client.createSession(dest, dict2props(options))
-    else:
-        raise Exception("implementation incomplete")
-
-    # set up a listener
-    self.setSessionListener(listener)
-
-    # set up a queue for inbound msgs
-    self.qInbound = Queue.Queue()
-    self.lockInbound = threading.Lock()
-    self.nInboundMessages = 0
-
-    self.lockOutbound = threading.Lock()
-
-
-
-</t>
-<t tx="davidmcnab.041004144338.37">def sendMessage(self, dest, payload):
-    """
-    Sends a message to another dest
-    
-    Arguments:
-        - dest - an L{I2PDestination} object
-        - payload - a string to send
-    """
-    dest = dest._item
-    payload = str2bytearray(payload)
-    self.lockOutbound.acquire()
-    try:
-        res = self._item.sendMessage(dest, payload)
-    except:
-        self.lockOutbound.release()
-        raise
-    self.lockOutbound.release()
-    return res
-</t>
-<t tx="davidmcnab.041004144338.38">def numMessages(self):
-    """
-    Returns the number of unretrieved inbound messages
-    """
-    self.lockInbound.acquire()
-    n = self.nInboundMessages
-    self.lockInbound.release()
-    return n
-</t>
-<t tx="davidmcnab.041004144338.39">def getMessage(self, blocking=1):
-    """
-    Returns the next available inbound message.
-    
-    If blocking is set to 1 (default), blocks
-    till another message comes in.
-    
-    If blocking is set to 0, returns None if there
-    are no available messages.
-    """
-    if blocking:
-        msg = self.qInbound.get()
-        #print "getMessage: acquiring lock"
-        self.lockInbound.acquire()
-        #print "getMessage: got lock"
-        self.nInboundMessages -= 1
-    else:
-        #print "getMessage: acquiring lock"
-        self.lockInbound.acquire()
-        #print "getMessage: got lock"
-        if self.nInboundMessages &gt; 0:
-            msg = self.qInbound.get()
-            self.nInboundMessages -= 1
-        else:
-            msg = None
-    self.lockInbound.release()
-    #print "getMessage: released lock"
-    return msg
-
-</t>
-<t tx="davidmcnab.041004144338.40">def setSessionListener(self, listener):
-    """
-    Designates an L{I2PSessionListener} object to listen to this session
-    """
-    self.listener = listener
-    listener.addSession(self)
-    self._item.setSessionListener(listener)
-
-
-</t>
-<t tx="davidmcnab.041004144338.41">def destroySession(self):
-    """
-    Destroys an existing session
-
-    Note that due to a jython quirk, calls to destroySession might
-    trigger a TypeError relating to arg mismatch - we ignore such
-    errors here because by the time the exception happens, the
-    session has already been successfully closed
-    """
-    try:
-        self._item.destroySession()
-    except TypeError:
-        pass
-
-</t>
-<t tx="davidmcnab.041004144338.42">#
-# handler methods which you should override
-#
-
-@others
-</t>
-<t tx="davidmcnab.041004144338.43">def on_message(self, msg):
-    """
-    Callback for when a message arrives.
-
-    Appends the message to the inbound queue, which you can check
-    with the numMessages() method, and read with getMessage()
-
-    You should override this if you want to handle inbound messages
-    asynchronously.
-    
-    Arguments:
-        - msg - a string that was sent by peer
-    """
-    #print "on_message: msg=%s" % msg
-    self.lockInbound.acquire()
-    #print "on_message: got lock"
-    self.qInbound.put(msg)
-    self.nInboundMessages += 1
-    self.lockInbound.release()
-    #print "on_message: released lock"
-
-</t>
-<t tx="davidmcnab.041004144338.44">def on_abuse(self, severity):
-    """
-    Callback indicating abuse is happening
-    
-    Arguments:
-        - severity - an int of abuse level, 1-100
-    """
-    print "on_abuse: severity=%s" % severity
-
-</t>
-<t tx="davidmcnab.041004144338.45">def on_disconnected(self):
-    """
-    Callback indicating remote peer disconnected
-    """
-    print "on_disconnected"
-
-</t>
-<t tx="davidmcnab.041004144338.46">def on_error(self, message, error):
-    """
-    Callback indicating an error occurred
-    """
-    print "on_error: message=%s error=%s" % (message, error)
-
-</t>
-<t tx="davidmcnab.041004144338.47">class I2PSessionListener(i2p.client.I2PSessionListener):
-    """
-    Wraps a java i2p.client.I2PSessionListener object
-    """
-    def __init__(self, *sessions):
-        self.sessions = list(sessions)
-
-    def addSession(self, session):
-        """
-        Adds an L{I2PSession} object to the list of sessions to listen on
-        
-        Note - you must also invoke the session's setSessionListener() method
-        (see I2PSession.setSessionListener)
-        """
-        if session not in self.sessions:
-            self.sessions.append(session)
-    
-    def delSession(self, session):
-        """
-        Stop listening to a given session
-        """
-        if session in self.sessions:
-            del self.sessions.index[session]
-
-    def messageAvailable(self, session, msgId, size):
-        """
-        Callback from java::
-            public void messageAvailable(
-                I2PSession session,
-                int msgId,
-                long size)
-        """
-        #print "listener - messageAvailable"
-
-        # try to find session in our sessions table
-        sessions = filter(lambda s, session=session: s._item == session, self.sessions)
-        if sessions:
-            #print "compare to self.session-&gt;%s" % (session == self.session._item)
-
-            # found a matching session - retrieve it
-            session = sessions[0]
-
-            # retrieve message and pass to callback
-            msg = session.receiveMessage(msgId)
-            msgStr = bytearray2str(msg)
-            session.on_message(msgStr)
-        else:
-            print "messageAvailable: unknown session=%s msgId=%s size=%s" % (session, msgId, size)
-
-    def reportAbuse(self, session, severity):
-        """
-        Callback from java::
-            public void reportAbuse(
-                I2PSession session,
-                int severity)
-        """
-        if self.session:
-            self.session.on_abuse(severity)
-        else:
-            print "reportAbuse: unknown session=%s severity=%s" % (session, severity)
-    
-    def disconnected(self, session):
-        """
-        Callback from java::
-            public void disconnected(I2PSession session)
-        """
-        if self.session:
-            self.session.on_disconnected()
-        else:
-            print "disconnected: unknown session=%s" % session
-
-    def errorOccurred(session, message, error):
-        """
-        Callback from java::
-            public void errorOccurred(
-                I2PSession session,
-                java.lang.String message,
-                java.lang.Throwable error)
-        """
-        if self.session:
-            self.session.on_error(message, error)
-        else:
-            print "errorOccurred: message=%s error=%s" % (message, error)
-
-</t>
-<t tx="davidmcnab.041004144338.48"></t>
-<t tx="davidmcnab.041004144338.49">class I2PSocket:
-    """
-    Wraps I2P streaming API into a form resembling python sockets
-    """
-    @others
-</t>
-<t tx="davidmcnab.041004144338.50">host = i2cpHost
-port = i2cpPort
-
-</t>
-<t tx="davidmcnab.041004144338.51">def __init__(self, dest=None, **kw):
-    """
-    Create an I2P streaming socket
-
-    Arguments:
-        - dest - a private destination to associate with this socket
-
-    Keywords:
-        - host - hostname on which i2cp is listening (default self.host)
-        - port - port on which i2cp listens (default self.port)
-
-    Internally used keywords (used for wrapping an accept()ed connection):
-        - dest
-        - remdest
-        - sock
-        - instream
-        - outstream
-    """
-    # set up null attribs
-    self.sockmgr = None
-    self.instream = None
-    self.outstream = None
-    self.sock = None
-    self._connected = 0
-    self._blocking = 1
-
-    # save dest (or lack thereof)
-    self.dest = dest
-
-    if kw.has_key('sock') \
-            and kw.has_key('remdest') \
-            and kw.has_key('instream') \
-            and kw.has_key('outstream'):
-
-        # wrapping an accept()'ed connection
-        log(4, "accept()'ed a connection, wrapping...")
-
-        self.sock = kw['sock']
-        self.dest = dest
-        self.remdest = kw['remdest']
-        self.instream = kw['instream']
-        self.outstream = kw['outstream']
-    else:
-        log(4, "creating new I2PSocket %s" % dest)
-
-        # process keywords
-        self.host = kw.get('host', self.host)
-        self.port = int(kw.get('port', self.port))
-
-        # we need a factory, don't we?
-        self.sockmgrFact = i2p.client.streaming.I2PSocketManagerFactory()
-
-</t>
-<t tx="davidmcnab.041004144338.52">def bind(self, dest=None):
-    """
-    'binds' the socket to a dest
-
-    dest is an I2PDestination object, which you may specify in the constructor
-    instead of here. However, we give you the option of specifying here for
-    some semantic compatibility with python sockets.
-    """
-    if dest is not None:
-        self.dest = dest
-    elif not self.dest:
-        # create new dest, client should interrogate it at some time
-        log(4, "bind: socket has no dest, creating one")
-        self.dest = I2PDestination()
-</t>
-<t tx="davidmcnab.041004144338.53">def listen(self, *args, **kw):
-    """
-    Sets up the object to receive connections
-    """
-    # sanity checks
-    if self.sockmgr:
-        raise I2PSocketError(".sockmgr already present - have you already called listen?")
-    if not self.dest:
-        raise I2PSocketError("socket is not bound to a destination")
-
-    log(4, "listening on socket")
-    
-    # create the socket manager
-    self._createSockmgr()
-    </t>
-<t tx="davidmcnab.041004144338.54">def accept(self):
-    """
-    Waits for incoming connections, and returns a new I2PSocket object
-    with the connection
-    """
-    # sanity check
-    if not self.sockmgr:
-        raise I2PSocketError(".listen() has not been called on this socket")
-
-    # accept a conn and get its streams
-    sock = self.sockmgr.getServerSocket().accept()
-    instream = sock.getInputStream()
-    outstream = sock.getOutputStream()
-    remdest = I2PDestination(dest=sock.getPeerDestination())
-
-    # wrap it and return it
-    sockobj = I2PSocket(dest=self.dest,
-                        remdest=remdest,
-                        sock=sock,
-                        instream=instream,
-                        outstream=outstream)
-    self._connected = 1
-    return sockobj
-
-</t>
-<t tx="davidmcnab.041004144338.55">def connect(self, remdest):
-    """
-    Connects to a remote destination
-
-    This has one totally major difference from the normal socket
-    paradigm, and that is that you can have n outbound connections
-    to different dests.
-    """
-    # sanity check
-    if self.sockmgr:
-        raise I2PSocketError(".sockmgr already present - have you already called listen/connect?")
-
-    # create whole new dest if none was provided to constructor
-    if self.dest is None:
-        log(4, "connect: creating whole new dest")
-        self.dest = I2PDestination()
-
-    # create the socket manager
-    self._createSockmgr()
-
-    # do the connect
-    #print "remdest._item = %s" % repr(remdest._item)
-
-    opts = net.i2p.client.streaming.I2PSocketOptions()
-    try:
-        log(4, "trying to connect to %s" % remdest.toBase64())
-        sock = self.sock = self.sockmgr.connect(remdest._item, opts)
-        self.remdest = remdest
-    except:
-        logException(2, "apparent exception, continuing...")
-
-    self.instream = sock.getInputStream()
-    self.outstream = sock.getOutputStream()
-
-    sockobj = I2PSocket(dest=self.dest,
-                        remdest=remdest,
-                        sock=sock,
-                        instream=self.instream,
-                        outstream=self.outstream)
-    self._connected = 1
-    return sockobj
-</t>
-<t tx="davidmcnab.041004144338.56">def recv(self, nbytes):
-    """
-    Reads nbytes of data from socket
-    """
-    # sanity check
-    if not self.instream:
-        raise I2PSocketError("Socket is not connected")
-    
-    # for want of better methods, read bytewise
-    chars = []
-    while nbytes &gt; 0:
-        byte = self.instream.read()
-        if byte &lt; 0:
-            break # got all we're gonna get
-        char = chr(byte)
-        chars.append(char)
-        #print "read: got a byte %s (%s)" % (byte, repr(char))
-        nbytes -= 1
-        
-    # got it all
-    buf = "".join(chars)
-    #print "recv: buf=%s" % repr(buf)
-    return buf
-
-
-</t>
-<t tx="davidmcnab.041004144338.57">def send(self, buf):
-    """
-    Sends buf thru socket
-    """
-    # sanity check
-    if not self.outstream:
-        raise I2PSocketError("Socket is not connected")
-
-    # and write it out
-    log(4, "send: writing '%s' to outstream..." % repr(buf))
-    outstream = self.outstream
-    for c in buf:
-        outstream.write(ord(c))
-
-    # flush just in case
-    log(4, "send: flushing...")
-    self.outstream.flush()
-
-    log(4, "send: done")
-
-</t>
-<t tx="davidmcnab.041004144338.58">def available(self):
-    """
-    Returns the number of bytes available for recv()
-    """
-    #print "available: sock is %s" % repr(self.sock)
-
-    return self.instream.available()
-
-
-</t>
-<t tx="davidmcnab.041004144338.59">def close(self):
-    """
-    Closes the socket
-    """
-    # sanity check
-    #if not self._connected:
-    #    raise I2PSocketError("Socket is not connected")
-
-    # shut up everything
-    try:
-        self.instream.close()
-    except:
-        pass
-    try:
-        self.outstream.close()
-    except:
-        pass
-    try:
-        self.sock.close()
-    except:
-        pass
-</t>
-<t tx="davidmcnab.041004144338.60">def _createSockmgr(self):
-
-    if getattr(self, 'sockmgr', None):
-        return
-
-    #options = {jI2PClient.PROP_TCP_HOST: self.host,
-    #           jI2PClient.PROP_TCP_PORT: self.port}
-    options = {}
-    props = dict2props(options)
-
-    # get a java stream thing from dest
-    stream = java.io.ByteArrayInputStream(self.dest._private)
-    
-    # create socket manager thing
-    self.sockmgr = self.sockmgrFact.createManager(stream, self.host, self.port, props)
-</t>
-<t tx="davidmcnab.041004144338.61"></t>
-<t tx="davidmcnab.041004144338.62">class I2PSamServer(ThreadingTCPServer):
-    """
-    A server which makes I2CP available via a socket
-    """
-	@others
-</t>
-<t tx="davidmcnab.041004144338.63">host = i2psamhost
-port = i2psamport
-
-i2cphost = i2cpHost
-i2cpport = i2cpPort
-
-version = version
-
-
-</t>
-<t tx="davidmcnab.041004144338.64">def __init__(self, i2pclient=None, **kw):
-    """
-    Create the client listener object
-    
-    Arguments:
-        - i2pclient - an I2PClient object - optional - if not
-          given, one will be created
-    
-    Keywords:
-        - host - host to listen on for client conns (default self.host ('127.0.0.1')
-        - port - port to listen on for client conns (default self.port (7656)
-        - i2cphost - host to talk to i2cp on (default self.i2cphost ('127.0.0.1'))
-        - i2cpport - port to talk to i2cp on (default self.i2cphost ('127.0.0.1'))
-    """
-
-    # create an I2PClient object if none given
-    if i2pclient is None:
-        i2pclient = I2PClient()
-    self.i2pclient = i2pclient
-
-    # get optional host/port for client and i2cp
-    self.host = kw.get('host', self.host)
-    self.port = int(kw.get('port', self.port))
-    self.i2cphost = kw.get('i2cphost', self.i2cphost)
-    self.i2cpport = int(kw.get('i2cpport', self.i2cpport))
-
-    # create record of current sessions, and a lock for it
-    self.sessions = {}
-    self.sessionsLock = threading.Lock()
-    self.streams = {}
-    self.streamsLock = threading.Lock()
-    self.samNextId = 1
-    self.samNextIdLock = threading.Lock()
-
-    # and create the server
-    try:
-        ThreadingTCPServer.__init__(
-            self, 
-            (self.host, self.port),
-            I2PSamClientHandler)
-    except:
-        log(4, "crashed with host=%s, port=%s" % (self.host, self.port))
-        raise
-
-</t>
-<t tx="davidmcnab.041004144338.65">def run(self):
-    """
-    Run the SAM server.
-
-    when connections come in, they are automatically
-    accepted, and an L{I2PClientHandler} object created,
-    and its L{handle} method invoked.
-    """
-    log(4, "Listening for client requests on %s:%s" % (self.host, self.port))
-    self.serve_forever()
-
-
-</t>
-<t tx="davidmcnab.041004144338.66">def finish_request(self, request, client_address):
-    """Finish one request by instantiating RequestHandlerClass."""
-    try:
-        self.RequestHandlerClass(request, client_address, self)
-    except:
-        pass
-    log(3, "Client session terminated")
-</t>
-<t tx="davidmcnab.041004144338.67">def samAllocId(self):
-    """
-    Allocates a new unique id as required by SAM protocol
-    """
-    self.samNextIdLock.acquire()
-    id = self.samNextId
-    self.samNextId += 1
-    self.samNextIdLock.release()
-    return id
-</t>
-<t tx="davidmcnab.041004144338.68">class I2PSamClientHandler(StreamRequestHandler):
-    r"""
-    Manages a single socket connection from a client.
-    
-    When a client connects to the SAM server, the I2PSamServer
-    object creates an instance of this class, and invokes its
-    handle method. See L{handle}.
-
-    Note that if a client terminates its connection to the server, the server
-    will destroy all current connections initiated by that client
-    
-    Size values are decimal
-    Connection is persistent
-    """
-	@others</t>
-<t tx="davidmcnab.041004144338.69">def handle(self):
-    """
-    Reads command/data messages from SAM Client, executes these,
-    and sends back responses.
-    
-    Plants callback hooks into I2PSession objects, so that when
-    data arrives via I2P, it can be immediately sent to the client.
-    """
-    self.localsessions = {}
-    self.globalsessions = self.server.sessions
-
-    self.localstreams = {} # keyed by sam stream id
-    self.globalstreams = self.server.streams
-
-    self.samSessionIsOpen = 0
-    self.samSessionStyle = ''
-
-    # localise the id allocator
-    self.samAllocId = self.server.samAllocId
-
-    # need a local sending lock
-    self.sendLock = threading.Lock()
-
-    log(5, "Got req from %s" % repr(self.client_address))
-
-    try:
-        self.namingService = i2p.client.naming.HostsTxtNamingService()
-    except:
-        logException(2, "Failed to create naming service object")
-
-    try:
-        while 1:
-            # get req
-            req = self.rfile.readline().strip()
-            flds = [s.strip() for s in req.split(" ")]
-            cmd = flds[0]
-            if cmd in ['HELLO', 'SESSION', 'STREAM', 'DATAGRAM', 'RAW', 'NAMING', 'DEST']:
-                topic, subtopic, args = self.samParse(flds)
-                method = getattr(self, "on_"+cmd, None)
-                method(topic, subtopic, args)
-            else:
-                method = getattr(self, "on_"+cmd, None)
-                if method:
-                    method(flds)
-                else:
-                    # bad shit
-                    self.wfile.write("error unknown command '%s'\n" % cmd)
-
-    except IOError:
-        log(3, "Client connection terminated")
-    except ValueError:
-        pass
-    except:
-        logException(4, "Client req handler crashed")
-        self.wfile.write("error\n")
-
-    # clean up sessions
-    for dest in self.localsessions.keys():
-        if dest in self.globalsessions.keys():
-            log(4, "forgetting global dest %s" % dest[:30])
-            del self.globalsessions[dest]
-
-    self.finish()
-    #thread.exit()
-
-</t>
-<t tx="davidmcnab.041004144338.70">def on_genkeys(self, flds):
-
-    log(4, "entered")
-
-    server = self.server
-    client = server.i2pclient
-    globalsessions = server.sessions
-    sessionsLock = server.sessionsLock
-
-    read = self.rfile.read
-    readline = self.rfile.readline
-    write = self.wfile.write
-    flush = self.wfile.flush
-
-    # genkeys
-    try:
-        dest = I2PDestination()
-        priv = dest.toBase64Private()
-        pub = dest.toBase64()
-        write("ok %s %s\n" % (pub, priv))
-    except:
-        write("error exception\n")
-</t>
-<t tx="davidmcnab.041004144338.71">def on_createsession(self, flds):
-
-    log(4, "entered")
-
-    server = self.server
-    client = server.i2pclient
-    globalsessions = server.sessions
-    sessionsLock = server.sessionsLock
-
-    read = self.rfile.read
-    readline = self.rfile.readline
-    write = self.wfile.write
-    flush = self.wfile.flush
-
-    sessionsLock.acquire()
-
-    try:
-        b64priv = flds[1]
-
-        # spit if someone else already has this dest
-        if b64priv in globalsessions.keys():
-            write("error dest in use\n")
-        elif b64priv in self.localsessions.keys():
-            # duh, already open locally, treat as ok
-            write("ok\n")
-        else:
-            # whole new session - set it up
-            dest = I2PDestination(base64private=b64priv)
-            log(4, "Creating session on dest '%s'" % b64priv[:40])
-            session = client.createSession(dest)
-            log(4, "Connecting session on dest '%s'" % b64priv[:40])
-            session.connect()
-            log(4, "Session on dest '%s' now live" % b64priv[:40])
-            
-            # and remember it
-            self.localsessions[b64priv] = session
-            globalsessions[b64priv] = session
-            
-            # and tell the client the good news
-            write("ok\n")
-    except:
-        logException(4, "createsession fail")
-        write("error exception\n")
-
-    sessionsLock.release()
-</t>
-<t tx="davidmcnab.041004144338.72">def on_destroysession(self, flds):
-
-    log(4, "entered")
-
-    server = self.server
-    client = server.i2pclient
-    globalsessions = server.sessions
-    sessionsLock = server.sessionsLock
-
-    read = self.rfile.read
-    readline = self.rfile.readline
-    write = self.wfile.write
-    flush = self.wfile.flush
-
-    sessionsLock.acquire()
-
-    try:
-        b64priv = flds[1]
-        
-        # spit if session not known
-        if not globalsessions.has_key(b64priv):
-            # no such session presently exists anywhere
-            write("error nosuchsession\n")
-        elif not self.localsessions.has_key(b64priv):
-            # session exists, but another client owns it
-            write("error notyoursession\n")
-        else:
-            # session exists and we own it
-            session = self.localsessions[b64priv]
-            del self.localsessions[b64priv]
-            del globalsessions[b64priv]
-            try:
-                session.destroySession()
-                write("ok\n")
-            except:
-                raise
-    except:
-        logException(4, "destroy session failed")
-        write("error exception\n")
-
-    sessionsLock.release()
-
-    log(4, "done")
-
-</t>
-<t tx="davidmcnab.041004144338.73">def on_send(self, flds):
-
-    #log(4, "entered: %s" % repr(flds))
-    log(4, "entered")
-
-    server = self.server
-    client = server.i2pclient
-    globalsessions = server.sessions
-    sessionsLock = server.sessionsLock
-
-    read = self.rfile.read
-    readline = self.rfile.readline
-    write = self.wfile.write
-    flush = self.wfile.flush
-
-    sessionsLock.acquire()
-
-    session = None
-    try:
-        size = int(flds[1])
-        b64priv = flds[2]
-        b64peer = flds[3]
-        msg = self._recvbytes(size)
-
-        # spit if session not known
-        if not globalsessions.has_key(b64priv):
-            # no such session presently exists anywhere
-            log(4, "no such session")
-            write("error nosuchsession\n")
-        elif not self.localsessions.has_key(b64priv):
-            # session exists, but another client owns it
-            write("error notyoursession\n")
-        else:
-            session = self.localsessions[b64priv]
-    except:
-        logException(2, "Send exception")
-        write("error exception on send command\n")
-
-    sessionsLock.release()
-
-    if not session:
-        return
-    
-    # now get/instantiate the remote dest
-    try:
-        peerDest = I2PDestination(base64=b64peer)
-    except:
-        peerDest = None
-        logException(2, "Send: bad remote dest")
-        write("error bad remote dest\n")
-    if not peerDest:
-        return
-
-    # and do the send
-    try:
-        res = session.sendMessage(peerDest, msg)
-    except:
-        logException(2, "Send: failed")
-        write("error exception on send\n")
-        res = None
-
-    if res is None:
-        return
-
-    # report result
-    if res:
-        write("ok\n")
-    else:
-        write("error send failed\n")
-
-    log(4, "done")
-
-</t>
-<t tx="davidmcnab.041004144338.74">def on_receive(self, flds):
-
-    log(4, "entered")
-
-    server = self.server
-    client = server.i2pclient
-    globalsessions = server.sessions
-    sessionsLock = server.sessionsLock
-
-    read = self.rfile.read
-    readline = self.rfile.readline
-    write = self.wfile.write
-    flush = self.wfile.flush
-
-    sessionsLock.acquire()
-
-    session = None
-    try:
-        b64priv = flds[1]
-
-        # spit if session not known
-        if not globalsessions.has_key(b64priv):
-            # no such session presently exists anywhere
-            write("error nosuchsession\n")
-        elif not self.localsessions.has_key(b64priv):
-            # session exists, but another client owns it
-            write("error notyoursession\n")
-        else:
-            session = self.localsessions[b64priv]
-    except:
-        logException(4, "receive command error")
-        write("error exception on receive command\n")
-    sessionsLock.release()
-
-    if not session:
-        log(4, "no session matching privdest %s" % b64priv[:30])
-        return
-    
-    # does this session have any received data?
-    if session.numMessages() &gt; 0:
-        msg = session.getMessage()
-        write("ok %s\n%s" % (len(msg), msg))
-    else:
-        write("ok 0\n")
-
-    log(4, "done")
-
-    return
-
-</t>
-<t tx="davidmcnab.041004144338.75">def on_HELLO(self, topic, subtopic, args):
-    """
-    Responds to client PING
-    """
-    log(4, "entered")
-    self.samSend("HELLO", "PONG")
-    log(4, "responded to HELLO")
-
-</t>
-<t tx="davidmcnab.041004144338.76">def on_SESSION(self, topic, subtopic, args):
-
-    log(4, "entered")
-
-    server = self.server
-    client = server.i2pclient
-    globalsessions = server.sessions
-    localsessions = self.localsessions
-    sessionsLock = server.sessionsLock
-
-    read = self.rfile.read
-    readline = self.rfile.readline
-    write = self.wfile.write
-    flush = self.wfile.flush
-
-    if subtopic == 'CREATE':
-        
-        if self.samSessionIsOpen:
-            self.samSend("SESSION", "STATUS",
-                         RESULT="I2P_ERROR",
-                         MESSAGE="Session_already_created",
-                         )
-            return
-
-        # get/validate STYLE arg
-        style = self.samSessionStyle = args.get('STYLE', None)
-        if style is None:
-            self.samSend("SESSION", "STATUS",
-                         RESULT="I2P_ERROR",
-                         MESSAGE="Missing_STYLE_argument",
-                         )
-            return
-        elif style not in ['STREAM', 'DATAGRAM', 'RAW']:
-            self.samSend("SESSION", "STATUS",
-                         RESULT="I2P_ERROR",
-                         MESSAGE="Invalid_STYLE_argument_'%s'" % style,
-                         )
-            return
-
-        # get/validate DESTINATION arg
-        dest = args.get('DESTINATION', None)
-        if dest == 'TRANSIENT':
-            # create new temporary dest
-            dest = self.samDest = I2PDestination()
-            destb64 = dest.toBase64Private()
-        else:
-            # make sure dest isn't globally or locally known
-            if dest in globalsessions.keys() or dest in localsessions.keys():
-                self.samSend("SESSION", "STATUS",
-                             RESULT="DUPLICATED_DEST",
-                             MESSAGE="Destination_'%s...'_already_in_use" % dest[:20],
-                             )
-                return
-
-            # try to reconstitute dest from given base64
-            try:
-                destb64 = dest
-                dest = I2PDestination(base64private=dest)
-            except:
-                self.samSend("SESSION", "STATUS",
-                             RESULT="INVALID_KEY",
-                             MESSAGE="Bad_destination_base64_string_'%s...'" % destb64[:20],
-                             )
-                return
-
-        # got valid dest now
-        self.dest = dest
-        self.samDestPub = dest.toBase64()
-
-        if style in ['RAW', 'DATAGRAM']:
-
-            if style == 'DATAGRAM':
-                # we need to know how big binary pub dests and sigs
-                self.samDestPubBin = dest.toBin()
-                self.samDestPubBinLen = len(self.samDestPubBin)
-                self.samSigLen = len(self.dest.sign("nothing"))
-                
-                log(4, "binary pub dests are %s bytes, sigs are %s bytes" % (
-                    self.samDestPubBinLen, self.samSigLen))
-
-            i2cpHost = args.get('I2CP.HOST', server.i2cphost)
-            i2cpPort = int(args.get('I2CP.PORT', server.i2cpport))
-
-            # both these styles require an I2PSession object
-            session = client.createSession(dest, host=i2cpHost, port=i2cpPort)
-            
-            # plug in our inbound message handler
-            session.on_message = self.on_message
-
-            log(4, "Connecting session on dest '%s'" % destb64[:40])
-            try:
-                session.connect()
-            except net.i2p.client.I2PSessionException:
-                self.samSend("SESSION", "STATUS",
-                             RESULT="I2P_ERROR",
-                             MESSAGE="Failed_to_connect_to_i2cp_port",
-                             )
-                logException(3, "Failed to connect I2PSession")
-                return
-                
-            log(4, "Session on dest '%s' now live" % destb64[:40])
-            
-            # and remember it
-            localsessions[destb64] = session
-            globalsessions[destb64] = session
-            self.samSession = session
-
-        else: # STREAM
-            # no need to create session object, because we're using streaming api
-            log(4, "Creating STREAM session")
-            
-            # what kind of stream?
-            direction = args.get('DIRECTION', 'BOTH')
-            if direction not in ['BOTH', 'RECEIVE', 'CREATE']:
-                self.samSend("SESSION", "STATUS",
-                    RESULT="I2P_ERROR",
-                    MESSAGE="Illegal_direction_keyword_%s" % direction.replace(" ","_"),
-                    )
-                return
-
-            if direction == 'BOTH':
-                self.canConnect = 1
-                self.canAccept = 1
-            elif direction == 'RECEIVE':
-                self.canConnect = 0
-                self.canAccept = 1
-            elif direction == 'CREATE':
-                self.canConnect = 1
-                self.canAccept = 0
-
-            # but we do need to mark it as being in use
-            localsessions[destb64] = globalsessions[destb64] = None
-
-            # make a local socket
-            sock = self.samSock = I2PSocket(dest)
-
-            # and we also need to fire up a socket listener, if not CREATE-only
-            if self.canAccept:
-                thread.start_new_thread(self.threadSocketListener, (sock, dest))
-
-        # finally, we can reply with the good news
-        self.samSend("SESSION", "STATUS",
-                     RESULT="OK",
-                     )
-
-    else: # subtopic != CREATE
-        self.samSend("SESSION", "STATUS",
-                     RESULT="I2P_ERROR",
-                     MESSAGE="Invalid_command_'SESSION_%s'" % subtopic,
-                     )
-        return
-
-</t>
-<t tx="davidmcnab.041004144338.77">def on_SESSION_CREATE(self, topic, subtopic, args):
-
-    log(4, "entered")
-
-    server = self.server
-    client = server.i2pclient
-    globalsessions = server.sessions
-    localsessions = self.localsessions
-    sessionsLock = server.sessionsLock
-
-    read = self.rfile.read
-    readline = self.rfile.readline
-    write = self.wfile.write
-    flush = self.wfile.flush
-
-</t>
-<t tx="davidmcnab.041004144338.78">def on_STREAM(self, topic, subtopic, args):
-
-    log(4, "entered")
-
-    server = self.server
-    client = server.i2pclient
-    globalsessions = server.sessions
-    sessionsLock = server.sessionsLock
-
-    read = self.rfile.read
-    readline = self.rfile.readline
-    write = self.wfile.write
-    flush = self.wfile.flush
-
-    if subtopic == 'CONNECT':
-        # who are we connecting to again?
-        remdest = I2PDestination(base64=args['DESTINATION'])
-        id = int(args['ID'])
-    
-        try:
-            log(4, "Trying to connect to remote peer %s..." % args['DESTINATION'])
-            sock = self.samSock.connect(remdest)
-            log(4, "Connected to remote peer %s..." % args['DESTINATION'])
-            self.localstreams[id] = sock
-            self.samSend("STREAM", "STATUS",
-                         RESULT='OK',
-                         ID=id,
-                         )
-            thread.start_new_thread(self.threadSocketReceiver, (sock, id))
-
-        except:
-            log(4, "Failed to connect to remote peer %s..." % args['DESTINATION'])
-            self.samSend("STREAM", "STATUS",
-                         RESULT='I2P_ERROR',
-                         MESSAGE='exception_on_connect',
-                         ID=id,
-                         )
-
-    elif subtopic == 'SEND':
-        # send to someone
-        id = int(args['ID'])
-        try:
-            sock = self.localstreams[id]
-            sock.send(args['DATA'])
-        except:
-            logException(4, "send failed")
-
-
-
-
-</t>
-<t tx="davidmcnab.041004144338.79">def on_DATAGRAM(self, topic, subtopic, args):
-    r"""
-    DATAGRAM SEND
-    DESTINATION=$base64key
-    SIZE=$numBytes\n[$numBytes of data]
-
-    All datagram messages have a signature/hash header, formatted as:
-        - sender's binary public dest
-        - S(H(sender_bin_pubdest + recipient_bin_pubdest + msg))
-    """
-    log(4, "entered")
-
-    # at this stage of things, we don't know how to handle anything except SEND
-    if subtopic != 'SEND':
-        log(3, "Got illegal subtopic '%s' in DATAGRAM command" % subtopic)
-        return
-
-    # get the details
-    peerdestb64 = args['DESTINATION']
-    peerdest = I2PDestination(base64=peerdestb64)
-    peerdestBin = base64dec(peerdestb64)
-    data = args['DATA']
-
-    # make up the header
-    log(4, "samDestPubBin (%s) %s" % (type(self.samDestPubBin), repr(self.samDestPubBin)))
-    log(4, "peerdestBin (%s) %s" % (type(peerdestBin), repr(peerdestBin)))
-    log(4, "data (%s) %s" % (type(data), repr(data)))
-
-    hashed = shahash(self.samDestPubBin + peerdestBin + data)
-    log(4, "hashed=%s" % repr(hashed))
-
-    sig = self.dest.sign(hashed)
-    log(4, "sig=%s" % repr(sig))
-    hdr = self.samDestPubBin + sig
-    
-    # send the thing
-    self.samSession.sendMessage(peerdest, hdr + data)
-
-</t>
-<t tx="davidmcnab.041004144338.80">def on_RAW(self, topic, subtopic, args):
-    r"""
-    RAW SEND
-    DESTINATION=$base64key
-    SIZE=$numBytes\n[$numBytes of data]
-    """
-    log(4, "entered")
-
-    # at this stage of things, we don't know how to handle anything except SEND
-    if subtopic != 'SEND':
-        return
-
-    # get the details
-    peerdest = I2PDestination(base64=args['DESTINATION'])
-    msg = args['DATA']
-
-    # send the thing
-    self.samSession.sendMessage(peerdest, msg)
-</t>
-<t tx="davidmcnab.041004144338.81">def on_NAMING(self, topic, subtopic, args):
-
-    log(4, "entered: %s %s %s" % (repr(topic), repr(subtopic), repr(args)))
-
-    # at this stage of things, we don't know how to handle anything except LOOKUP
-    if subtopic != 'LOOKUP':
-        return
-
-    # get the details
-    host = args['NAME']
-
-    log(4, "looking up host %s" % host)
-    
-    # try to lookup
-    jdest = self.namingService.lookup(host)
-
-    if not jdest:
-        log(4, "host %s not found" % host)
-        self.samSend("NAMING", "REPLY",
-                     RESULT="KEY_NOT_FOUND",
-                     NAME=host,
-                     )
-        return
-
-    try:
-        b64 = I2PDestination(dest=jdest).toBase64()
-        self.samSend("NAMING", "REPLY",
-                     RESULT="OK",
-                     NAME=host,
-                     VALUE=b64,
-                     )
-        log(4, "host %s found and valid key returned" % host)
-        return
-    except:
-        log(4, "host %s found but key invalid" % host)
-        self.samSend("NAMING", "REPLY",
-                     RESULT="INVALID_KEY",
-                     NAME=host,
-                     )
-
-</t>
-<t tx="davidmcnab.041004144338.82">def on_DEST(self, topic, subtopic, args):
-
-    log(4, "Generating dest")
-
-    dest = I2PDestination()
-    priv = dest.toBase64Private()
-    pub = dest.toBase64()
-
-    log(4, "Sending dest to client")
-
-    self.samSend("DEST", "REPLY", PUB=pub, PRIV=priv)
-
-    log(4, "done")
-</t>
-<t tx="davidmcnab.041004144338.83">def on_message(self, msg):
-    """
-    This callback gets plugged into the I2PSession object,
-    so we can asychronously notify our client when stuff arrives
-    """
-    if self.samSessionStyle == 'RAW':
-        self.samSend("RAW", "RECEIVE", msg)
-
-    elif self.samSessionStyle == 'DATAGRAM':
-        # ain't so simple, we gotta rip and validate the header
-        remdestBin = msg[:self.samDestPubBinLen]
-        log(4, "remdestBin=%s" % repr(remdestBin))
-
-        sig = msg[self.samDestPubBinLen:self.samDestPubBinLen+self.samSigLen]
-        log(4, "sig=%s" % repr(sig))
-
-        data = msg[self.samDestPubBinLen+self.samSigLen:]
-        log(4, "data=%s" % repr(data))
-        
-        # now try to verify
-        hashed = shahash(remdestBin + self.samDestPubBin + data)
-        log(4, "hashed=%s" % repr(hashed))
-
-        remdest = I2PDestination(bin=remdestBin)
-        if remdest.verify(hashed, sig):
-            # fine - very good, pass it on
-            log(4, "sig from peer is valid")
-            self.samSend("DATAGRAM", "RECEIVE", data,
-                         DESTINATION=remdest.toBase64(),
-                         )
-        else:
-            log(4, "DATAGRAM sig from peer is invalid")
-</t>
-<t tx="davidmcnab.041004144338.84">def threadSocketListener(self, sock, dest):
-    """
-    Listens for incoming socket connections, and
-    notifies the client accordingly
-    """
-    destb64 = dest.toBase64()
-
-    log(4, "Listening for connections to %s..." % destb64)
-
-    sock.bind()
-    sock.listen()
-
-    while 1:
-        log(4, "Awaiting next connection to %s..." % destb64)
-        newsock = sock.accept()
-        log(4, "Got connection to %s..." % destb64)
-
-        # need an id, negative
-        id = - self.server.samAllocId()
-
-        # register it in local and global streams
-        self.localstreams[id] = self.globalstreams[id] = newsock
-
-        # fire up the receiver thread
-        thread.start_new_thread(self.threadSocketReceiver, (newsock, id))
-        
-        # who is connected to us?
-        remdest = newsock.remdest
-        remdest_b64 = remdest.toBase64()
-        
-        # and notify the client
-        self.samSend("STREAM", "CONNECTED",
-                     DESTINATION=remdest_b64,
-                     ID=id)
-
-</t>
-<t tx="davidmcnab.041004144338.85">def samParse(self, flds):
-    """
-    carves up a SAM command, returns it as a 3-tuple:
-        - cmd - command string
-        - subcmd - subcommand string
-        - dargs - dict of args
-    """
-    cmd = flds[0]
-    subcmd = flds[1]
-    args = flds[2:]
-    
-    dargs = {}
-    for arg in args:
-        try:
-            name, val = arg.split("=", 1)
-        except:
-            logException(3, "failed to process %s in %s" % (repr(arg), repr(flds)))
-            raise
-        dargs[name] = val
-
-    # read and add data if any
-    if dargs.has_key('SIZE'):
-        size = dargs['SIZE'] = int(dargs['SIZE'])
-        dargs['DATA'] = self._recvbytes(size)
-
-    #log(4, "\n".join([cmd+" "+subcmd] + [("%s=%s (...)" % (k,v[:40])) for k,v in dargs.items()]))
-    log(4, "\n".join([cmd+" "+subcmd] + [("%s=%s (...)" % (k,v)) for k,v in dargs.items()]))
-
-    return cmd, subcmd, dargs
-
-
-
-</t>
-<t tx="davidmcnab.041004144338.86">def samSend(self, topic, subtopic, data=None, **kw):
-    """
-    Sends a SAM message (reply?) back to client
-    
-    Arguments:
-        - topic - the first word in the reply, eg 'STREAM'
-        - subtopic - the second word of the reply, eg 'CONNECTED'
-        - data - a string of raw data to send back (optional)
-    Keywords:
-        - extra 'name=value' items to pass back.
-    
-    Notes:
-        1. SIZE is not required. If sending back data, it will
-           be sized and a SIZE arg inserted automatically.
-        2. a dict of values can be passed to the 'args' keyword, in lieu
-           of direct keywords. This allows for cases where arg names would
-           cause python syntax clashes, eg 'tunnels.depthInbound'
-    """
-    items = [topic, subtopic]
-
-    # stick in SIZE if needed
-    if data is not None:
-        kw['SIZE'] = str(len(data))
-    else:
-        data = '' # for later
-
-    self.samCreateArgsList(kw, items)
-    
-    # and whack it together
-    buf = " ".join(items) + '\n' + data
-
-    # and ship it
-    self.sendLock.acquire()
-    try:
-        self._sendbytes(buf)
-    except:
-        self.sendLock.release()
-        raise
-    self.sendLock.release()
-
-</t>
-<t tx="davidmcnab.041004144338.87">def samCreateArgsList(self, kw1, lst):
-    for k,v in kw1.items():
-        if k == 'args':
-            self.samCreateArgsList(v, lst)
-        else:
-            lst.append("=".join([str(k), str(v)]))
-</t>
-<t tx="davidmcnab.041004144338.88">def _sendbytes(self, raw):
-
-    self.wfile.write(raw)
-    self.wfile.flush()
-</t>
-<t tx="davidmcnab.041004144338.89">def _recvbytes(self, count):
-    """
-    Does a guaranteed read of n bytes
-    """
-    read = self.rfile.read
-
-    chunks = []
-    needed = count
-    while needed &gt; 0:
-        chunk = read(needed)
-        chunklen = len(chunk)
-        needed -= chunklen
-        chunks.append(chunk)
-    raw = "".join(chunks)
-
-    # done
-    return raw
-
-</t>
-<t tx="davidmcnab.041004144338.90">class NoPrivateKey(Exception):
-    """Destination object has no private key"""
-
-class I2PSocketError(Exception):
-    """Error working with I2PSocket objects"""
-</t>
-<t tx="davidmcnab.041004144338.91"></t>
-<t tx="davidmcnab.041004144338.92">def shahash(s):
-    """
-    Calculates SHA Hash of a string, as a string, using
-    I2P hashing facility
-    """
-    h = net.i2p.crypto.SHA256Generator().calculateHash(s)
-    h = bytearray2str(h.getData())
-    return h
-</t>
-<t tx="davidmcnab.041004144338.93">def base64enc(s):
-    return net.i2p.data.Base64.encode(s)
-</t>
-<t tx="davidmcnab.041004144338.94">def base64dec(s):
-    return bytearray2str(net.i2p.data.Base64.decode(s))
-
-</t>
-<t tx="davidmcnab.041004144338.95">def str2bytearray(s):
-    """
-    Convenience - converts python string to java-friendly byte array
-    """
-    a = []
-    for c in s:
-        n = ord(c)
-        if n &gt;= 128:
-            n = n - 256
-        a.append(n)
-    return a
-
-</t>
-<t tx="davidmcnab.041004144338.96">def bytearray2str(a):
-    """
-    Convenience - converts java-friendly byte array to python string
-    """
-    chars = []
-    for n in a:
-        if n &lt; 0:
-            n += 256
-        chars.append(chr(n))
-    return "".join(chars)
-
-</t>
-<t tx="davidmcnab.041004144338.97">def byteoutstream2str(bs):
-    """
-    Convenience - converts java-friendly byteoutputstream to python string
-    """
-    chars = []
-    while 1:
-        c = bs.read()
-        if c &gt;= 0:
-            chars.append(chr(c))
-        else:
-            break
-    return "".join(chars)
-
-</t>
-<t tx="davidmcnab.041004144338.98">def dict2props(d):
-    """
-    Converts a python dict d into a java.util.Properties object
-    """
-    props = java.util.Properties()
-    for k,v in d.items():
-        props[k] = str(v)
-    return props
-
-
-</t>
-<t tx="davidmcnab.041004144338.99">def takeKey(somedict, keyname, default=None):
-    """
-    Utility function to destructively read a key from a given dict.
-    Same as the dict's 'takeKey' method, except that the key (if found)
-    sill be deleted from the dictionary.
-    """
-    if somedict.has_key(keyname):
-        val = somedict[keyname]
-        del somedict[keyname]
-    else:
-        val = default
-    return val
-</t>
-<t tx="davidmcnab.041004144338.100">def log(level, msg, nPrev=0):
-
-    # ignore messages that are too trivial for chosen verbosity
-    if level &gt; verbosity:
-        return
-
-    loglock.acquire()
-    try:
-        # rip the stack
-        caller = traceback.extract_stack()[-(2+nPrev)]
-        path, line, func = caller[:3]
-        path = os.path.split(path)[1]
-        full = "%s:%s:%s():\n* %s" % (
-            path,
-            line,
-            func,
-            msg.replace("\n", "\n   + "))
-        now = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
-        msg = "%s %s\n" % (now, full)
-    
-        if logfile == sys.stdout:
-            print msg
-        else:
-            file(logfile, "a").write(msg+"\n")
-    except:
-        s = StringIO.StringIO()
-        traceback.print_exc(file=s)
-        print s.getvalue()
-        print "Logger crashed"
-    loglock.release()</t>
-<t tx="davidmcnab.041004144338.101">def logException(level, msg=''):
-    s = StringIO.StringIO()
-    traceback.print_exc(file=s)
-    log(level, "%s\n%s" % (s.getvalue(), msg), 1)
-</t>
-<t tx="davidmcnab.041004144338.102">def usage(detailed=0):
-    
-    print "Usage: %s &lt;options&gt; [&lt;command&gt;]" % sys.argv[0]
-    if not detailed:
-        print "Run with '-h' to get detailed help"
-        sys.exit(0)
-
-    print "I2PSAM is a bridge that allows I2P client programs to access the"
-    print "I2P network by talking over a plaintext socket connection."
-    print "References:"
-    print "   - http://www.freenet.org.nz/i2p - source, doco, downloadables"
-    print "   - http://drupal.i2p.net/node/view/144 - I2P SAM specification"
-    print
-    print "Options:"
-    print "  -h, -?, --help        - display this help"
-    print "  -v, --version         - print program version"
-    print "  -V, --verbosity=n     - set verbosity to n, default 2, 1==quiet, 4==noisy"
-    print "  -H, --listenhost=host - specify host to listen on for client connections"
-    print "  -P, --listenport=port - port to listen on for client connections"
-    print "      --i2cphost=host   - hostname of I2P router's I2CP interface"
-    print "      --i2cpport=port   - port of I2P router's I2CP interface"
-    print
-    print "Commands:"
-    print "     (run with no commands to launch SAM server)"
-    print "     samserver - runs as a SAM server"
-    print "     test - run a suite of self-tests"
-    print "     testsocket - run only the socket test"
-    print "     testbidirsocket - run socket test in bidirectional mode"
-    print
-    
-    sys.exit(0)
-</t>
-<t tx="davidmcnab.041004144338.103">def main():
-
-    argv = sys.argv
-    argc = len(argv)
-
-    # -------------------------------------------------
-    # do the getopt command line parsing
-
-    try:
-        opts, args = getopt.getopt(sys.argv[1:],
-                                   "h?vV:H:P:",
-                                   ['help', 'version', 'verbosity=',
-                                    'listenhost=', 'listenport=',
-                                    'i2cphost=', 'i2cpport=',
-                                    ])
-    except:
-        traceback.print_exc(file=sys.stdout)
-        usage("You entered an invalid option")
-
-    #print "args=%s" % args
-
-    serveropts = {}
-    for opt, val in opts:
-        if opt in ['-h', '-?', '--help']:
-            usage(1)
-        elif opt in ['-v', '--version']:
-            print "I2P SAM version %s" % version
-            sys.exit(0)
-        elif opt in ['-V', '--verbosity']:
-            serveropts['verbosity'] = int(val)
-        elif opt in ['-H', '--listenhost']:
-            serveropts['host'] = val
-        elif opt in ['-P', '--listenport']:
-            serveropts['port'] = int(val)
-        elif opt in ['--i2cphost']:
-            serveropts['i2cphost'] = val
-        elif opt in ['--i2cpport']:
-            serveropts['i2cpport'] = int(val)
-        else:
-            usage(0)
-
-    # --------------------------------------------------
-    # now run in required mode, default is 'samserver'
-
-    if len(args) == 0:
-        cmd = 'samserver'
-    else:
-        cmd = args[0]
-
-    if cmd == 'samserver':
-
-        log(2, "Running I2P SAM Server...")
-        server = I2PSamServer(**serveropts)
-        server.run()
-
-    elif cmd == 'test':
-        
-        print "RUNNING full I2PSAM Jython TEST SUITE"
-        testsigs()
-        testdests()
-        testsession()
-        testsocket()
-
-    elif cmd == 'testsocket':
-        
-        print "RUNNING SOCKET TEST"
-        testsocket(0)
-
-    elif cmd == 'testbidirsocket':
-        print "RUNNING BIDIRECTIONAL SOCKET TEST"
-        testsocket(1)
-
-    else:
-        # spit at unrecognised option
-        usage(0)
-
-</t>
-<t tx="davidmcnab.041004144338.104"></t>
-<t tx="davidmcnab.041004144338.105">def testdests():
-    """
-    Demo function which tests out dest generation and import/export
-    """
-    print
-    print "********************************************"
-    print "Testing I2P destination create/export/import"
-    print "********************************************"
-    print
-
-    print "Generating a destination"
-    d1 = I2PDestination()
-
-    print "Exporting and importing dest1 in several forms"
-
-    print "public binary string..."
-    d1_bin = d1.toBin()
-    d2_bin = I2PDestination(bin=d1_bin)
-
-    print "public binary file..."
-    d1.toBinFile("temp-d1-bin")
-    d2_binfile = I2PDestination(binfile="temp-d1-bin")
-
-    print "private binary string..."
-    d1_binprivate = d1.toBinPrivate()
-    d2_binprivate = I2PDestination(binprivate=d1_binprivate)
-
-    print "private binary file..."
-    d1.toBinFilePrivate("temp-d1-bin-private")
-    d2_binfileprivate = I2PDestination(binfileprivate="temp-d1-bin-private")
-
-    print "public base64 string..."
-    d1_b64 = d1.toBase64()
-    d2_b64 = I2PDestination(base64=d1_b64)
-
-    print "public base64 file..."
-    d1.toBase64File("temp-d1-b64")
-    d2_b64file = I2PDestination(base64file="temp-d1-b64")
-
-    print "private base64 string..."
-    d1_base64private = d1.toBase64Private()
-    d2_b64private = I2PDestination(base64private=d1_base64private)
-
-    print "private base64 file..."
-    d1.toBase64FilePrivate("temp-d1-b64-private")
-    d2_b64fileprivate = I2PDestination(base64fileprivate="temp-d1-b64-private")
-
-    print "All destination creation/import/export tests passed!"
-
-
-</t>
-<t tx="davidmcnab.041004144338.106">def testsigs():
-    global d1, d1pub, d1sig, d1res
-    
-    print
-    print "********************************************"
-    print "Testing I2P dest-based signatures"
-    print "********************************************"
-    print
-    
-    print "Creating dest..."
-    d1 = I2PDestination()
-
-    s_good = "original stuff that we're signing"
-    s_bad = "non-original stuff we're trying to forge"
-    
-    print "Signing some shit against d1..."
-    d1sig = d1.sign(s_good)
-
-    print "Creating public dest d1pub"
-    d1pub = I2PDestination(bin=d1.toBin())
-
-    print "Verifying original data with d1pub"
-    res = d1pub.verify(s_good, d1sig)
-    print "Result: %s (should be 1)" % repr(res)
-    
-    print "Trying to verify on a different string"
-    res1 = d1pub.verify(s_bad, d1sig)
-    print "Result: %s (should be 0)" % repr(res1)
-    
-    if res and not res1:
-        print "signing/verifying test passed"
-    else:
-        print "SIGNING/VERIFYING TEST FAILED"
-
-</t>
-<t tx="davidmcnab.041004144338.107">def testsession():
-
-    global c, d1, d2, s1, s2
-
-    print
-    print "********************************************"
-    print "Testing I2P dest-&gt;dest messaging"
-    print "********************************************"
-    print
-    
-    print "Creating I2P client..."
-    c = I2PClient()
-
-    print "Creating destination d1..."
-    d1 = c.createDestination()
-
-    print "Creating destination d2..."
-    d2 = c.createDestination()
-
-    print "Creating destination d3..."
-    d3 = c.createDestination()
-
-    print "Creating session s1 on dest d1..."
-    s1 = c.createSession(d1, host='localhost', port=7654)
-
-    print "Creating session s2 on dest d2..."
-    s2 = c.createSession(d2)
-
-    print "Connecting session s1..."
-    s1.connect()
-
-    print "Connecting session s2..."
-    s2.connect()
-
-    print "Sending message from s1 to d2..."
-    s1.sendMessage(d2, "Hi there, s2!!")
-
-    print "Retrieving message from s2..."
-    print "got: %s" % repr(s2.getMessage())
-
-    print "Sending second message from s1 to d2..."
-    s1.sendMessage(d2, "Hi there again, s2!!")
-
-    print "Retrieving message from s2..."
-    print "got: %s" % repr(s2.getMessage())
-
-    print "Sending message from s1 to d3 (should take ages then fail)..."
-    res = s1.sendMessage(d3, "This is futile!!")
-    print "result of that send was %s (should have been 0)" % res
-
-    print "Destroying session s1..."
-    s1.destroySession()
-
-    print "Destroying session s2..."
-    s2.destroySession()
-
-    print "session tests passed!"
-</t>
-<t tx="davidmcnab.041004144338.108">def testsocket(bidirectional=0):
-
-    global d1, d2, s1, s2
-
-    print
-    print "********************************************"
-    print "Testing I2P streaming interface"
-    print "********************************************"
-    print
-    
-    print "Creating destinations..."
-    dServer = I2PDestination()
-    dClient = I2PDestination()
-
-    print "Creating sockets..."
-    sServer = I2PSocket(dServer)
-    sClient = I2PSocket(dClient)
-
-    # server thread which simply reads a line at a time, then echoes
-    # that line back to the client
-    def servThread(s):
-        print "server: binding socket"
-        s.bind()
-        print "server: setting socket to listen"
-        s.listen()
-        print "server: awaiting connection"
-        sock = s.accept()
-        print "server: got connection"
-
-        sock.send("Hello, echoing...\n")
-        buf = ''
-        while 1:
-            c = sock.recv(1)
-            if c == '':
-                sock.close()
-                print "server: socket closed"
-                break
-
-            buf += c
-            if c == '\n':
-                sock.send("SERVER: "+buf)
-                buf = ''
-
-    # client thread which reads lines and prints them to stdout
-    def clientThread(s):
-        buf = ''
-        while 1:
-            c = s.recv(1)
-            if c == '':
-                s.close()
-                print "client: socket closed"
-                break
-            buf += c
-            if c == '\n':
-                print "client: got %s" % repr(buf)
-                buf = ''
-
-    print "launching server thread..."
-    thread.start_new_thread(servThread, (sServer,))
-
-    if bidirectional:
-        # dummy thread which accepts connections TO client socket
-        def threadDummy(s):
-            print "dummy: listening"
-            s.listen()
-            print "dummy: accepting"
-    
-            sock = s.accept()
-            print "dummy: got connection"
-    
-        print "test - launching dummy client accept thread"
-        thread.start_new_thread(threadDummy, (sClient,))
-
-    print "client: trying to connect"
-    sClient.connect(dServer)
-
-    print "client: connected, launching rx thread"
-    thread.start_new_thread(clientThread, (sClient,))
-
-    while 1:
-        line = raw_input("Enter something (q to quit)&gt; ")
-        if line == 'q':
-            print "closing client socket"
-            sClient.close()
-            break
-        sClient.send(line+"\n")
-
-    print "I2PSocket test apparently succeeded"
-
-
-</t>
-<t tx="davidmcnab.041004144338.109">if __name__ == '__main__':
-    main()
-
-</t>
-<t tx="davidmcnab.041004144551">@first #!/usr/bin/env python
-"""
-Implements a client API for I2CP messaging via SAM
-
-Very simple I2P messaging interface, which should prove easy
-to reimplement in your language of choice
-
-This module can be used from cpython or jython
-
-Run this module without arguments to see a demo in action
-(requires SAM server to be already running)
-"""
-@others
-
-</t>
-<t tx="davidmcnab.041004144551.1">import sys, os, socket, thread, threading, Queue, traceback, StringIO, time
-
-from pdb import set_trace
-
-</t>
-<t tx="davidmcnab.041004144551.2"># -----------------------------------------
-# server access settings
-
-i2psamhost = '127.0.0.1'
-i2psamport = 7656
-
-# ------------------------------------------
-# logging settings
-
-# 1=v.quiet, 2=normal, 3=verbose, 4=debug, 5=painful
-verbosity = 5
-
-# change to a filename to log there instead
-logfile = sys.stdout
-
-# when set to 1, and when logfile != sys.stdout, log msgs are written
-# both to logfile and console stdout
-log2console = 1
-
-# don't touch this!
-loglock = threading.Lock()
-
-</t>
-<t tx="davidmcnab.041004144551.3">class I2PServerFail(Exception):
-    """
-    A failure in connecting to the I2CP server
-    """
-
-class I2PCommandFail(Exception):
-    """
-    A failure in an I2CP command
-    """
-    pass
-
-class I2PStreamClosed(Exception):
-    """
-    Stream is not open
-    """
-</t>
-<t tx="davidmcnab.041004144551.4">class I2PSamClient:
-    """
-    Implements a reference client for accessing I2CP via i2psam
-    
-    Connects to i2psam's I2PSamServer, sends commands
-    and receives results
-
-    The primitives should be reasonably self-explanatory
-
-    Usage summary:
-        1. create one or more I2PSamClient instances per process (1 should be fine)
-        2. invoke the L{genkeys} method to create destination keypairs
-        3. create sessions objects via the L{createSession} method
-        4. use these session objects to send and receive data
-        5. destroy the session objects when you're done
-    
-    Refer to the function L{demo} for a simple example
-    """
-    @others
-</t>
-<t tx="davidmcnab.041004144551.5"># server host/port settings exist here in case you might
-# have a reason for overriding in a subclass
-
-host = i2psamhost
-port = i2psamport
-
-i2cpHost = None
-i2cpPort = None
-
-</t>
-<t tx="davidmcnab.041004144551.6">def __init__(self, **kw):
-    """
-    Creates a client connection to i2psam listener
-    
-    Keywords:
-        - host - host to connect to (default 127.0.0.1)
-        - port - port to connect to (default 7656)
-    """
-    # get optional host/port
-    log(4, "entered")
-
-    self.host = kw.get('host', self.host)
-    self.port = int(kw.get('port', self.port))
-
-    self.cmdLock = threading.Lock()
-
-    self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-
-    self.lockHello = threading.Lock()
-    self.sendLock = threading.Lock()
-    self.qNewDests = Queue.Queue()
-    self.qSession = Queue.Queue()
-    self.qDatagrams = Queue.Queue()
-    self.qRawMessages = Queue.Queue()
-
-    self.namingReplies = {}
-    self.namingCache = {}
-
-    self.streams = {} # currently open streams, keyed by id
-    self.streamConnectReplies = {} # holds queues awaiting connect resp, keyed by id
-    self.qNewStreams = Queue.Queue() # incoming connections
-
-    self.samNextIdLock = threading.Lock()
-    self.samNextId = 1
-
-    self.isRunning = 1
-
-
-    log(4, "trying connection to SAM server...")
-    try:
-        self.sock.connect((self.host, self.port))
-    except:
-        raise I2PServerFail(
-            "Connection to i2psam server failed\n"
-            "(are you sure your I2P router is running, and\n"
-            "listening for I2CP connections on %s:%s?)" % (self.host, self.port)
-            )
-
-    # fire up receiver thread
-    thread.start_new_thread(self.threadRx, ())
-
-    # ping the server
-    try:
-        log(4, "trying to ping SAM server...")
-        self.samHello()
-    except:
-        logException(4, "Exception on handshaking")
-        raise I2PServerFail("Failed to handshake with i2psam server")
-
-    # connected fine
-    log(2, "I2CP Client successfully connected")
-</t>
-<t tx="davidmcnab.041004144551.7">def createSession(self, privdest):
-    """
-    DEPRECATED - use sam* methods instead!
-
-    Creates a session using private destkey
-    """
-    #3. createsession:
-    #    - client-&gt;server:
-    #        - createsession &lt;base64private&gt;\n
-    #    - server-&gt;client:
-    #        - ok\n  OR
-    #        - error[ &lt;reason&gt;]\n
-
-    self.cmdLock.acquire()
-    try:
-        self._sendline("createsession %s" % privdest)
-        respitems = self._recvline().split(" ", 1)
-        if respitems[0] == 'ok':
-            res = None
-        else:
-            res = respitems[1]
-    except:
-        logException(2, "createsession fail")
-        self.cmdLock.release()
-        raise
-
-    self.cmdLock.release()
-
-    if res:
-        raise I2PCommandFail("createsession fail: "+res)
-
-    return I2PRemoteSession(self, privdest)
-
-</t>
-<t tx="davidmcnab.041004144551.8">def destroySession(self, privdest):
-    """
-    DEPRECATED - use sam* methods instead!
-
-    Destrlys a session using private destkey
-    """
-    #4. destroysession:
-    #    - client-&gt;server:
-    #        - destroysession &lt;base64private&gt;\n
-    #    - server-&gt;client:
-    #        - ok\n OR
-    #        - error[ &lt;reason&gt;]\n
-
-    self.cmdLock.acquire()
-    try:
-        self._sendline("destroysession %s" % privdest)
-        respitems = self._recvline().split(" ", 1)
-        if respitems[0] == 'ok':
-            res = None
-        else:
-            res = respitems[1]
-    except:
-        logException(2, "destroysession fail")
-        self.cmdLock.release()
-        raise
-
-    self.cmdLock.release()
-
-    if res:
-        raise I2PCommandFail("destroysession fail: " + res)
-
-    return res
-
-</t>
-<t tx="davidmcnab.041004144551.9">def send(self, privdest, peerdest, msg):
-    """
-    DEPRECATED - use sam* methods instead!
-
-    Sends a block of data from local dest to remote dest
-    """
-    #5. send:
-    #    - client-&gt;server:
-    #        - send &lt;size&gt; &lt;localbase64private&gt; &lt;remotebase64dest&gt;\ndata
-    #    - server-&gt;client:
-    #        - ok\n OR
-    #        - error[ &lt;reason&gt;]\n
-
-    self.cmdLock.acquire()
-    try:
-        self._sendline("send %s %s %s" % (len(msg), privdest, peerdest))
-        self._sendbytes(msg)
-        line = self._recvline()
-        #print "** %s" % line
-        respitems = line.split(" ", 1)
-        if respitems[0] == 'ok':
-            res = None
-        else:
-            res = " ".join(respitems[1:])
-    except:
-        logException(2, "send fail")
-        self.cmdLock.release()
-        raise
-
-    self.cmdLock.release()
-
-    if res:
-        raise I2PCommandFail("send fail: " + res)
-
-    return res
-
-</t>
-<t tx="davidmcnab.041004144551.10">def receive(self, privdest):
-    """
-    DEPRECATED - use sam* methods instead!
-
-    receives a block of data, returning string, or None if no data available
-    """
-    #6. receive:
-    #    - client-&gt;server:
-    #        - receive &lt;localbase64private&gt;\n
-    #    - server-&gt;client:
-    #        - ok &lt;size&gt;\ndata OR
-    #        - error[ &lt;reason&gt;]\n
-
-    self.cmdLock.acquire()
-    try:
-        self._sendline("receive %s" % privdest)
-        respitems = self._recvline().split(" ", 1)
-        if respitems[0] == 'ok':
-            res = None
-            size = int(respitems[1])
-            msg = self._recvbytes(size)
-            res = None
-        else:
-            res = respitems[1]
-    except:
-        logException(2, "receive fail")
-        self.cmdLock.release()
-        raise
-
-    self.cmdLock.release()
-
-    if res:
-        raise I2PCommandFail("destroysession fail: " + res)
-
-    return msg
-</t>
-<t tx="davidmcnab.041004144551.11"></t>
-<t tx="davidmcnab.041004144551.12">def samHello(self):
-    """
-    Sends a quick HELLO PING to SAM server and awaits response
-    Arguments:
-        - none
-
-    Keywords:
-        - none
-    
-    Returns:
-        - nothing (None) if ping sent and pong received, or raises an exception if
-          failed
-    """
-    self.lockHello.acquire()
-    self.samSend("HELLO", "PING")
-    self.lockHello.acquire()
-    self.lockHello.release()
-</t>
-<t tx="davidmcnab.041004144551.13">def samSessionCreate(self, style, dest, **kw):
-    """
-    Creates a SAM session
-    
-    Arguments:
-        - style - one of 'STREAM', 'DATAGRAM' or 'RAW'
-        - dest - base64 private destination
-    
-    Keywords:
-        - direction - only used for STREAM sessions, can be RECEIVE,
-          CREATE or BOTH (default BOTH)
-        - i2cphost - hostname for the SAM bridge to contact i2p router on
-        - i2cpport - port for the SAM bridge to contact i2p router on
-    
-    Returns:
-        - 'OK' if session was created successfully, or a tuple
-          (keyword, message) if not
-    """
-    kw1 = dict(kw)
-    kw1['STYLE'] = self.samStyle = style
-    kw1['DESTINATION'] = dest
-    if style == 'STREAM':
-        direction = kw.get('direction', 'BOTH')
-        kw1['DIRECTION'] = direction
-        if direction == 'BOTH':
-            self.canAccept = 1
-            self.canConnect = 1
-        elif direction == 'RECEIVE':
-            self.canAccept = 1
-            self.canConnect = 0
-        elif direction == 'CREATE':
-            self.canAccept = 0
-            self.canConnect = 1
-        else:
-            raise I2PCommandFail("direction keyword must be one of RECEIVE, CREATE or BOTH")
-
-    # stick in i2cp host/port if specified
-    if kw.has_key('i2cphost'):
-        kw1['I2CP.HOST'] = kw['i2cphost']
-    if kw.has_key('i2cpport'):
-        kw1['I2CP.PORT'] = kw['i2cpport']
-    
-    self.samSend("SESSION", "CREATE",
-                 **kw1)
-    subtopic, args = self.qSession.get()
-
-    if args['RESULT'] == 'OK':
-        return 'OK'
-    else:
-        return (args['RESULT'], args['MESSAGE'])
-</t>
-<t tx="davidmcnab.041004144551.14">def samDestGenerate(self):
-    """
-    Creates a whole new dest and returns an tuple pub, priv as
-    base64 public and private destination keys
-    """
-    self.samSend("DEST", "GENERATE")
-    pub, priv = self.qNewDests.get()
-    return pub, priv
-</t>
-<t tx="davidmcnab.041004144551.15">def samRawSend(self, peerdest, msg):
-    """
-    Sends a raw anon message to another peer
-    
-    peerdest is the public base64 destination key of the peer
-    """
-    self.samSend("RAW", "SEND", msg,
-                 DESTINATION=peerdest,
-                 )
-</t>
-<t tx="davidmcnab.041004144551.16">def samRawCheck(self):
-    """
-    Returns 1 if there are received raw messages available, 0 if not
-    """
-    return not self.qRawMessages.empty()
-</t>
-<t tx="davidmcnab.041004144551.17">def samRawReceive(self, blocking=1):
-    """
-    Returns the next raw message available,
-    blocking if none is available and the blocking arg is set to 0
-
-    If blocking is 0, and no messages are available, returns None.
-    
-    Remember that you can check for availability with
-    the .samRawCheck() method
-    """
-    if not blocking:
-        if self.qRawMessages.empty():
-            return None
-    return self.qRawMessages.get()
-    </t>
-<t tx="davidmcnab.041004144551.18">def samDatagramSend(self, peerdest, msg):
-    """
-    Sends a repliable datagram message to another peer
-
-    peerdest is the public base64 destination key of the peer
-    """
-    self.samSend("DATAGRAM", "SEND", msg,
-                 DESTINATION=peerdest,
-                 )
-</t>
-<t tx="davidmcnab.041004144551.19">def samDatagramCheck(self):
-    """
-    Returns 1 if there are datagram messages received messages available, 0 if not
-    """
-    return not self.qDatagrams.empty()
-</t>
-<t tx="davidmcnab.041004144551.20">def samDatagramReceive(self, blocking=1):
-    """
-    Returns the next datagram message available,
-    blocking if none is available.
-
-    If blocking is set to 0, and no messages are available,
-    returns None.
-    
-    Remember that you can check for availability with
-    the .samRawCheck() method
-    
-    Returns 2-tuple: dest, msg
-    where dest is the base64 destination of the peer from
-    whom the message was received
-    """
-    if not blocking:
-        if self.qDatagrams.empty():
-            return None
-    return self.qDatagrams.get()
-</t>
-<t tx="davidmcnab.041004144551.21">def samNamingLookup(self, host):
-    """
-    Looks up a host in hosts.txt
-    """
-    # try the cache first
-    if self.namingCache.has_key(host):
-        log(4, "found host %s in cache" % host)
-        return self.namingCache[host]
-
-    # make a queue for reply
-    q = self.namingReplies[host] = Queue.Queue()
-    
-    # send off req
-    self.samSend("NAMING", "LOOKUP",
-                 NAME=host,
-                 )
-
-    # get resp
-    resp = q.get()
-
-    result = resp.get('RESULT', 'none')
-    if result == 'OK':
-        log(4, "adding host %s to cache" % host)
-        val = resp['VALUE']
-        self.namingCache[host] = val
-        return val
-    else:
-        raise I2PCommandFail("Error looking up '%s': %s %s" % (
-            host, result, resp.get('MESSAGE', '')))
-
-</t>
-<t tx="davidmcnab.041004144551.22">def samParse(self, flds):
-    """
-    carves up a SAM command, returns it as a 3-tuple:
-        - cmd - command string
-        - subcmd - subcommand string
-        - dargs - dict of args
-    """
-    cmd = flds[0]
-    subcmd = flds[1]
-    args = flds[2:]
-    
-    dargs = {}
-    for arg in args:
-        try:
-            name, val = arg.split("=", 1)
-        except:
-            logException(3, "failed to process %s in %s" % (repr(arg), repr(flds)))
-            raise
-        dargs[name] = val
-
-    # read and add data if any
-    if dargs.has_key('SIZE'):
-        size = dargs['SIZE'] = int(dargs['SIZE'])
-        dargs['DATA'] = self._recvbytes(size)
-
-    #log(4, "\n".join([cmd+" "+subcmd] + [("%s=%s (...)" % (k,v[:40])) for k,v in dargs.items()]))
-    log(4, "\n".join([cmd+" "+subcmd] + [("%s=%s (...)" % (k,v)) for k,v in dargs.items()]))
-
-    return cmd, subcmd, dargs
-
-
-
-
-
-</t>
-<t tx="davidmcnab.041004144551.23">def samSend(self, topic, subtopic, data=None, **kw):
-    """
-    Sends a SAM message (reply?) back to client
-    
-    Arguments:
-        - topic - the first word in the reply, eg 'STREAM'
-        - subtopic - the second word of the reply, eg 'CONNECTED'
-        - data - a string of raw data to send back (optional)
-    Keywords:
-        - extra 'name=value' items to pass back.
-    
-    Notes:
-        1. SIZE is not required. If sending back data, it will
-           be sized and a SIZE arg inserted automatically.
-        2. a dict of values can be passed to the 'args' keyword, in lieu
-           of direct keywords. This allows for cases where arg names would
-           cause python syntax clashes, eg 'tunnels.depthInbound'
-    """
-    items = [topic, subtopic]
-
-    # stick in SIZE if needed
-    if data is not None:
-        kw['SIZE'] = str(len(data))
-    else:
-        data = '' # for later
-
-    self.samCreateArgsList(kw, items)
-    
-    # and whack it together
-    buf = " ".join(items) + '\n' + data
-
-    # and ship it
-    self.sendLock.acquire()
-    try:
-        self._sendbytes(buf)
-    except:
-        self.sendLock.release()
-        raise
-    self.sendLock.release()
-
-</t>
-<t tx="davidmcnab.041004144551.24">def samCreateArgsList(self, kw1, lst):
-    for k,v in kw1.items():
-        if k == 'args':
-            self.samCreateArgsList(v, lst)
-        else:
-            lst.append("=".join([str(k), str(v)]))
-</t>
-<t tx="davidmcnab.041004144551.25"></t>
-<t tx="davidmcnab.041004144551.26">def threadRx(self):
-    """
-    Handles all incoming stuff from SAM, storing in
-    local queues as appropriate
-    """
-    while self.isRunning:
-        try:
-            log(4, "Awaiting next message from server")
-            line = self._recvline()
-            if line == '':
-                log(3, "I2P server socket closed")
-                return
-            flds = line.split(" ")
-            topic, subtopic, args = self.samParse(flds)
-            log(4, "Got %s %s %s" % (topic, subtopic, args))
-            handleMsg = getattr(self, "on_"+topic, None)
-            if handleMsg:
-                handleMsg(topic, subtopic, args)
-            else:
-                log(2, "No handler for '%s' message" % topic)
-        except:
-            #logException(3, "Exception handling %s %s\n%s" % (topic, subtopic, args))
-            logException(3, "Exception handling %s" % repr(line))
-</t>
-<t tx="davidmcnab.041004144551.27">def on_HELLO(self, topic, subtopic, args):
-    """
-    Handles HELLO PONG messages from server
-    """
-    # just wake up the caller
-    log(4, "got HELLO")
-    self.lockHello.release()
-</t>
-<t tx="davidmcnab.041004144551.28">def on_SESSION(self, topic, subtopic, args):
-    """
-    Handles SESSION messages from server
-    """
-    # just stick whatever on the queue and wake up the caller
-    res = subtopic, args
-    self.qSession.put(res)
-</t>
-<t tx="davidmcnab.041004144551.29">def on_STREAM(self, topic, subtopic, args):
-    """
-    Handles STREAM messages from server
-
-    STREAM STATUS
-    RESULT=$result
-    ID=$id
-    [MESSAGE=...]
-
-    STREAM CONNECTED
-    DESTINATION=$base64key
-    ID=$id
-
-    STREAM RECEIVED
-    ID=$id
-    SIZE=$numBytes\n[$numBytes of data]
-
-    STREAM CLOSED
-    RESULT=$result
-    ID=$id
-    [MESSAGE=...]
-    """
-    log(4, "got %s %s %s" % (topic, subtopic, args))
-
-    # which stream?
-    id = int(args['ID'])
-
-    # result of prior connection attempt
-    if subtopic == 'STATUS':
-        # stick it on the queue that the caller is waiting on and let the
-        # caller interpret the result
-        self.streamConnectReplies[id].put(args)
-        return 
-
-    # notice of incoming connection
-    if subtopic == 'CONNECTED':
-
-        # grab details
-        dest = args['DESTINATION']
-
-        # wrap it in a stream obj
-        conn = I2PSAMStream(self, id, dest)
-        self.streams[id] = conn
-
-        # and put it there for anyone calling samStreamAccept()
-        self.qNewStreams.put(conn)
-
-        # done
-        return
-
-    # notice of received data
-    elif subtopic == 'RECEIVED':
-        # grab details
-        data = args['DATA']
-
-        # lookup the connection
-        conn = self.streams.get(id, None)
-        if not conn:
-            # conn not known, just ditch
-            log(2, "got data, but don't recall any conn with id %s" % id)
-            return
-
-        # and post the received data
-        conn._notifyIncomingData(data)
-
-        log(4, "wrote data to conn's inbound queue")
-        
-        # done
-        return
-
-    elif subtopic == 'CLOSED':
-        # lookup the connection
-        conn = self.streams.get(id, None)
-        if not conn:
-            # conn not known, just ditch
-            return
-
-        # mark conn as closed and forget it
-        conn._notifyIncomingData("") # special signal to close
-        conn.isOpen = 0
-        del self.streams[id]
-
-        # done
-        return
-
-
-
-</t>
-<t tx="davidmcnab.041004144551.30">def on_DATAGRAM(self, topic, subtopic, args):
-    """
-    Handles DATAGRAM messages from server
-    """
-    remdest = args['DESTINATION']
-    data = args['DATA']
-    
-    self.qDatagrams.put((remdest, data))
-</t>
-<t tx="davidmcnab.041004144551.31">def on_RAW(self, topic, subtopic, args):
-    """
-    Handles RAW messages from server
-    """
-    data = args['DATA']
-
-    log(3, "Got anonymous datagram %s" % repr(data))
-    self.qRawMessages.put(data)
-</t>
-<t tx="davidmcnab.041004144551.32">def on_NAMING(self, topic, subtopic, args):
-    """
-    Handles NAMING messages from server
-    """
-    # just find out hostname, and stick it on resp q
-    host = args['NAME']
-    self.namingReplies[host].put(args)
-</t>
-<t tx="davidmcnab.041004144551.33">def on_DEST(self, topic, subtopic, args):
-    """
-    Handles DEST messages from server
-    """
-    pubkey = args['PUB']
-    privkey = args['PRIV']
-    res = pubkey, privkey
-    self.qNewDests.put(res)
-</t>
-<t tx="davidmcnab.041004144551.34"></t>
-<t tx="davidmcnab.041004144551.35">def _recvline(self):
-    """
-    Guaranteed read of a full line
-    """
-    chars = []
-    while 1:
-        c = self.sock.recv(1)
-        if c in ['', '\n']:
-            break
-        chars.append(c)
-    return "".join(chars)
-</t>
-<t tx="davidmcnab.041004144551.36">def _recvbytes(self, num):
-    """
-    Guaranteed read of num bytes
-    """
-    if num &lt;= 0:
-        return ""
-
-    reqd = num
-    chunks = []
-    while reqd &gt; 0:
-        chunk = self.sock.recv(reqd)
-        if not chunk:
-            raise I2PServerFail("Buffer read fail")
-        chunks.append(chunk)
-        reqd -= len(chunk)
-    return "".join(chunks)
-</t>
-<t tx="davidmcnab.041004144551.37">def _sendbytes(self, buf):
-    """
-    Guaranteed complete send of a buffer
-    """
-    reqd = len(buf)
-    while reqd &gt; 0:
-        nsent = self.sock.send(buf)
-        if nsent == 0:
-            raise I2PServerFail("Send to server failed")
-        buf = buf[nsent:]
-        reqd -= nsent
-</t>
-<t tx="davidmcnab.041004144551.38">def _sendline(self, line):
-    """
-    just tacks on a newline and sends
-    """
-    self._sendbytes(line+"\n")
-</t>
-<t tx="davidmcnab.041004144551.39">class I2PRemoteSession:
-    """
-    DEPRECATED
-
-    Wrapper for I2CP connections
-    
-    Do not instantiate this directly - it gets created by
-    I2PSamClient.createSession()
-    """    
-    @others
-</t>
-<t tx="davidmcnab.041004144551.40">def __init__(self, client, dest):
-    """
-    Do not instantiate this directly
-    """
-    self.client = client
-    self.dest = dest
-</t>
-<t tx="davidmcnab.041004144551.41">def send(self, peerdest, msg):
-    """
-    """
-    return self.client.send(self.dest, peerdest, msg)
-</t>
-<t tx="davidmcnab.041004144551.42">def receive(self):
-    
-    return self.client.receive(self.dest)
-</t>
-<t tx="davidmcnab.041004144551.43">def destroy(self):
-    
-    return self.client.destroySession(self.dest)
-
-</t>
-<t tx="davidmcnab.041004144551.44"></t>
-<t tx="davidmcnab.041004144551.45">def log(level, msg, nPrev=0):
-
-    # ignore messages that are too trivial for chosen verbosity
-    if level &gt; verbosity:
-        return
-
-    loglock.acquire()
-    try:
-        # rip the stack
-        caller = traceback.extract_stack()[-(2+nPrev)]
-        path, line, func = caller[:3]
-        path = os.path.split(path)[1]
-        full = "%s:%s:%s():\n* %s" % (
-            path,
-            line,
-            func,
-            msg.replace("\n", "\n   + "))
-        now = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
-        msg = "%s %s\n" % (now, full)
-    
-        if logfile == sys.stdout:
-            print msg
-        else:
-            file(logfile, "a").write(msg+"\n")
-    except:
-        s = StringIO.StringIO()
-        traceback.print_exc(file=s)
-        print s.getvalue()
-        print "Logger crashed"
-    loglock.release()</t>
-<t tx="davidmcnab.041004144551.46">def logException(level, msg=''):
-    s = StringIO.StringIO()
-    traceback.print_exc(file=s)
-    log(level, "%s\n%s" % (s.getvalue(), msg), 1)
-</t>
-<t tx="davidmcnab.041004144551.47">def demoNAMING():
-    """
-    Demonstrates the NAMING service
-    """
-    print "Starting SAM NAMING demo..."
-    print
-
-    print "Instantiating client connection..."
-    c0 = I2PSamClient()
-    print "Client connection created"
-
-    for host in ['duck.i2p', 'nonexistent.i2p']:
-        print "Sending query for host '%s'..." % host
-        try:
-            res = c0.samNamingLookup(host)
-            print "query for %s returned:" % host
-            print repr(res)
-        except I2PCommandFail, e:
-            print "got exception: %s" % repr(e.args)
-    
-    print
-    print "---------------------------------"
-    print "NAMING service tests succeeded"
-    print "---------------------------------"
-    print
-
-
-</t>
-<t tx="davidmcnab.041004144551.48">def demoRAW():
-    """
-    Runs a demo of SAM RAW messaging
-    """
-    print "Starting SAM RAW demo..."
-    print
-
-    print "Instantiating client connections..."
-    c1 = I2PSamClient()
-    c2 = I2PSamClient()
-
-    print "Creating dests via SAM"
-    pub1, priv1 = c1.samDestGenerate()
-    pub2, priv2 = c2.samDestGenerate()
-    print "SAM Dests generated ok"
-    
-    print "Creating SAM RAW SESSION on connection c1..."
-    res = c1.samSessionCreate("RAW", priv1)
-    if res != 'OK':
-        print "Failed to create session on connection c1: %s" % repr(res)
-        return
-    print "Session on connection c1 created successfully"
-
-    print "Creating SAM SESSION on connection c2..."
-    res = c2.samSessionCreate("RAW", priv2)
-    if res != 'OK':
-        print "Failed to create session on connection c2: %s" % repr(res)
-        return
-    print "Session on connection c2 created successfully"
-
-    msg = "Hi there!"
-    print "sending from c1 to c2: %s" % repr(msg)
-    c1.samRawSend(pub2, msg)
-
-    print "now try to receive from c2 (will block)..."
-    msg1 = c2.samRawReceive()
-    print "Connection c2 got %s" % repr(msg1)
-
-    print
-    print "---------------------------------"
-    print "RAW data transfer tests succeeded"
-    print "---------------------------------"
-    print
-
-</t>
-<t tx="davidmcnab.041004144551.49">def demoDATAGRAM():
-    """
-    Runs a demo of SAM DATAGRAM messaging
-    """
-    print "Starting SAM DATAGRAM demo..."
-    print
-
-    print "Instantiating 2 more client connections..."
-    c3 = I2PSamClient()
-    c4 = I2PSamClient()
-
-    print "Creating more dests via SAM"
-    pub3, priv3 = c3.samDestGenerate()
-    pub4, priv4 = c4.samDestGenerate()
-
-    print "Creating SAM DATAGRAM SESSION on connection c3..."
-    res = c3.samSessionCreate("DATAGRAM", priv3)
-    if res != 'OK':
-        print "Failed to create DATAGRAM session on connection c3: %s" % repr(res)
-        return
-    print "DATAGRAM Session on connection c3 created successfully"
-
-    print "Creating SAM DATAGRAM SESSION on connection c4..."
-    res = c4.samSessionCreate("DATAGRAM", priv4)
-    if res != 'OK':
-        print "Failed to create DATAGRAM session on connection c4: %s" % repr(res)
-        return
-    print "Session on connection c4 created successfully"
-
-    msg = "Hi there, this is a datagram!"
-    print "sending from c3 to c4: %s" % repr(msg)
-    c3.samDatagramSend(pub4, msg)
-
-    print "now try to receive from c4 (will block)..."
-    remdest, msg1 = c4.samDatagramReceive()
-    print "Connection c4 got %s from %s..." % (repr(msg1), repr(remdest))
-
-
-    print
-    print "--------------------------------------"
-    print "DATAGRAM data transfer tests succeeded"
-    print "--------------------------------------"
-    print
-
-</t>
-<t tx="davidmcnab.041004144551.50">def demoSTREAM():
-    """
-    Runs a demo of SAM STREAM messaging
-    """
-    print "Starting SAM STREAM demo..."
-    print
-
-    print "Instantiating client c6..."
-    c6 = I2PSamClient()
-
-    print "Creating dest for c6"
-    pub6, priv6 = c6.samDestGenerate()
-
-    print "Creating SAM STREAM SESSION on connection c6..."
-    res = c6.samSessionCreate("STREAM", priv6, direction="RECEIVE")
-    if res != 'OK':
-        print "Failed to create STREAM session on connection c6: %s" % repr(res)
-        return
-    print "STREAM Session on connection c6 created successfully"
-
-    print "Launching acceptor thread..."
-    thread.start_new_thread(demoSTREAM_thread, (c6,))
-
-    #print "sleep a while and give the server a chance..."
-    #time.sleep(10)
-
-    print "----------------------------------------"
-
-    print "Instantiating client c5..."
-    c5 = I2PSamClient()
-
-    print "Creating dest for c5"
-    pub5, priv5 = c5.samDestGenerate()
-
-    print "Creating SAM STREAM SESSION on connection c5..."
-    res = c5.samSessionCreate("STREAM", priv5, direction="CREATE")
-    if res != 'OK':
-        print "Failed to create STREAM session on connection c5: %s" % repr(res)
-        return
-    print "STREAM Session on connection c5 created successfully"
-
-    print "----------------------------------------"
-
-    print "Making connection from c5 to c6..."
-
-    #set_trace()
-
-    try:
-        conn_c5 = c5.samStreamConnect(pub6)
-    except:
-        print "Stream Connection failed"
-        return
-    print "Stream connect succeeded"
-    
-    print "Receiving from c5..."
-    buf = conn_c5.readline()
-    print "Got %s" % repr(buf)
-
-    #print "Try to accept connection on c6..."
-    #conn_c6 = c6.sam
-
-    print
-    print "--------------------------------------"
-    print "DATAGRAM data transfer tests succeeded"
-    print "--------------------------------------"
-    print
-
-
-
-
-
-</t>
-<t tx="davidmcnab.041004144551.51">def demo():
-    """
-    This is a simple and straightforward demo of talking to
-    the i2psam server socket via the I2PSamClient class.
-    
-    Read the source, Luke, it's never been so easy...
-    """
-    print
-    print "-----------------------------------------"
-    print "Running i2psamclient demo..."
-    print "-----------------------------------------"
-    print
-
-    #demoNAMING()
-    #demoRAW()
-    #demoDATAGRAM()
-    demoSTREAM()
-
-    print
-    print "-----------------------------------------"
-    print "Demo Finished"
-    print "-----------------------------------------"
-
-    return
-</t>
-<t tx="davidmcnab.041004144551.52">if __name__ == '__main__':
-
-    demo()
-</t>
-<t tx="davidmcnab.041204020513">def samStreamConnect(self, dest):
-    """
-    Makes a STREAM connection to a remote dest
-
-    STREAM STATUS
-    RESULT=$result
-    ID=$id
-    [MESSAGE=...]
-    """
-    # need an ID
-    id = self.samAllocId()
-    
-    # create queue for connect reply
-    q = self.streamConnectReplies[id] = Queue.Queue()
-
-    # send req
-    self.samSend("STREAM", "CONNECT",
-                 ID=id,
-                 DESTINATION=dest,
-                 )
-
-    # await reply - comes back as a dict
-    resp = q.get()
-
-    # ditch queue
-    del self.streamConnectReplies[id]
-    del q
-    
-    # check out response
-    result = resp['RESULT']
-    if result == 'OK':
-        conn = I2PSAMStream(self, id, dest)
-        self.streams[id] = conn
-        return conn
-    else:
-        msg = resp.get('MESSAGE', '')
-        raise I2PCommandFail(result, msg, "STREAM CONNECT")
-
-</t>
-<t tx="davidmcnab.041204042212">def samAllocId(self):
-    """
-    Allocates a new unique id as required by SAM protocol
-    """
-    self.samNextIdLock.acquire()
-    id = self.samNextId
-    self.samNextId += 1
-    self.samNextIdLock.release()
-    return id
-</t>
-<t tx="davidmcnab.041204042212.1">class I2PSAMStream:
-    """
-    Wrapper for a stream object
-    """
-    @others
-</t>
-<t tx="davidmcnab.041204042212.2">def __init__(self, client, id, dest):
-    """
-    """
-    self.client = client
-    self.id = id
-    self.dest = dest
-
-    self.qIncomingData = Queue.Queue()
-
-    self.inbuf = ''
-    self.isOpen = 1
-</t>
-<t tx="davidmcnab.041204044135">def _notifyIncomingData(self, data):
-    """
-    Called by client receiver to notify incoming data
-    """
-    log(4, "got %s" % repr(data))
-    self.qIncomingData.put(data)
-</t>
-<t tx="davidmcnab.041204044735">def samStreamSend(self, conn, data):
-    """
-    DO NOT CALL THIS DIRECTLY
-    
-    Invoked by an I2PSAMStream object to transfer data
-    Use the object's .send() method instead.
-    
-    conn is the I2PSAMStream
-
-    STREAM SEND 
-    ID=$id
-    SIZE=$numBytes\n[$numBytes of data]
-    """
-    # dispatch
-    self.samSend("STREAM", "SEND", data, ID=conn.id)
-
-    # useless, but mimics socket paradigm
-    return len(data)
-
-</t>
-<t tx="davidmcnab.041204044735.1">def send(self, data):
-    """
-    Sends data to a stream connection
-    """
-    # barf if stream not open
-    if not self.isOpen:
-        raise I2PStreamClosed
-
-    # can send
-    return self.client.samStreamSend(self, data)
-</t>
-<t tx="davidmcnab.041204050339">def samStreamClose(self, conn):
-    """
-    DO NOT CALL DIRECTLY
-    
-    Invoked by I2PSAMStream to close stream
-    Use the object's .send() method instead.
-    
-    STREAM CLOSE
-    ID=$id
-    """
-    self.samSend("STREAM", "CLOSE", ID=conn.id)
-    del self.streams[conn.id]
-
-</t>
-<t tx="davidmcnab.041204050339.1">def recv(self, size):
-    """
-    Retrieves n bytes from peer
-    """
-    chunks = []
-
-    while self.isOpen and size &gt; 0:
-        # try internal buffer first
-        if self.inbuf:
-            chunk = self.inbuf[:size]
-            chunklen = len(chunk)
-            self.inbuf = self.inbuf[chunklen:]
-            chunks.append(chunk)
-            size -= chunklen
-        else:
-            # replenish input buffer
-            log(4, "I2PSAMStream.recv: replenishing input buffer")
-            buf = self.qIncomingData.get()
-            if buf == '':
-                # connection closed by peer
-                self.isOpen = 0
-                break
-            else:
-                # got more data
-                log(4, "I2PSAMStream: queue returned %s" % repr(buf))
-                self.inbuf += buf
-
-    # return whatever we've got, hopefully all
-    return "".join(chunks)
-
-
-</t>
-<t tx="davidmcnab.041204050339.2">def close(self):
-    """
-    close this stream connection
-    """
-    log(4, "closing stream")
-    self.client.samStreamClose(self)
-    log(4, "stream closed")
-    self.isOpen = 0
-
-    # and just to make sure...
-    self.qIncomingData.put("") # busts out of recv() loops
-
-</t>
-<t tx="davidmcnab.041204050511">def __del__(self):
-    """
-    Dropping last ref to this object closes stream
-    """
-    self.close()
-</t>
-<t tx="davidmcnab.041204203651">def demoSTREAM_thread(sess):
-    
-    while 1:
-        sock = sess.samStreamAccept()
-        log(4, "got incoming connection")
-
-        print "**ACCEPTOR SLEEPING 10 secs BEFORE SENDING"
-        
-        time.sleep(10)
-
-        sock.send("Hi there, what do you want?\n")
-
-        print "**ACCEPTOR SLEEPING 5 MINS BEFORE CLOSING"
-        time.sleep(300)
-        print "**ACCEPTOR CLOSING STREAM"
-
-        sock.close()
-
-</t>
-<t tx="davidmcnab.041204204235">def samStreamAccept(self):
-    """
-    Waits for an incoming connection, returning a wrapped conn obj
-    """
-    log(4, "waiting for connection")
-    conn = self.qNewStreams.get()
-    log(4, "got connection")
-    return conn
-</t>
-<t tx="davidmcnab.041304205426">def threadSocketReceiver(self, sock, id):
-    """
-    One of these gets launched each time a new stream connection
-    is created. Due to the lack of callback mechanism within the
-    ministreaming API, we have to actively poll for and send back
-    received data
-    """
-    while 1:
-        #avail = sock.available()
-        #if avail &lt;= 0:
-        #    print "threadSocketReceiver: waiting for data on %s (%s avail)..." % (id, avail)
-        #    time.sleep(5)
-        #    continue
-        #log(4, "reading a byte")
-
-        try:
-            buf = sock.recv(1)
-        except:
-            logException(4, "Exception reading first byte")
-        
-        if buf == '':
-            log(4, "stream closed")
-
-            # notify a close
-            self.samSend("STREAM", "CLOSED",
-                         ID=id)
-            return
-
-        # grab more if there's any available
-        navail = sock.available()
-        if navail &gt; 0:
-            #log(4, "%d more bytes available, reading..." % navail)
-            rest = sock.recv(navail)
-            buf += rest
-        
-        # send if off
-        log(4, "got from peer: %s" % repr(buf))
-        
-        self.samSend("STREAM", "RECEIVED", buf,
-                     ID=id,
-                     )
-
-
-
-
-</t>
-<t tx="davidmcnab.041304235615">def readline(self):
-    """
-    Read a line of text from stream, return the line without trailing newline
-    
-    This method really shouldn't exist in a class that's trying to look a bit
-    like a socket object, but what the hell!
-    """
-    chars = []
-    while 1:
-        char = self.recv(1)
-        if char in ['', '\n']:
-            break
-        chars.append(char)
-    return "".join(chars)
-</t>
-</tnodes>
-</leo_file>
diff --git a/apps/sam/doc/README b/apps/sam/doc/README
index 7c2ff3dd31..8e8736de54 100644
--- a/apps/sam/doc/README
+++ b/apps/sam/doc/README
@@ -1,27 +1,8 @@
-SAM - Simple Anonymous Messaging - is a protocol which allows
-I2P applications to access the I2P network via an unencrypted
-TCP socket connection.
-
-Interim SAM protocol specification can be found at:
-http://drupal.i2p.net/node/view/144
-
-At time of first writing this README, an implementation of the SAM
-server has been implemented in Jython (www.jython.org).
-
-You can find the server code, and build files, in the ../jython
-directory.
-
-A python client implementation, containing demo functions,
-can be found in the ../python directory.
-
-I2P developers are strongly encouraged to create SAM
-client implementations in other languages, most importantly,
-popular portable languages like C/C++, Perl and Ruby.
-
-The 'code.leo' file in this directory is used by the Leo
-code editor (http://leo.sf.net), to manage the source in a
-flexible tree format. While I can't insist on it, I'd
-massively appreciate it if you could use this editor when
-making additions and changes to the files herein, because
-it will save me a lot of maintenance effort.
-
+The Simple Anonymous Messaging protocol provides a way for
+client applications to communicate anonymously over I2P without 
+having to deal with the complexities of I2CP.  More information can
+be found at http://www.i2p.net/sam and a comparison of the various
+client access techniques is up at http://www.i2p.net/applications
+
+There are a few SAM libraries available in this package, as well as
+a Java implementation of the SAM bridge.  
\ No newline at end of file
diff --git a/apps/sam/jython/README b/apps/sam/jython/README
deleted file mode 100644
index 0d61fae466..0000000000
--- a/apps/sam/jython/README
+++ /dev/null
@@ -1,54 +0,0 @@
------------------------------------
-Instructions for building i2psam.jar
-------------------------------------
-
-1) Requirements
-
-You will need:
-
- - jython - www.jython.org
-
-Note that you don't need python to build the SAM server
-
-IMPORTANT - when you're installing jython, and running
- 'java on jython_n.n.class',
-make sure you run the java.exe that's in your SDK, not the one in your
-JRE.
-
----------------------------------------------------------
-
-2) Preparation
-
- - add the main jython directory to your PATH. Test this by typing
-   'jythonc' from a shell prompt.
-
-----------------------------------------------------------
-
-3) Building
-
- - type 'ant build'
-
-----------------------------------------------------------
-
-4) Installing
-
-Copy i2psam.jar to wherever the jar files live on your i2p installation,
-usually <i2pbasedir>/lib
-
-Find jython.jar, and copy it there too
-
-----------------------------------------------------------
-
-5) Running
-
-(assuming that you're putting the start script into your main i2p
-runtime directory, where the I2P jars live in a 'lib' subdirectory)
-
-You will need to launch i2psam.jar with a command like:
-
- java -cp lib/jython.jar:lib/i2p.jar:lib/mstreaming.jar:lib/i2psam.jar i2psam
-
-or on windows,
-
- java -cp lib\jython.jar;lib\i2p.jar;lib\mstreaming.jar;lib\i2psam.jar i2psam
-
diff --git a/apps/sam/jython/build.xml b/apps/sam/jython/build.xml
deleted file mode 100644
index 4a7656152f..0000000000
--- a/apps/sam/jython/build.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project basedir="." default="all" name="sam">
-
-    <target name="all" depends="build" />
-
-    <target name="build" depends="builddep, jar" />
-
-    <target name="builddep">
-        <ant dir="../../../core/java/" target="build" />
-        <ant dir="../../ministreaming/java/" target="build" />
-    </target>
-
-    <target name="jar">
-
-        <condition property="jythonext" value=".bat">
-            <os family="windows" />
-        </condition>
-        <condition property="jythonext" value="">
-            <not>
-                <os family="windows" />
-            </not>
-        </condition>
-
-        <exec executable="jythonc${jythonext}" dir=".">
-            <env key="CLASSPATH" path="../../../core/java/build/i2p.jar:../../ministreaming/java/build/mstreaming.jar"/>
-            <arg value="--jar"/><arg path="./i2psam.jar"/>
-            <arg path="./src/i2psam.py"/>
-        </exec>
-    </target>
-
-    <target name="clean">
-        <delete file="i2psam.jar" />
-        <delete dir="./jpywork" />
-    </target>
-
-</project>
diff --git a/apps/sam/jython/src/i2psam.py b/apps/sam/jython/src/i2psam.py
deleted file mode 100644
index fef0b482ee..0000000000
--- a/apps/sam/jython/src/i2psam.py
+++ /dev/null
@@ -1,2579 +0,0 @@
-#!/usr/bin/env jython
-#@+leo-ver=4
-#@+node:@file jython/src/i2psam.py
-#@@first
-r"""
-Implements I2P SAM Server. (refer U{http://drupal.i2p.net/node/view/144})
-
-Also contains useful classes for jython programs,
-which wrap the I2P java classes into more python-compatible
-paradigms.
-
-If you run this module (or the i2psam.jar file created from it)
-without arguments, it'll run an I2P SAM server bridge, listening
-on port 7656.
-
-The file i2psamclient.py contains python client classes and a
-demo program.
-
-Latest vers of this file is available from U{http://www.freenet.org.nz/i2p/i2psam.py}
-Latest epydoc-generated doco at U{http://www.freenet.org.nz/i2p/i2pjyDoc}
-
-The i2psam.jar file is built from this module with the following
-command (requires jython and java 1.4.x+ to be installed)::
-
-  CLASSPATH=/path/to/i2p.jar:/path/to/mstreaming.jar \
-          jythonc -jar i2psam.jar --all -A net.invisiblenet i2psam.py
-
-"""
-
-#@+others
-#@+node:imports
-# python imports
-import sys, os, time, Queue, thread, threading, StringIO, traceback, getopt
-from SocketServer import ThreadingTCPServer, StreamRequestHandler
-
-# java imports
-import java
-
-# i2p-specific imports
-import net.i2p
-import net.i2p.client # to shut up epydoc
-#import net.i2p.client.I2PClient
-#import net.i2p.client.I2PClientFactory
-#import net.i2p.client.I2PSessionListener
-import net.i2p.client.naming
-import net.i2p.client.streaming
-import net.i2p.crypto
-import net.i2p.data
-
-# handy shorthand refs
-i2p = net.i2p
-jI2PClient = i2p.client.I2PClient
-
-# import my own helper hack module
-#import I2PHelper
-
-#@-node:imports
-#@+node:globals
-clientFactory = i2p.client.I2PClientFactory
-
-#i2phelper = I2PHelper()
-
-PROP_RELIABILITY_BEST_EFFORT = i2p.client.I2PClient.PROP_RELIABILITY_BEST_EFFORT
-PROP_RELIABILITY_GUARANTEED = i2p.client.I2PClient.PROP_RELIABILITY_GUARANTEED
-
-version = "0.1.0"
-
-# host/port that our socketserver listens on
-i2psamhost = "127.0.0.1"
-i2psamport = 7656
-
-# host/port that I2P's I2CP listens on
-i2cpHost = "127.0.0.1"
-i2cpPort = 7654
-
-#print "i2cpPort=%s" % repr(i2cpPort)
-
-# ------------------------------------------
-# logging settings
-
-# 1=v.quiet, 2=normal, 3=verbose, 4=debug, 5=painful
-verbosity = 2
-
-# change to a filename to log there instead
-logfile = sys.stdout
-
-# when set to 1, and when logfile != sys.stdout, log msgs are written
-# both to logfile and console stdout
-log2console = 1
-
-# don't touch this!
-loglock = threading.Lock()
-
-
-#@-node:globals
-#@+node:class JavaWrapper
-class JavaWrapper:
-    """
-    Wraps a java object as attribute '_item', and forwards
-    __getattr__ to it.
-    
-    All the classes here derive from this
-    """
-    def __init__(self, item):
-        self._item = item
-    
-    def __getattr__(self, attr):
-        return getattr(self._item, attr)
-    
-
-#@-node:class JavaWrapper
-#@+node:class I2PDestination
-class I2PDestination(JavaWrapper):
-    """
-    Wraps java I2P destination objects, with a big difference - these
-    objects store the private parts.
-    """
-    #@    @+others
-    #@+node:__init__
-    def __init__(self, **kw):
-        """
-        Versatile constructor
-        
-        Keywords (choose only one option):
-            - (none) - create a whole new dest
-            - dest, private - wrap an existing I2P java dest with private stream
-              (private is a byte array)
-            - bin - reconstitute a public-only dest from a binary string
-            - binfile - reconstitute public-only from a binary file
-            - binprivate - reconsistitute private dest from binary string
-            - binfileprivate - reconsistitute private dest from binary file pathname
-            - base64 - reconstitute public-only from base64 string
-            - base64file - reconstitute public-only from file containing base64
-            - base64private - reconstitute private from string containing base64
-            - base64fileprivate - reconstitute private from file containing base64
-    
-        also:
-            - client - a java net.i2p.client.I2PClient object
-              (avoids need for temporary client object when creating new dests)
-        """
-        dest = i2p.data.Destination()
-        JavaWrapper.__init__(self, dest)
-        self._private = None
-    
-        if kw.has_key('dest'):
-            self._item = kw['dest']
-            if kw.has_key('private'):
-                self._private = kw['private']
-    
-        elif kw.has_key('bin'):
-            self.fromBin(kw['bin'])
-    
-        elif kw.has_key('binfile'):
-            self.fromBinFilePrivate(kw['binfile'])
-    
-        elif kw.has_key('binprivate'):
-            self.fromBinPrivate(kw['binprivate'])
-    
-        elif kw.has_key('binfileprivate'):
-            self.fromBinFilePrivate(kw['binfileprivate'])
-    
-        elif kw.has_key('base64'):
-            self.fromBase64(kw['base64'])
-    
-        elif kw.has_key('base64file'):
-            self.fromBase64File(kw['base64file'])
-        
-        elif kw.has_key('base64private'):
-            self.fromBase64Private(kw['base64private'])
-    
-        elif kw.has_key('base64fileprivate'):
-            self.fromBase64FilePrivate(kw['base64fileprivate'])
-    
-        else:
-            # create a whole new one, with a temporary client object (if needed)
-            if kw.has_key('client'):
-                client = kw['client']
-            else:
-                client = clientFactory.createClient()
-            bytestream = java.io.ByteArrayOutputStream()
-            self._item = client.createDestination(bytestream)
-            self._private = bytestream.toByteArray()
-    
-    #@-node:__init__
-    #@+node:toBin
-    def toBin(self):
-        """
-        Returns a binary string of dest
-        """
-        return bytearray2str(self.toByteArray())
-    
-    #@-node:toBin
-    #@+node:toBinFile
-    def toBinFile(self, path):
-        """
-        Writes out public binary to a file
-        """
-        f = open(path, "wb")
-        f.write(self.toBin())
-        f.flush()
-        f.close()
-    
-    #@-node:toBinFile
-    #@+node:toBinPrivate
-    def toBinPrivate(self):
-        """
-        Returns the private key string as binary
-        """
-        if self._private == None:
-            raise NoPrivateKey
-        return bytearray2str(self._private)
-    
-    #@-node:toBinPrivate
-    #@+node:toBinFilePrivate
-    def toBinFilePrivate(self, path):
-        """
-        Writes out a binary file with the dest info
-        """
-        f = open(path, "wb")
-        f.write(self.toBinPrivate())
-        f.flush()
-        f.close()
-    
-    #@-node:toBinFilePrivate
-    #@+node:toBase64
-    def toBase64(self):
-        """
-        Returns base64 string of public part
-        """
-        return self._item.toBase64()
-    
-    #@-node:toBase64
-    #@+node:toBase64Private
-    def toBase64Private(self):
-        """
-        Exports dest as base64, including private stuff
-        """
-        if self._private == None:
-            raise NoPrivateKey
-        return i2p.data.Base64.encode(self._private)
-    
-    #@-node:toBase64Private
-    #@+node:toBase64File
-    def toBase64File(self, path):
-        """
-        Exports dest to file as base64
-        """
-        f = open(path, "wb")
-        f.write(self.toBase64())
-        f.flush()
-        f.close()
-    
-    #@-node:toBase64File
-    #@+node:toBase64FilePrivate
-    def toBase64FilePrivate(self, path):
-        """
-        Writes out a base64 file with the private dest info
-        """
-        f = open(path, "wb")
-        f.write(self.toBase64Private())
-        f.flush()
-        f.close()
-    
-    #@-node:toBase64FilePrivate
-    #@+node:fromBin
-    def fromBin(self, bin):
-        """
-        Loads this dest from a binary string
-        """
-        self._item.fromByteArray(str2bytearray(bin))
-        self._private = None
-    
-    #@-node:fromBin
-    #@+node:fromBinFile
-    def fromBinFile(self, path):
-        """
-        Loads public part from file containing binary
-        """
-        f = open(path, "rb")
-        self.fromBin(f.read())
-        f.close()
-    
-    #@-node:fromBinFile
-    #@+node:fromBinPrivate
-    def fromBinPrivate(self, s):
-        """
-        Loads this dest object from a base64 private key string
-        """
-        bytes = str2bytearray(s)
-        self._private = bytes
-        stream = java.io.ByteArrayInputStream(bytes)
-        self._item.readBytes(stream)
-    
-    #@-node:fromBinPrivate
-    #@+node:fromBinFilePrivate
-    def fromBinFilePrivate(self, path):
-        """
-        Loads this dest object, given the pathname of a file containing
-        a binary destkey
-        """
-        self.fromBinPrivate(open(path, "rb").read())
-    
-    #@-node:fromBinFilePrivate
-    #@+node:fromBase64
-    def fromBase64(self, b64):
-        """
-        Loads this dest from a base64 string
-        """
-        self._item.fromBase64(b64)
-        self._private = None
-    
-    #@-node:fromBase64
-    #@+node:fromBase64File
-    def fromBase64File(self, path):
-        """
-        Loads public part from file containing base64
-        """
-        f = open(path, "rb")
-        self.fromBase64(f.read())
-        f.close()
-    
-    #@-node:fromBase64File
-    #@+node:fromBase64Private
-    def fromBase64Private(self, s):
-        """
-        Loads this dest object from a base64 private key string
-        """
-        bytes = i2p.data.Base64.decode(s)
-        self._private = bytes
-        stream = java.io.ByteArrayInputStream(bytes)
-        self._item.readBytes(stream)
-    
-    #@-node:fromBase64Private
-    #@+node:fromBase64PrivateFile
-    def fromBase64FilePrivate(self, path):
-        """
-        Loads this dest from a base64 file containing private key
-        """
-        self.fromBase64Private(open(path, "rb").read())
-    
-    #@-node:fromBase64PrivateFile
-    #@+node:sign
-    def sign(self, s):
-        """
-        Signs a string using this dest's priv key
-        """
-        # get byte stream
-        bytes = str2bytearray(s)
-    
-        # stream up our private bytes
-        stream = java.io.ByteArrayInputStream(self._private)
-    
-        # temporary dest object
-        d = i2p.data.Destination()
-    
-        # suck the public part off the stream
-        d.readBytes(stream)
-    
-        # temporary private key object
-        privkey = i2p.data.PrivateKey()
-        privkey.readBytes(stream)
-        
-        # now we should just have the signing key portion left in the stream
-        signingkey = i2p.data.SigningPrivateKey()
-        signingkey.readBytes(stream)
-        
-        # create DSA engine
-        dsa = i2p.crypto.DSAEngine()
-        
-        sig = dsa.sign(bytes, signingkey)
-    
-        rawsig = bytearray2str(sig.getData())
-    
-        return rawsig
-    
-    #@-node:sign
-    #@+node:verify
-    def verify(self, s, sig):
-        """
-        Verifies a string against this dest, to test if it was actually
-        signed by whoever has the dest privkey
-        """
-        # get byte stream from data
-        databytes = str2bytearray(s)
-    
-        # get signature stream from sig
-        sigstream = java.io.ByteArrayInputStream(str2bytearray(sig))
-    
-        # make a signature object
-        signature = i2p.data.Signature()
-        signature.readBytes(sigstream)
-    
-        # get signature verify key
-        pubkey = self.getSigningPublicKey()
-    
-    	#log(4, "databytes=%s, pubkey=%s" % (repr(databytes), repr(pubkey)))
-        
-        # now get a verification
-        dsa = i2p.crypto.DSAEngine()
-        result = dsa.verifySignature(signature, databytes, pubkey)
-    
-        return result
-    
-    
-    
-    #@-node:verify
-    #@+node:hasPrivate
-    def hasPrivate(self):
-        """
-        Returns True if this dest has private parts, False if not
-        """
-    
-        if self._private:
-            return 1
-        else:
-            return 0
-    #@-node:hasPrivate
-    #@-others
-
-#@-node:class I2PDestination
-#@+node:class I2PClient
-class I2PClient(JavaWrapper):
-    """
-    jython-comfortable wrapper for java I2P client class
-    """
-    #@    @+others
-    #@+node:__init__
-    def __init__(self, **kw):
-        """
-        I2PClient constructor
-        
-        No args or keywords as yet
-        """
-        client = clientFactory.createClient()
-        JavaWrapper.__init__(self, client)
-    
-    #@-node:__init__
-    #@+node:createDestination
-    def createDestination(self, **kw):
-        """
-        Creates a destination, either a new one, or from a bin or base64 file
-        
-        Keywords:
-            - see L{I2PDestination} constructor
-        """
-        return I2PDestination(**kw)
-    
-    #@-node:createDestination
-    #@+node:createSession
-    def createSession(self, dest, sessionClass=None, **kw):
-        """
-        Create a session
-    
-        Arguments:
-            - dest - an L{I2PDestination} object which MUST contain a private portion
-            - sessionClass - if given, this should be a subclass
-              of I2PSession. This allows you to implement your own handlers.
-    
-        Keywords:
-            - session options (refer javadocs)
-        """
-        if sessionClass is None:
-            sessionClass = I2PSession
-    
-        if not dest.hasPrivate():
-            raise NoPrivateKey("Dest object has no private key")
-    
-        #print kw
-        #session = self._item.createSession(destStream, dict2props(kw))
-        session = sessionClass(client=self, dest=dest, **kw)
-        return session
-        #return sessionClass(session=session)
-    
-    #@-node:createSession
-    #@-others
-
-#@-node:class I2PClient
-#@+node:class I2PSession
-class I2PSession(JavaWrapper):
-    """
-    Wraps an I2P client session
-
-    You can subclass this, overriding the on_* handler callbacks,
-    and pass it as an argument to I2PClient.createSession
-
-    In the default 'on_message' callback, message retrieval is
-    synchronous - inbound messages get written to an internal queue,
-    which you can checked with numMessages() and retrieved from via
-    getMessage(). You may override on_message() if you
-    want to handle incoming messages asynchronously yourself.
-
-    Note - as far as I can tell, this class should be thread-safe.
-    """
-    #@    @+others
-    #@+node:attributes
-    host = i2cpHost
-    port = i2cpPort
-    #@-node:attributes
-    #@+node:__init__
-    def __init__(self, **kw):
-        """
-        I2PSession constructor
-    
-        Keywords:
-            - either:
-                - session - a java i2p session object
-            - or:
-                - client - an L{I2PClient} object
-                - dest - an L{I2PDestination} object
-        Also:
-            - listener - an L{I2PSessionListener} object.
-    
-        Router-level options:
-            - reliability - one of 'guaranteed' and 'besteffort' (default 'besteffort')
-            - host - host on which router is running
-            - port - port on which router is listening
-        """
-        #
-        # grab options destined for java class
-        #
-        options = {}
-    
-        reliability = takeKey(kw, 'reliability', 'besteffort')
-        if reliability == 'guaranteed':
-            reliability = jI2PClient.PROP_RELIABILITY_GUARANTEED
-        else:
-            reliability = jI2PClient.PROP_RELIABILITY_BEST_EFFORT
-        options[jI2PClient.PROP_RELIABILITY] = reliability
-    
-        host = takeKey(kw, 'host', self.host)
-        options[jI2PClient.PROP_TCP_HOST] = host
-    
-        port = takeKey(kw, 'port', self.port)
-        options[jI2PClient.PROP_TCP_PORT] = str(port)
-    
-        if kw.has_key('reliability'):
-            reliability = kw['reliability']
-    
-        if kw.has_key('listener'):
-            listener = kw['listener']
-            del kw['listener']
-        else:
-            listener = I2PSessionListener()
-    
-        #print options
-    
-        #
-        # other keywords handled locally
-        #
-        if kw.has_key('session'):
-            session = kw['session']
-            del kw['session']
-            JavaWrapper.__init__(self, session)
-        elif kw.has_key('client') and kw.has_key('dest'):
-            client = kw['client']
-            dest = kw['dest']
-            del kw['client']
-            del kw['dest']
-            destStream = java.io.ByteArrayInputStream(dest._private)
-            session = self._item = client._item.createSession(destStream, dict2props(options))
-            #client.createSession(dest, dict2props(options))
-        else:
-            raise Exception("implementation incomplete")
-    
-        # set up a listener
-        self.setSessionListener(listener)
-    
-        # set up a queue for inbound msgs
-        self.qInbound = Queue.Queue()
-        self.lockInbound = threading.Lock()
-        self.nInboundMessages = 0
-    
-        self.lockOutbound = threading.Lock()
-    
-    
-    
-    #@-node:__init__
-    #@+node:sendMessage
-    def sendMessage(self, dest, payload):
-        """
-        Sends a message to another dest
-        
-        Arguments:
-            - dest - an L{I2PDestination} object
-            - payload - a string to send
-        """
-        dest = dest._item
-        payload = str2bytearray(payload)
-        self.lockOutbound.acquire()
-        try:
-            res = self._item.sendMessage(dest, payload)
-        except:
-            self.lockOutbound.release()
-            raise
-        self.lockOutbound.release()
-        return res
-    #@-node:sendMessage
-    #@+node:numMessages
-    def numMessages(self):
-        """
-        Returns the number of unretrieved inbound messages
-        """
-        self.lockInbound.acquire()
-        n = self.nInboundMessages
-        self.lockInbound.release()
-        return n
-    #@-node:numMessages
-    #@+node:getMessage
-    def getMessage(self, blocking=1):
-        """
-        Returns the next available inbound message.
-        
-        If blocking is set to 1 (default), blocks
-        till another message comes in.
-        
-        If blocking is set to 0, returns None if there
-        are no available messages.
-        """
-        if blocking:
-            msg = self.qInbound.get()
-            #print "getMessage: acquiring lock"
-            self.lockInbound.acquire()
-            #print "getMessage: got lock"
-            self.nInboundMessages -= 1
-        else:
-            #print "getMessage: acquiring lock"
-            self.lockInbound.acquire()
-            #print "getMessage: got lock"
-            if self.nInboundMessages > 0:
-                msg = self.qInbound.get()
-                self.nInboundMessages -= 1
-            else:
-                msg = None
-        self.lockInbound.release()
-        #print "getMessage: released lock"
-        return msg
-    
-    #@-node:getMessage
-    #@+node:setSessionListener
-    def setSessionListener(self, listener):
-        """
-        Designates an L{I2PSessionListener} object to listen to this session
-        """
-        self.listener = listener
-        listener.addSession(self)
-        self._item.setSessionListener(listener)
-    
-    
-    #@-node:setSessionListener
-    #@+node:destroySession
-    def destroySession(self):
-        """
-        Destroys an existing session
-    
-        Note that due to a jython quirk, calls to destroySession might
-        trigger a TypeError relating to arg mismatch - we ignore such
-        errors here because by the time the exception happens, the
-        session has already been successfully closed
-        """
-        try:
-            self._item.destroySession()
-        except TypeError:
-            pass
-    
-    #@-node:destroySession
-    #@+node:CALLBACKS
-    #
-    # handler methods which you should override
-    #
-    
-    #@+others
-    #@+node:on_message
-    def on_message(self, msg):
-        """
-        Callback for when a message arrives.
-    
-        Appends the message to the inbound queue, which you can check
-        with the numMessages() method, and read with getMessage()
-    
-        You should override this if you want to handle inbound messages
-        asynchronously.
-        
-        Arguments:
-            - msg - a string that was sent by peer
-        """
-        #print "on_message: msg=%s" % msg
-        self.lockInbound.acquire()
-        #print "on_message: got lock"
-        self.qInbound.put(msg)
-        self.nInboundMessages += 1
-        self.lockInbound.release()
-        #print "on_message: released lock"
-    
-    #@-node:on_message
-    #@+node:on_abuse
-    def on_abuse(self, severity):
-        """
-        Callback indicating abuse is happening
-        
-        Arguments:
-            - severity - an int of abuse level, 1-100
-        """
-        print "on_abuse: severity=%s" % severity
-    
-    #@-node:on_abuse
-    #@+node:on_disconnected
-    def on_disconnected(self):
-        """
-        Callback indicating remote peer disconnected
-        """
-        print "on_disconnected"
-    
-    #@-node:on_disconnected
-    #@+node:on_error
-    def on_error(self, message, error):
-        """
-        Callback indicating an error occurred
-        """
-        print "on_error: message=%s error=%s" % (message, error)
-    
-    #@-node:on_error
-    #@-others
-    #@-node:CALLBACKS
-    #@-others
-#@-node:class I2PSession
-#@+node:class I2PSessionListener
-class I2PSessionListener(i2p.client.I2PSessionListener):
-    """
-    Wraps a java i2p.client.I2PSessionListener object
-    """
-    def __init__(self, *sessions):
-        self.sessions = list(sessions)
-
-    def addSession(self, session):
-        """
-        Adds an L{I2PSession} object to the list of sessions to listen on
-        
-        Note - you must also invoke the session's setSessionListener() method
-        (see I2PSession.setSessionListener)
-        """
-        if session not in self.sessions:
-            self.sessions.append(session)
-    
-    def delSession(self, session):
-        """
-        Stop listening to a given session
-        """
-        if session in self.sessions:
-            del self.sessions.index[session]
-
-    def messageAvailable(self, session, msgId, size):
-        """
-        Callback from java::
-            public void messageAvailable(
-                I2PSession session,
-                int msgId,
-                long size)
-        """
-        #print "listener - messageAvailable"
-
-        # try to find session in our sessions table
-        sessions = filter(lambda s, session=session: s._item == session, self.sessions)
-        if sessions:
-            #print "compare to self.session->%s" % (session == self.session._item)
-
-            # found a matching session - retrieve it
-            session = sessions[0]
-
-            # retrieve message and pass to callback
-            msg = session.receiveMessage(msgId)
-            msgStr = bytearray2str(msg)
-            session.on_message(msgStr)
-        else:
-            print "messageAvailable: unknown session=%s msgId=%s size=%s" % (session, msgId, size)
-
-    def reportAbuse(self, session, severity):
-        """
-        Callback from java::
-            public void reportAbuse(
-                I2PSession session,
-                int severity)
-        """
-        if self.session:
-            self.session.on_abuse(severity)
-        else:
-            print "reportAbuse: unknown session=%s severity=%s" % (session, severity)
-    
-    def disconnected(self, session):
-        """
-        Callback from java::
-            public void disconnected(I2PSession session)
-        """
-        if self.session:
-            self.session.on_disconnected()
-        else:
-            print "disconnected: unknown session=%s" % session
-
-    def errorOccurred(session, message, error):
-        """
-        Callback from java::
-            public void errorOccurred(
-                I2PSession session,
-                java.lang.String message,
-                java.lang.Throwable error)
-        """
-        if self.session:
-            self.session.on_error(message, error)
-        else:
-            print "errorOccurred: message=%s error=%s" % (message, error)
-
-#@-node:class I2PSessionListener
-#@+node:class I2PSocket
-class I2PSocket:
-    """
-    Wraps I2P streaming API into a form resembling python sockets
-    """
-    #@    @+others
-    #@+node:attributes
-    host = i2cpHost
-    port = i2cpPort
-    
-    #@-node:attributes
-    #@+node:__init__
-    def __init__(self, dest=None, **kw):
-        """
-        Create an I2P streaming socket
-    
-        Arguments:
-            - dest - a private destination to associate with this socket
-    
-        Keywords:
-            - host - hostname on which i2cp is listening (default self.host)
-            - port - port on which i2cp listens (default self.port)
-    
-        Internally used keywords (used for wrapping an accept()ed connection):
-            - dest
-            - remdest
-            - sock
-            - instream
-            - outstream
-        """
-        # set up null attribs
-        self.sockmgr = None
-        self.instream = None
-        self.outstream = None
-        self.sock = None
-        self._connected = 0
-        self._blocking = 1
-    
-        # save dest (or lack thereof)
-        self.dest = dest
-    
-        if kw.has_key('sock') \
-                and kw.has_key('remdest') \
-                and kw.has_key('instream') \
-                and kw.has_key('outstream'):
-    
-            # wrapping an accept()'ed connection
-            log(4, "accept()'ed a connection, wrapping...")
-    
-            self.sock = kw['sock']
-            self.dest = dest
-            self.remdest = kw['remdest']
-            self.instream = kw['instream']
-            self.outstream = kw['outstream']
-        else:
-            log(4, "creating new I2PSocket %s" % dest)
-    
-            # process keywords
-            self.host = kw.get('host', self.host)
-            self.port = int(kw.get('port', self.port))
-    
-            # we need a factory, don't we?
-            self.sockmgrFact = i2p.client.streaming.I2PSocketManagerFactory()
-    
-    #@-node:__init__
-    #@+node:bind
-    def bind(self, dest=None):
-        """
-        'binds' the socket to a dest
-    
-        dest is an I2PDestination object, which you may specify in the constructor
-        instead of here. However, we give you the option of specifying here for
-        some semantic compatibility with python sockets.
-        """
-        if dest is not None:
-            self.dest = dest
-        elif not self.dest:
-            # create new dest, client should interrogate it at some time
-            log(4, "bind: socket has no dest, creating one")
-            self.dest = I2PDestination()
-    #@-node:bind
-    #@+node:listen
-    def listen(self, *args, **kw):
-        """
-        Sets up the object to receive connections
-        """
-        # sanity checks
-        if self.sockmgr:
-            raise I2PSocketError(".sockmgr already present - have you already called listen?")
-        if not self.dest:
-            raise I2PSocketError("socket is not bound to a destination")
-    
-        log(4, "listening on socket")
-        
-        # create the socket manager
-        self._createSockmgr()
-        
-    #@nonl
-    #@-node:listen
-    #@+node:accept
-    def accept(self):
-        """
-        Waits for incoming connections, and returns a new I2PSocket object
-        with the connection
-        """
-        # sanity check
-        if not self.sockmgr:
-            raise I2PSocketError(".listen() has not been called on this socket")
-    
-        # accept a conn and get its streams
-        sock = self.sockmgr.getServerSocket().accept()
-        instream = sock.getInputStream()
-        outstream = sock.getOutputStream()
-        remdest = I2PDestination(dest=sock.getPeerDestination())
-    
-        # wrap it and return it
-        sockobj = I2PSocket(dest=self.dest,
-                            remdest=remdest,
-                            sock=sock,
-                            instream=instream,
-                            outstream=outstream)
-        self._connected = 1
-        return sockobj
-    
-    #@-node:accept
-    #@+node:connect
-    def connect(self, remdest):
-        """
-        Connects to a remote destination
-    
-        This has one totally major difference from the normal socket
-        paradigm, and that is that you can have n outbound connections
-        to different dests.
-        """
-        # sanity check
-        if self.sockmgr:
-            raise I2PSocketError(".sockmgr already present - have you already called listen/connect?")
-    
-        # create whole new dest if none was provided to constructor
-        if self.dest is None:
-            log(4, "connect: creating whole new dest")
-            self.dest = I2PDestination()
-    
-        # create the socket manager
-        self._createSockmgr()
-    
-        # do the connect
-        #print "remdest._item = %s" % repr(remdest._item)
-    
-        opts = net.i2p.client.streaming.I2PSocketOptions()
-        try:
-            log(4, "trying to connect to %s" % remdest.toBase64())
-            sock = self.sock = self.sockmgr.connect(remdest._item, opts)
-            self.remdest = remdest
-        except:
-            logException(2, "apparent exception, continuing...")
-    
-        self.instream = sock.getInputStream()
-        self.outstream = sock.getOutputStream()
-    
-        sockobj = I2PSocket(dest=self.dest,
-                            remdest=remdest,
-                            sock=sock,
-                            instream=self.instream,
-                            outstream=self.outstream)
-        self._connected = 1
-        return sockobj
-    #@-node:connect
-    #@+node:recv
-    def recv(self, nbytes):
-        """
-        Reads nbytes of data from socket
-        """
-        # sanity check
-        if not self.instream:
-            raise I2PSocketError("Socket is not connected")
-        
-        # for want of better methods, read bytewise
-        chars = []
-        while nbytes > 0:
-            byte = self.instream.read()
-            if byte < 0:
-                break # got all we're gonna get
-            char = chr(byte)
-            chars.append(char)
-            #print "read: got a byte %s (%s)" % (byte, repr(char))
-            nbytes -= 1
-            
-        # got it all
-        buf = "".join(chars)
-        #print "recv: buf=%s" % repr(buf)
-        return buf
-    
-    
-    #@-node:recv
-    #@+node:send
-    def send(self, buf):
-        """
-        Sends buf thru socket
-        """
-        # sanity check
-        if not self.outstream:
-            raise I2PSocketError("Socket is not connected")
-    
-        # and write it out
-        log(4, "send: writing '%s' to outstream..." % repr(buf))
-        outstream = self.outstream
-        for c in buf:
-            outstream.write(ord(c))
-    
-        # flush just in case
-        log(4, "send: flushing...")
-        self.outstream.flush()
-    
-        log(4, "send: done")
-    
-    #@-node:send
-    #@+node:available
-    def available(self):
-        """
-        Returns the number of bytes available for recv()
-        """
-        #print "available: sock is %s" % repr(self.sock)
-    
-        return self.instream.available()
-    
-    
-    #@-node:available
-    #@+node:close
-    def close(self):
-        """
-        Closes the socket
-        """
-        # sanity check
-        #if not self._connected:
-        #    raise I2PSocketError("Socket is not connected")
-    
-        # shut up everything
-        try:
-            self.instream.close()
-        except:
-            pass
-        try:
-            self.outstream.close()
-        except:
-            pass
-        try:
-            self.sock.close()
-        except:
-            pass
-    #@-node:close
-    #@+node:_createSockmgr
-    def _createSockmgr(self):
-    
-        if getattr(self, 'sockmgr', None):
-            return
-    
-        #options = {jI2PClient.PROP_TCP_HOST: self.host,
-        #           jI2PClient.PROP_TCP_PORT: self.port}
-        options = {}
-        props = dict2props(options)
-    
-        # get a java stream thing from dest
-        stream = java.io.ByteArrayInputStream(self.dest._private)
-        
-        # create socket manager thing
-        self.sockmgr = self.sockmgrFact.createManager(stream, self.host, self.port, props)
-    #@-node:_createSockmgr
-    #@-others
-#@-node:class I2PSocket
-#@+node:class I2PSamServer
-class I2PSamServer(ThreadingTCPServer):
-    """
-    A server which makes I2CP available via a socket
-    """
-    #@	@+others
-    #@+node:attributes
-    host = i2psamhost
-    port = i2psamport
-    
-    i2cphost = i2cpHost
-    i2cpport = i2cpPort
-    
-    version = version
-    
-    
-    #@-node:attributes
-    #@+node:__init__
-    def __init__(self, i2pclient=None, **kw):
-        """
-        Create the client listener object
-        
-        Arguments:
-            - i2pclient - an I2PClient object - optional - if not
-              given, one will be created
-        
-        Keywords:
-            - host - host to listen on for client conns (default self.host ('127.0.0.1')
-            - port - port to listen on for client conns (default self.port (7656)
-            - i2cphost - host to talk to i2cp on (default self.i2cphost ('127.0.0.1'))
-            - i2cpport - port to talk to i2cp on (default self.i2cphost ('127.0.0.1'))
-        """
-    
-        # create an I2PClient object if none given
-        if i2pclient is None:
-            i2pclient = I2PClient()
-        self.i2pclient = i2pclient
-    
-        # get optional host/port for client and i2cp
-        self.host = kw.get('host', self.host)
-        self.port = int(kw.get('port', self.port))
-        self.i2cphost = kw.get('i2cphost', self.i2cphost)
-        self.i2cpport = int(kw.get('i2cpport', self.i2cpport))
-    
-        # create record of current sessions, and a lock for it
-        self.sessions = {}
-        self.sessionsLock = threading.Lock()
-        self.streams = {}
-        self.streamsLock = threading.Lock()
-        self.samNextId = 1
-        self.samNextIdLock = threading.Lock()
-    
-        # and create the server
-        try:
-            ThreadingTCPServer.__init__(
-                self, 
-                (self.host, self.port),
-                I2PSamClientHandler)
-        except:
-            log(4, "crashed with host=%s, port=%s" % (self.host, self.port))
-            raise
-    
-    #@-node:__init__
-    #@+node:run
-    def run(self):
-        """
-        Run the SAM server.
-    
-        when connections come in, they are automatically
-        accepted, and an L{I2PClientHandler} object created,
-        and its L{handle} method invoked.
-        """
-        log(4, "Listening for client requests on %s:%s" % (self.host, self.port))
-        self.serve_forever()
-    
-    
-    #@-node:run
-    #@+node:finish_request
-    def finish_request(self, request, client_address):
-        """Finish one request by instantiating RequestHandlerClass."""
-        try:
-            self.RequestHandlerClass(request, client_address, self)
-        except:
-            pass
-        log(3, "Client session terminated")
-    #@-node:finish_request
-    #@+node:samAllocId
-    def samAllocId(self):
-        """
-        Allocates a new unique id as required by SAM protocol
-        """
-        self.samNextIdLock.acquire()
-        id = self.samNextId
-        self.samNextId += 1
-        self.samNextIdLock.release()
-        return id
-    #@-node:samAllocId
-    #@-others
-#@-node:class I2PSamServer
-#@+node:class I2PSamClientHandler
-class I2PSamClientHandler(StreamRequestHandler):
-    r"""
-    Manages a single socket connection from a client.
-    
-    When a client connects to the SAM server, the I2PSamServer
-    object creates an instance of this class, and invokes its
-    handle method. See L{handle}.
-
-    Note that if a client terminates its connection to the server, the server
-    will destroy all current connections initiated by that client
-    
-    Size values are decimal
-    Connection is persistent
-    """
-    #@	@+others
-    #@+node:handle
-    def handle(self):
-        """
-        Reads command/data messages from SAM Client, executes these,
-        and sends back responses.
-        
-        Plants callback hooks into I2PSession objects, so that when
-        data arrives via I2P, it can be immediately sent to the client.
-        """
-        self.localsessions = {}
-        self.globalsessions = self.server.sessions
-    
-        self.localstreams = {} # keyed by sam stream id
-        self.globalstreams = self.server.streams
-    
-        self.samSessionIsOpen = 0
-        self.samSessionStyle = ''
-    
-        # localise the id allocator
-        self.samAllocId = self.server.samAllocId
-    
-        # need a local sending lock
-        self.sendLock = threading.Lock()
-    
-        log(5, "Got req from %s" % repr(self.client_address))
-    
-        try:
-            self.namingService = i2p.client.naming.HostsTxtNamingService()
-        except:
-            logException(2, "Failed to create naming service object")
-    
-        try:
-            while 1:
-                # get req
-                req = self.rfile.readline().strip()
-                flds = [s.strip() for s in req.split(" ")]
-                cmd = flds[0]
-                if cmd in ['HELLO', 'SESSION', 'STREAM', 'DATAGRAM', 'RAW', 'NAMING', 'DEST']:
-                    topic, subtopic, args = self.samParse(flds)
-                    method = getattr(self, "on_"+cmd, None)
-                    method(topic, subtopic, args)
-                else:
-                    method = getattr(self, "on_"+cmd, None)
-                    if method:
-                        method(flds)
-                    else:
-                        # bad shit
-                        self.wfile.write("error unknown command '%s'\n" % cmd)
-    
-        except IOError:
-            log(3, "Client connection terminated")
-        except ValueError:
-            pass
-        except:
-            logException(4, "Client req handler crashed")
-            self.wfile.write("error\n")
-    
-        # clean up sessions
-        for dest in self.localsessions.keys():
-            if dest in self.globalsessions.keys():
-                log(4, "forgetting global dest %s" % dest[:30])
-                del self.globalsessions[dest]
-    
-        self.finish()
-        #thread.exit()
-    
-    #@-node:handle
-    #@+node:on_genkeys
-    def on_genkeys(self, flds):
-    
-        log(4, "entered")
-    
-        server = self.server
-        client = server.i2pclient
-        globalsessions = server.sessions
-        sessionsLock = server.sessionsLock
-    
-        read = self.rfile.read
-        readline = self.rfile.readline
-        write = self.wfile.write
-        flush = self.wfile.flush
-    
-        # genkeys
-        try:
-            dest = I2PDestination()
-            priv = dest.toBase64Private()
-            pub = dest.toBase64()
-            write("ok %s %s\n" % (pub, priv))
-        except:
-            write("error exception\n")
-    #@-node:on_genkeys
-    #@+node:on_createsession
-    def on_createsession(self, flds):
-    
-        log(4, "entered")
-    
-        server = self.server
-        client = server.i2pclient
-        globalsessions = server.sessions
-        sessionsLock = server.sessionsLock
-    
-        read = self.rfile.read
-        readline = self.rfile.readline
-        write = self.wfile.write
-        flush = self.wfile.flush
-    
-        sessionsLock.acquire()
-    
-        try:
-            b64priv = flds[1]
-    
-            # spit if someone else already has this dest
-            if b64priv in globalsessions.keys():
-                write("error dest in use\n")
-            elif b64priv in self.localsessions.keys():
-                # duh, already open locally, treat as ok
-                write("ok\n")
-            else:
-                # whole new session - set it up
-                dest = I2PDestination(base64private=b64priv)
-                log(4, "Creating session on dest '%s'" % b64priv[:40])
-                session = client.createSession(dest)
-                log(4, "Connecting session on dest '%s'" % b64priv[:40])
-                session.connect()
-                log(4, "Session on dest '%s' now live" % b64priv[:40])
-                
-                # and remember it
-                self.localsessions[b64priv] = session
-                globalsessions[b64priv] = session
-                
-                # and tell the client the good news
-                write("ok\n")
-        except:
-            logException(4, "createsession fail")
-            write("error exception\n")
-    
-        sessionsLock.release()
-    #@-node:on_createsession
-    #@+node:on_destroysession
-    def on_destroysession(self, flds):
-    
-        log(4, "entered")
-    
-        server = self.server
-        client = server.i2pclient
-        globalsessions = server.sessions
-        sessionsLock = server.sessionsLock
-    
-        read = self.rfile.read
-        readline = self.rfile.readline
-        write = self.wfile.write
-        flush = self.wfile.flush
-    
-        sessionsLock.acquire()
-    
-        try:
-            b64priv = flds[1]
-            
-            # spit if session not known
-            if not globalsessions.has_key(b64priv):
-                # no such session presently exists anywhere
-                write("error nosuchsession\n")
-            elif not self.localsessions.has_key(b64priv):
-                # session exists, but another client owns it
-                write("error notyoursession\n")
-            else:
-                # session exists and we own it
-                session = self.localsessions[b64priv]
-                del self.localsessions[b64priv]
-                del globalsessions[b64priv]
-                try:
-                    session.destroySession()
-                    write("ok\n")
-                except:
-                    raise
-        except:
-            logException(4, "destroy session failed")
-            write("error exception\n")
-    
-        sessionsLock.release()
-    
-        log(4, "done")
-    
-    #@-node:on_destroysession
-    #@+node:on_send
-    def on_send(self, flds):
-    
-        #log(4, "entered: %s" % repr(flds))
-        log(4, "entered")
-    
-        server = self.server
-        client = server.i2pclient
-        globalsessions = server.sessions
-        sessionsLock = server.sessionsLock
-    
-        read = self.rfile.read
-        readline = self.rfile.readline
-        write = self.wfile.write
-        flush = self.wfile.flush
-    
-        sessionsLock.acquire()
-    
-        session = None
-        try:
-            size = int(flds[1])
-            b64priv = flds[2]
-            b64peer = flds[3]
-            msg = self._recvbytes(size)
-    
-            # spit if session not known
-            if not globalsessions.has_key(b64priv):
-                # no such session presently exists anywhere
-                log(4, "no such session")
-                write("error nosuchsession\n")
-            elif not self.localsessions.has_key(b64priv):
-                # session exists, but another client owns it
-                write("error notyoursession\n")
-            else:
-                session = self.localsessions[b64priv]
-        except:
-            logException(2, "Send exception")
-            write("error exception on send command\n")
-    
-        sessionsLock.release()
-    
-        if not session:
-            return
-        
-        # now get/instantiate the remote dest
-        try:
-            peerDest = I2PDestination(base64=b64peer)
-        except:
-            peerDest = None
-            logException(2, "Send: bad remote dest")
-            write("error bad remote dest\n")
-        if not peerDest:
-            return
-    
-        # and do the send
-        try:
-            res = session.sendMessage(peerDest, msg)
-        except:
-            logException(2, "Send: failed")
-            write("error exception on send\n")
-            res = None
-    
-        if res is None:
-            return
-    
-        # report result
-        if res:
-            write("ok\n")
-        else:
-            write("error send failed\n")
-    
-        log(4, "done")
-    
-    #@-node:on_send
-    #@+node:on_receive
-    def on_receive(self, flds):
-    
-        log(4, "entered")
-    
-        server = self.server
-        client = server.i2pclient
-        globalsessions = server.sessions
-        sessionsLock = server.sessionsLock
-    
-        read = self.rfile.read
-        readline = self.rfile.readline
-        write = self.wfile.write
-        flush = self.wfile.flush
-    
-        sessionsLock.acquire()
-    
-        session = None
-        try:
-            b64priv = flds[1]
-    
-            # spit if session not known
-            if not globalsessions.has_key(b64priv):
-                # no such session presently exists anywhere
-                write("error nosuchsession\n")
-            elif not self.localsessions.has_key(b64priv):
-                # session exists, but another client owns it
-                write("error notyoursession\n")
-            else:
-                session = self.localsessions[b64priv]
-        except:
-            logException(4, "receive command error")
-            write("error exception on receive command\n")
-        sessionsLock.release()
-    
-        if not session:
-            log(4, "no session matching privdest %s" % b64priv[:30])
-            return
-        
-        # does this session have any received data?
-        if session.numMessages() > 0:
-            msg = session.getMessage()
-            write("ok %s\n%s" % (len(msg), msg))
-        else:
-            write("ok 0\n")
-    
-        log(4, "done")
-    
-        return
-    
-    #@-node:on_receive
-    #@+node:on_HELLO
-    def on_HELLO(self, topic, subtopic, args):
-        """
-        Responds to client PING
-        """
-        log(4, "entered")
-        self.samSend("HELLO", "PONG")
-        log(4, "responded to HELLO")
-    
-    #@-node:on_HELLO
-    #@+node:on_SESSION
-    def on_SESSION(self, topic, subtopic, args):
-    
-        log(4, "entered")
-    
-        server = self.server
-        client = server.i2pclient
-        globalsessions = server.sessions
-        localsessions = self.localsessions
-        sessionsLock = server.sessionsLock
-    
-        read = self.rfile.read
-        readline = self.rfile.readline
-        write = self.wfile.write
-        flush = self.wfile.flush
-    
-        if subtopic == 'CREATE':
-            
-            if self.samSessionIsOpen:
-                self.samSend("SESSION", "STATUS",
-                             RESULT="I2P_ERROR",
-                             MESSAGE="Session_already_created",
-                             )
-                return
-    
-            # get/validate STYLE arg
-            style = self.samSessionStyle = args.get('STYLE', None)
-            if style is None:
-                self.samSend("SESSION", "STATUS",
-                             RESULT="I2P_ERROR",
-                             MESSAGE="Missing_STYLE_argument",
-                             )
-                return
-            elif style not in ['STREAM', 'DATAGRAM', 'RAW']:
-                self.samSend("SESSION", "STATUS",
-                             RESULT="I2P_ERROR",
-                             MESSAGE="Invalid_STYLE_argument_'%s'" % style,
-                             )
-                return
-    
-            # get/validate DESTINATION arg
-            dest = args.get('DESTINATION', None)
-            if dest == 'TRANSIENT':
-                # create new temporary dest
-                dest = self.samDest = I2PDestination()
-                destb64 = dest.toBase64Private()
-            else:
-                # make sure dest isn't globally or locally known
-                if dest in globalsessions.keys() or dest in localsessions.keys():
-                    self.samSend("SESSION", "STATUS",
-                                 RESULT="DUPLICATED_DEST",
-                                 MESSAGE="Destination_'%s...'_already_in_use" % dest[:20],
-                                 )
-                    return
-    
-                # try to reconstitute dest from given base64
-                try:
-                    destb64 = dest
-                    dest = I2PDestination(base64private=dest)
-                except:
-                    self.samSend("SESSION", "STATUS",
-                                 RESULT="INVALID_KEY",
-                                 MESSAGE="Bad_destination_base64_string_'%s...'" % destb64[:20],
-                                 )
-                    return
-    
-            # got valid dest now
-            self.dest = dest
-            self.samDestPub = dest.toBase64()
-    
-            if style in ['RAW', 'DATAGRAM']:
-    
-                if style == 'DATAGRAM':
-                    # we need to know how big binary pub dests and sigs
-                    self.samDestPubBin = dest.toBin()
-                    self.samDestPubBinLen = len(self.samDestPubBin)
-                    self.samSigLen = len(self.dest.sign("nothing"))
-                    
-                    log(4, "binary pub dests are %s bytes, sigs are %s bytes" % (
-                        self.samDestPubBinLen, self.samSigLen))
-    
-                i2cpHost = args.get('I2CP.HOST', server.i2cphost)
-                i2cpPort = int(args.get('I2CP.PORT', server.i2cpport))
-    
-                # both these styles require an I2PSession object
-                session = client.createSession(dest, host=i2cpHost, port=i2cpPort)
-                
-                # plug in our inbound message handler
-                session.on_message = self.on_message
-    
-                log(4, "Connecting session on dest '%s'" % destb64[:40])
-                try:
-                    session.connect()
-                except net.i2p.client.I2PSessionException:
-                    self.samSend("SESSION", "STATUS",
-                                 RESULT="I2P_ERROR",
-                                 MESSAGE="Failed_to_connect_to_i2cp_port",
-                                 )
-                    logException(3, "Failed to connect I2PSession")
-                    return
-                    
-                log(4, "Session on dest '%s' now live" % destb64[:40])
-                
-                # and remember it
-                localsessions[destb64] = session
-                globalsessions[destb64] = session
-                self.samSession = session
-    
-            else: # STREAM
-                # no need to create session object, because we're using streaming api
-                log(4, "Creating STREAM session")
-                
-                # what kind of stream?
-                direction = args.get('DIRECTION', 'BOTH')
-                if direction not in ['BOTH', 'RECEIVE', 'CREATE']:
-                    self.samSend("SESSION", "STATUS",
-                        RESULT="I2P_ERROR",
-                        MESSAGE="Illegal_direction_keyword_%s" % direction.replace(" ","_"),
-                        )
-                    return
-    
-                if direction == 'BOTH':
-                    self.canConnect = 1
-                    self.canAccept = 1
-                elif direction == 'RECEIVE':
-                    self.canConnect = 0
-                    self.canAccept = 1
-                elif direction == 'CREATE':
-                    self.canConnect = 1
-                    self.canAccept = 0
-    
-                # but we do need to mark it as being in use
-                localsessions[destb64] = globalsessions[destb64] = None
-    
-                # make a local socket
-                sock = self.samSock = I2PSocket(dest)
-    
-                # and we also need to fire up a socket listener, if not CREATE-only
-                if self.canAccept:
-                    thread.start_new_thread(self.threadSocketListener, (sock, dest))
-    
-            # finally, we can reply with the good news
-            self.samSend("SESSION", "STATUS",
-                         RESULT="OK",
-                         )
-    
-        else: # subtopic != CREATE
-            self.samSend("SESSION", "STATUS",
-                         RESULT="I2P_ERROR",
-                         MESSAGE="Invalid_command_'SESSION_%s'" % subtopic,
-                         )
-            return
-    
-    #@-node:on_SESSION
-    #@+node:on_SESSION_CREATE
-    def on_SESSION_CREATE(self, topic, subtopic, args):
-    
-        log(4, "entered")
-    
-        server = self.server
-        client = server.i2pclient
-        globalsessions = server.sessions
-        localsessions = self.localsessions
-        sessionsLock = server.sessionsLock
-    
-        read = self.rfile.read
-        readline = self.rfile.readline
-        write = self.wfile.write
-        flush = self.wfile.flush
-    
-    #@-node:on_SESSION_CREATE
-    #@+node:on_STREAM
-    def on_STREAM(self, topic, subtopic, args):
-    
-        log(4, "entered")
-    
-        server = self.server
-        client = server.i2pclient
-        globalsessions = server.sessions
-        sessionsLock = server.sessionsLock
-    
-        read = self.rfile.read
-        readline = self.rfile.readline
-        write = self.wfile.write
-        flush = self.wfile.flush
-    
-        if subtopic == 'CONNECT':
-            # who are we connecting to again?
-            remdest = I2PDestination(base64=args['DESTINATION'])
-            id = int(args['ID'])
-        
-            try:
-                log(4, "Trying to connect to remote peer %s..." % args['DESTINATION'])
-                sock = self.samSock.connect(remdest)
-                log(4, "Connected to remote peer %s..." % args['DESTINATION'])
-                self.localstreams[id] = sock
-                self.samSend("STREAM", "STATUS",
-                             RESULT='OK',
-                             ID=id,
-                             )
-                thread.start_new_thread(self.threadSocketReceiver, (sock, id))
-    
-            except:
-                log(4, "Failed to connect to remote peer %s..." % args['DESTINATION'])
-                self.samSend("STREAM", "STATUS",
-                             RESULT='I2P_ERROR',
-                             MESSAGE='exception_on_connect',
-                             ID=id,
-                             )
-    
-        elif subtopic == 'SEND':
-            # send to someone
-            id = int(args['ID'])
-            try:
-                sock = self.localstreams[id]
-                sock.send(args['DATA'])
-            except:
-                logException(4, "send failed")
-    
-    
-    
-    
-    #@-node:on_STREAM
-    #@+node:on_DATAGRAM
-    def on_DATAGRAM(self, topic, subtopic, args):
-        r"""
-        DATAGRAM SEND
-        DESTINATION=$base64key
-        SIZE=$numBytes\n[$numBytes of data]
-    
-        All datagram messages have a signature/hash header, formatted as:
-            - sender's binary public dest
-            - S(H(sender_bin_pubdest + recipient_bin_pubdest + msg))
-        """
-        log(4, "entered")
-    
-        # at this stage of things, we don't know how to handle anything except SEND
-        if subtopic != 'SEND':
-            log(3, "Got illegal subtopic '%s' in DATAGRAM command" % subtopic)
-            return
-    
-        # get the details
-        peerdestb64 = args['DESTINATION']
-        peerdest = I2PDestination(base64=peerdestb64)
-        peerdestBin = base64dec(peerdestb64)
-        data = args['DATA']
-    
-        # make up the header
-        log(4, "samDestPubBin (%s) %s" % (type(self.samDestPubBin), repr(self.samDestPubBin)))
-        log(4, "peerdestBin (%s) %s" % (type(peerdestBin), repr(peerdestBin)))
-        log(4, "data (%s) %s" % (type(data), repr(data)))
-    
-        hashed = shahash(self.samDestPubBin + peerdestBin + data)
-        log(4, "hashed=%s" % repr(hashed))
-    
-        sig = self.dest.sign(hashed)
-        log(4, "sig=%s" % repr(sig))
-        hdr = self.samDestPubBin + sig
-        
-        # send the thing
-        self.samSession.sendMessage(peerdest, hdr + data)
-    
-    #@-node:on_DATAGRAM
-    #@+node:on_RAW
-    def on_RAW(self, topic, subtopic, args):
-        r"""
-        RAW SEND
-        DESTINATION=$base64key
-        SIZE=$numBytes\n[$numBytes of data]
-        """
-        log(4, "entered")
-    
-        # at this stage of things, we don't know how to handle anything except SEND
-        if subtopic != 'SEND':
-            return
-    
-        # get the details
-        peerdest = I2PDestination(base64=args['DESTINATION'])
-        msg = args['DATA']
-    
-        # send the thing
-        self.samSession.sendMessage(peerdest, msg)
-    #@-node:on_RAW
-    #@+node:on_NAMING
-    def on_NAMING(self, topic, subtopic, args):
-    
-        log(4, "entered: %s %s %s" % (repr(topic), repr(subtopic), repr(args)))
-    
-        # at this stage of things, we don't know how to handle anything except LOOKUP
-        if subtopic != 'LOOKUP':
-            return
-    
-        # get the details
-        host = args['NAME']
-    
-        log(4, "looking up host %s" % host)
-        
-        # try to lookup
-        jdest = self.namingService.lookup(host)
-    
-        if not jdest:
-            log(4, "host %s not found" % host)
-            self.samSend("NAMING", "REPLY",
-                         RESULT="KEY_NOT_FOUND",
-                         NAME=host,
-                         )
-            return
-    
-        try:
-            b64 = I2PDestination(dest=jdest).toBase64()
-            self.samSend("NAMING", "REPLY",
-                         RESULT="OK",
-                         NAME=host,
-                         VALUE=b64,
-                         )
-            log(4, "host %s found and valid key returned" % host)
-            return
-        except:
-            log(4, "host %s found but key invalid" % host)
-            self.samSend("NAMING", "REPLY",
-                         RESULT="INVALID_KEY",
-                         NAME=host,
-                         )
-    
-    #@-node:on_NAMING
-    #@+node:on_DEST
-    def on_DEST(self, topic, subtopic, args):
-    
-        log(4, "Generating dest")
-    
-        dest = I2PDestination()
-        priv = dest.toBase64Private()
-        pub = dest.toBase64()
-    
-        log(4, "Sending dest to client")
-    
-        self.samSend("DEST", "REPLY", PUB=pub, PRIV=priv)
-    
-        log(4, "done")
-    #@-node:on_DEST
-    #@+node:on_message
-    def on_message(self, msg):
-        """
-        This callback gets plugged into the I2PSession object,
-        so we can asychronously notify our client when stuff arrives
-        """
-        if self.samSessionStyle == 'RAW':
-            self.samSend("RAW", "RECEIVE", msg)
-    
-        elif self.samSessionStyle == 'DATAGRAM':
-            # ain't so simple, we gotta rip and validate the header
-            remdestBin = msg[:self.samDestPubBinLen]
-            log(4, "remdestBin=%s" % repr(remdestBin))
-    
-            sig = msg[self.samDestPubBinLen:self.samDestPubBinLen+self.samSigLen]
-            log(4, "sig=%s" % repr(sig))
-    
-            data = msg[self.samDestPubBinLen+self.samSigLen:]
-            log(4, "data=%s" % repr(data))
-            
-            # now try to verify
-            hashed = shahash(remdestBin + self.samDestPubBin + data)
-            log(4, "hashed=%s" % repr(hashed))
-    
-            remdest = I2PDestination(bin=remdestBin)
-            if remdest.verify(hashed, sig):
-                # fine - very good, pass it on
-                log(4, "sig from peer is valid")
-                self.samSend("DATAGRAM", "RECEIVE", data,
-                             DESTINATION=remdest.toBase64(),
-                             )
-            else:
-                log(4, "DATAGRAM sig from peer is invalid")
-    #@-node:on_message
-    #@+node:threadSocketListener
-    def threadSocketListener(self, sock, dest):
-        """
-        Listens for incoming socket connections, and
-        notifies the client accordingly
-        """
-        destb64 = dest.toBase64()
-    
-        log(4, "Listening for connections to %s..." % destb64)
-    
-        sock.bind()
-        sock.listen()
-    
-        while 1:
-            log(4, "Awaiting next connection to %s..." % destb64)
-            newsock = sock.accept()
-            log(4, "Got connection to %s..." % destb64)
-    
-            # need an id, negative
-            id = - self.server.samAllocId()
-    
-            # register it in local and global streams
-            self.localstreams[id] = self.globalstreams[id] = newsock
-    
-            # fire up the receiver thread
-            thread.start_new_thread(self.threadSocketReceiver, (newsock, id))
-            
-            # who is connected to us?
-            remdest = newsock.remdest
-            remdest_b64 = remdest.toBase64()
-            
-            # and notify the client
-            self.samSend("STREAM", "CONNECTED",
-                         DESTINATION=remdest_b64,
-                         ID=id)
-    
-    #@-node:threadSocketListener
-    #@+node:threadSocketReceiver
-    def threadSocketReceiver(self, sock, id):
-        """
-        One of these gets launched each time a new stream connection
-        is created. Due to the lack of callback mechanism within the
-        ministreaming API, we have to actively poll for and send back
-        received data
-        """
-        while 1:
-            #avail = sock.available()
-            #if avail <= 0:
-            #    print "threadSocketReceiver: waiting for data on %s (%s avail)..." % (id, avail)
-            #    time.sleep(5)
-            #    continue
-            #log(4, "reading a byte")
-    
-            try:
-                buf = sock.recv(1)
-            except:
-                logException(4, "Exception reading first byte")
-            
-            if buf == '':
-                log(4, "stream closed")
-    
-                # notify a close
-                self.samSend("STREAM", "CLOSED",
-                             ID=id)
-                return
-    
-            # grab more if there's any available
-            navail = sock.available()
-            if navail > 0:
-                #log(4, "%d more bytes available, reading..." % navail)
-                rest = sock.recv(navail)
-                buf += rest
-            
-            # send if off
-            log(4, "got from peer: %s" % repr(buf))
-            
-            self.samSend("STREAM", "RECEIVED", buf,
-                         ID=id,
-                         )
-    
-    
-    
-    
-    #@-node:threadSocketReceiver
-    #@+node:samParse
-    def samParse(self, flds):
-        """
-        carves up a SAM command, returns it as a 3-tuple:
-            - cmd - command string
-            - subcmd - subcommand string
-            - dargs - dict of args
-        """
-        cmd = flds[0]
-        subcmd = flds[1]
-        args = flds[2:]
-        
-        dargs = {}
-        for arg in args:
-            try:
-                name, val = arg.split("=", 1)
-            except:
-                logException(3, "failed to process %s in %s" % (repr(arg), repr(flds)))
-                raise
-            dargs[name] = val
-    
-        # read and add data if any
-        if dargs.has_key('SIZE'):
-            size = dargs['SIZE'] = int(dargs['SIZE'])
-            dargs['DATA'] = self._recvbytes(size)
-    
-        #log(4, "\n".join([cmd+" "+subcmd] + [("%s=%s (...)" % (k,v[:40])) for k,v in dargs.items()]))
-        log(4, "\n".join([cmd+" "+subcmd] + [("%s=%s (...)" % (k,v)) for k,v in dargs.items()]))
-    
-        return cmd, subcmd, dargs
-    
-    
-    
-    #@-node:samParse
-    #@+node:samSend
-    def samSend(self, topic, subtopic, data=None, **kw):
-        """
-        Sends a SAM message (reply?) back to client
-        
-        Arguments:
-            - topic - the first word in the reply, eg 'STREAM'
-            - subtopic - the second word of the reply, eg 'CONNECTED'
-            - data - a string of raw data to send back (optional)
-        Keywords:
-            - extra 'name=value' items to pass back.
-        
-        Notes:
-            1. SIZE is not required. If sending back data, it will
-               be sized and a SIZE arg inserted automatically.
-            2. a dict of values can be passed to the 'args' keyword, in lieu
-               of direct keywords. This allows for cases where arg names would
-               cause python syntax clashes, eg 'tunnels.depthInbound'
-        """
-        items = [topic, subtopic]
-    
-        # stick in SIZE if needed
-        if data is not None:
-            kw['SIZE'] = str(len(data))
-        else:
-            data = '' # for later
-    
-        self.samCreateArgsList(kw, items)
-        
-        # and whack it together
-        buf = " ".join(items) + '\n' + data
-    
-        # and ship it
-        self.sendLock.acquire()
-        try:
-            self._sendbytes(buf)
-        except:
-            self.sendLock.release()
-            raise
-        self.sendLock.release()
-    
-    #@-node:samSend
-    #@+node:samCreateArgsList
-    def samCreateArgsList(self, kw1, lst):
-        for k,v in kw1.items():
-            if k == 'args':
-                self.samCreateArgsList(v, lst)
-            else:
-                lst.append("=".join([str(k), str(v)]))
-    #@-node:samCreateArgsList
-    #@+node:_sendbytes
-    def _sendbytes(self, raw):
-    
-        self.wfile.write(raw)
-        self.wfile.flush()
-    #@-node:_sendbytes
-    #@+node:_recvbytes
-    def _recvbytes(self, count):
-        """
-        Does a guaranteed read of n bytes
-        """
-        read = self.rfile.read
-    
-        chunks = []
-        needed = count
-        while needed > 0:
-            chunk = read(needed)
-            chunklen = len(chunk)
-            needed -= chunklen
-            chunks.append(chunk)
-        raw = "".join(chunks)
-    
-        # done
-        return raw
-    
-    #@-node:_recvbytes
-    #@-others
-#@nonl
-#@-node:class I2PSamClientHandler
-#@+node:Exceptions
-class NoPrivateKey(Exception):
-    """Destination object has no private key"""
-
-class I2PSocketError(Exception):
-    """Error working with I2PSocket objects"""
-#@-node:Exceptions
-#@+node:shahash
-def shahash(s):
-    """
-    Calculates SHA Hash of a string, as a string, using
-    I2P hashing facility
-    """
-    h = net.i2p.crypto.SHA256Generator().calculateHash(s)
-    h = bytearray2str(h.getData())
-    return h
-#@-node:shahash
-#@+node:base64enc
-def base64enc(s):
-    return net.i2p.data.Base64.encode(s)
-#@-node:base64enc
-#@+node:base64dec
-def base64dec(s):
-    return bytearray2str(net.i2p.data.Base64.decode(s))
-
-#@-node:base64dec
-#@+node:str2bytearray
-def str2bytearray(s):
-    """
-    Convenience - converts python string to java-friendly byte array
-    """
-    a = []
-    for c in s:
-        n = ord(c)
-        if n >= 128:
-            n = n - 256
-        a.append(n)
-    return a
-
-#@-node:str2bytearray
-#@+node:bytearray2str
-def bytearray2str(a):
-    """
-    Convenience - converts java-friendly byte array to python string
-    """
-    chars = []
-    for n in a:
-        if n < 0:
-            n += 256
-        chars.append(chr(n))
-    return "".join(chars)
-
-#@-node:bytearray2str
-#@+node:byteoutstream2str
-def byteoutstream2str(bs):
-    """
-    Convenience - converts java-friendly byteoutputstream to python string
-    """
-    chars = []
-    while 1:
-        c = bs.read()
-        if c >= 0:
-            chars.append(chr(c))
-        else:
-            break
-    return "".join(chars)
-
-#@-node:byteoutstream2str
-#@+node:dict2props
-def dict2props(d):
-    """
-    Converts a python dict d into a java.util.Properties object
-    """
-    props = java.util.Properties()
-    for k,v in d.items():
-        props[k] = str(v)
-    return props
-
-
-#@-node:dict2props
-#@+node:takeKey
-def takeKey(somedict, keyname, default=None):
-    """
-    Utility function to destructively read a key from a given dict.
-    Same as the dict's 'takeKey' method, except that the key (if found)
-    sill be deleted from the dictionary.
-    """
-    if somedict.has_key(keyname):
-        val = somedict[keyname]
-        del somedict[keyname]
-    else:
-        val = default
-    return val
-#@-node:takeKey
-#@+node:log
-def log(level, msg, nPrev=0):
-
-    # ignore messages that are too trivial for chosen verbosity
-    if level > verbosity:
-        return
-
-    loglock.acquire()
-    try:
-        # rip the stack
-        caller = traceback.extract_stack()[-(2+nPrev)]
-        path, line, func = caller[:3]
-        path = os.path.split(path)[1]
-        full = "%s:%s:%s():\n* %s" % (
-            path,
-            line,
-            func,
-            msg.replace("\n", "\n   + "))
-        now = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
-        msg = "%s %s\n" % (now, full)
-    
-        if logfile == sys.stdout:
-            print msg
-        else:
-            file(logfile, "a").write(msg+"\n")
-    except:
-        s = StringIO.StringIO()
-        traceback.print_exc(file=s)
-        print s.getvalue()
-        print "Logger crashed"
-    loglock.release()
-#@nonl
-#@-node:log
-#@+node:logException
-def logException(level, msg=''):
-    s = StringIO.StringIO()
-    traceback.print_exc(file=s)
-    log(level, "%s\n%s" % (s.getvalue(), msg), 1)
-#@-node:logException
-#@+node:testdests
-def testdests():
-    """
-    Demo function which tests out dest generation and import/export
-    """
-    print
-    print "********************************************"
-    print "Testing I2P destination create/export/import"
-    print "********************************************"
-    print
-
-    print "Generating a destination"
-    d1 = I2PDestination()
-
-    print "Exporting and importing dest1 in several forms"
-
-    print "public binary string..."
-    d1_bin = d1.toBin()
-    d2_bin = I2PDestination(bin=d1_bin)
-
-    print "public binary file..."
-    d1.toBinFile("temp-d1-bin")
-    d2_binfile = I2PDestination(binfile="temp-d1-bin")
-
-    print "private binary string..."
-    d1_binprivate = d1.toBinPrivate()
-    d2_binprivate = I2PDestination(binprivate=d1_binprivate)
-
-    print "private binary file..."
-    d1.toBinFilePrivate("temp-d1-bin-private")
-    d2_binfileprivate = I2PDestination(binfileprivate="temp-d1-bin-private")
-
-    print "public base64 string..."
-    d1_b64 = d1.toBase64()
-    d2_b64 = I2PDestination(base64=d1_b64)
-
-    print "public base64 file..."
-    d1.toBase64File("temp-d1-b64")
-    d2_b64file = I2PDestination(base64file="temp-d1-b64")
-
-    print "private base64 string..."
-    d1_base64private = d1.toBase64Private()
-    d2_b64private = I2PDestination(base64private=d1_base64private)
-
-    print "private base64 file..."
-    d1.toBase64FilePrivate("temp-d1-b64-private")
-    d2_b64fileprivate = I2PDestination(base64fileprivate="temp-d1-b64-private")
-
-    print "All destination creation/import/export tests passed!"
-
-
-#@-node:testdests
-#@+node:testsigs
-def testsigs():
-    global d1, d1pub, d1sig, d1res
-    
-    print
-    print "********************************************"
-    print "Testing I2P dest-based signatures"
-    print "********************************************"
-    print
-    
-    print "Creating dest..."
-    d1 = I2PDestination()
-
-    s_good = "original stuff that we're signing"
-    s_bad = "non-original stuff we're trying to forge"
-    
-    print "Signing some shit against d1..."
-    d1sig = d1.sign(s_good)
-
-    print "Creating public dest d1pub"
-    d1pub = I2PDestination(bin=d1.toBin())
-
-    print "Verifying original data with d1pub"
-    res = d1pub.verify(s_good, d1sig)
-    print "Result: %s (should be 1)" % repr(res)
-    
-    print "Trying to verify on a different string"
-    res1 = d1pub.verify(s_bad, d1sig)
-    print "Result: %s (should be 0)" % repr(res1)
-    
-    if res and not res1:
-        print "signing/verifying test passed"
-    else:
-        print "SIGNING/VERIFYING TEST FAILED"
-
-#@-node:testsigs
-#@+node:testsession
-def testsession():
-
-    global c, d1, d2, s1, s2
-
-    print
-    print "********************************************"
-    print "Testing I2P dest->dest messaging"
-    print "********************************************"
-    print
-    
-    print "Creating I2P client..."
-    c = I2PClient()
-
-    print "Creating destination d1..."
-    d1 = c.createDestination()
-
-    print "Creating destination d2..."
-    d2 = c.createDestination()
-
-    print "Creating destination d3..."
-    d3 = c.createDestination()
-
-    print "Creating session s1 on dest d1..."
-    s1 = c.createSession(d1, host='localhost', port=7654)
-
-    print "Creating session s2 on dest d2..."
-    s2 = c.createSession(d2)
-
-    print "Connecting session s1..."
-    s1.connect()
-
-    print "Connecting session s2..."
-    s2.connect()
-
-    print "Sending message from s1 to d2..."
-    s1.sendMessage(d2, "Hi there, s2!!")
-
-    print "Retrieving message from s2..."
-    print "got: %s" % repr(s2.getMessage())
-
-    print "Sending second message from s1 to d2..."
-    s1.sendMessage(d2, "Hi there again, s2!!")
-
-    print "Retrieving message from s2..."
-    print "got: %s" % repr(s2.getMessage())
-
-    print "Sending message from s1 to d3 (should take ages then fail)..."
-    res = s1.sendMessage(d3, "This is futile!!")
-    print "result of that send was %s (should have been 0)" % res
-
-    print "Destroying session s1..."
-    s1.destroySession()
-
-    print "Destroying session s2..."
-    s2.destroySession()
-
-    print "session tests passed!"
-#@-node:testsession
-#@+node:testsocket
-def testsocket(bidirectional=0):
-
-    global d1, d2, s1, s2
-
-    print
-    print "********************************************"
-    print "Testing I2P streaming interface"
-    print "********************************************"
-    print
-    
-    print "Creating destinations..."
-    dServer = I2PDestination()
-    dClient = I2PDestination()
-
-    print "Creating sockets..."
-    sServer = I2PSocket(dServer)
-    sClient = I2PSocket(dClient)
-
-    # server thread which simply reads a line at a time, then echoes
-    # that line back to the client
-    def servThread(s):
-        print "server: binding socket"
-        s.bind()
-        print "server: setting socket to listen"
-        s.listen()
-        print "server: awaiting connection"
-        sock = s.accept()
-        print "server: got connection"
-
-        sock.send("Hello, echoing...\n")
-        buf = ''
-        while 1:
-            c = sock.recv(1)
-            if c == '':
-                sock.close()
-                print "server: socket closed"
-                break
-
-            buf += c
-            if c == '\n':
-                sock.send("SERVER: "+buf)
-                buf = ''
-
-    # client thread which reads lines and prints them to stdout
-    def clientThread(s):
-        buf = ''
-        while 1:
-            c = s.recv(1)
-            if c == '':
-                s.close()
-                print "client: socket closed"
-                break
-            buf += c
-            if c == '\n':
-                print "client: got %s" % repr(buf)
-                buf = ''
-
-    print "launching server thread..."
-    thread.start_new_thread(servThread, (sServer,))
-
-    if bidirectional:
-        # dummy thread which accepts connections TO client socket
-        def threadDummy(s):
-            print "dummy: listening"
-            s.listen()
-            print "dummy: accepting"
-    
-            sock = s.accept()
-            print "dummy: got connection"
-    
-        print "test - launching dummy client accept thread"
-        thread.start_new_thread(threadDummy, (sClient,))
-
-    print "client: trying to connect"
-    sClient.connect(dServer)
-
-    print "client: connected, launching rx thread"
-    thread.start_new_thread(clientThread, (sClient,))
-
-    while 1:
-        line = raw_input("Enter something (q to quit)> ")
-        if line == 'q':
-            print "closing client socket"
-            sClient.close()
-            break
-        sClient.send(line+"\n")
-
-    print "I2PSocket test apparently succeeded"
-
-
-#@-node:testsocket
-#@+node:usage
-def usage(detailed=0):
-    
-    print "Usage: %s <options> [<command>]" % sys.argv[0]
-    if not detailed:
-        print "Run with '-h' to get detailed help"
-        sys.exit(0)
-
-    print "I2PSAM is a bridge that allows I2P client programs to access the"
-    print "I2P network by talking over a plaintext socket connection."
-    print "References:"
-    print "   - http://www.freenet.org.nz/i2p - source, doco, downloadables"
-    print "   - http://drupal.i2p.net/node/view/144 - I2P SAM specification"
-    print
-    print "Options:"
-    print "  -h, -?, --help        - display this help"
-    print "  -v, --version         - print program version"
-    print "  -V, --verbosity=n     - set verbosity to n, default 2, 1==quiet, 4==noisy"
-    print "  -H, --listenhost=host - specify host to listen on for client connections"
-    print "  -P, --listenport=port - port to listen on for client connections"
-    print "      --i2cphost=host   - hostname of I2P router's I2CP interface"
-    print "      --i2cpport=port   - port of I2P router's I2CP interface"
-    print
-    print "Commands:"
-    print "     (run with no commands to launch SAM server)"
-    print "     samserver - runs as a SAM server"
-    print "     test - run a suite of self-tests"
-    print "     testsocket - run only the socket test"
-    print "     testbidirsocket - run socket test in bidirectional mode"
-    print
-    
-    sys.exit(0)
-#@-node:usage
-#@+node:main
-def main():
-
-    argv = sys.argv
-    argc = len(argv)
-
-    # -------------------------------------------------
-    # do the getopt command line parsing
-
-    try:
-        opts, args = getopt.getopt(sys.argv[1:],
-                                   "h?vV:H:P:",
-                                   ['help', 'version', 'verbosity=',
-                                    'listenhost=', 'listenport=',
-                                    'i2cphost=', 'i2cpport=',
-                                    ])
-    except:
-        traceback.print_exc(file=sys.stdout)
-        usage("You entered an invalid option")
-
-    #print "args=%s" % args
-
-    serveropts = {}
-    for opt, val in opts:
-        if opt in ['-h', '-?', '--help']:
-            usage(1)
-        elif opt in ['-v', '--version']:
-            print "I2P SAM version %s" % version
-            sys.exit(0)
-        elif opt in ['-V', '--verbosity']:
-            serveropts['verbosity'] = int(val)
-        elif opt in ['-H', '--listenhost']:
-            serveropts['host'] = val
-        elif opt in ['-P', '--listenport']:
-            serveropts['port'] = int(val)
-        elif opt in ['--i2cphost']:
-            serveropts['i2cphost'] = val
-        elif opt in ['--i2cpport']:
-            serveropts['i2cpport'] = int(val)
-        else:
-            usage(0)
-
-    # --------------------------------------------------
-    # now run in required mode, default is 'samserver'
-
-    if len(args) == 0:
-        cmd = 'samserver'
-    else:
-        cmd = args[0]
-
-    if cmd == 'samserver':
-
-        log(2, "Running I2P SAM Server...")
-        server = I2PSamServer(**serveropts)
-        server.run()
-
-    elif cmd == 'test':
-        
-        print "RUNNING full I2PSAM Jython TEST SUITE"
-        testsigs()
-        testdests()
-        testsession()
-        testsocket()
-
-    elif cmd == 'testsocket':
-        
-        print "RUNNING SOCKET TEST"
-        testsocket(0)
-
-    elif cmd == 'testbidirsocket':
-        print "RUNNING BIDIRECTIONAL SOCKET TEST"
-        testsocket(1)
-
-    else:
-        # spit at unrecognised option
-        usage(0)
-
-#@-node:main
-#@+node:MAINLINE
-if __name__ == '__main__':
-    main()
-
-#@-node:MAINLINE
-#@-others
-
-
-#@-node:@file jython/src/i2psam.py
-#@-leo
diff --git a/apps/sam/python/src/i2psamclient.py b/apps/sam/python/src/i2psamclient.py
deleted file mode 100644
index 2070bfd9ab..0000000000
--- a/apps/sam/python/src/i2psamclient.py
+++ /dev/null
@@ -1,1334 +0,0 @@
-#!/usr/bin/env python
-#@+leo-ver=4
-#@+node:@file python/src/i2psamclient.py
-#@@first
-"""
-Implements a client API for I2CP messaging via SAM
-
-Very simple I2P messaging interface, which should prove easy
-to reimplement in your language of choice
-
-This module can be used from cpython or jython
-
-Run this module without arguments to see a demo in action
-(requires SAM server to be already running)
-"""
-#@+others
-#@+node:imports
-import sys, os, socket, thread, threading, Queue, traceback, StringIO, time
-
-from pdb import set_trace
-
-#@-node:imports
-#@+node:globals
-# -----------------------------------------
-# server access settings
-
-i2psamhost = '127.0.0.1'
-i2psamport = 7656
-
-# ------------------------------------------
-# logging settings
-
-# 1=v.quiet, 2=normal, 3=verbose, 4=debug, 5=painful
-verbosity = 5
-
-# change to a filename to log there instead
-logfile = sys.stdout
-
-# when set to 1, and when logfile != sys.stdout, log msgs are written
-# both to logfile and console stdout
-log2console = 1
-
-# don't touch this!
-loglock = threading.Lock()
-
-#@-node:globals
-#@+node:exceptions
-class I2PServerFail(Exception):
-    """
-    A failure in connecting to the I2CP server
-    """
-
-class I2PCommandFail(Exception):
-    """
-    A failure in an I2CP command
-    """
-    pass
-
-class I2PStreamClosed(Exception):
-    """
-    Stream is not open
-    """
-#@-node:exceptions
-#@+node:class I2PSamClient
-class I2PSamClient:
-    """
-    Implements a reference client for accessing I2CP via i2psam
-    
-    Connects to i2psam's I2PSamServer, sends commands
-    and receives results
-
-    The primitives should be reasonably self-explanatory
-
-    Usage summary:
-        1. create one or more I2PSamClient instances per process (1 should be fine)
-        2. invoke the L{genkeys} method to create destination keypairs
-        3. create sessions objects via the L{createSession} method
-        4. use these session objects to send and receive data
-        5. destroy the session objects when you're done
-    
-    Refer to the function L{demo} for a simple example
-    """
-    #@    @+others
-    #@+node:attributes
-    # server host/port settings exist here in case you might
-    # have a reason for overriding in a subclass
-    
-    host = i2psamhost
-    port = i2psamport
-    
-    i2cpHost = None
-    i2cpPort = None
-    
-    #@-node:attributes
-    #@+node:__init__
-    def __init__(self, **kw):
-        """
-        Creates a client connection to i2psam listener
-        
-        Keywords:
-            - host - host to connect to (default 127.0.0.1)
-            - port - port to connect to (default 7656)
-        """
-        # get optional host/port
-        log(4, "entered")
-    
-        self.host = kw.get('host', self.host)
-        self.port = int(kw.get('port', self.port))
-    
-        self.cmdLock = threading.Lock()
-    
-        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-    
-        self.lockHello = threading.Lock()
-        self.sendLock = threading.Lock()
-        self.qNewDests = Queue.Queue()
-        self.qSession = Queue.Queue()
-        self.qDatagrams = Queue.Queue()
-        self.qRawMessages = Queue.Queue()
-    
-        self.namingReplies = {}
-        self.namingCache = {}
-    
-        self.streams = {} # currently open streams, keyed by id
-        self.streamConnectReplies = {} # holds queues awaiting connect resp, keyed by id
-        self.qNewStreams = Queue.Queue() # incoming connections
-    
-        self.samNextIdLock = threading.Lock()
-        self.samNextId = 1
-    
-        self.isRunning = 1
-    
-    
-        log(4, "trying connection to SAM server...")
-        try:
-            self.sock.connect((self.host, self.port))
-        except:
-            raise I2PServerFail(
-                "Connection to i2psam server failed\n"
-                "(are you sure your I2P router is running, and\n"
-                "listening for I2CP connections on %s:%s?)" % (self.host, self.port)
-                )
-    
-        # fire up receiver thread
-        thread.start_new_thread(self.threadRx, ())
-    
-        # ping the server
-        try:
-            log(4, "trying to ping SAM server...")
-            self.samHello()
-        except:
-            logException(4, "Exception on handshaking")
-            raise I2PServerFail("Failed to handshake with i2psam server")
-    
-        # connected fine
-        log(2, "I2CP Client successfully connected")
-    #@-node:__init__
-    #@+node:createSession
-    def createSession(self, privdest):
-        """
-        DEPRECATED - use sam* methods instead!
-    
-        Creates a session using private destkey
-        """
-        #3. createsession:
-        #    - client->server:
-        #        - createsession <base64private>\n
-        #    - server->client:
-        #        - ok\n  OR
-        #        - error[ <reason>]\n
-    
-        self.cmdLock.acquire()
-        try:
-            self._sendline("createsession %s" % privdest)
-            respitems = self._recvline().split(" ", 1)
-            if respitems[0] == 'ok':
-                res = None
-            else:
-                res = respitems[1]
-        except:
-            logException(2, "createsession fail")
-            self.cmdLock.release()
-            raise
-    
-        self.cmdLock.release()
-    
-        if res:
-            raise I2PCommandFail("createsession fail: "+res)
-    
-        return I2PRemoteSession(self, privdest)
-    
-    #@-node:createSession
-    #@+node:destroySession
-    def destroySession(self, privdest):
-        """
-        DEPRECATED - use sam* methods instead!
-    
-        Destrlys a session using private destkey
-        """
-        #4. destroysession:
-        #    - client->server:
-        #        - destroysession <base64private>\n
-        #    - server->client:
-        #        - ok\n OR
-        #        - error[ <reason>]\n
-    
-        self.cmdLock.acquire()
-        try:
-            self._sendline("destroysession %s" % privdest)
-            respitems = self._recvline().split(" ", 1)
-            if respitems[0] == 'ok':
-                res = None
-            else:
-                res = respitems[1]
-        except:
-            logException(2, "destroysession fail")
-            self.cmdLock.release()
-            raise
-    
-        self.cmdLock.release()
-    
-        if res:
-            raise I2PCommandFail("destroysession fail: " + res)
-    
-        return res
-    
-    #@-node:destroySession
-    #@+node:send
-    def send(self, privdest, peerdest, msg):
-        """
-        DEPRECATED - use sam* methods instead!
-    
-        Sends a block of data from local dest to remote dest
-        """
-        #5. send:
-        #    - client->server:
-        #        - send <size> <localbase64private> <remotebase64dest>\ndata
-        #    - server->client:
-        #        - ok\n OR
-        #        - error[ <reason>]\n
-    
-        self.cmdLock.acquire()
-        try:
-            self._sendline("send %s %s %s" % (len(msg), privdest, peerdest))
-            self._sendbytes(msg)
-            line = self._recvline()
-            #print "** %s" % line
-            respitems = line.split(" ", 1)
-            if respitems[0] == 'ok':
-                res = None
-            else:
-                res = " ".join(respitems[1:])
-        except:
-            logException(2, "send fail")
-            self.cmdLock.release()
-            raise
-    
-        self.cmdLock.release()
-    
-        if res:
-            raise I2PCommandFail("send fail: " + res)
-    
-        return res
-    
-    #@-node:send
-    #@+node:receive
-    def receive(self, privdest):
-        """
-        DEPRECATED - use sam* methods instead!
-    
-        receives a block of data, returning string, or None if no data available
-        """
-        #6. receive:
-        #    - client->server:
-        #        - receive <localbase64private>\n
-        #    - server->client:
-        #        - ok <size>\ndata OR
-        #        - error[ <reason>]\n
-    
-        self.cmdLock.acquire()
-        try:
-            self._sendline("receive %s" % privdest)
-            respitems = self._recvline().split(" ", 1)
-            if respitems[0] == 'ok':
-                res = None
-                size = int(respitems[1])
-                msg = self._recvbytes(size)
-                res = None
-            else:
-                res = respitems[1]
-        except:
-            logException(2, "receive fail")
-            self.cmdLock.release()
-            raise
-    
-        self.cmdLock.release()
-    
-        if res:
-            raise I2PCommandFail("destroysession fail: " + res)
-    
-        return msg
-    #@-node:receive
-    #@+node:samHello
-    def samHello(self):
-        """
-        Sends a quick HELLO PING to SAM server and awaits response
-        Arguments:
-            - none
-    
-        Keywords:
-            - none
-        
-        Returns:
-            - nothing (None) if ping sent and pong received, or raises an exception if
-              failed
-        """
-        self.lockHello.acquire()
-        self.samSend("HELLO", "PING")
-        self.lockHello.acquire()
-        self.lockHello.release()
-    #@-node:samHello
-    #@+node:samSessionCreate
-    def samSessionCreate(self, style, dest, **kw):
-        """
-        Creates a SAM session
-        
-        Arguments:
-            - style - one of 'STREAM', 'DATAGRAM' or 'RAW'
-            - dest - base64 private destination
-        
-        Keywords:
-            - direction - only used for STREAM sessions, can be RECEIVE,
-              CREATE or BOTH (default BOTH)
-            - i2cphost - hostname for the SAM bridge to contact i2p router on
-            - i2cpport - port for the SAM bridge to contact i2p router on
-        
-        Returns:
-            - 'OK' if session was created successfully, or a tuple
-              (keyword, message) if not
-        """
-        kw1 = dict(kw)
-        kw1['STYLE'] = self.samStyle = style
-        kw1['DESTINATION'] = dest
-        if style == 'STREAM':
-            direction = kw.get('direction', 'BOTH')
-            kw1['DIRECTION'] = direction
-            if direction == 'BOTH':
-                self.canAccept = 1
-                self.canConnect = 1
-            elif direction == 'RECEIVE':
-                self.canAccept = 1
-                self.canConnect = 0
-            elif direction == 'CREATE':
-                self.canAccept = 0
-                self.canConnect = 1
-            else:
-                raise I2PCommandFail("direction keyword must be one of RECEIVE, CREATE or BOTH")
-    
-        # stick in i2cp host/port if specified
-        if kw.has_key('i2cphost'):
-            kw1['I2CP.HOST'] = kw['i2cphost']
-        if kw.has_key('i2cpport'):
-            kw1['I2CP.PORT'] = kw['i2cpport']
-        
-        self.samSend("SESSION", "CREATE",
-                     **kw1)
-        subtopic, args = self.qSession.get()
-    
-        if args['RESULT'] == 'OK':
-            return 'OK'
-        else:
-            return (args['RESULT'], args['MESSAGE'])
-    #@-node:samSessionCreate
-    #@+node:samDestGenerate
-    def samDestGenerate(self):
-        """
-        Creates a whole new dest and returns an tuple pub, priv as
-        base64 public and private destination keys
-        """
-        self.samSend("DEST", "GENERATE")
-        pub, priv = self.qNewDests.get()
-        return pub, priv
-    #@-node:samDestGenerate
-    #@+node:samRawSend
-    def samRawSend(self, peerdest, msg):
-        """
-        Sends a raw anon message to another peer
-        
-        peerdest is the public base64 destination key of the peer
-        """
-        self.samSend("RAW", "SEND", msg,
-                     DESTINATION=peerdest,
-                     )
-    #@-node:samRawSend
-    #@+node:samRawCheck
-    def samRawCheck(self):
-        """
-        Returns 1 if there are received raw messages available, 0 if not
-        """
-        return not self.qRawMessages.empty()
-    #@-node:samRawCheck
-    #@+node:samRawReceive
-    def samRawReceive(self, blocking=1):
-        """
-        Returns the next raw message available,
-        blocking if none is available and the blocking arg is set to 0
-    
-        If blocking is 0, and no messages are available, returns None.
-        
-        Remember that you can check for availability with
-        the .samRawCheck() method
-        """
-        if not blocking:
-            if self.qRawMessages.empty():
-                return None
-        return self.qRawMessages.get()
-        
-    #@nonl
-    #@-node:samRawReceive
-    #@+node:samDatagramSend
-    def samDatagramSend(self, peerdest, msg):
-        """
-        Sends a repliable datagram message to another peer
-    
-        peerdest is the public base64 destination key of the peer
-        """
-        self.samSend("DATAGRAM", "SEND", msg,
-                     DESTINATION=peerdest,
-                     )
-    #@-node:samDatagramSend
-    #@+node:samDatagramCheck
-    def samDatagramCheck(self):
-        """
-        Returns 1 if there are datagram messages received messages available, 0 if not
-        """
-        return not self.qDatagrams.empty()
-    #@-node:samDatagramCheck
-    #@+node:samDatagramReceive
-    def samDatagramReceive(self, blocking=1):
-        """
-        Returns the next datagram message available,
-        blocking if none is available.
-    
-        If blocking is set to 0, and no messages are available,
-        returns None.
-        
-        Remember that you can check for availability with
-        the .samRawCheck() method
-        
-        Returns 2-tuple: dest, msg
-        where dest is the base64 destination of the peer from
-        whom the message was received
-        """
-        if not blocking:
-            if self.qDatagrams.empty():
-                return None
-        return self.qDatagrams.get()
-    #@-node:samDatagramReceive
-    #@+node:samStreamConnect
-    def samStreamConnect(self, dest):
-        """
-        Makes a STREAM connection to a remote dest
-    
-        STREAM STATUS
-        RESULT=$result
-        ID=$id
-        [MESSAGE=...]
-        """
-        # need an ID
-        id = self.samAllocId()
-        
-        # create queue for connect reply
-        q = self.streamConnectReplies[id] = Queue.Queue()
-    
-        # send req
-        self.samSend("STREAM", "CONNECT",
-                     ID=id,
-                     DESTINATION=dest,
-                     )
-    
-        # await reply - comes back as a dict
-        resp = q.get()
-    
-        # ditch queue
-        del self.streamConnectReplies[id]
-        del q
-        
-        # check out response
-        result = resp['RESULT']
-        if result == 'OK':
-            conn = I2PSAMStream(self, id, dest)
-            self.streams[id] = conn
-            return conn
-        else:
-            msg = resp.get('MESSAGE', '')
-            raise I2PCommandFail(result, msg, "STREAM CONNECT")
-    
-    #@-node:samStreamConnect
-    #@+node:samStreamAccept
-    def samStreamAccept(self):
-        """
-        Waits for an incoming connection, returning a wrapped conn obj
-        """
-        log(4, "waiting for connection")
-        conn = self.qNewStreams.get()
-        log(4, "got connection")
-        return conn
-    #@-node:samStreamAccept
-    #@+node:samStreamSend
-    def samStreamSend(self, conn, data):
-        """
-        DO NOT CALL THIS DIRECTLY
-        
-        Invoked by an I2PSAMStream object to transfer data
-        Use the object's .send() method instead.
-        
-        conn is the I2PSAMStream
-    
-        STREAM SEND 
-        ID=$id
-        SIZE=$numBytes\n[$numBytes of data]
-        """
-        # dispatch
-        self.samSend("STREAM", "SEND", data, ID=conn.id)
-    
-        # useless, but mimics socket paradigm
-        return len(data)
-    
-    #@-node:samStreamSend
-    #@+node:samStreamClose
-    def samStreamClose(self, conn):
-        """
-        DO NOT CALL DIRECTLY
-        
-        Invoked by I2PSAMStream to close stream
-        Use the object's .send() method instead.
-        
-        STREAM CLOSE
-        ID=$id
-        """
-        self.samSend("STREAM", "CLOSE", ID=conn.id)
-        del self.streams[conn.id]
-    
-    #@-node:samStreamClose
-    #@+node:samNamingLookup
-    def samNamingLookup(self, host):
-        """
-        Looks up a host in hosts.txt
-        """
-        # try the cache first
-        if self.namingCache.has_key(host):
-            log(4, "found host %s in cache" % host)
-            return self.namingCache[host]
-    
-        # make a queue for reply
-        q = self.namingReplies[host] = Queue.Queue()
-        
-        # send off req
-        self.samSend("NAMING", "LOOKUP",
-                     NAME=host,
-                     )
-    
-        # get resp
-        resp = q.get()
-    
-        result = resp.get('RESULT', 'none')
-        if result == 'OK':
-            log(4, "adding host %s to cache" % host)
-            val = resp['VALUE']
-            self.namingCache[host] = val
-            return val
-        else:
-            raise I2PCommandFail("Error looking up '%s': %s %s" % (
-                host, result, resp.get('MESSAGE', '')))
-    
-    #@-node:samNamingLookup
-    #@+node:samParse
-    def samParse(self, flds):
-        """
-        carves up a SAM command, returns it as a 3-tuple:
-            - cmd - command string
-            - subcmd - subcommand string
-            - dargs - dict of args
-        """
-        cmd = flds[0]
-        subcmd = flds[1]
-        args = flds[2:]
-        
-        dargs = {}
-        for arg in args:
-            try:
-                name, val = arg.split("=", 1)
-            except:
-                logException(3, "failed to process %s in %s" % (repr(arg), repr(flds)))
-                raise
-            dargs[name] = val
-    
-        # read and add data if any
-        if dargs.has_key('SIZE'):
-            size = dargs['SIZE'] = int(dargs['SIZE'])
-            dargs['DATA'] = self._recvbytes(size)
-    
-        #log(4, "\n".join([cmd+" "+subcmd] + [("%s=%s (...)" % (k,v[:40])) for k,v in dargs.items()]))
-        log(4, "\n".join([cmd+" "+subcmd] + [("%s=%s (...)" % (k,v)) for k,v in dargs.items()]))
-    
-        return cmd, subcmd, dargs
-    
-    
-    
-    
-    
-    #@-node:samParse
-    #@+node:samSend
-    def samSend(self, topic, subtopic, data=None, **kw):
-        """
-        Sends a SAM message (reply?) back to client
-        
-        Arguments:
-            - topic - the first word in the reply, eg 'STREAM'
-            - subtopic - the second word of the reply, eg 'CONNECTED'
-            - data - a string of raw data to send back (optional)
-        Keywords:
-            - extra 'name=value' items to pass back.
-        
-        Notes:
-            1. SIZE is not required. If sending back data, it will
-               be sized and a SIZE arg inserted automatically.
-            2. a dict of values can be passed to the 'args' keyword, in lieu
-               of direct keywords. This allows for cases where arg names would
-               cause python syntax clashes, eg 'tunnels.depthInbound'
-        """
-        items = [topic, subtopic]
-    
-        # stick in SIZE if needed
-        if data is not None:
-            kw['SIZE'] = str(len(data))
-        else:
-            data = '' # for later
-    
-        self.samCreateArgsList(kw, items)
-        
-        # and whack it together
-        buf = " ".join(items) + '\n' + data
-    
-        # and ship it
-        self.sendLock.acquire()
-        try:
-            self._sendbytes(buf)
-        except:
-            self.sendLock.release()
-            raise
-        self.sendLock.release()
-    
-    #@-node:samSend
-    #@+node:samCreateArgsList
-    def samCreateArgsList(self, kw1, lst):
-        for k,v in kw1.items():
-            if k == 'args':
-                self.samCreateArgsList(v, lst)
-            else:
-                lst.append("=".join([str(k), str(v)]))
-    #@-node:samCreateArgsList
-    #@+node:threadRx
-    def threadRx(self):
-        """
-        Handles all incoming stuff from SAM, storing in
-        local queues as appropriate
-        """
-        while self.isRunning:
-            try:
-                log(4, "Awaiting next message from server")
-                line = self._recvline()
-                if line == '':
-                    log(3, "I2P server socket closed")
-                    return
-                flds = line.split(" ")
-                topic, subtopic, args = self.samParse(flds)
-                log(4, "Got %s %s %s" % (topic, subtopic, args))
-                handleMsg = getattr(self, "on_"+topic, None)
-                if handleMsg:
-                    handleMsg(topic, subtopic, args)
-                else:
-                    log(2, "No handler for '%s' message" % topic)
-            except:
-                #logException(3, "Exception handling %s %s\n%s" % (topic, subtopic, args))
-                logException(3, "Exception handling %s" % repr(line))
-    #@-node:threadRx
-    #@+node:on_HELLO
-    def on_HELLO(self, topic, subtopic, args):
-        """
-        Handles HELLO PONG messages from server
-        """
-        # just wake up the caller
-        log(4, "got HELLO")
-        self.lockHello.release()
-    #@-node:on_HELLO
-    #@+node:on_SESSION
-    def on_SESSION(self, topic, subtopic, args):
-        """
-        Handles SESSION messages from server
-        """
-        # just stick whatever on the queue and wake up the caller
-        res = subtopic, args
-        self.qSession.put(res)
-    #@-node:on_SESSION
-    #@+node:on_STREAM
-    def on_STREAM(self, topic, subtopic, args):
-        """
-        Handles STREAM messages from server
-    
-        STREAM STATUS
-        RESULT=$result
-        ID=$id
-        [MESSAGE=...]
-    
-        STREAM CONNECTED
-        DESTINATION=$base64key
-        ID=$id
-    
-        STREAM RECEIVED
-        ID=$id
-        SIZE=$numBytes\n[$numBytes of data]
-    
-        STREAM CLOSED
-        RESULT=$result
-        ID=$id
-        [MESSAGE=...]
-        """
-        log(4, "got %s %s %s" % (topic, subtopic, args))
-    
-        # which stream?
-        id = int(args['ID'])
-    
-        # result of prior connection attempt
-        if subtopic == 'STATUS':
-            # stick it on the queue that the caller is waiting on and let the
-            # caller interpret the result
-            self.streamConnectReplies[id].put(args)
-            return 
-    
-        # notice of incoming connection
-        if subtopic == 'CONNECTED':
-    
-            # grab details
-            dest = args['DESTINATION']
-    
-            # wrap it in a stream obj
-            conn = I2PSAMStream(self, id, dest)
-            self.streams[id] = conn
-    
-            # and put it there for anyone calling samStreamAccept()
-            self.qNewStreams.put(conn)
-    
-            # done
-            return
-    
-        # notice of received data
-        elif subtopic == 'RECEIVED':
-            # grab details
-            data = args['DATA']
-    
-            # lookup the connection
-            conn = self.streams.get(id, None)
-            if not conn:
-                # conn not known, just ditch
-                log(2, "got data, but don't recall any conn with id %s" % id)
-                return
-    
-            # and post the received data
-            conn._notifyIncomingData(data)
-    
-            log(4, "wrote data to conn's inbound queue")
-            
-            # done
-            return
-    
-        elif subtopic == 'CLOSED':
-            # lookup the connection
-            conn = self.streams.get(id, None)
-            if not conn:
-                # conn not known, just ditch
-                return
-    
-            # mark conn as closed and forget it
-            conn._notifyIncomingData("") # special signal to close
-            conn.isOpen = 0
-            del self.streams[id]
-    
-            # done
-            return
-    
-    
-    
-    #@-node:on_STREAM
-    #@+node:on_DATAGRAM
-    def on_DATAGRAM(self, topic, subtopic, args):
-        """
-        Handles DATAGRAM messages from server
-        """
-        remdest = args['DESTINATION']
-        data = args['DATA']
-        
-        self.qDatagrams.put((remdest, data))
-    #@-node:on_DATAGRAM
-    #@+node:on_RAW
-    def on_RAW(self, topic, subtopic, args):
-        """
-        Handles RAW messages from server
-        """
-        data = args['DATA']
-    
-        log(3, "Got anonymous datagram %s" % repr(data))
-        self.qRawMessages.put(data)
-    #@-node:on_RAW
-    #@+node:on_NAMING
-    def on_NAMING(self, topic, subtopic, args):
-        """
-        Handles NAMING messages from server
-        """
-        # just find out hostname, and stick it on resp q
-        host = args['NAME']
-        self.namingReplies[host].put(args)
-    #@-node:on_NAMING
-    #@+node:on_DEST
-    def on_DEST(self, topic, subtopic, args):
-        """
-        Handles DEST messages from server
-        """
-        pubkey = args['PUB']
-        privkey = args['PRIV']
-        res = pubkey, privkey
-        self.qNewDests.put(res)
-    #@-node:on_DEST
-    #@+node:samAllocId
-    def samAllocId(self):
-        """
-        Allocates a new unique id as required by SAM protocol
-        """
-        self.samNextIdLock.acquire()
-        id = self.samNextId
-        self.samNextId += 1
-        self.samNextIdLock.release()
-        return id
-    #@-node:samAllocId
-    #@+node:_recvline
-    def _recvline(self):
-        """
-        Guaranteed read of a full line
-        """
-        chars = []
-        while 1:
-            c = self.sock.recv(1)
-            if c in ['', '\n']:
-                break
-            chars.append(c)
-        return "".join(chars)
-    #@-node:_recvline
-    #@+node:_recvbytes
-    def _recvbytes(self, num):
-        """
-        Guaranteed read of num bytes
-        """
-        if num <= 0:
-            return ""
-    
-        reqd = num
-        chunks = []
-        while reqd > 0:
-            chunk = self.sock.recv(reqd)
-            if not chunk:
-                raise I2PServerFail("Buffer read fail")
-            chunks.append(chunk)
-            reqd -= len(chunk)
-        return "".join(chunks)
-    #@-node:_recvbytes
-    #@+node:_sendbytes
-    def _sendbytes(self, buf):
-        """
-        Guaranteed complete send of a buffer
-        """
-        reqd = len(buf)
-        while reqd > 0:
-            nsent = self.sock.send(buf)
-            if nsent == 0:
-                raise I2PServerFail("Send to server failed")
-            buf = buf[nsent:]
-            reqd -= nsent
-    #@-node:_sendbytes
-    #@+node:_sendline
-    def _sendline(self, line):
-        """
-        just tacks on a newline and sends
-        """
-        self._sendbytes(line+"\n")
-    #@-node:_sendline
-    #@-others
-#@-node:class I2PSamClient
-#@+node:class I2PSAMStream
-class I2PSAMStream:
-    """
-    Wrapper for a stream object
-    """
-    #@    @+others
-    #@+node:__init__
-    def __init__(self, client, id, dest):
-        """
-        """
-        self.client = client
-        self.id = id
-        self.dest = dest
-    
-        self.qIncomingData = Queue.Queue()
-    
-        self.inbuf = ''
-        self.isOpen = 1
-    #@-node:__init__
-    #@+node:send
-    def send(self, data):
-        """
-        Sends data to a stream connection
-        """
-        # barf if stream not open
-        if not self.isOpen:
-            raise I2PStreamClosed
-    
-        # can send
-        return self.client.samStreamSend(self, data)
-    #@-node:send
-    #@+node:recv
-    def recv(self, size):
-        """
-        Retrieves n bytes from peer
-        """
-        chunks = []
-    
-        while self.isOpen and size > 0:
-            # try internal buffer first
-            if self.inbuf:
-                chunk = self.inbuf[:size]
-                chunklen = len(chunk)
-                self.inbuf = self.inbuf[chunklen:]
-                chunks.append(chunk)
-                size -= chunklen
-            else:
-                # replenish input buffer
-                log(4, "I2PSAMStream.recv: replenishing input buffer")
-                buf = self.qIncomingData.get()
-                if buf == '':
-                    # connection closed by peer
-                    self.isOpen = 0
-                    break
-                else:
-                    # got more data
-                    log(4, "I2PSAMStream: queue returned %s" % repr(buf))
-                    self.inbuf += buf
-    
-        # return whatever we've got, hopefully all
-        return "".join(chunks)
-    
-    
-    #@-node:recv
-    #@+node:readline
-    def readline(self):
-        """
-        Read a line of text from stream, return the line without trailing newline
-        
-        This method really shouldn't exist in a class that's trying to look a bit
-        like a socket object, but what the hell!
-        """
-        chars = []
-        while 1:
-            char = self.recv(1)
-            if char in ['', '\n']:
-                break
-            chars.append(char)
-        return "".join(chars)
-    #@-node:readline
-    #@+node:close
-    def close(self):
-        """
-        close this stream connection
-        """
-        log(4, "closing stream")
-        self.client.samStreamClose(self)
-        log(4, "stream closed")
-        self.isOpen = 0
-    
-        # and just to make sure...
-        self.qIncomingData.put("") # busts out of recv() loops
-    
-    #@-node:close
-    #@+node:__del__
-    def __del__(self):
-        """
-        Dropping last ref to this object closes stream
-        """
-        self.close()
-    #@-node:__del__
-    #@+node:_notifyIncomingData
-    def _notifyIncomingData(self, data):
-        """
-        Called by client receiver to notify incoming data
-        """
-        log(4, "got %s" % repr(data))
-        self.qIncomingData.put(data)
-    #@-node:_notifyIncomingData
-    #@-others
-#@-node:class I2PSAMStream
-#@+node:class I2PRemoteSession
-class I2PRemoteSession:
-    """
-    DEPRECATED
-
-    Wrapper for I2CP connections
-    
-    Do not instantiate this directly - it gets created by
-    I2PSamClient.createSession()
-    """    
-    #@    @+others
-    #@+node:__init__
-    def __init__(self, client, dest):
-        """
-        Do not instantiate this directly
-        """
-        self.client = client
-        self.dest = dest
-    #@-node:__init__
-    #@+node:send
-    def send(self, peerdest, msg):
-        """
-        """
-        return self.client.send(self.dest, peerdest, msg)
-    #@-node:send
-    #@+node:recv
-    def receive(self):
-        
-        return self.client.receive(self.dest)
-    #@-node:recv
-    #@+node:destroy
-    def destroy(self):
-        
-        return self.client.destroySession(self.dest)
-    
-    #@-node:destroy
-    #@-others
-#@-node:class I2PRemoteSession
-#@+node:log
-def log(level, msg, nPrev=0):
-
-    # ignore messages that are too trivial for chosen verbosity
-    if level > verbosity:
-        return
-
-    loglock.acquire()
-    try:
-        # rip the stack
-        caller = traceback.extract_stack()[-(2+nPrev)]
-        path, line, func = caller[:3]
-        path = os.path.split(path)[1]
-        full = "%s:%s:%s():\n* %s" % (
-            path,
-            line,
-            func,
-            msg.replace("\n", "\n   + "))
-        now = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
-        msg = "%s %s\n" % (now, full)
-    
-        if logfile == sys.stdout:
-            print msg
-        else:
-            file(logfile, "a").write(msg+"\n")
-    except:
-        s = StringIO.StringIO()
-        traceback.print_exc(file=s)
-        print s.getvalue()
-        print "Logger crashed"
-    loglock.release()
-#@nonl
-#@-node:log
-#@+node:logException
-def logException(level, msg=''):
-    s = StringIO.StringIO()
-    traceback.print_exc(file=s)
-    log(level, "%s\n%s" % (s.getvalue(), msg), 1)
-#@-node:logException
-#@+node:demoNAMING
-def demoNAMING():
-    """
-    Demonstrates the NAMING service
-    """
-    print "Starting SAM NAMING demo..."
-    print
-
-    print "Instantiating client connection..."
-    c0 = I2PSamClient()
-    print "Client connection created"
-
-    for host in ['duck.i2p', 'nonexistent.i2p']:
-        print "Sending query for host '%s'..." % host
-        try:
-            res = c0.samNamingLookup(host)
-            print "query for %s returned:" % host
-            print repr(res)
-        except I2PCommandFail, e:
-            print "got exception: %s" % repr(e.args)
-    
-    print
-    print "---------------------------------"
-    print "NAMING service tests succeeded"
-    print "---------------------------------"
-    print
-
-
-#@-node:demoNAMING
-#@+node:demoRAW
-def demoRAW():
-    """
-    Runs a demo of SAM RAW messaging
-    """
-    print "Starting SAM RAW demo..."
-    print
-
-    print "Instantiating client connections..."
-    c1 = I2PSamClient()
-    c2 = I2PSamClient()
-
-    print "Creating dests via SAM"
-    pub1, priv1 = c1.samDestGenerate()
-    pub2, priv2 = c2.samDestGenerate()
-    print "SAM Dests generated ok"
-    
-    print "Creating SAM RAW SESSION on connection c1..."
-    res = c1.samSessionCreate("RAW", priv1)
-    if res != 'OK':
-        print "Failed to create session on connection c1: %s" % repr(res)
-        return
-    print "Session on connection c1 created successfully"
-
-    print "Creating SAM SESSION on connection c2..."
-    res = c2.samSessionCreate("RAW", priv2)
-    if res != 'OK':
-        print "Failed to create session on connection c2: %s" % repr(res)
-        return
-    print "Session on connection c2 created successfully"
-
-    msg = "Hi there!"
-    print "sending from c1 to c2: %s" % repr(msg)
-    c1.samRawSend(pub2, msg)
-
-    print "now try to receive from c2 (will block)..."
-    msg1 = c2.samRawReceive()
-    print "Connection c2 got %s" % repr(msg1)
-
-    print
-    print "---------------------------------"
-    print "RAW data transfer tests succeeded"
-    print "---------------------------------"
-    print
-
-#@-node:demoRAW
-#@+node:demoDATAGRAM
-def demoDATAGRAM():
-    """
-    Runs a demo of SAM DATAGRAM messaging
-    """
-    print "Starting SAM DATAGRAM demo..."
-    print
-
-    print "Instantiating 2 more client connections..."
-    c3 = I2PSamClient()
-    c4 = I2PSamClient()
-
-    print "Creating more dests via SAM"
-    pub3, priv3 = c3.samDestGenerate()
-    pub4, priv4 = c4.samDestGenerate()
-
-    print "Creating SAM DATAGRAM SESSION on connection c3..."
-    res = c3.samSessionCreate("DATAGRAM", priv3)
-    if res != 'OK':
-        print "Failed to create DATAGRAM session on connection c3: %s" % repr(res)
-        return
-    print "DATAGRAM Session on connection c3 created successfully"
-
-    print "Creating SAM DATAGRAM SESSION on connection c4..."
-    res = c4.samSessionCreate("DATAGRAM", priv4)
-    if res != 'OK':
-        print "Failed to create DATAGRAM session on connection c4: %s" % repr(res)
-        return
-    print "Session on connection c4 created successfully"
-
-    msg = "Hi there, this is a datagram!"
-    print "sending from c3 to c4: %s" % repr(msg)
-    c3.samDatagramSend(pub4, msg)
-
-    print "now try to receive from c4 (will block)..."
-    remdest, msg1 = c4.samDatagramReceive()
-    print "Connection c4 got %s from %s..." % (repr(msg1), repr(remdest))
-
-
-    print
-    print "--------------------------------------"
-    print "DATAGRAM data transfer tests succeeded"
-    print "--------------------------------------"
-    print
-
-#@-node:demoDATAGRAM
-#@+node:demoSTREAM
-def demoSTREAM():
-    """
-    Runs a demo of SAM STREAM messaging
-    """
-    print "Starting SAM STREAM demo..."
-    print
-
-    print "Instantiating client c6..."
-    c6 = I2PSamClient()
-
-    print "Creating dest for c6"
-    pub6, priv6 = c6.samDestGenerate()
-
-    print "Creating SAM STREAM SESSION on connection c6..."
-    res = c6.samSessionCreate("STREAM", priv6, direction="RECEIVE")
-    if res != 'OK':
-        print "Failed to create STREAM session on connection c6: %s" % repr(res)
-        return
-    print "STREAM Session on connection c6 created successfully"
-
-    print "Launching acceptor thread..."
-    thread.start_new_thread(demoSTREAM_thread, (c6,))
-
-    #print "sleep a while and give the server a chance..."
-    #time.sleep(10)
-
-    print "----------------------------------------"
-
-    print "Instantiating client c5..."
-    c5 = I2PSamClient()
-
-    print "Creating dest for c5"
-    pub5, priv5 = c5.samDestGenerate()
-
-    print "Creating SAM STREAM SESSION on connection c5..."
-    res = c5.samSessionCreate("STREAM", priv5, direction="CREATE")
-    if res != 'OK':
-        print "Failed to create STREAM session on connection c5: %s" % repr(res)
-        return
-    print "STREAM Session on connection c5 created successfully"
-
-    print "----------------------------------------"
-
-    print "Making connection from c5 to c6..."
-
-    #set_trace()
-
-    try:
-        conn_c5 = c5.samStreamConnect(pub6)
-    except:
-        print "Stream Connection failed"
-        return
-    print "Stream connect succeeded"
-    
-    print "Receiving from c5..."
-    buf = conn_c5.readline()
-    print "Got %s" % repr(buf)
-
-    #print "Try to accept connection on c6..."
-    #conn_c6 = c6.sam
-
-    print
-    print "--------------------------------------"
-    print "DATAGRAM data transfer tests succeeded"
-    print "--------------------------------------"
-    print
-
-
-
-
-
-#@-node:demoSTREAM
-#@+node:demoSTREAM_thread
-def demoSTREAM_thread(sess):
-    
-    while 1:
-        sock = sess.samStreamAccept()
-        log(4, "got incoming connection")
-
-        print "**ACCEPTOR SLEEPING 10 secs BEFORE SENDING"
-        
-        time.sleep(10)
-
-        sock.send("Hi there, what do you want?\n")
-
-        print "**ACCEPTOR SLEEPING 5 MINS BEFORE CLOSING"
-        time.sleep(300)
-        print "**ACCEPTOR CLOSING STREAM"
-
-        sock.close()
-
-#@-node:demoSTREAM_thread
-#@+node:demo
-def demo():
-    """
-    This is a simple and straightforward demo of talking to
-    the i2psam server socket via the I2PSamClient class.
-    
-    Read the source, Luke, it's never been so easy...
-    """
-    print
-    print "-----------------------------------------"
-    print "Running i2psamclient demo..."
-    print "-----------------------------------------"
-    print
-
-    #demoNAMING()
-    #demoRAW()
-    #demoDATAGRAM()
-    demoSTREAM()
-
-    print
-    print "-----------------------------------------"
-    print "Demo Finished"
-    print "-----------------------------------------"
-
-    return
-#@-node:demo
-#@+node:MAINLINE
-if __name__ == '__main__':
-
-    demo()
-#@-node:MAINLINE
-#@-others
-
-#@-node:@file python/src/i2psamclient.py
-#@-leo
-- 
GitLab