diff --git a/apps/sam/csharp/src/I2P.SAM.Client/README b/apps/sam/csharp/src/I2P.SAM.Client/README new file mode 100644 index 000000000..9fc1ca6f6 --- /dev/null +++ b/apps/sam/csharp/src/I2P.SAM.Client/README @@ -0,0 +1,84 @@ +sam-sharp + + +DESCRIPTION + + sam-sharp is a .NET SAM client library for I2P written in C#. It aims to be + compatible with platforms that implement .NET's base class library API (such + as Mono and DotGNU Portable.NET) and to be usable from all languages + conforming to the ECMA-standardized Common Language Infrastructure, including + C# and VB.NET. + + +MINIMUM REQUIREMENTS + + * Mono and mcs 1.0 or higher (Linux, Mac OS X, Windows) + + - or - + + MS .NET Framework SDK 1.0 or higher (Windows) + + - or - + + DotGNU Portable .NET, latest version recommended (*BSD, AIX, Cygwin, Linux, + Mac OS X, MinGW, Solaris) + + +OPTIONAL REQUIREMENTS + + * NAnt 0.85 or higher is needed to use sam-sharp.build. Sorry, NAnt does not + yet support Portable.NET. + + * NUnit 2.2.1 or later is needed to run the (soon-to-be-added) unit tests. If + you have the Mono mcs package installed then you already have NUnit. + + +DOCUMENTATION + + Pre-generated docs will be submitted to CVS soon. In the meantime you may + generate standalone documentation from the embedded XML doc comments by + issuing the following commands from sam-sharp's src/ directory: + + Mono: + + mkdir ../doc + mcs -doc:../doc/sam-sharp_doc.xml *.cs + + MS .NET: + + mkdir ../doc + csc /doc:../doc/sam-sharp_doc.xml *.cs + + DotGNU Portable.NET: + + mkdir ../doc + csdoc -o ../doc/sam-sharp_doc.xml *.cs + + The resulting XML doc can be converted to HTML using either Visual Studio .NET + (Tools > Build Comment Web Pages) or Portable.NET's csdoc2html tool: + + csdoc2html -o ../doc ../doc/sam-sharp_doc.xml + + +ACKNOWLEDGMENTS + + sam-sharp is a port of jrandom's public domain Java SAM client library. + + +LICENSE + + This work is released into the public domain. + + +AUTHORS + + jrandom (original Java SAM client library) + smeghead (C# port of the Java SAM client library) + + +MAINTAINERS + + smeghead + + +$Id$ diff --git a/apps/sam/csharp/src/I2P.SAM.Client/SamBaseEventHandler.cs b/apps/sam/csharp/src/I2P.SAM.Client/SamBaseEventHandler.cs new file mode 100644 index 000000000..f8d0d766c --- /dev/null +++ b/apps/sam/csharp/src/I2P.SAM.Client/SamBaseEventHandler.cs @@ -0,0 +1,160 @@ +using System; +using System.Collections.Specialized; +using System.Threading; + +namespace I2P.SAM.Client +{ + /// + /// Optional base class providing basic SAM event handling and helper + /// methods. + /// + public class SamBaseEventHandler + { + private object _helloLock = new Object(); + private string _helloOk; + private NameValueCollection _namingReplies = new NameValueCollection(); + private object _namingReplyLock = new Object(); + private SamReader _samReader; + private object _sessionCreateLock = new Object(); + private string _sessionCreateOk; + + /// + /// Creates a new SamBaseEventHandler instance and registers + /// overridable handler methods for all events generated by the given + /// SamReader. + /// + public SamBaseEventHandler(SamReader samReader) { + _samReader = samReader; + _samReader.DestReplyReceived += new DestReplyReceivedHandler(OnDestReplyReceived); + _samReader.HelloReplyReceived += new HelloReplyReceivedHandler(OnHelloReplyReceived); + _samReader.NamingReplyReceived += new NamingReplyReceivedHandler(OnNamingReplyReceived); + _samReader.SessionStatusReceived += new SessionStatusReceivedHandler(OnSessionStatusReceived); + _samReader.StreamClosedReceived += new StreamClosedReceivedHandler(OnStreamClosedReceived); + _samReader.StreamConnectedReceived += new StreamConnectedReceivedHandler(OnStreamConnectedReceived); + _samReader.StreamDataReceived += new StreamDataReceivedHandler(OnStreamDataReceived); + _samReader.StreamStatusReceived += new StreamStatusReceivedHandler(OnStreamStatusReceived); + _samReader.UnknownMessageReceived += new UnknownMessageReceivedHandler(OnUnknownMessageReceived); + } + + public virtual void OnDestReplyReceived(string publicKey, string privateKey) { + } + + public virtual void OnHelloReplyReceived(bool ok) { + lock (_helloLock) { + if (ok) + _helloOk = Boolean.TrueString; + else + _helloOk = Boolean.FalseString; + + Monitor.PulseAll(_helloLock); + } + } + + public virtual void OnNamingReplyReceived(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 virtual void OnSessionStatusReceived(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 virtual void OnStreamClosedReceived(string result, int id, string message) { + } + + public virtual void OnStreamConnectedReceived(string remoteDestination, int id) { + } + + public virtual void OnStreamDataReceived(int id, byte[] data, int offset, int length) { + } + + public virtual void OnStreamStatusReceived(string result, int id, string message) { + } + + public virtual void OnUnknownMessageReceived(string major, string minor, NameValueCollection parameters) { + Console.WriteLine("wrt, [" + major + "] [" + minor + "] [" + parameters + "]"); + } + + // Helper methods below. + + /// + /// Waits for a SAM connection to be established. + /// + /// + /// This method blocks until the connection is established. + /// + /// true if the handshake was successful. + public virtual bool WaitForHelloReply() { + while (true) { + lock (_helloLock) { + if (_helloOk == null) + Monitor.Wait(_helloLock); + else + return Boolean.Parse(_helloOk); + } + } + } + + /// + /// Waits for a SAM naming reply message. + /// + /// + /// This method blocks until all naming replies are received. + /// + /// The name to be looked for, or ME. + /// The matching destination for name, or null if + /// the key was not able to be retrieved. + public virtual 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); + } + } + } + } + + /// + /// Waits for a SAM session to be created. + /// + /// + /// This method blocks until a SAM session is created. + /// + /// true if the SAM session was created successfully. + // + public virtual bool WaitForSessionCreateReply() { + while (true) { + lock (_sessionCreateLock) { + if (_sessionCreateOk == null) + Monitor.Wait(_sessionCreateLock); + else + return Boolean.Parse(_sessionCreateOk); + } + } + } + } +} \ No newline at end of file diff --git a/apps/sam/csharp/src/I2P.SAM.Client/SamClientEventListener.cs b/apps/sam/csharp/src/I2P.SAM.Client/SamClientEventListener.cs deleted file mode 100644 index ab6459d33..000000000 --- a/apps/sam/csharp/src/I2P.SAM.Client/SamClientEventListener.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.Collections.Specialized; - -namespace I2P.SAM.Client -{ - /// - /// Async event notification interface for SAM clients. - /// - 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); - } -} \ No newline at end of file diff --git a/apps/sam/csharp/src/I2P.SAM.Client/SamClientEventListenerImpl.cs b/apps/sam/csharp/src/I2P.SAM.Client/SamClientEventListenerImpl.cs deleted file mode 100644 index edd304558..000000000 --- a/apps/sam/csharp/src/I2P.SAM.Client/SamClientEventListenerImpl.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.Collections.Specialized; - -namespace I2P.SAM.Client -{ - /// - /// Basic noop client event listener. - /// - 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) {} - } -} diff --git a/apps/sam/csharp/src/I2P.SAM.Client/SamEventHandler.cs b/apps/sam/csharp/src/I2P.SAM.Client/SamEventHandler.cs deleted file mode 100644 index 0dfaf19c5..000000000 --- a/apps/sam/csharp/src/I2P.SAM.Client/SamEventHandler.cs +++ /dev/null @@ -1,116 +0,0 @@ -using System; -using System.Collections.Specialized; -using System.Threading; - -namespace I2P.SAM.Client -{ - /// - /// Simple helper implementation of a SamClientEventListener. - /// - 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. - */ - - /// - /// Wait for the connection to be established, returning true if - /// everything went ok. - /// - public bool WaitForHelloReply() { - while (true) { - lock (_helloLock) { - if (_helloOk == null) - Monitor.Wait(_helloLock); - else - return Boolean.Parse(_helloOk); - } - } - } - - /// - /// Return the destination found matching the name, or null if - /// the key was not able to be retrieved. - /// - /// The name to be looked for, or "ME". - 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); - } - } - } - } - - /// - /// Wait for the session to be created, returning true if everything - /// went ok. - /// - public bool WaitForSessionCreateReply() { - while (true) { - lock (_sessionCreateLock) { - if (_sessionCreateOk == null) - Monitor.Wait(_sessionCreateLock); - else - return Boolean.Parse(_sessionCreateOk); - } - } - } - } -} \ No newline at end of file diff --git a/apps/sam/csharp/src/I2P.SAM.Client/SamReader.cs b/apps/sam/csharp/src/I2P.SAM.Client/SamReader.cs index ac99d7d29..c90a70f72 100644 --- a/apps/sam/csharp/src/I2P.SAM.Client/SamReader.cs +++ b/apps/sam/csharp/src/I2P.SAM.Client/SamReader.cs @@ -8,19 +8,37 @@ using System.Threading; namespace I2P.SAM.Client { + public delegate void DestReplyReceivedHandler(string publicKey, string privateKey); + public delegate void HelloReplyReceivedHandler(bool ok); + public delegate void NamingReplyReceivedHandler(string name, string result, string valueString, string message); + public delegate void SessionStatusReceivedHandler(string result, string destination, string message); + public delegate void StreamClosedReceivedHandler(string result, int id, string message); + public delegate void StreamConnectedReceivedHandler(string remoteDestination, int id); + public delegate void StreamDataReceivedHandler(int id, byte[] data, int offset, int length); + public delegate void StreamStatusReceivedHandler(string result, int id, string message); + public delegate void UnknownMessageReceivedHandler(string major, string minor, NameValueCollection parameters); + /// - /// Read from a socket, producing events for any SAM message read. + /// Reads from a socket stream, producing events for any SAM message read. /// public class SamReader { - private bool _isLive; - private SamClientEventListener _listener; - private NetworkStream _samStream; - private StreamReader _streamReader; + public event DestReplyReceivedHandler DestReplyReceived; + public event HelloReplyReceivedHandler HelloReplyReceived; + public event NamingReplyReceivedHandler NamingReplyReceived; + public event SessionStatusReceivedHandler SessionStatusReceived; + public event StreamClosedReceivedHandler StreamClosedReceived; + public event StreamConnectedReceivedHandler StreamConnectedReceived; + public event StreamDataReceivedHandler StreamDataReceived; + public event StreamStatusReceivedHandler StreamStatusReceived; + public event UnknownMessageReceivedHandler UnknownMessageReceived; - public SamReader(NetworkStream samStream, SamClientEventListener listener) { + private bool _isLive; + private NetworkStream _samStream; + private StreamReader _samStreamReader; + + public SamReader(NetworkStream samStream) { _samStream = samStream; - _listener = listener; } public void RunThread() { @@ -31,11 +49,11 @@ namespace I2P.SAM.Client string line = null; - _streamReader = new StreamReader(_samStream); + _samStreamReader = new StreamReader(_samStream); try { - line = _streamReader.ReadLine(); - _streamReader.Close(); + line = _samStreamReader.ReadLine(); + _samStreamReader.Close(); } catch (IOException ioe) { Console.Error.WriteLine("Error reading from SAM: {1}", ioe); } catch (OutOfMemoryException oome) { @@ -98,11 +116,11 @@ namespace I2P.SAM.Client string result = parameters.Get("RESULT"); if (result.Equals("OK")) - _listener.HelloReplyReceived(true); + HelloReplyReceived(true); else - _listener.HelloReplyReceived(false); + HelloReplyReceived(false); } else { - _listener.UnknownMessageReceived(major, minor, parameters); + UnknownMessageReceived(major, minor, parameters); } break; @@ -115,9 +133,9 @@ namespace I2P.SAM.Client string destination = parameters.Get("DESTINATION"); string message = parameters.Get("MESSAGE"); - _listener.SessionStatusReceived(result, destination, message); + SessionStatusReceived(result, destination, message); } else { - _listener.UnknownMessageReceived(major, minor, parameters); + UnknownMessageReceived(major, minor, parameters); } break; @@ -136,9 +154,9 @@ namespace I2P.SAM.Client string valueString = parameters.Get("VALUE"); string message = parameters.Get("MESSAGE"); - _listener.NamingReplyReceived(name, result, valueString, message); + NamingReplyReceived(name, result, valueString, message); } else { - _listener.UnknownMessageReceived(major, minor, parameters); + UnknownMessageReceived(major, minor, parameters); } break; @@ -150,16 +168,16 @@ namespace I2P.SAM.Client string pub = parameters.Get("PUB"); string priv = parameters.Get("PRIV"); - _listener.DestReplyReceived(pub, priv); + DestReplyReceived(pub, priv); } else { - _listener.UnknownMessageReceived(major, minor, parameters); + UnknownMessageReceived(major, minor, parameters); } break; default : - _listener.UnknownMessageReceived(major, minor, parameters); + UnknownMessageReceived(major, minor, parameters); break; } } @@ -169,8 +187,8 @@ namespace I2P.SAM.Client /* * 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. + * switch() contexts. Nested switch() is broken with Mono/mcs 1.0.5, + * 1.1.3, and SVN head. */ if (minor.Equals("STATUS")) { @@ -179,9 +197,9 @@ namespace I2P.SAM.Client string message = parameters.Get("MESSAGE"); try { - _listener.StreamStatusReceived(result, Int32.Parse(id), message); + StreamStatusReceived(result, Int32.Parse(id), message); } catch { - _listener.UnknownMessageReceived(major, minor, parameters); + UnknownMessageReceived(major, minor, parameters); } } else if (minor.Equals("CONNECTED")) { @@ -190,9 +208,9 @@ namespace I2P.SAM.Client string id = parameters.Get("ID"); try { - _listener.StreamConnectedReceived(destination, Int32.Parse(id)); + StreamConnectedReceived(destination, Int32.Parse(id)); } catch { - _listener.UnknownMessageReceived(major, minor, parameters); + UnknownMessageReceived(major, minor, parameters); } } else if (minor.Equals("CLOSED")) { @@ -202,9 +220,9 @@ namespace I2P.SAM.Client string message = parameters.Get("MESSAGE"); try { - _listener.StreamClosedReceived(result, Int32.Parse(id), message); + StreamClosedReceived(result, Int32.Parse(id), message); } catch { - _listener.UnknownMessageReceived(major, minor, parameters); + UnknownMessageReceived(major, minor, parameters); } } else if (minor.Equals("RECEIVED")) { @@ -224,23 +242,24 @@ namespace I2P.SAM.Client bytesRead = _samStream.Read(data, 0, sizeValue); if (bytesRead != sizeValue) { - _listener.UnknownMessageReceived(major, minor, parameters); + UnknownMessageReceived(major, minor, parameters); return; } } catch { _isLive = false; - _listener.UnknownMessageReceived(major, minor, parameters); + UnknownMessageReceived(major, minor, parameters); + return; } - _listener.StreamDataReceived(idValue, data, 0, sizeValue); + StreamDataReceived(idValue, data, 0, sizeValue); } catch (FormatException fe) { - _listener.UnknownMessageReceived(major, minor, parameters); + UnknownMessageReceived(major, minor, parameters); } } else { - _listener.UnknownMessageReceived(major, minor, parameters); + UnknownMessageReceived(major, minor, parameters); } } else { - _listener.UnknownMessageReceived(major, minor, parameters); + UnknownMessageReceived(major, minor, parameters); } } diff --git a/apps/sam/csharp/src/I2P.SAM.Client/sam-sharp.build b/apps/sam/csharp/src/I2P.SAM.Client/sam-sharp.build new file mode 100644 index 000000000..73f9dd347 --- /dev/null +++ b/apps/sam/csharp/src/I2P.SAM.Client/sam-sharp.build @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/history.txt b/history.txt index 0202d0364..f245931f3 100644 --- a/history.txt +++ b/history.txt @@ -1,4 +1,10 @@ -$Id: history.txt,v 1.131 2005/01/21 20:53:02 jrandom Exp $ +$Id: history.txt,v 1.132 2005/01/23 03:22:11 jrandom Exp $ + +2005-01-24 smeghead + * C#-ification of sam-sharp: interface greatly simplified using delegates + and events; SamBaseEventHandler provides basic implementation and helper + methods but is now optional. + * NAnt buildfile and README added for sam-sharp. 2005-01-23 smeghead * Port the java SAM client library to mono/C# and released into the