[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

[or-cvs] Add C# controller lib port from Oliver Rau.



Update of /home/or/cvsroot/control/cs/control/ControlConnectionClasses
In directory moria:/tmp/cvs-serv5623/control/ControlConnectionClasses

Added Files:
	Delegates.cs ITorControlConnection.cs TorControl.cs 
	TorControlConnection0.cs TorControlConnection1.cs 
	TorControlConnectionBase.cs TorControlConnectionFactory.cs 
Log Message:
Add C# controller lib port from Oliver Rau.

--- NEW FILE: Delegates.cs ---
/*
 * File Delegates.cs
 * 
 * Copyright (C) 2005 Oliver Rau (olra0001@xxxxxxxxxxxxxxxxxxx)
 * 
 * See LICENSE file for copying information 
 * 
 * Created on 08.08.2005 20:37
 * 
 * $Id: Delegates.cs,v 1.1 2005/11/09 21:47:04 nickm Exp $
 */

using System;
using System.Collections;

namespace Tor.Control
{
	#region Delegates
	/// <summary>
	/// Represents the method that will handle the CiruitStatus event.
	/// </summary>
	/// <param name="sender">The source of the event.</param>
	/// <param name="e">An <see cref="CircuitStatusEventArgs">CircuitStatusEventArgs</see> that contains
	/// the event data.
	/// </param>
	public delegate void CircuitStatusEventHandler (object sender, CircuitStatusEventArgs  e);
	/// <summary>
	/// Represents the method that will handle the StreamStatus event.
	/// </summary>
	/// <param name="sender">The source of the event.</param>
	/// <param name="e">An <see cref="StreamStatusEventArgs">StreamStatusEventArgs</see> that contains
	/// the event data.</param>
	public delegate void StreamStatusEventHandler  (object sender, StreamStatusEventArgs   e);
	/// <summary>
	/// Represents the method that will handle the OrConnStatus event.
	/// </summary>
	/// <param name="sender">The source of the event.</param>
	/// <param name="e">An <see cref="OrConnStatusEventArgs">OrConnStatusEventArgs</see> that contains
	/// the event data.</param>
	public delegate void OrConnStatusEventHandler  (object sender, OrConnStatusEventArgs   e);
	/// <summary>
	/// Represents the method that will handle the BandwidthUsed event.
	/// </summary>
	/// <param name="sender">The source of the event.</param>
	/// <param name="e">An <see cref="BandwidthUsedEventArgs">BandwidthUsedEventArgs</see> that contains
	/// the event data.</param>
	public delegate void BandwidthUsedEventHandler (object sender, BandwidthUsedEventArgs  e);
	/// <summary>
	/// Represents the method that will handle the NewDescriptors event.
	/// </summary>
	/// <param name="sender">The source of the event.</param>
	/// <param name="e">An <see cref="NewDescriptorsEventArgs">NewDescriptorsEventArgs</see> that contains
	/// the event data.</param>
	public delegate void NewDescriptorsEventHandler(object sender, NewDescriptorsEventArgs e);
	/// <summary>
	/// Represents the method that will handle the Message event.
	/// </summary>
	/// <param name="sender">The source of the event.</param>
	/// <param name="e">An <see cref="MessageEventArgs">MessageEventArgs</see> that contains
	/// the event data.</param>
	public delegate void MessageEventHandler       (object sender, MessageEventArgs        e);
	/// <summary>
	/// Represents the method that will handle the Unrecognized  event.
	/// </summary>
	/// <param name="sender">The source of the event.</param>
	/// <param name="e">An <see cref="UnrecognizedEventArgs">UnrecognizedEventArgs</see> that contains
	/// the event data.</param>
	public delegate void UnrecognizedEventHandler  (object sender, UnrecognizedEventArgs   e);
	#endregion Delegates
	
	/// <summary>
	/// Provides data for the <b>CircuitStatus</b> event.
	/// </summary>
	public class CircuitStatusEventArgs : EventArgs
	{
		string status;
		string circID;
		string path;
		
		#region Getter
		/// <summary>
		/// Gets the circuit ID.
		/// </summary>
		public string CircID {
			get {
				return circID;
			}
		}
		/// <summary>
		/// Gets the circuit path.
		/// </summary>
		public string Path {
			get {
				return path;
			}
		}
		/// <summary>
		/// Gets the circuit status.
		/// </summary>
		public string Status {
			get {
				return status;
			}
		}
		#endregion Getter
		
		/// <summary>
		/// Initializes a new instance of the <see cref="CircuitStatusEventArgs">CircuitStatusEventArgs</see> class.
		/// </summary>
		/// <param name="status">The status of the circuit.</param>
		/// <param name="circID">The ID of the circuit.</param>
		/// <param name="path">The path of the circuit.</param>
		public CircuitStatusEventArgs(string status, string circID, string path)
		{
			this.status = status;
			this.circID = circID;
			this.path   = path;
		}
	}
	
	/// <summary>
	/// Provides data for the <b>StreamStatus</b> event.
	/// </summary>
	public class StreamStatusEventArgs : EventArgs
	{
		string status;
		string streamID;
		string target;
		
		#region Getter
		/// <summary>
		/// Gets the stream ID.
		/// </summary>
		public string StreamID {
			get {
				return streamID;
			}
		}
		/// <summary>
		/// Gets the stream target.
		/// </summary>
		public string Target {
			get {
				return target;
			}
		}
		/// <summary>
		/// Gets the stream status.
		/// </summary>
		public string Status {
			get {
				return status;
			}
		}
		#endregion Getter
		
		/// <summary>
		/// Initializes a new instance of the <see cref="StreamStatusEventArgs">StreamStatusEventArgs</see> class.
		/// </summary>
		/// <param name="status">The status of the stream.</param>
		/// <param name="streamID">The ID of the stream.</param>
		/// <param name="target">The target of the stream.</param>
		public StreamStatusEventArgs(string status, string streamID, string target)
		{
			this.status   = status;
			this.streamID = streamID;
			this.target   = target;
		}
	}
	
	/// <summary>
	/// Provides data for the <b>OrConnStatus</b> event.
	/// </summary>
	public class OrConnStatusEventArgs : EventArgs
	{
		string status;
		string orName;
		
		#region Getter
		/// <summary>
		/// Gets the or name.
		/// </summary>
		public string OrName {
			get {
				return orName;
			}
		}
		/// <summary>
		/// Gets the or status.
		/// </summary>
		public string Status {
			get {
				return status;
			}
		}
		#endregion Getter
		
		/// <summary>
		/// Initializes a new instance of the <see cref="OrConnStatusEventArgs">OrConnStatusEventArgs</see> class.
		/// </summary>
		/// <param name="status">The status of the or.</param>
		/// <param name="orName">The name of the or.</param>
		public OrConnStatusEventArgs(string status, string orName)
		{
			this.status = status;
			this.orName = orName;
		}
	}
	
	/// <summary>
	/// Provides data for the <b>BandwidthUsed</b> event.
	/// </summary>
	public class BandwidthUsedEventArgs : EventArgs
	{
		long read;
		long written;
		
		#region Getter / Setter
		/// <summary>
		/// Gets the bytes read.
		/// </summary>
		public long Read {
			get {
				return read;
			}
		}
		/// <summary>
		/// Gets the bytes written.
		/// </summary>
		public long Written {
			get {
				return written;
			}
		}
		#endregion Getter
		
		/// <summary>
		/// Initializes a new instance of the <see cref="BandwidthUsedEventArgs">BandwidthUsedEventArgs</see> class.
		/// </summary>
		/// <param name="read">The bytes read.</param>
		/// <param name="written">The bytes written.</param>
		public BandwidthUsedEventArgs(long read, long written)
		{
			this.read    = read;
			this.written = written;
		}
	}
	
	/// <summary>
	/// Provides data for the <b>NewDescriptors</b> event.
	/// </summary>
	public class NewDescriptorsEventArgs : EventArgs
	{
		IList orList;
		
		/// <summary>
		/// Gets the list of or's.
		/// </summary>
		public IList OrList {
			get {
				return orList;
			}
		}
		
		/// <summary>
		/// Initializes a new instance of the <see cref="NewDescriptorsEventArgs">NewDescriptorsEventArgs</see> class.
		/// </summary>
		/// <param name="orList">The list of or's.</param>
		public NewDescriptorsEventArgs(IList orList)
		{
			this.orList = orList;
		}
	}
	
	/// <summary>
	/// Provides data for the <b>Message</b> event.
	/// </summary>
	public class MessageEventArgs : EventArgs
	{
		string severity;
		string msg;
		
		#region Getter
		/// <summary>
		/// Gets the message.
		/// </summary>
		public string Msg {
			get {
				return msg;
			}
		}
		/// <summary>
		/// Gets the severity of the message.
		/// </summary>
		public string Severity {
			get {
				return severity;
			}
			set {
				severity = value;
			}
		}
		#endregion Getter
		
		/// <summary>
		/// Initializes a new instance of the <see cref="MessageEventArgs">MessageEventArgs</see> class.
		/// </summary>
		/// <param name="severity">The severity of the message.</param>
		/// <param name="msg">The message.</param>
		public MessageEventArgs(string severity, string msg)
		{
			this.severity = severity;
			this.msg      = msg;
		}
	}
	
	/// <summary>
	/// Provides data for the <b>Unrecognized</b> event.
	/// </summary>
	public class UnrecognizedEventArgs : EventArgs
	{
		string type;
		string msg;
		
		#region Getter
		/// <summary>
		/// Gets the message of the event.
		/// </summary>
		public string Msg {
			get {
				return msg;
			}
		}
		
		/// <summary>
		/// Gets the type of the event.
		/// </summary>
		public string Type {
			get {
				return type;
			}
		}
		#endregion Getter
		
		/// <summary>
		/// Initializes a new instance of the <see cref="UnrecognizedEventArgs">UnrecognizedEventArgs</see> class.
		/// </summary>
		/// <param name="type">The type of the event.</param>
		/// <param name="msg">The message of the event.</param>
		public UnrecognizedEventArgs(string type, string msg)
		{
			this.msg = msg;
			this.type = type;
		}
		
	}
	
}

--- NEW FILE: ITorControlConnection.cs ---
/*
 * File ITorControlConnection
 * 
 * Copyright (C) 2005 Oliver Rau (olra0001@xxxxxxxxxxxxxxxxxxx)
 * 
 * See LICENSE file for copying information 
 * 
 * Created on 08.08.2005 20:37
 * 
 * $Id: ITorControlConnection.cs,v 1.1 2005/11/09 21:47:04 nickm Exp $
 */

using System;
using System.Collections;
using System.IO;
using System.Threading;

namespace Tor.Control
{
	/// <summary>
	/// Defines the methods for all Tor Control Connections.
	/// </summary>
	public interface ITorControlConnection
	{
		event CircuitStatusEventHandler  CircuitStatus;
		event StreamStatusEventHandler   StreamStatus;
		event OrConnStatusEventHandler   OrConnStatus;
		event BandwidthUsedEventHandler  BandwidthUsed;
		event NewDescriptorsEventHandler NewDescriptors;
		event MessageEventHandler        Message;
		event UnrecognizedEventHandler   Unrecognized;
		
		Thread LaunchThread(bool daemon);
		
		void EnableDebug();
		void EnableDebug(Stream debug);
		
		void DisableDebug();
		
		void SetConf(string key, string value);
		void SetConf(IList kvList);
		
		IList GetConf(string key);
		IList GetConf(IList keys);
		
		void SetEvents(IList events);
		
		void Authenticate(byte[] auth);
		
		void SaveConf();
		
		void Signal(string signal);
		
		Hashtable MapAddresses(IList kvLines);
		Hashtable MapAddresses(Hashtable addresses);
		
		Hashtable GetInfo(IList keys);
		string    GetInfo(string key);
		
		string ExtendCircuit(string circID, string path);
		void   CloseCircuit(string circID, bool ifUnused);
		
		void AttachStream(string streamID, string circID);
		void RedirectStream(string streamID, string address);
		void CloseStream(string streamID, byte reason);
		
		string PostDescriptor(string desc);
		

	}
}

--- NEW FILE: TorControl.cs ---
/*
 * File ITorControlCommands.cs
 * 
 * Copyright (C) 2005 Oliver Rau (olra0001@xxxxxxxxxxxxxxxxxxx)
 * 
 * See LICENSE file for copying information 
 * 
 * Created on 08.08.2005 20:37
 * 
 * $Id: TorControl.cs,v 1.1 2005/11/09 21:47:04 nickm Exp $
 */

using System;

namespace Tor.Control
{
	/// <summary>
	/// This class holds basic constants and values needed for communication with to.
	/// </summary>
	public class TorControl
	{
		public enum Commands {
			Error      = 0x0000,
			Done       = 0x0001,
			SetConf    = 0x0002,
			GetConf    = 0x0003,
			ConfValue  = 0x0004,
			SetEvents  = 0x0005,
			Event      = 0x0006,
			Auth       = 0x0007,
			SaveConf   = 0x0008,
			Signal     = 0x0009,
			MapAddress = 0x000A,
			GetInfo    = 0x000B,
			InfoValue  = 0x000C,
			ExtendCircuit  = 0x000D,
			AttachStream   = 0x000E,
			PostDescriptor = 0x000F,
			FragmentHeader = 0x0010,
			Fragment       = 0x0011,
			RedirectStream = 0x0012,
			CloseStream    = 0x0013,
			CloseCircuit   = 0x0014
		}

		public static string[] CommandNames = {
			"ERROR",
			"DONE",
			"SETCONF",
			"GETCONF",
			"CONFVALUE",
			"SETEVENTS",
			"EVENT",
			"AUTH",
			"SAVECONF",
			"SIGNAL",
			"MAPADDRESS",
			"GETINFO",
			"INFOVALUE",
			"EXTENDCIRCUIT",
			"ATTACHSTREAM",
			"POSTDESCRIPTOR",
			"FRAGMENTHEADER",
			"FRAGMENT",
			"REDIRECTSTREAM",
			"CLOSESTREAM",
			"CLOSECIRCUIT"
		};

		public enum Event {
			CircSatus     = 0x0001,
			StreamStatus  = 0x0002,
			OrConnStatus  = 0x0003,
			Bandwidth     = 0x0004,
			NewDescriptor = 0x0005,
			MsgDebug      = 0x0007,
			MsgInfo       = 0x0008,
			MsgNotice     = 0x0009,
			MsgWarn       = 0x000A,
			MsgError      = 0x000B
		}

		public static string[] EventNames = {
			"(0)",
			"CIRC",
			"STREAM",
			"ORCONN",
			"BW",
			"OLDLOG",
			"NEWDESC",
			"DEBUG",
			"INFO",
			"NOTICE",
			"WARN",
			"ERR"
		};
		
		public enum CircStatus {
			Launched = 0x01,
			Built    = 0x02,
			Extended = 0x03,
			Failed   = 0x04,
			Closed   = 0x05
		}
		
		public string[] CircStatusNames = {
			"LAUNCHED",
			"BUILT",
			"EXTENDED",
			"FAILED",
			"CLOSED"
		};
		
		public enum StreamStatus {
			SentConnect = 0x00,
			SentResolve = 0x01,
			Succeeded   = 0x02,
			Failed      = 0x03,
			Closed      = 0x04,
			NewConnect  = 0x05,
			NewResolve  = 0x06,
			Deatched    = 0x07
		}

		public static string[] StreamStatusNames = {
			"SENT_CONNECT",
			"SENT_RESOLVE",
			"SUCCEEDED",
			"FAILED",
			"CLOSED",
			"NEW_CONNECT",
			"NEW_RESOLVE",
			"DETACHED"
		};

		public enum ORConnStatus {
			Launched  = 0x00,
			Connected = 0x01,
			Failed    = 0x02,
			Closed    = 0x03
		}

		public static string[] ORConnStatusNames = {
			"LAUNCHED","CONNECTED","FAILED","CLOSED"
		};

		public enum Signal {
			Hup  = 0x01,
			Int  = 0x02,
			Usr1 = 0x0A,
			Usr2 = 0x0C,
			Term = 0x0F
		}

		public static string[] ErrorMsgs = {
			"Unspecified error",
			"Internal error",
			"Unrecognized message type",
			"Syntax error",
			"Unrecognized configuration key",
			"Invalid configuration value",
			"Unrecognized byte code",
			"Unauthorized",
			"Failed authentication attempt",
			"Resource exhausted",
			"No such stream",
			"No such circuit",
			"No such OR"
		};

	}
}

--- NEW FILE: TorControlConnection0.cs ---
/*
 * File TorControlConnection0.cs
 * 
 * Copyright (C) 2005 Oliver Rau (olra0001@xxxxxxxxxxxxxxxxxxx)
 * 
 * See LICENSE file for copying information 
 * 
 * Created on 16.09.2005 20:46
 * 
 * $Id: TorControlConnection0.cs,v 1.1 2005/11/09 21:47:04 nickm Exp $
 */

using System;
using System.Collections;
using System.IO;
using System.Net.Sockets;
using System.Text;
using System.Threading;

namespace Tor.Control
{
	/// <summary>
	/// A connection to a running Tor process.
	/// </summary>
	public class TorControlConnection0 : TorControlConnectionBase
	{
		#region Events
		public override event CircuitStatusEventHandler  CircuitStatus;
		public override event StreamStatusEventHandler   StreamStatus;
		public override event OrConnStatusEventHandler   OrConnStatus;
		public override event BandwidthUsedEventHandler  BandwidthUsed;
		public override event NewDescriptorsEventHandler NewDescriptors;
		public override event MessageEventHandler        Message;
		public override event UnrecognizedEventHandler   Unrecognized;
		#endregion
		
		BigEndianReader input;
		BigEndianWriter output;
		
		public TorControlConnection0(TcpClient connection) : this(connection.GetStream())
		{}
		
		public TorControlConnection0(Stream s) : this(new BigEndianReader(s), 
		                                              new BigEndianWriter(s))
		{}
		
		public TorControlConnection0(BigEndianReader i, BigEndianWriter o)
		{
			this.output = o;
			this.input  = i;
			
			// Synchronize the internal queue by default
			this.waiters = Queue.Synchronized(new Queue());
		}
		
		protected void SendCommand0(TorControl.Commands type, byte[] cmd)
		{
			int length = cmd == null ? 0 : cmd.Length;
			
			output.WriteShort(length);
			output.WriteShort((int)type);
			
			if (cmd != null && cmd.Length > 0)
				output.Write(cmd);
		}
		
		protected void SendCommand(TorControl.Commands type, byte[] cmd)
		{
			lock (output) {
				if (cmd == null || cmd.Length <= 65535) {
					SendCommand0(type, cmd);
					return;
				}
				
				int length = cmd.Length;
				output.WriteShort(65535);
				output.WriteShort((int)TorControl.Commands.FragmentHeader);
				output.WriteShort((int)type);
				output.WriteInt(length);
				output.Write(cmd, 0, 65535);
				
				for (int pos = 65535; pos < length; pos += 65535) {
					int flen = length-pos < 65535 ? length -pos : 65535;
					output.WriteShort(flen);
					output.WriteShort((int)TorControl.Commands.Fragment);
					output.Write(cmd, pos, flen);
				}
			}
		}
		
		protected Cmd ReadCommand0()
		{
			int len = input.ReadUInt16();
			int cmd = input.ReadUInt16();
			
			byte[] result;
			
			if (len > 0)
				result = input.ReadBytes(len);
			else
				result = new byte[0];
			
			return new Cmd((TorControl.Commands)cmd, result);
		}
		
		protected Cmd ReadCommand()
		{
			lock (input) {
				Cmd c = ReadCommand0();
				if (c.Type != TorControl.Commands.Fragment && 
				    c.Type != TorControl.Commands.FragmentHeader)
					return c;
				
				if (c.Type == TorControl.Commands.Fragment)
					throw new TorControlSyntaxException("Fragment without header");
				
				int realType = Bytes.GetU16(c.Body, 0);
				int realLen  = Bytes.GetU32(c.Body, 2);
				
				Cmd outCmd = new Cmd((TorControl.Commands)realType, realLen);
				Array.Copy(c.Body, 6, outCmd.Body, 0, c.Body.Length - 6);
				
				int pos = c.Body.Length-6;
				
				while (pos < realLen) {
					c = ReadCommand0();
					if (c.Type != TorControl.Commands.Fragment)
						throw new TorControlSyntaxException("Incomplete fragmented message");
					
					Array.Copy(c.Body, 0, outCmd.Body, pos, c.Body.Length);
					pos += c.Body.Length;
				}
				
				return outCmd;
			}
		}
		
		protected override void React()
		{
			while (true) {
				Cmd c = ReadCommand();
				if (c.Type == TorControl.Commands.Event)
					HandleEvent(c);
				else {
					Waiter w;
					lock (waiters.SyncRoot) {
						w = (Waiter) waiters.Dequeue();
					}
					w.Response = c;
				}
			}
		}
		
		protected Cmd _SendAndWaitForResponse(TorControl.Commands type, byte[] cmd)
		{
			CheckThread();
			Waiter w = new Waiter();
			
			lock (waiters.SyncRoot) {
				SendCommand(type, cmd);
				waiters.Enqueue(w);
			}
			return (Cmd) w.Response;
		}
		
		protected Cmd SendAndWaitForResponse(TorControl.Commands type, byte[] cmd,
		                                     TorControl.Commands exType1, TorControl.Commands exType2,
		                                     TorControl.Commands exType3, TorControl.Commands exType4)
		{
			Cmd c = _SendAndWaitForResponse(type, cmd);
			if (c.Type == TorControl.Commands.Error)
				throw new TorControlException(Bytes.GetU16(c.Body, 0),
				                              Bytes.GetNulTerminatedStr(c.Body,2));
			
			if (c.Type == exType1 || c.Type == exType2 || c.Type == exType3 ||
			    c.Type == exType4)
				return c;
			
			throw new TorControlSyntaxException("Unexpected reply type: " + c.Type);
		}
		
		protected Cmd SendAndWaitForResponse(TorControl.Commands type, byte[] cmd)
		{
			return SendAndWaitForResponse(type, cmd, 
			                              TorControl.Commands.Done, TorControl.Commands.Done, 
			                              TorControl.Commands.Done, TorControl.Commands.Done);
		}
		
		protected Cmd SendAndWaitForResponse(TorControl.Commands type, byte[] cmd, 
		                                     TorControl.Commands exType1)
		{
			return SendAndWaitForResponse(type, cmd, exType1, exType1, exType1, exType1);
		}
		
		protected Cmd SendAndWaitForResponse(TorControl.Commands type, byte[] cmd, 
		                                     TorControl.Commands exType1, TorControl.Commands exType2)
		{
			return SendAndWaitForResponse(type, cmd, exType1, exType2, exType2, exType2);
		}
		
		protected Cmd SendAndWaitForResponse(TorControl.Commands type, byte[] cmd, 
		                                     TorControl.Commands exType1, TorControl.Commands exType2, 
		                                     TorControl.Commands exType3)
		{
			return SendAndWaitForResponse(type, cmd, exType1, exType2, exType3, exType3);
		}
		
		protected void HandleEvent(Cmd c)
		{
			TorControl.Event type = (TorControl.Event)Bytes.GetU16(c.Body, 0);
			
			switch (type) {
				case TorControl.Event.CircSatus:
					if (this.CircuitStatus != null) {
						CircuitStatusEventArgs args;
						args = new CircuitStatusEventArgs(TorControl.StreamStatusNames[c.Body[2]],
						                                  Bytes.GetU32S(c.Body, 3),
						                                  Bytes.GetNulTerminatedStr(c.Body, 7));
						CircuitStatus(this, args);
					}
					break;
				case TorControl.Event.StreamStatus:
					if (this.StreamStatus != null) {
						StreamStatusEventArgs args;
						args = new StreamStatusEventArgs(TorControl.StreamStatusNames[c.Body[2]],
						                                 Bytes.GetU32S(c.Body, 3).ToString(),
						                                 Bytes.GetNulTerminatedStr(c.Body, 7));
						StreamStatus(this, args);
					}
					break;
				case TorControl.Event.OrConnStatus:
					if (this.OrConnStatus != null) {
						OrConnStatusEventArgs args;
						args = new OrConnStatusEventArgs(TorControl.ORConnStatusNames[c.Body[2]],
						                                 Bytes.GetNulTerminatedStr(c.Body, 3));
						OrConnStatus(this, args);
					}
					break;
				case TorControl.Event.Bandwidth:
					if (this.BandwidthUsed != null) {
						BandwidthUsedEventArgs args;
						args = new BandwidthUsedEventArgs(Bytes.GetU32(c.Body, 2),
						                                  Bytes.GetU32(c.Body, 6));
						BandwidthUsed(this, args);
					}
					break;
				case TorControl.Event.NewDescriptor:
					if (this.NewDescriptors != null) {
						IList lst = new ArrayList();
						Bytes.SplitStr(lst, c.Body, 2, (byte)',');
						NewDescriptorsEventArgs args;
						args = new NewDescriptorsEventArgs(lst);
						
						NewDescriptors(this, args);
					}
					break;
				case TorControl.Event.MsgDebug:
					if (this.Message != null) {
						MessageEventArgs args;
						args = new MessageEventArgs("DEBUG", Bytes.GetNulTerminatedStr(c.Body, 2));
						Message(this, args);
					}
					break;
				case TorControl.Event.MsgInfo:
					if (this.Message != null) {
						MessageEventArgs args;
						args = new MessageEventArgs("INFO", Bytes.GetNulTerminatedStr(c.Body, 2));
						Message(this, args);
					}
					break;
				case TorControl.Event.MsgNotice:
					if (this.Message != null) {
						MessageEventArgs args;
						args = new MessageEventArgs("NOTICE", Bytes.GetNulTerminatedStr(c.Body, 2));
						Message(this, args);
					}
					break;
				case TorControl.Event.MsgWarn:
					if (this.Message != null) {
						MessageEventArgs args;
						args = new MessageEventArgs("WARN", Bytes.GetNulTerminatedStr(c.Body, 2));
						Message(this, args);
					}
					break;
				case TorControl.Event.MsgError:
					if (this.Message != null) {
						MessageEventArgs args;
						args = new MessageEventArgs("ERR", Bytes.GetNulTerminatedStr(c.Body, 2));
						Message(this, args);
					}
					break;
				default:
					if (this.Unrecognized != null) {
						// TODO: Check if this is practicable
						UnrecognizedEventArgs args;
						args = new UnrecognizedEventArgs("UNKNOWN",
						                                 Bytes.GetNulTerminatedStr(c.Body, 3));
						Unrecognized(this, args);
					}
					break;
			}
		}
		
		public override void SetConf(IList kvList)
		{
			StringBuilder sb = new StringBuilder();
			
			foreach (string kv in kvList) {
				sb.Append(kv).Append("\n");
			}
			
			// TODO: here is ASCII fixed, if another charset is used, here should be the magic
			SendAndWaitForResponse(TorControl.Commands.SetConf, Encoding.ASCII.GetBytes(sb.ToString()));
		}
		
		public override IList GetConf(IList keys)
		{
			StringBuilder sb = new StringBuilder();
			foreach (string key in keys) {
				sb.Append(key).Append("\n");
			}
			
			Cmd c = SendAndWaitForResponse(TorControl.Commands.GetConf, Encoding.ASCII.GetBytes(sb.ToString()),
			                               TorControl.Commands.ConfValue);
			
			IList lines = new ArrayList();
			Bytes.SplitStr(lines, c.Body, 0, (byte)'\n');
			IList result = new ArrayList();
			
			foreach (string kv in lines) {
				int idx = kv.IndexOf(" ");
				result.Add(new ConfigEntry(kv.Substring(0, idx),
				                           kv.Substring(idx+1)));
			}
			
			return result;
		}
		
		public override void SetEvents(IList events)
		{
			byte[] ba = new byte[events.Count * 2];
			int i=0;
			foreach (object ev in events) {
				short e = -1;
				if (ev.GetType() == typeof(short)) {
					e = (short)ev;
				} else {
					string s = ((string)ev).ToUpper();
					for (int j = 0; j < TorControl.EventNames.Length; ++j) {
						if (TorControl.EventNames[j] == s) {
							e = (short)j;
							break;
						}
					}
					
					if (e < 0)
						throw new TorControlException("Unkown v0 code for event '" + s +"'");
				}
				
				Bytes.SetU16(ba, i, e);
				i+=2;
			}
			
			SendAndWaitForResponse(TorControl.Commands.SetEvents, ba);
			
		}
		
		public override void Authenticate(byte[] auth)
		{
			if (auth == null)
				auth = new byte[0];
			
			SendAndWaitForResponse(TorControl.Commands.Auth, auth);
		}
		
		public override void SaveConf()
		{
			SendAndWaitForResponse(TorControl.Commands.SaveConf, new byte[0]);
		}
		
		public override void Signal(string signal)
		{
			TorControl.Signal sig;
			signal = signal.ToUpper();
			
			if (signal == "HUP" || signal == "RELOAD")
				sig = TorControl.Signal.Hup;
			else if (signal == "INT" || signal == "SHUTDOWN")
				sig = TorControl.Signal.Int;
			else if (signal == "USR1" || signal == "DUMP")
				sig = TorControl.Signal.Usr1;
			else if (signal == "USR2" || signal == "DEBUG")
				sig = TorControl.Signal.Usr2;
			else if (signal == "TERM" || signal == "HALT")
				sig = TorControl.Signal.Term;
			else
				throw new TorControlException("Unrecognized value for signal()");
			
			byte[] ba = { (byte) sig};
			SendAndWaitForResponse(TorControl.Commands.Signal, ba);
		}
		
		public override Hashtable MapAddresses(IList kvLines)
		{
			StringBuilder sb = new StringBuilder();
			foreach (string line in kvLines) {
				sb.Append(line).Append("\n");
			}
			Cmd c = SendAndWaitForResponse(TorControl.Commands.MapAddress, Encoding.ASCII.GetBytes(sb.ToString()));
			Hashtable result = new Hashtable();
			IList lst = new ArrayList();
			
			Bytes.SplitStr(lst, c.Body, 0, (byte)'\n');
			foreach (string kv in lst) {
				int idx = kv.IndexOf(" ");
				result.Add(kv.Substring(0, idx), kv.Substring(idx+1));
			}
			
			return result;
		}
		
		public override Hashtable GetInfo(IList keys)
		{
			StringBuilder sb = new StringBuilder();
			
			foreach (string key in keys) {
				sb.Append(key).Append("\n");
			}
			
			Cmd c = SendAndWaitForResponse(TorControl.Commands.GetInfo, Encoding.ASCII.GetBytes(sb.ToString()),
			                               TorControl.Commands.InfoValue);
			
			Hashtable m = new Hashtable();
			IList lst = new ArrayList();
			Bytes.SplitStr(lst, c.Body, 0, (byte)0);
			if ((lst.Count % 2) != 0)
				throw new TorControlSyntaxException("Odd number of substrings from GETINFO");
			
			for (int i =0; i < lst.Count ; i+=2) {
				object k = lst[i];
				object v = lst[i+1];
				
				m.Add(k, v);
			}
			
			return m;
		}
		
		public override string ExtendCircuit(string circID, string path)
		{
			byte[] p = Encoding.ASCII.GetBytes(path);
			byte[] ba = new byte[p.Length+4];
			Bytes.SetU32(ba, 0, int.Parse(circID));
			Array.Copy(p, 0, ba, 4, p.Length);
			Cmd c = SendAndWaitForResponse(TorControl.Commands.ExtendCircuit, ba);
			return Bytes.GetU32(c.Body, 0).ToString();
		}
		
		public override void AttachStream(string streamID, string circID)
		{
			byte[] ba = new byte[8];
			Bytes.SetU32(ba, 0, int.Parse(streamID));
			Bytes.SetU32(ba, 4, int.Parse(circID));
			SendAndWaitForResponse(TorControl.Commands.AttachStream, ba);
		}
		
		public override string PostDescriptor(string desc)
		{
			return Encoding.ASCII.GetString((SendAndWaitForResponse(TorControl.Commands.PostDescriptor, 
			                                                        Encoding.ASCII.GetBytes(desc)).Body));
		}
		
		public override void RedirectStream(string streamID, string address)
		{
			byte[] addr = Encoding.ASCII.GetBytes(address);
			byte[] ba = new byte[addr.Length + 4];
			Bytes.SetU32(ba, 0, int.Parse(streamID));
			Array.Copy(addr, 0, ba, 4, addr.Length);
			SendAndWaitForResponse(TorControl.Commands.RedirectStream, ba);
		}
		
		public override void CloseStream(string streamID, byte reason)
		{
			byte[] ba = new byte[6];
			Bytes.SetU32(ba, 0, int.Parse(streamID));
			ba[4] = reason;
			ba[5] = 0;
			SendAndWaitForResponse(TorControl.Commands.CloseStream, ba);
		}
		
		public override void CloseCircuit(string circID, bool ifUsed)
		{
			byte[] ba = new byte[5];
			Bytes.SetU32(ba, 0, int.Parse(circID));
			ba[4] = (byte)(ifUsed ? 1:0);
			SendAndWaitForResponse(TorControl.Commands.CloseCircuit, ba);
		}
	}
}

--- NEW FILE: TorControlConnection1.cs ---
/*
 * File TorControlConnection1.cs
 * 
 * Copyright (C) 2005 Oliver Rau (olra0001@xxxxxxxxxxxxxxxxxxx)
 * 
 * See LICENSE file for copying information 
 * 
 * Created on 08.08.2005 20:37
 * 
 * $Id: TorControlConnection1.cs,v 1.1 2005/11/09 21:47:04 nickm Exp $
 */

using System;
using System.Collections;
using System.IO;
using System.Net.Sockets;
using System.Text;

namespace Tor.Control
{
	/// <summary>
	/// Description of TorControlConnection1.
	/// </summary>
	public class TorControlConnection1 : TorControlConnectionBase
	{
		#region Events
		public override event CircuitStatusEventHandler  CircuitStatus;
		public override event StreamStatusEventHandler   StreamStatus;
		public override event OrConnStatusEventHandler   OrConnStatus;
		public override event BandwidthUsedEventHandler  BandwidthUsed;
		public override event NewDescriptorsEventHandler NewDescriptors;
		public override event MessageEventHandler        Message;
		public override event UnrecognizedEventHandler   Unrecognized;
		#endregion
		
		StreamReader input;
		StreamWriter output;
		
		public TorControlConnection1(TcpClient connection) : this(connection.GetStream())
		{}
		
		public TorControlConnection1(Stream s) : this(new StreamReader(s), 
		                                              new StreamWriter(s))
		{}
		
		public TorControlConnection1(StreamReader i, StreamWriter o)
		{
			this.output = o;
			this.input  = i;
			
			// Synchronize the internal queue by default
			this.waiters = Queue.Synchronized(new Queue());
		}
		
		protected void WriteEscaped(string s)
		{
			string[] tokens = s.Split('\n');
			
			string temp;
			foreach (string line in tokens) {
				if (line.StartsWith("."))
					temp = "." + line;
				
				if (line.EndsWith("\r"))
					temp = line + "\n";
				else
					temp = line + "\r\n";
				
				WriteDebug(">> "+temp);
				
				output.Write(temp);
			}
			
			output.Write(".\r\n");
			
			WriteDebug(">> .\n");
		}
		
		public static string Quote(string s)
		{
			StringBuilder sb = new StringBuilder('\"');
			for (int i=0; i < s.Length; ++i) {
				char c = s.ToCharArray()[i];
				switch (c) {
					case '\r':
					case '\n':
					case '\\':
					case '\"':
						sb.Append('\\');
						break;
				}
			}
			
			sb.Append('\"');
			return sb.ToString();
		}
		
		protected ArrayList ReadReply()
		{
			ArrayList reply = new ArrayList();
			
			string c;
			
			do {
				string line = input.ReadLine();
				WriteDebug("<< "+line);
				
				// TODO: maybe fetch if line is null
				if (line.Length < 4)
					throw new TorControlSyntaxException("Line (\"" + line + "\") too short");
				
				string status = line.Substring(0, 3);
				
				c = line.Substring(3,1);
				string msg = line.Substring(4);
				string rest = null;
				
				if (c == "+") {
					StringBuilder data = new StringBuilder();
					
					while (true) {
						line = input.ReadLine();
						
						WriteDebug("<< "+line);
						if (line.Equals("."))
							break;
						else if (line.StartsWith("."))
							line = line.Substring(2);
						
						data.Append(line).Append("\n");
					}
					
					rest = data.ToString();
				}
				reply.Add(new ReplyLine(status, msg, rest));
				
			} while (c != " ");
			
			return reply;
		}
		
		protected override void React()
		{
			while (true) {
				ArrayList lst = ReadReply();
				
				if (((ReplyLine)lst[0]).Status.StartsWith("6"))
					HandleEvent(lst);
				else {
					Waiter w;
					
					lock (waiters.SyncRoot) {
						w = (Waiter) waiters.Dequeue();
						WriteDebug("fetched waiter: " + w.Id);
					}
					
					w.Response = lst;
				}
			}
		}
		
		protected IList SendAndWaitForResponse(string s, string rest)
		{
			CheckThread();
			
			Waiter w = new Waiter();
			
			WriteDebug(">> " + s);
			
			lock (waiters.SyncRoot) {
				output.Write(s);
				output.Flush();
				
				if (rest != null)
					WriteEscaped(rest);
				
				
				waiters.Enqueue(w);
				WriteDebug("added waiter: " + w.Id);
			}
			
			ArrayList lst = (ArrayList) w.Response;
			
			foreach (ReplyLine c in lst) {
				if (! c.Status.StartsWith("2"))
					throw new TorControlException("Error reply: " + c.Msg);
			}
			
			return lst;
		}
		
		protected void HandleEvent(ArrayList events)
		{
			foreach (ReplyLine line in events) {
				int idx = line.Msg.IndexOf(" ");
				string tp = line.Msg.Substring(0, idx).ToUpper();
				
				string rest = line.Msg.Substring(idx+1);
				
				if (tp == "CIRC") {
					if (this.CircuitStatus != null) {
						string[] pars = rest.Split(' ');
						CircuitStatusEventArgs args = new CircuitStatusEventArgs(pars[1], 
						                                                         pars[0], 
						                                                         pars[2]);
						
						CircuitStatus(this, args);
					}
				} else if (tp == "STREAM") {
					if (this.StreamStatus != null) {
						string[] pars = rest.Split(' ');
						StreamStatusEventArgs args = new StreamStatusEventArgs(pars[1], 
						                                                       pars[0],
						                                                       pars[3]);
						
						StreamStatus(this, args);
					}
				} else if (tp == "ORCONN") {
					if (this.OrConnStatus != null) {
						string[] pars = rest.Split(' ');
						OrConnStatusEventArgs args = new OrConnStatusEventArgs(pars[1], 
						                                                       pars[0]);
						
						OrConnStatus(this, args);
					}
				} else if (tp == "BW") {
					if (this.BandwidthUsed != null) {
						string[] pars = rest.Split(' ');
						BandwidthUsedEventArgs args = new BandwidthUsedEventArgs(long.Parse(pars[0]),
						                                                         long.Parse(pars[1]));
						
						BandwidthUsed(this, args);
					}
				} else if (tp == "NEWDESC") {
					if (this.NewDescriptors != null) {
						IList lst = Bytes.SplitStr(null, rest);
						NewDescriptorsEventArgs args = new NewDescriptorsEventArgs(lst);
						
						NewDescriptors(this, args);
					}
				} else if (tp == "DEBUG"  ||
				           tp == "INFO"   ||
				           tp == "NOTICE" ||
				           tp == "WARN"   ||
				           tp == "ERR") {
					if (this.Message != null) {
						MessageEventArgs args = new MessageEventArgs(tp, rest);
						
						Message(this, args);
					}
				} else {
					if (this.Unrecognized != null) {
						UnrecognizedEventArgs args = new UnrecognizedEventArgs(tp, rest);
						
						Unrecognized(this, args);
					}
				}
			}
		}
		
		public override void SetConf(IList kvList)
		{
			if (kvList.Count == 0)
				return;
			
			StringBuilder sb = new StringBuilder("SETCONF");
			foreach (string kv in kvList) {
				int i = kv.IndexOf(" ");
				if (i == -1)
					sb.Append(" ").Append(kv);
				
				sb.Append(" ").Append(kv.Substring(0, i)).Append("=").Append(Quote(kv.Substring(i+1)));
			}
			
			sb.Append("\r\n");
			
			SendAndWaitForResponse(sb.ToString(), null);
		}
		
		public override IList GetConf(IList keys)
		{
			StringBuilder sb = new StringBuilder("GETCONF");
			
			foreach (string key in keys) {
				sb.Append(" ").Append(key);
			}
			
			sb.Append("\r\n");
			ArrayList lst = (ArrayList) SendAndWaitForResponse(sb.ToString(), null);
			ArrayList result = new ArrayList();
			
			foreach (ReplyLine rl in lst) {
				string kv = rl.Msg;
				int idx = kv.IndexOf("=");
				result.Add(new ConfigEntry(kv.Substring(0, idx), kv.Substring(idx+1)));
			}
			
			return result;
		}
		
		public override void SetEvents(IList events)
		{
			StringBuilder sb = new StringBuilder("SETEVENTS");
			
			foreach (object ev in events) {
				if (ev.GetType() == typeof(string)) {
					sb.Append(" ").Append((string) ev);
				} else {
					int i = (int) ev;
					sb.Append(" ").Append(TorControl.EventNames[i]);
				}
			}
			
			sb.Append("\r\n");
			SendAndWaitForResponse(sb.ToString(), null);
		}
		
		public override void Authenticate(byte[] auth)
		{
			string cmd = "AUTHENTICATE " + Bytes.Hex(auth) + "\r\n";
			SendAndWaitForResponse(cmd, null);
			
		}
		
		public override void SaveConf()
		{
			SendAndWaitForResponse("SAVECONF\r\n", null);
		}
		
		public override void Signal(string signal)
		{
			string cmd = "SIGNAL " + signal + "\r\n";
			SendAndWaitForResponse(cmd, null);
		}
		
		public override Hashtable MapAddresses(IList kvLines)
		{
			StringBuilder sb = new StringBuilder("MAPADDRESS");
			
			foreach (string kv in kvLines) {
				int i = kv.IndexOf(" ");
				sb.Append(" ").Append(kv.Substring(0, i)).Append("=").Append(Quote(kv.Substring(i+1)));
			}
			
			sb.Append("\r\n");
			ArrayList lst = (ArrayList) SendAndWaitForResponse(sb.ToString(), null);
			
			Hashtable result = new Hashtable();
			
			foreach (ReplyLine rl in lst) {
				string kv = rl.Msg;
				int idx = kv.IndexOf("=");
				result.Add(kv.Substring(0, idx), kv.Substring(idx+1));
			}
			
			return result;
		}
		
		public override Hashtable GetInfo(IList keys)
		{
			StringBuilder sb = new StringBuilder("GETINFO");
			foreach (string key in keys) {
				sb.Append(" ").Append(key);
			}
			
			sb.Append("\r\n");
			ArrayList lst = (ArrayList) SendAndWaitForResponse(sb.ToString(), null);
			
			Hashtable result = new Hashtable();
			
			foreach (ReplyLine rl in lst) {
				int idx = rl.Msg.IndexOf("=");
				
				if (idx<0)
					break;
				
				string k = rl.Msg.Substring(0, idx);
				
				string v;
				
				if (rl.Rest != null)
					v = rl.Rest;
				else
					v = rl.Msg.Substring(idx+1);
				
				result.Add(k, v);
			}
			
			return result;
		}
		
		public override string ExtendCircuit(string circID, string path)
		{
			ArrayList lst = (ArrayList) SendAndWaitForResponse("EXTENDCIRCUIT " + circID + " " + path + "\r\n", null);
			
			return ((ReplyLine)lst[0]).Msg;
		}
		
		public override void AttachStream(string streamID, string circID)
		{
			SendAndWaitForResponse("ATTACHSTREAM " + streamID + " " + circID + "\r\n", null);
		}
		
		public override string PostDescriptor(string desc)
		{
			ArrayList lst = (ArrayList) SendAndWaitForResponse("+POSTDESCRIPTOR\r\n", desc);
			
			return ((ReplyLine)lst[0]).Msg;
		}
		
		public override void RedirectStream(string streamID, string address)
		{
				SendAndWaitForResponse("REDIRECTSTREAM " + streamID + " " + address + "\r\n", null);
		}
		
		public override void CloseStream(string streamID, byte reason)
		{
			SendAndWaitForResponse("CLOSESTREAM " + streamID + " " + reason + "\r\n", null);
		}
		
		public override void CloseCircuit(string circID, bool ifUnused)
		{
			SendAndWaitForResponse("CLOSECIRCUIT " + circID + (ifUnused? " IFUNUSED":"") + "\r\n", null);
		}
	}
}

--- NEW FILE: TorControlConnectionBase.cs ---
/*
 * File TorControlConnectionBase.cs
 * 
 * Copyright (C) 2005 Oliver Rau (olra0001@xxxxxxxxxxxxxxxxxxx)
 * 
 * See LICENSE file for copying information 
 * 
 * Created on 08.08.2005 20:37
 * 
 * $Id: TorControlConnectionBase.cs,v 1.1 2005/11/09 21:47:04 nickm Exp $
 */

using System;
using System.Collections;
using System.IO;
using System.Threading;

namespace Tor.Control
{
	/// <summary>
	/// This is a basic implementation to the ITorControllConnection interface, which holds
	/// the basic methods needed by the TorControllConnection.
	/// This class may be use to write future classes according to newer revision of the
	/// TorControlConnectionProtocol.
	/// </summary>
	public abstract class TorControlConnectionBase : ITorControlConnection
	{
		#region Events
		public abstract event CircuitStatusEventHandler  CircuitStatus;
		public abstract event StreamStatusEventHandler   StreamStatus;
		public abstract event OrConnStatusEventHandler   OrConnStatus;
		public abstract event BandwidthUsedEventHandler  BandwidthUsed;
		public abstract event NewDescriptorsEventHandler NewDescriptors;
		public abstract event MessageEventHandler        Message;
		public abstract event UnrecognizedEventHandler   Unrecognized;
		#endregion
		
		protected Queue  waiters;
		protected Thread thread;
		
		protected bool         debug = false;
		protected StreamWriter debugStream;
		
		protected TorControlConnectionBase()
		{
			this.waiters = Queue.Synchronized(new Queue());
		}
		
		#region Debugging methods
		public void EnableDebug()
		{
			debug = true;
			this.debugStream = null;
		}
		
		public void EnableDebug(Stream debugStream)
		{
			debug = true;
			
			this.debugStream = new StreamWriter(debugStream);
			
		}
		
		public void DisableDebug()
		{
			debug = false;
			this.debugStream = null;
		}
		
		public void WriteDebug(string message)
		{
			if (!debug)
				return;
			
			if (this.debugStream == null)
				Console.WriteLine(message);
			else
				this.debugStream.WriteLine(message);
				
		}
		#endregion Debugging methods
		
		protected void Run()
		{
			try {
				React();
				
				//TODO: specify the Exception more exactly
			} catch (Exception ex) {
				throw new ApplicationException();
			}
		}
		
		/// <summary>
		/// Start a thread to react to Tor's responses in the background.
		/// This is necessary to handle asynchronous events and synchronous
		/// responses that arrive independantly over the same socket.
		/// </summary>
		/// <param name="daemon">If the thread should be run in the background.</param>
		/// <returns>The new thread.</returns>
		public Thread LaunchThread(bool daemon)
		{
			Thread th = new Thread(new ThreadStart(Run));
			th.Name = "TorConnectionWorker";
			
			th.IsBackground = daemon;
			th.Start();
			this.thread = th;
			return th;
		}
		
		protected void CheckThread()
		{
			if (thread == null)
				LaunchThread(true);
		}
		
		protected abstract void React();
		
		/// <summary>
		/// Change the value of the configuration option 'key' to 'val'.
		/// </summary>
		/// <param name="key"></param>
		/// <param name="value"></param>
		public void SetConf(string key, string value)
		{
			IList lst = new ArrayList();
			lst.Add(key + " " + value);
			SetConf(lst);
		}
		
		public void SetConf(Hashtable kvHt)
		{
			IList lst = new ArrayList();
			
			foreach (string key in kvHt.Keys) {
				lst.Add(key + " " + kvHt[key] + "\n");
			}
			SetConf(lst);
		}
		
		public abstract void SetConf(IList kvList);
		
		public IList GetConf(string key)
		{
			IList lst = new ArrayList();
			lst.Add(key);
			return GetConf(lst);
		}
		
		public abstract IList GetConf(IList keys);
		
		public abstract void SetEvents(IList events);
		
		public abstract void Authenticate(byte[] auth);
		public abstract void SaveConf();
		public abstract void Signal(string signal);
		
		public abstract Hashtable MapAddresses(IList kvLines);
		
		public Hashtable MapAddresses(Hashtable addresses)
		{
			IList kvList = new ArrayList();
			
			foreach (string key in addresses.Keys) {
				kvList.Add(key + " " + addresses[key]);
			}
			
			return MapAddresses(kvList);
		}
		
		public abstract Hashtable GetInfo(IList keys);
		
		public string GetInfo(string key)
		{
			IList lst = new ArrayList();
			lst.Add(key);
			Hashtable m = GetInfo(lst);
			return (string) m[key];
		}
		
    public abstract string ExtendCircuit(string circID, string path);

    public abstract void AttachStream(string streamID, string circID);

    public abstract string PostDescriptor(string desc);

    public abstract void RedirectStream(string streamID, string address);

    public abstract void CloseStream(string streamID, byte reason);

    public abstract void CloseCircuit(string circID, bool ifUnused);
	}
}

--- NEW FILE: TorControlConnectionFactory.cs ---
/*
 * File TorControlConnectionFactory.cs
 * 
 * Copyright (C) 2005 Oliver Rau (olra0001@xxxxxxxxxxxxxxxxxxx)
 * 
 * See LICENSE file for copying information 
 * 
 * Created on 08.08.2005 20:37
 * 
 * $Id: TorControlConnectionFactory.cs,v 1.1 2005/11/09 21:47:04 nickm Exp $
 */

using System;
using System.IO;
using System.Net.Sockets;

namespace Tor.Control
{
	/// <summary>
	/// This is the factory class for the TorConnection, which checks the version for you
	/// and then loads the corresponding TorControlConnection.
	/// </summary>
	public class TorControlConnectionFactory
	{
		protected static int DetectVersion(Stream stream)
		{
			byte[] buffer = {0, 0, 13, 10};
			
			stream.Write(buffer, 0, 4);
			
			BinaryReader br = new BinaryReader(stream);
			
			int len = br.ReadUInt16();
			int tp  = br.ReadUInt16();
			
			if (tp == 0) {
				byte[] err = new byte[len];
				br.Read(err, 0, err.Length);
				return 0;
			} else if ((len & 0xff00) != 0x0a00 &&
			           (len & 0x00ff) != 0x000a &&
			           (tp  & 0xff00) != 0x0a00 &&
			           (tp  & 0x00ff) != 0x000a) {
				while (br.Read() != '\n');
			}
			
			return 1;
		}
		
		public static ITorControlConnection GetConnection(TcpClient client)
		{
			int version = DetectVersion(client.GetStream());
			
			// TODO: Checkin future versions here
			if (version == 0)
				return new TorControlConnection0(client);
			else
				return new TorControlConnection1(client);
		}

	}
}