2005-01-23 smeghead

* Port the java SAM client library to mono/C# and released into the
      public domain.  The 0.1 version of this port is available in CVS as
      i2p/apps/sam/csharp/src/I2P.SAM.Client.  The other nonfunctional C#
      library has been removed.
This commit is contained in:
jrandom
2005-01-23 08:22:11 +00:00
committed by zzz
parent 14023163b3
commit d86e2c0f59
12 changed files with 482 additions and 440 deletions

View File

@ -0,0 +1,32 @@
using System.Reflection;
using System.Runtime.CompilerServices;
// Information about this assembly is defined by the following
// attributes.
//
// change them to the information which is associated with the assembly
// you compile.
[assembly: AssemblyTitle("sam-sharp")]
[assembly: AssemblyDescription("Mono/.NET SAM client for I2P")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("sam-sharp")]
[assembly: AssemblyCopyright("Released into the Public Domain")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// The assembly version has following format :
//
// Major.Minor.Build.Revision
//
// You can specify all values by your own or you can build default build and revision
// numbers with the '*' character (the default):
[assembly: AssemblyVersion("0.1")]
// The following attributes specify the key for the sign of your assembly. See the
// .NET Framework documentation for more information about signing.
// This is not required, if you don't want signing let these attributes like they're.
[assembly: AssemblyDelaySign(false)]
[assembly: AssemblyKeyFile("")]

View File

@ -0,0 +1,26 @@
namespace I2P.SAM.Client
{
public struct SamBridgeMessages
{
public const string NAMING_REPLY_INVALID_KEY = "INVALID_KEY";
public const string NAMING_REPLY_KEY_NOT_FOUND = "KEY_NOT_FOUND";
public const string NAMING_REPLY_OK = "OK";
public const string SESSION_STATUS_DUPLICATE_DEST = "DUPLICATE_DEST";
public const string SESSION_STATUS_I2P_ERROR = "I2P_ERROR";
public const string SESSION_STATUS_INVALID_KEY = "INVALID_KEY";
public const string SESSION_STATUS_OK = "OK";
public const string STREAM_CLOSED_CANT_REACH_PEER = "CANT_REACH_PEER";
public const string STREAM_CLOSED_I2P_ERROR = "I2P_ERROR";
public const string STREAM_CLOSED_PEER_NOT_FOUND = "PEER_NOT_FOUND";
public const string STREAM_CLOSED_TIMEOUT = "CLOSED";
public const string STREAM_CLOSED_OK = "OK";
public const string STREAM_STATUS_CANT_REACH_PEER = "CANT_REACH_PEER";
public const string STREAM_STATUS_I2P_ERROR = "I2P_ERROR";
public const string STREAM_STATUS_INVALID_KEY = "INVALID_KEY";
public const string STREAM_STATUS_TIMEOUT = "TIMEOUT";
public const string STREAM_STATUS_OK = "OK";
}
}

View File

@ -0,0 +1,20 @@
using System.Collections.Specialized;
namespace I2P.SAM.Client
{
/// <summary>
/// Async event notification interface for SAM clients.
/// </summary>
public interface SamClientEventListener
{
void HelloReplyReceived(bool ok);
void SessionStatusReceived(string result, string destination, string message);
void StreamStatusReceived(string result, int id, string message);
void StreamConnectedReceived(string remoteDestination, int id);
void StreamClosedReceived(string result, int id, string message);
void StreamDataReceived(int id, byte[] data, int offset, int length);
void NamingReplyReceived(string name, string result, string valueString, string message);
void DestReplyReceived(string publicKey, string privateKey);
void UnknownMessageReceived(string major, string minor, NameValueCollection parameters);
}
}

View File

@ -0,0 +1,20 @@
using System.Collections.Specialized;
namespace I2P.SAM.Client
{
/// <summary>
/// Basic noop client event listener.
/// </summary>
public class SamClientEventListenerImpl : SamClientEventListener
{
public virtual void DestReplyReceived(string publicKey, string privateKey) {}
public virtual void HelloReplyReceived(bool ok) {}
public virtual void NamingReplyReceived(string name, string result, string valueString, string message) {}
public virtual void SessionStatusReceived(string result, string destination, string message) {}
public virtual void StreamClosedReceived(string result, int id, string message) {}
public virtual void StreamConnectedReceived(string remoteDestination, int id) {}
public virtual void StreamDataReceived(int id, byte[] data, int offset, int length) {}
public virtual void StreamStatusReceived(string result, int id, string message) {}
public virtual void UnknownMessageReceived(string major, string minor, NameValueCollection parameters) {}
}
}

View File

@ -0,0 +1,116 @@
using System;
using System.Collections.Specialized;
using System.Threading;
namespace I2P.SAM.Client
{
/// <summary>
/// Simple helper implementation of a SamClientEventListener.
/// </summary>
public class SamEventHandler : SamClientEventListenerImpl
{
private object _helloLock = new Object();
private String _helloOk;
private NameValueCollection _namingReplies = new NameValueCollection();
private object _namingReplyLock = new Object();
private object _sessionCreateLock = new Object();
private String _sessionCreateOk;
public override void HelloReplyReceived(bool ok) {
lock (_helloLock) {
if (ok)
_helloOk = Boolean.TrueString;
else
_helloOk = Boolean.FalseString;
Monitor.PulseAll(_helloLock);
}
}
public override void NamingReplyReceived(string name, string result, string valueString, string message) {
lock (_namingReplyLock) {
if (result.Equals(SamBridgeMessages.NAMING_REPLY_OK))
_namingReplies.Add(name, valueString);
else
_namingReplies.Add(name, result);
Monitor.PulseAll(_namingReplyLock);
}
}
public override void SessionStatusReceived(string result, string destination, string message) {
lock (_sessionCreateLock) {
if (result.Equals(SamBridgeMessages.SESSION_STATUS_OK))
_sessionCreateOk = Boolean.TrueString;
else
_sessionCreateOk = Boolean.FalseString;
Monitor.PulseAll(_sessionCreateLock);
}
}
public override void UnknownMessageReceived(string major, string minor, NameValueCollection parameters) {
Console.WriteLine("wrt, [" + major + "] [" + minor + "] [" + parameters + "]");
}
/*
* Blocking lookup calls below.
*/
/// <summary>
/// Wait for the connection to be established, returning true if
/// everything went ok.
/// </summary>
public bool WaitForHelloReply() {
while (true) {
lock (_helloLock) {
if (_helloOk == null)
Monitor.Wait(_helloLock);
else
return Boolean.Parse(_helloOk);
}
}
}
/// <summary>
/// Return the destination found matching the name, or <c>null</c> if
/// the key was not able to be retrieved.
/// </summary>
/// <param name="name">The name to be looked for, or "ME".</param>
public string WaitForNamingReply(string name) {
while (true) {
lock (_namingReplyLock) {
try {
string valueString = _namingReplies[name];
_namingReplies.Remove(name);
if (valueString.Equals(SamBridgeMessages.NAMING_REPLY_INVALID_KEY))
return null;
else if (valueString.Equals(SamBridgeMessages.NAMING_REPLY_KEY_NOT_FOUND))
return null;
else
return valueString;
} catch (ArgumentNullException ane) {
Monitor.Wait(_namingReplyLock);
}
}
}
}
/// <summary>
/// Wait for the session to be created, returning true if everything
/// went ok.
/// </summary>
public bool WaitForSessionCreateReply() {
while (true) {
lock (_sessionCreateLock) {
if (_sessionCreateOk == null)
Monitor.Wait(_sessionCreateLock);
else
return Boolean.Parse(_sessionCreateOk);
}
}
}
}
}

View File

@ -0,0 +1,259 @@
using System;
using System.Collections;
using System.Collections.Specialized;
using System.IO;
using System.Net.Sockets;
using System.Text;
using System.Threading;
namespace I2P.SAM.Client
{
/// <summary>
/// Read from a socket, producing events for any SAM message read.
/// </summary>
public class SamReader
{
private bool _isLive;
private SamClientEventListener _listener;
private NetworkStream _samStream;
private StreamReader _streamReader;
public SamReader(NetworkStream samStream, SamClientEventListener listener) {
_samStream = samStream;
_listener = listener;
}
public void RunThread() {
NameValueCollection parameters = new NameValueCollection();
while (_isLive) {
string line = null;
_streamReader = new StreamReader(_samStream);
try {
line = _streamReader.ReadLine();
_streamReader.Close();
} catch (IOException ioe) {
Console.Error.WriteLine("Error reading from SAM: {1}", ioe);
} catch (OutOfMemoryException oome) {
Console.Error.WriteLine("Out of memory while reading from SAM: {1}", oome);
return;
}
if (line == null) {
break;
}
string[] tokens = line.Split(new char[1] { ' ' });
if (tokens.Length < 2) {
Console.Error.WriteLine("Invalid SAM line: [" + line + "]");
_isLive = false;
return;
}
IEnumerator tokensEnumerator = tokens.GetEnumerator();
tokensEnumerator.MoveNext();
string major = (string) tokensEnumerator.Current;
tokensEnumerator.MoveNext();
string minor = (string) tokensEnumerator.Current;
parameters.Clear();
while (tokensEnumerator.MoveNext()) {
string pair = (string) tokensEnumerator.Current;
int equalsPosition = pair.IndexOf('=');
if ( (equalsPosition > 0) && (equalsPosition < pair.Length - 1) ) {
string name = pair.Substring(0, equalsPosition);
string valueString = pair.Substring(equalsPosition + 1);
while ( (valueString[0] == '\"') && (valueString.Length > 0) )
valueString = valueString.Substring(1);
while ( (valueString.Length > 0) && (valueString[valueString.Length - 1] == '\"') )
valueString = valueString.Substring(0, valueString.Length - 1);
parameters.Set(name, valueString);
}
}
ProcessEvent(major, minor, parameters);
}
}
private void ProcessEvent(string major, string minor, NameValueCollection parameters) {
switch (major)
{
case "HELLO" :
if (minor.Equals("REPLY")) {
string result = parameters.Get("RESULT");
if (result.Equals("OK"))
_listener.HelloReplyReceived(true);
else
_listener.HelloReplyReceived(false);
} else {
_listener.UnknownMessageReceived(major, minor, parameters);
}
break;
case "SESSION" :
if (minor.Equals("STATUS")) {
string result = parameters.Get("RESULT");
string destination = parameters.Get("DESTINATION");
string message = parameters.Get("MESSAGE");
_listener.SessionStatusReceived(result, destination, message);
} else {
_listener.UnknownMessageReceived(major, minor, parameters);
}
break;
case "STREAM" :
ProcessStream(major, minor, parameters);
break;
case "NAMING" :
if (minor.Equals("REPLY")) {
string name = parameters.Get("NAME");
string result = parameters.Get("RESULT");
string valueString = parameters.Get("VALUE");
string message = parameters.Get("MESSAGE");
_listener.NamingReplyReceived(name, result, valueString, message);
} else {
_listener.UnknownMessageReceived(major, minor, parameters);
}
break;
case "DEST" :
if (minor.Equals("REPLY")) {
string pub = parameters.Get("PUB");
string priv = parameters.Get("PRIV");
_listener.DestReplyReceived(pub, priv);
} else {
_listener.UnknownMessageReceived(major, minor, parameters);
}
break;
default :
_listener.UnknownMessageReceived(major, minor, parameters);
break;
}
}
private void ProcessStream(string major, string minor, NameValueCollection parameters) {
/*
* Would use another tidy switch() statement here but the Mono
* compiler presently gets variable scopes confused within nested
* switch() contexts. Broken with Mono/mcs 1.0.5, 1.1.3, and SVN
* head.
*/
if (minor.Equals("STATUS")) {
string result = parameters.Get("RESULT");
string id = parameters.Get("ID");
string message = parameters.Get("MESSAGE");
try {
_listener.StreamStatusReceived(result, Int32.Parse(id), message);
} catch {
_listener.UnknownMessageReceived(major, minor, parameters);
}
} else if (minor.Equals("CONNECTED")) {
string destination = parameters.Get("DESTINATION");
string id = parameters.Get("ID");
try {
_listener.StreamConnectedReceived(destination, Int32.Parse(id));
} catch {
_listener.UnknownMessageReceived(major, minor, parameters);
}
} else if (minor.Equals("CLOSED")) {
string result = parameters.Get("RESULT");
string id = parameters.Get("ID");
string message = parameters.Get("MESSAGE");
try {
_listener.StreamClosedReceived(result, Int32.Parse(id), message);
} catch {
_listener.UnknownMessageReceived(major, minor, parameters);
}
} else if (minor.Equals("RECEIVED")) {
string id = parameters.Get("ID");
string size = parameters.Get("SIZE");
if (id != null) {
try {
int idValue = Int32.Parse(id);
int sizeValue = Int32.Parse(size);
byte[] data = new byte[sizeValue];
int bytesRead;
try {
bytesRead = _samStream.Read(data, 0, sizeValue);
if (bytesRead != sizeValue) {
_listener.UnknownMessageReceived(major, minor, parameters);
return;
}
} catch {
_isLive = false;
_listener.UnknownMessageReceived(major, minor, parameters);
}
_listener.StreamDataReceived(idValue, data, 0, sizeValue);
} catch (FormatException fe) {
_listener.UnknownMessageReceived(major, minor, parameters);
}
} else {
_listener.UnknownMessageReceived(major, minor, parameters);
}
} else {
_listener.UnknownMessageReceived(major, minor, parameters);
}
}
public void StartReading() {
_isLive = true;
ThreadStart threadStart = new ThreadStart(RunThread);
Thread thread = new Thread(threadStart);
thread.Name = "SAM Reader";
thread.Start();
}
public void StopReading() {
_isLive = false;
}
}
}