Subversion Repositories general

Compare Revisions

No changes between revisions

Ignore whitespace Rev 1235 → Rev 1236

/TCPproxy/tags/0002-second_public/Network.cs
0,0 → 1,2570
using System;
using System.Collections;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;
 
// FIXME:
// - deny write access to all public properties
// - for text/xml get encoding from body if not specified in header
// - option to not store parsed data, just raw packets and reparse on demand
// - implement all others (not 'identity' and 'chunked') transfer- and content- encodings
// - benchmark one-by-one byte iterator vs. block read
// - locks for HttpMessage in parser
namespace TCPproxy
{
public enum BinLogTypes
{
None = 1,
TcpConnection
}
 
public enum LogLevel
{
Critical,
Error,
Warning,
Important,
Info,
Debug
}
 
public enum SocketState
{
None,
Connecting,
Connected,
ShutdownSend,
ShutdownReceived,
Closed
}
 
public enum TcpMessageDirection
{
None,
Local,
Remote
}
 
public enum HttpVersion
{
V0_9,
V1_0,
V1_1
}
 
public enum HttpTransferEncoding
{
None, // there is no body
Identity,
Chunked,
Gzip,
Compress,
Deflate,
Unknown
}
 
public enum HttpContentEncoding
{
None, // there is no body
Identity,
Gzip,
Compress,
Deflate,
Unknown
}
 
public class TcpLogEventArgs : EventArgs
{
private readonly LogLevel level;
private readonly string message;
private readonly Exception exception;
 
public LogLevel Level
{
get { return level; }
}
 
public string Message
{
get { return message; }
}
 
public Exception Exception
{
get { return exception; }
}
 
internal TcpLogEventArgs(LogLevel level, string message, Exception exception)
{
this.level = level;
this.message = message;
this.exception = exception;
}
}
 
public class TcpEventArgs : EventArgs
{
internal TcpEventArgs()
{
}
}
 
public class TcpConnectionEventArgs : EventArgs
{
private readonly TcpConnection tcp;
 
public TcpConnection Tcp
{
get { return tcp; }
}
 
internal TcpConnectionEventArgs(TcpConnection tcp)
{
this.tcp = tcp;
}
}
 
public class TcpHttpEventArgs : EventArgs
{
private readonly HttpMessage http;
 
public HttpMessage Http
{
get { return http; }
}
 
internal TcpHttpEventArgs(HttpMessage http)
{
this.http = http;
}
}
 
public delegate void TcpLogEventHandler(object sender, TcpLogEventArgs e);
 
public delegate void TcpEventHandler(object sender, TcpEventArgs e);
 
public delegate void TcpConnectionEventHandler(object sender, TcpConnectionEventArgs e);
 
public delegate void TcpHttpEventHandler(object sender, TcpHttpEventArgs e);
 
public class TcpListener
{
private int listenPort = -1;
private IPAddress resendHost;
private int resendPort = -1;
private Socket socket;
private int tcpId = 0;
private ArrayList tcpConnections = new ArrayList();
 
public TcpListener(int listenPort, IPAddress resendHost, int resendPort)
{
this.listenPort = listenPort;
this.resendHost = resendHost;
this.resendPort = resendPort;
}
 
public void StartListening()
{
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socket.Bind(new IPEndPoint(IPAddress.Any, listenPort));
socket.Listen(100);
ThreadDebugger.Add(socket.BeginAccept(new AsyncCallback(OnClientConnect), null), "StartListening");
SendLog(null, LogLevel.Important, "Listen on " + socket.LocalEndPoint);
}
 
public void StopListening()
{
try
{
if(socket != null)
{
SendLog(null, LogLevel.Important, "Stop listening " + socket.LocalEndPoint);
socket.Close();
}
}
catch(ObjectDisposedException) // socket is already closed
{
}
catch(Exception ex)
{
Console.WriteLine(ex.Message + " (" + ex.GetType().Name + ")\n" + ex.StackTrace);
}
}
 
public void CancelAll()
{
ArrayList tcpConnectionsCopy;
 
lock(tcpConnections)
{
tcpConnectionsCopy = new ArrayList(tcpConnections);
}
 
foreach(TcpConnection tcp in tcpConnectionsCopy)
{
tcp.Cancel();
}
}
 
protected virtual void OnClientConnect(IAsyncResult asyn)
{
try
{
Socket worker = socket.EndAccept(asyn);
ThreadDebugger.Add(socket.BeginAccept(new AsyncCallback(OnClientConnect), null), "OnClientConnect"); // wait for next client
 
TcpConnection tcp = new TcpConnection(string.Format("{0:0000}", tcpId++));
tcp.Close += new TcpEventHandler(TcpConnectionClosed);
OnNewTcp(new TcpConnectionEventArgs(tcp));
 
lock(tcpConnections)
{
tcpConnections.Add(tcp);
}
 
tcp.Continue(resendHost, resendPort, worker);
}
catch(ObjectDisposedException) {} // socket is closed
catch(Exception ex)
{
Console.WriteLine(ex.Message + " (" + ex.GetType().Name + ")\n" + ex.StackTrace);
}
}
 
protected virtual void TcpConnectionClosed(object sender, TcpEventArgs e)
{
lock(tcpConnections)
{
tcpConnections.Remove((TcpConnection)sender);
}
}
 
public event TcpConnectionEventHandler NewTcp;
 
protected virtual void OnNewTcp(TcpConnectionEventArgs e)
{
if(NewTcp != null)
{
NewTcp(this, e);
}
}
 
public event TcpLogEventHandler Log;
 
protected virtual void OnLog(TcpLogEventArgs e)
{
if(Log != null)
{
Log(this, e);
}
}
 
protected virtual void SendLog(TcpConnection tcp, LogLevel level, string message)
{
TcpLogEventArgs e = new TcpLogEventArgs(level, message, null);
OnLog(e);
}
 
protected virtual void SendLog(TcpConnection tcp, LogLevel level, Exception ex)
{
TcpLogEventArgs e = new TcpLogEventArgs(level, null, ex);
OnLog(e);
}
 
public void ReadBinLog(BinaryReader reader)
{
// tcp connections
TcpConnection tcp;
while((tcp = TcpConnection.ReadBinLog(reader)) != null) {
OnNewTcp(new TcpConnectionEventArgs(tcp));
}
}
}
 
public class TcpConnection
{
private string id;
private DateTime startTimestamp = DateTime.MinValue;
private DateTime localEndTimestamp = DateTime.MinValue;
private DateTime remoteEndTimestamp = DateTime.MinValue;
private IPEndPoint localPoint;
private IPEndPoint remotePoint;
private LinkedList messages = new LinkedList();
private LinkedListReadOnly messagesReadOnly;
private SocketState localState = SocketState.None;
private SocketState remoteState = SocketState.None;
private SocketWorker worker;
private LinkedList https = new LinkedList();
private HttpParser httpParser;
 
public string Id
{
get { return id; }
}
 
public DateTime StartTimestamp
{
get { return startTimestamp; }
}
 
public DateTime LocalEndTimestamp
{
get { return localEndTimestamp; }
}
 
public DateTime RemoteEndTimestamp
{
get { return remoteEndTimestamp; }
}
 
public IPEndPoint LocalPoint
{
get { return localPoint; }
}
 
public IPEndPoint RemotePoint
{
get { return remotePoint; }
}
 
public LinkedListReadOnly Messages
{
get { return new LinkedListReadOnly(messages); }
}
 
public SocketState LocalState
{
get { return localState; }
}
 
public SocketState RemoteState
{
get { return remoteState; }
}
 
private int CountBytes(TcpMessageDirection direction)
{
int count = 0;
 
foreach(TcpMessage message in messages)
{
if(message.Direction == direction)
count += message.Length;
}
return count;
}
 
public int SentBytes
{
get { return CountBytes(TcpMessageDirection.Local); }
}
 
public int ReceivedBytes
{
get { return CountBytes(TcpMessageDirection.Remote); }
}
 
internal TcpConnection(string id)
{
this.id = id;
this.httpParser = HttpParser.Parse(this);
this.messagesReadOnly = new LinkedListReadOnly(messages);
}
 
internal void Continue(IPAddress resendHost, int resendPort, Socket localSocket)
{
SendLog(LogLevel.Important, "Client connected from " + ((IPEndPoint)localSocket.RemoteEndPoint).ToString());
SetLocalState(SocketState.Connected);
this.worker = new SocketWorker(resendHost, resendPort, localSocket, this);
}
 
public void Cancel()
{
if(worker != null)
worker.Cancel();
}
 
protected TcpMessage Append(TcpMessageDirection direction, byte[] newBytes)
{
return Append(direction, newBytes, newBytes.Length);
}
 
protected TcpMessage Append(TcpMessageDirection direction, byte[] newBytes, int length)
{
if(newBytes == null) return null;
 
TcpMessage message;
 
lock(this)
{
message = new TcpMessage();
message.Direction = direction;
messages.Add(message);
message.Append(newBytes, length);
}
 
httpParser.NewMessageArived();
OnUpdate(new TcpEventArgs());
 
return message;
}
 
internal void AddHttpMessage(HttpMessage http)
{
lock(this)
{
https.Add(http);
TcpHttpEventArgs e = new TcpHttpEventArgs(http);
OnNewHttp(e);
}
}
 
public override string ToString() // FIXME delete the method
{
return id + " " + startTimestamp.ToString("HH:mm:ss.ffff");
}
 
protected void SetLocalPoint(IPEndPoint localPoint)
{
this.localPoint = localPoint;
OnUpdate(new TcpEventArgs());
}
 
protected void SetRemotePoint(IPEndPoint remotePoint)
{
this.remotePoint = remotePoint;
OnUpdate(new TcpEventArgs());
}
 
protected void SetLocalState(SocketState localState)
{
if(this.localState == SocketState.None && localState == SocketState.Connecting)
{
startTimestamp = DateTime.Now;
}
else if(this.localState == SocketState.None && localState == SocketState.Connected)
{
startTimestamp = DateTime.Now;
}
else if(this.localState == SocketState.None && localState == SocketState.Closed)
{
}
else if(this.localState == SocketState.Connecting && localState == SocketState.Connected)
{
}
else if(this.localState == SocketState.Connecting && localState == SocketState.Closed)
{
if(localEndTimestamp == DateTime.MinValue) localEndTimestamp = DateTime.Now;
}
else if(this.localState == SocketState.Connected && localState == SocketState.ShutdownSend)
{
}
else if(this.localState == SocketState.Connected && localState == SocketState.ShutdownReceived)
{
if(localEndTimestamp == DateTime.MinValue) localEndTimestamp = DateTime.Now;
}
else if(this.localState == SocketState.Connected && localState == SocketState.Closed)
{
if(localEndTimestamp == DateTime.MinValue) localEndTimestamp = DateTime.Now;
}
else if(this.localState == SocketState.ShutdownSend && localState == SocketState.Closed)
{
if(localEndTimestamp == DateTime.MinValue) localEndTimestamp = DateTime.Now;
}
else if(this.localState == SocketState.ShutdownSend && localState == SocketState.ShutdownReceived)
{
if(localEndTimestamp == DateTime.MinValue) localEndTimestamp = DateTime.Now;
}
else if(this.localState == SocketState.ShutdownReceived && localState == SocketState.ShutdownSend)
{
}
else if(this.localState == SocketState.ShutdownReceived && localState == SocketState.Closed)
{
if(localEndTimestamp == DateTime.MinValue) localEndTimestamp = DateTime.Now;
}
else if(this.localState == SocketState.Closed && localState == SocketState.Closed)
{
if(localEndTimestamp == DateTime.MinValue) localEndTimestamp = DateTime.Now;
}
else
{
throw new Exception("Wrong local socket state change: from " + this.localState + " to " + localState);
}
this.localState = localState;
if(this.localState == SocketState.Closed) httpParser.NewMessageArived();
OnUpdate(new TcpEventArgs());
}
 
protected void SetRemoteState(SocketState remoteState)
{
if(this.remoteState == SocketState.None && remoteState == SocketState.Connecting)
{
}
else if(this.remoteState == SocketState.None && remoteState == SocketState.Connected)
{
}
else if(this.remoteState == SocketState.None && remoteState == SocketState.Closed)
{
}
else if(this.remoteState == SocketState.Connecting && remoteState == SocketState.Connected)
{
}
else if(this.remoteState == SocketState.Connecting && remoteState == SocketState.Closed)
{
if(remoteEndTimestamp == DateTime.MinValue) remoteEndTimestamp = DateTime.Now;
}
else if(this.remoteState == SocketState.Connected && remoteState == SocketState.ShutdownSend)
{
}
else if(this.remoteState == SocketState.Connected && remoteState == SocketState.ShutdownReceived)
{
if(remoteEndTimestamp == DateTime.MinValue) remoteEndTimestamp = DateTime.Now;
}
else if(this.remoteState == SocketState.Connected && remoteState == SocketState.Closed)
{
if(remoteEndTimestamp == DateTime.MinValue) remoteEndTimestamp = DateTime.Now;
}
else if(this.remoteState == SocketState.ShutdownSend && remoteState == SocketState.Closed)
{
if(remoteEndTimestamp == DateTime.MinValue) remoteEndTimestamp = DateTime.Now;
}
else if(this.remoteState == SocketState.ShutdownSend && remoteState == SocketState.ShutdownReceived)
{
if(remoteEndTimestamp == DateTime.MinValue) remoteEndTimestamp = DateTime.Now;
}
else if(this.remoteState == SocketState.ShutdownReceived && remoteState == SocketState.ShutdownSend)
{
}
else if(this.remoteState == SocketState.ShutdownReceived && remoteState == SocketState.Closed)
{
if(remoteEndTimestamp == DateTime.MinValue) remoteEndTimestamp = DateTime.Now;
}
else if(this.remoteState == SocketState.Closed && remoteState == SocketState.Closed)
{
if(remoteEndTimestamp == DateTime.MinValue) remoteEndTimestamp = DateTime.Now;
}
else
{
throw new Exception("Wrong remote socket state change: from " + this.remoteState + " to " + remoteState);
}
this.remoteState = remoteState;
if(this.remoteState == SocketState.Closed) httpParser.NewMessageArived();
OnUpdate(new TcpEventArgs());
}
 
public event TcpEventHandler Update;
 
protected virtual void OnUpdate(TcpEventArgs e)
{
if(Update != null)
{
Update(this, e);
}
}
 
public event TcpHttpEventHandler NewHttp;
 
protected virtual void OnNewHttp(TcpHttpEventArgs e)
{
if(NewHttp != null)
{
NewHttp(this, e);
}
}
 
public event TcpEventHandler Close;
 
protected virtual void OnClose(TcpEventArgs e)
{
if(Close != null)
{
Close(this, e);
}
}
 
public event TcpLogEventHandler Log;
 
protected virtual void OnLog(TcpLogEventArgs e)
{
if(Log != null)
{
Log(this, e);
}
}
 
protected virtual void SendLog(LogLevel level, string message)
{
TcpLogEventArgs e = new TcpLogEventArgs(level, message, null);
OnLog(e);
}
 
protected virtual void SendLog(LogLevel level, Exception ex)
{
TcpLogEventArgs e = new TcpLogEventArgs(level, null, ex);
OnLog(e);
}
 
private void WriteEndPoint(BinaryWriter writer, IPEndPoint endPoint)
{
// end point as (family as int, ip length, ip as bytes, port)
byte[] ipBuf = endPoint.Address.GetAddressBytes();
writer.Write((UInt32)endPoint.AddressFamily);
writer.Write((UInt32)ipBuf.Length);
writer.Write(ipBuf);
writer.Write((UInt32)endPoint.Port);
}
 
public void WriteBinLog(BinaryWriter writer)
{
// header
writer.Write((byte)BinLogTypes.TcpConnection);
 
// id as (length, UTF-8)
byte[] idBuf = Encoding.UTF8.GetBytes(id);
writer.Write((UInt32)idBuf.Length);
writer.Write(idBuf);
 
// timestamps as ticks
writer.Write((UInt64)startTimestamp.Ticks);
writer.Write((UInt64)localEndTimestamp.Ticks);
writer.Write((UInt64)remoteEndTimestamp.Ticks);
 
// end points as (family as int, ip length, ip as bytes, port)
WriteEndPoint(writer, localPoint);
WriteEndPoint(writer, remotePoint);
 
// states as byte
writer.Write((byte)localState);
writer.Write((byte)remoteState);
 
// each tcp message as (direction as byte, timestamp in ticks, length, content)
foreach(TcpMessage message in messages) {
writer.Write((byte)message.Direction);
writer.Write((UInt64)message.Timestamp.Ticks);
writer.Write((UInt32)message.Length);
writer.Write(message.Bytes, 0, message.Length);
}
 
// end of stream marker
writer.Write((byte)TcpMessageDirection.None);
}
 
private static IPEndPoint ReadEndPoint(BinaryReader reader)
{
// end point as (family as int, ip length, ip as bytes, port)
AddressFamily fam = (AddressFamily)reader.ReadInt32();
int addressLength = reader.ReadInt32();
byte[] addressBytes = reader.ReadBytes(addressLength);
int port = reader.ReadInt32();
IPAddress address = new IPAddress(addressBytes);
 
return new IPEndPoint(address, port);
}
 
internal static TcpConnection ReadBinLog(BinaryReader reader)
{
// header
BinLogTypes recordType = (BinLogTypes)reader.ReadByte();
 
if(recordType == BinLogTypes.None)
return null;
else if(recordType == BinLogTypes.TcpConnection) {
}
else
throw new Exception("Wrong data type");
 
// id as (length, UTF-8)
int idLength = (int)reader.ReadUInt32();
string id = Encoding.UTF8.GetString(reader.ReadBytes(idLength));
 
TcpConnection tcp = new TcpConnection(id);
 
// timestamps as ticks
tcp.startTimestamp = new DateTime((long)reader.ReadUInt64());
tcp.localEndTimestamp = new DateTime((long)reader.ReadUInt64());
tcp.remoteEndTimestamp = new DateTime((long)reader.ReadUInt64());
 
// end points as (family as int, ip length, ip as bytes, port)
tcp.localPoint = ReadEndPoint(reader);
tcp.remotePoint = ReadEndPoint(reader);
 
// states as byte - read but ignore
reader.ReadByte();
reader.ReadByte();
 
// each tcp message as (direction as byte, timestamp in ticks, length, content)
tcp.localState = SocketState.Closed;
tcp.remoteState = SocketState.Closed;
for(;;) {
TcpMessageDirection direction = (TcpMessageDirection)reader.ReadByte();
if(direction == TcpMessageDirection.None) break; // end of stream marker
 
DateTime timestamp = new DateTime((long)reader.ReadUInt64());
int bufLength = (int)reader.ReadUInt32();
byte[] buf = reader.ReadBytes(bufLength);
 
TcpMessage msg = tcp.Append(direction, buf);
msg.SetTimestamp(timestamp);
}
tcp.OnUpdate(new TcpEventArgs());
 
return tcp;
}
 
protected class SocketWorker
{
private enum SendCommandType
{
Send,
Shutdown,
Reset
}
 
private class SendCommand
{
public byte[] buffer = null;
public int length = 0;
public SendCommandType cmdType = SendCommandType.Send;
}
 
private static int BUF_SIZE = 2048;
 
private TcpConnection tcp;
private Socket localSocket;
private Socket remoteSocket;
private byte[] localDataBuffer;
private byte[] remoteDataBuffer;
private AsyncCallback receiveLocalMethod;
private AsyncCallback receiveRemoteMethod;
private Queue localSendQueue = new Queue();
private Queue remoteSendQueue = new Queue();
private AutoResetEvent localSendEvent = new AutoResetEvent(false);
private AutoResetEvent remoteSendEvent = new AutoResetEvent(false);
private bool localSocketSendShutdown = false;
private bool localSocketReceiveShutdown = false;
private bool remoteSocketSendShutdown = false;
private bool remoteSocketReceiveShutdown = false;
private ManualResetEvent remoteSocketEvent = new ManualResetEvent(false);
private Thread localSendThread;
private Thread remoteSendThread;
 
public SocketWorker(IPAddress resendHost, int resendPort, Socket localSocket, TcpConnection tcp)
{
try
{
tcp.SendLog(LogLevel.Debug, string.Format("Local socket: {0}:{1} <-> {2}:{3}",
((IPEndPoint)localSocket.LocalEndPoint).Address,
((IPEndPoint)localSocket.LocalEndPoint).Port,
((IPEndPoint)localSocket.RemoteEndPoint).Address,
((IPEndPoint)localSocket.RemoteEndPoint).Port));
 
this.localSocket = localSocket;
this.tcp = tcp;
receiveLocalMethod = new AsyncCallback(OnLocalReceived);
receiveRemoteMethod = new AsyncCallback(OnRemoteReceived);
 
tcp.SetLocalPoint((IPEndPoint)localSocket.RemoteEndPoint);
this.localSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.NoDelay, 1);
 
localSendThread = new Thread(new ThreadStart(LocalSendProc));
ThreadDebugger.Add(localSendThread, "LocalSendProc");
localSendThread.Name = "SocketWorker.LocalSendProc";
localSendThread.Start();
 
ContinueLocalReceive();
 
if(resendHost == null)
{
remoteSocket = null;
}
else
{
tcp.SetRemoteState(SocketState.Connecting);
 
IPEndPoint point = new IPEndPoint(resendHost, resendPort);
remoteSocket = new Socket(point.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
remoteSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.NoDelay, 1);
remoteSocket.Connect(point);
tcp.SetRemoteState(SocketState.Connected);
tcp.SetRemotePoint((IPEndPoint)remoteSocket.RemoteEndPoint);
 
remoteSendThread = new Thread(new ThreadStart(RemoteSendProc));
ThreadDebugger.Add(remoteSendThread, "RemoteSendProc");
remoteSendThread.Name = "SocketWorker.RemoteSendProc";
remoteSendThread.Start();
 
ContinueRemoteReceive();
remoteSocketEvent.Set(); // remote socket ready to send data
tcp.SendLog(LogLevel.Info, "Connected to server " + tcp.RemotePoint.ToString());
 
tcp.SendLog(LogLevel.Debug, string.Format("Remote socket: {0}:{1} <-> {2}:{3}",
((IPEndPoint)remoteSocket.LocalEndPoint).Address,
((IPEndPoint)remoteSocket.LocalEndPoint).Port,
((IPEndPoint)remoteSocket.RemoteEndPoint).Address,
((IPEndPoint)remoteSocket.RemoteEndPoint).Port));
}
}
catch(Exception ex)
{
tcp.SendLog(LogLevel.Warning, ex);
Cancel();
}
}
 
private void ContinueLocalReceive()
{
try
{
localDataBuffer = new byte[BUF_SIZE];
ThreadDebugger.Add(
localSocket.BeginReceive(localDataBuffer, 0, BUF_SIZE, SocketFlags.None, receiveLocalMethod, this),
"ContinueLocalReceive");
}
catch(ObjectDisposedException ex) // the socket is closed
{
tcp.SendLog(LogLevel.Info, ex);
Cancel();
}
catch(Exception ex)
{
tcp.SendLog(LogLevel.Warning, ex);
Cancel();
}
}
 
private void ContinueRemoteReceive()
{
try
{
remoteDataBuffer = new byte[BUF_SIZE];
ThreadDebugger.Add(
remoteSocket.BeginReceive(remoteDataBuffer, 0, BUF_SIZE, SocketFlags.None, receiveRemoteMethod, this),
"ContinueRemoteReceive");
}
catch(ObjectDisposedException ex) // the socket is closed
{
tcp.SendLog(LogLevel.Info, ex);
Cancel();
}
catch(Exception ex)
{
tcp.SendLog(LogLevel.Warning, ex);
Cancel();
}
}
 
private void CheckLocalSocket()
{
lock(localSocket)
{
try
{
if(localSocketReceiveShutdown && localSocketSendShutdown)
{
if(localSocket.Connected) localSocket.Close();
tcp.SetLocalState(SocketState.Closed);
}
}
catch(Exception ex) // in any case we want to close the socket
{
tcp.SendLog(LogLevel.Warning, ex);
}
}
}
 
private void CheckRemoteSocket()
{
lock(remoteSocket)
{
try
{
if(remoteSocketReceiveShutdown && remoteSocketSendShutdown)
{
if(remoteSocket.Connected) remoteSocket.Close();
tcp.SetRemoteState(SocketState.Closed);
}
}
catch(Exception ex) // in any case we want to close the socket
{
tcp.SendLog(LogLevel.Warning, ex);
}
}
}
 
private void OnLocalReceived(IAsyncResult asyn)
{
try
{
int bytesReceived = 0;
bool reset = false;
 
try
{
bytesReceived = localSocket.EndReceive(asyn);
}
catch(ObjectDisposedException)
{
reset = true;
}
catch(SocketException ex)
{
if(ex.ErrorCode == 10054)
reset = true;
else
throw ex;
}
 
if(reset)
{
tcp.SendLog(LogLevel.Info, "Got reset from local end");
 
lock(localSocket)
{
if(localSocket.Connected) localSocket.Close();
tcp.SetLocalState(SocketState.Closed);
}
 
SendCommand cmd = new SendCommand();
cmd.cmdType = SendCommandType.Reset;
lock(localSendQueue)
{
localSendQueue.Enqueue(cmd);
localSendEvent.Set();
}
}
else if(bytesReceived <= 0)
{
tcp.SendLog(LogLevel.Info, "Got showdown from local end");
 
localSocket.Shutdown(SocketShutdown.Receive);
tcp.SetLocalState(SocketState.ShutdownReceived);
localSocketReceiveShutdown = true;
CheckLocalSocket();
 
SendCommand cmd = new SendCommand();
cmd.cmdType = SendCommandType.Shutdown;
lock(localSendQueue)
{
localSendQueue.Enqueue(cmd);
localSendEvent.Set();
}
}
else
{
tcp.SendLog(LogLevel.Debug, string.Format("Local received {0} bytes", bytesReceived));
 
SendCommand cmd = new SendCommand();
cmd.buffer = localDataBuffer;
cmd.length = bytesReceived;
lock(localSendQueue)
{
localSendQueue.Enqueue(cmd);
localSendEvent.Set();
}
ContinueLocalReceive();
}
}
catch(Exception ex)
{
tcp.SendLog(LogLevel.Warning, ex);
Cancel();
}
}
 
private void LocalSendProc()
{
try
{
while(true)
{
SendCommand cmd;
 
if(localSendQueue.Count == 0)
{
localSendEvent.WaitOne();
}
 
lock(localSendQueue)
{
localSendEvent.Reset();
cmd = (SendCommand)localSendQueue.Dequeue();
}
 
if(cmd.cmdType == SendCommandType.Reset) // reset marker
{
if(remoteSocket == null || !remoteSocket.Connected) remoteSocketEvent.WaitOne();
tcp.SendLog(LogLevel.Debug, string.Format("Send reset to remote end"));
lock(remoteSocket)
{
if(!remoteSocket.Connected) remoteSocket.Close();
tcp.SetRemoteState(SocketState.Closed);
}
 
break; // no more send allowed
}
else if(cmd.cmdType == SendCommandType.Shutdown) // shutdown marker
{
if(remoteSocket == null || !remoteSocket.Connected) remoteSocketEvent.WaitOne();
tcp.SendLog(LogLevel.Debug, string.Format("Send shutdown to remote end"));
remoteSocket.Shutdown(SocketShutdown.Send);
tcp.SetRemoteState(SocketState.ShutdownSend);
remoteSocketSendShutdown = true;
CheckRemoteSocket();
 
break; // no more send allowed
}
else
{
// store received bytes
tcp.Append(TcpMessageDirection.Local, cmd.buffer, cmd.length);
 
// forward it
if(remoteSocket == null || !remoteSocket.Connected) remoteSocketEvent.WaitOne();
tcp.SendLog(LogLevel.Debug, string.Format("Send {0} bytes to remote end", cmd.length));
remoteSocket.Send(cmd.buffer, cmd.length, SocketFlags.None);
}
}
}
catch(ThreadAbortException)
{
}
catch(Exception ex)
{
tcp.SendLog(LogLevel.Warning, ex);
Cancel();
}
}
 
private void OnRemoteReceived(IAsyncResult asyn)
{
try
{
int bytesReceived = 0;
bool reset = false;
 
try
{
bytesReceived = remoteSocket.EndReceive(asyn);
}
catch(ObjectDisposedException)
{
reset = true;
}
catch(SocketException ex)
{
if(ex.ErrorCode == 10054)
reset = true;
else
throw ex;
}
 
if(reset)
{
tcp.SendLog(LogLevel.Info, "Got reset from remote end");
 
lock(remoteSocket)
{
if(remoteSocket.Connected) remoteSocket.Close();
tcp.SetRemoteState(SocketState.Closed);
}
 
SendCommand cmd = new SendCommand();
cmd.cmdType = SendCommandType.Reset;
lock(remoteSendQueue)
{
remoteSendQueue.Enqueue(cmd);
remoteSendEvent.Set();
}
}
else if(bytesReceived <= 0)
{
tcp.SendLog(LogLevel.Info, "Got showdown from remote end");
 
remoteSocket.Shutdown(SocketShutdown.Receive);
tcp.SetRemoteState(SocketState.ShutdownReceived);
remoteSocketReceiveShutdown = true;
CheckRemoteSocket();
 
SendCommand cmd = new SendCommand();
cmd.cmdType = SendCommandType.Shutdown;
lock(remoteSendQueue)
{
remoteSendQueue.Enqueue(cmd);
remoteSendEvent.Set();
}
}
else
{
tcp.SendLog(LogLevel.Debug, string.Format("Remote received {0} bytes", bytesReceived));
 
SendCommand cmd = new SendCommand();
cmd.buffer = remoteDataBuffer;
cmd.length = bytesReceived;
lock(remoteSendQueue)
{
remoteSendQueue.Enqueue(cmd);
remoteSendEvent.Set();
}
ContinueRemoteReceive();
}
}
catch(Exception ex)
{
tcp.SendLog(LogLevel.Warning, ex);
Cancel();
}
}
 
private void RemoteSendProc()
{
try
{
while(true)
{
SendCommand cmd;
 
if(remoteSendQueue.Count == 0)
{
remoteSendEvent.WaitOne();
}
 
lock(remoteSendQueue)
{
remoteSendEvent.Reset();
cmd = (SendCommand)remoteSendQueue.Dequeue();
}
 
if(cmd.cmdType == SendCommandType.Reset) // reset marker
{
tcp.SendLog(LogLevel.Debug, string.Format("Send reset to local end"));
lock(localSocket)
{
if(localSocket.Connected) localSocket.Close();
tcp.SetLocalState(SocketState.Closed);
}
 
break; // no more send allowed
}
else if(cmd.cmdType == SendCommandType.Shutdown) // shutdown marker
{
tcp.SendLog(LogLevel.Debug, string.Format("Send shutdown to local end"));
localSocket.Shutdown(SocketShutdown.Send);
tcp.SetLocalState(SocketState.ShutdownSend);
localSocketSendShutdown = true;
CheckLocalSocket();
 
break; // no more send allowed
}
else
{
// store received bytes
tcp.Append(TcpMessageDirection.Remote, cmd.buffer, cmd.length);
 
// forward it
tcp.SendLog(LogLevel.Debug, string.Format("Send {0} bytes to local end", cmd.length));
localSocket.Send(cmd.buffer, cmd.length, SocketFlags.None);
}
}
}
catch(ThreadAbortException)
{
}
catch(Exception ex)
{
tcp.SendLog(LogLevel.Warning, ex);
Cancel();
}
}
 
public void Cancel()
{
tcp.SendLog(LogLevel.Important, "Connection canceled");
 
try
{
if(localSendThread != null && localSendThread.IsAlive) localSendThread.Abort();
if(remoteSendThread != null && remoteSendThread.IsAlive) remoteSendThread.Abort();
 
// close sockets
try
{
if(localSocket != null)
{
lock(localSocket)
{
if(localSocket.Connected) localSocket.Close();
}
}
}
catch(Exception ex) // in any case we want to close the socket
{
tcp.SendLog(LogLevel.Warning, ex);
}
tcp.SetLocalState(SocketState.Closed);
 
try
{
if(remoteSocket != null)
{
lock(remoteSocket)
{
if(remoteSocket.Connected) remoteSocket.Close();
}
}
}
catch(Exception ex) // in any case we want to close the socket
{
tcp.SendLog(LogLevel.Warning, ex);
}
tcp.SetRemoteState(SocketState.Closed);
 
// return
tcp.OnClose(new TcpEventArgs());
}
catch(Exception ex)
{
tcp.SendLog(LogLevel.Warning, ex);
}
}
}
}
 
internal class HttpParser
{
private enum HttpCharType
{
None,
Control,
Digit,
UpAlpha,
LoAlpha,
NonChar,
Separator,
CrLf,
}
 
private static HttpCharType[] charTypes = null;
private static bool[] tokenChars = null;
private static char[] charValues = {
'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
'\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0',
' ', '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?',
'@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_',
'`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~', '\0'
};
 
private static void InitTables()
{
if(charTypes != null) return;
 
// main table
charTypes = new HttpCharType[256];
 
for(int i = 0; i < charTypes.Length; i++) charTypes[i] = HttpCharType.None;
 
for(int i = 0; i <= 31; i++) charTypes[i] = HttpCharType.Control;
charTypes[127] = HttpCharType.Control; // <del>
 
for(int i = 48; i <= 57; i++) charTypes[i] = HttpCharType.Digit;
 
for(int i = 65; i <= 90; i++) charTypes[i] = HttpCharType.UpAlpha;
for(int i = 97; i <= 122; i++) charTypes[i] = HttpCharType.LoAlpha;
 
for(int i = 128; i < charTypes.Length; i++) charTypes[i] = HttpCharType.NonChar;
 
charTypes[ 40] = HttpCharType.Separator; // (
charTypes[ 41] = HttpCharType.Separator; // )
charTypes[ 60] = HttpCharType.Separator; // <
charTypes[ 62] = HttpCharType.Separator; // >
charTypes[ 64] = HttpCharType.Separator; // @
charTypes[ 44] = HttpCharType.Separator; // ,
charTypes[ 59] = HttpCharType.Separator; // ;
charTypes[ 58] = HttpCharType.Separator; // :
charTypes[ 92] = HttpCharType.Separator; // \
charTypes[ 34] = HttpCharType.Separator; // "
charTypes[ 47] = HttpCharType.Separator; // /
charTypes[ 91] = HttpCharType.Separator; // [
charTypes[ 93] = HttpCharType.Separator; // ]
charTypes[ 63] = HttpCharType.Separator; // ?
charTypes[ 61] = HttpCharType.Separator; // =
charTypes[123] = HttpCharType.Separator; // {
charTypes[125] = HttpCharType.Separator; // }
charTypes[ 32] = HttpCharType.Separator; // <space>
charTypes[ 9] = HttpCharType.Separator; // <tab>
 
charTypes[ 13] = HttpCharType.CrLf; // <CR>
charTypes[ 10] = HttpCharType.CrLf; // <LF>
 
// token table
tokenChars = new bool[256];
for(int i = 0; i < tokenChars.Length; i++)
{
tokenChars[i] = !(charTypes[i] == HttpCharType.NonChar
|| charTypes[i] == HttpCharType.Control || charTypes[i] == HttpCharType.Separator
|| charTypes[i] == HttpCharType.CrLf);
}
}
 
private class ParsePosition
{
private TcpConnection messages;
private IEnumerator messagesEnum;
private TcpMessage tcp = null;
private int tcpPos;
private int tcpLen;
private bool tcpEnd = false;
private AutoResetEvent newMessageEvent;
private AutoResetEvent nextMessageEvent;
 
public ParsePosition(TcpConnection messages, AutoResetEvent nextMessageEvent)
{
this.messages = messages;
this.messagesEnum = messages.Messages.GetEnumerator();
this.newMessageEvent = new AutoResetEvent(false);
this.nextMessageEvent = nextMessageEvent;
}
 
public AutoResetEvent NewMessageEvent
{
get { return newMessageEvent; }
}
 
public bool IsEnd
{
get { return tcpEnd; }
}
 
public TcpMessage CurrentMessage
{
get { return tcp; }
}
 
private bool MoveNext()
{
for(bool moved = false; !moved; )
{
lock(messages)
{
newMessageEvent.Reset();
moved = messagesEnum.MoveNext();
 
if(!moved && messages.LocalState == SocketState.Closed && messages.RemoteState == SocketState.Closed)
return false;
}
 
if(moved) break;
 
if(!newMessageEvent.WaitOne())
throw new Exception("Cannot get next TCP message");
 
lock(messages)
{
if(messages.LocalState == SocketState.Closed && messages.RemoteState == SocketState.Closed)
return false;
 
moved = messagesEnum.MoveNext();
}
}
 
return true;
}
 
private void NextTcp()
{
if(tcpEnd) return;
 
TcpMessage newTcp = null;
 
do
{
if(!MoveNext())
{
tcpEnd = true;
break;
}
newTcp = (TcpMessage)messagesEnum.Current;
}
while(tcp != null && tcp.Direction != newTcp.Direction);
// FIXME correct? no hang?
 
if(!tcpEnd)
{
tcp = newTcp;
tcpLen = tcp.Length;
tcpPos = -1;
if(nextMessageEvent != null) nextMessageEvent.Set();
}
}
 
public void Back()
{
tcpPos--; // FIXME double check if it's always possible to go to _one_ step back
}
 
public byte NextOctet()
{
tcpPos++;
 
if(tcp == null || tcpPos >= tcpLen)
{
NextTcp();
tcpPos++;
}
if(tcpEnd) return 0;
 
return tcp.Bytes[tcpPos];
}
 
public byte NextOctetAndBack()
{
byte b = NextOctet();
if(!tcpEnd) Back();
 
return b;
}
 
public void SetDirection(TcpMessageDirection direction)
{
do
{
if(!MoveNext())
{
tcp = null;
tcpEnd = true;
break;
}
tcp = (TcpMessage)messagesEnum.Current;
}
while(tcp.Direction != direction);
 
if(!tcpEnd)
{
tcpLen = tcp.Length;
tcpPos = -1;
}
}
}
 
private TcpConnection messages;
private AutoResetEvent requestEvent = new AutoResetEvent(false); // new request found
private AutoResetEvent nextMessageEvent = new AutoResetEvent(false); // request goes to next TCP message
private LinkedList https = new LinkedList();
private ParsePosition requestPos;
private ParsePosition responsePos;
private Thread runThread;
 
public static HttpParser Parse(TcpConnection messages)
{
HttpParser parser = new HttpParser(messages);
parser.runThread = new Thread(new ThreadStart(parser.Run));
ThreadDebugger.Add(parser.runThread, "HttpParser.Run");
parser.runThread.Name = "HttpParser.Run";
parser.runThread.Start();
 
return parser;
}
 
public void NewMessageArived()
{
requestPos.NewMessageEvent.Set();
responsePos.NewMessageEvent.Set();
}
 
public Thread RunThread
{
get { return runThread; }
}
 
private HttpParser(TcpConnection messages)
{
this.messages = messages;
this.requestPos = new ParsePosition(messages, nextMessageEvent);
this.responsePos = new ParsePosition(messages, null);
InitTables();
}
 
/// <summary>
/// Try to recognize the stored TCP packets as sequence of HTTP messages (request-response)
/// </summary>
private void Run()
{
Thread responseThread = null;
 
try
{
responseThread = new Thread(new ThreadStart(MatchResponses));
ThreadDebugger.Add(responseThread, "MatchResponses");
responseThread.Name = "HttpParser.MatchResponses";
responseThread.Start();
 
// find requests
while(!requestPos.IsEnd)
{
HttpMessage http = new HttpMessage();
lock(https)
{
https.Add(http);
requestEvent.Set(); // new request available
}
 
messages.AddHttpMessage(http);
SkipEmptyLines(requestPos);
http.Request.StartTimestamp = requestPos.CurrentMessage.Timestamp;
 
ParseRequestLine(requestPos, http);
http.UpdateHttpMessage();
 
ParseHeaders(requestPos, http, http.Request);
SetRequestProperties(http, http.Request);
http.UpdateHttpMessage();
 
ParseBody(requestPos, http, http.Request);
if("text" == http.Request.ContentType && "xml" == http.Request.ContentSubtype)
{
http.Request.Xml = new XmlMessage(http.Request.Text);
}
http.UpdateHttpMessage();
 
http.Request.Complete = true;
http.UpdateHttpMessage();
 
SkipEmptyLines(requestPos);
}
 
responseThread.Join();
}
catch(Exception ex)
{
Console.WriteLine(ex.Message + " (" + ex.GetType().Name + ")\n" + ex.StackTrace);
if(responseThread != null) responseThread.Abort();
}
}
 
private void MatchResponses()
{
try
{
IEnumerator httpEnum = https.GetEnumerator();
 
if(!nextMessageEvent.WaitOne()) throw new Exception("Cannot get first message of request");
 
responsePos.SetDirection(requestPos.CurrentMessage.Direction == TcpMessageDirection.Local
? TcpMessageDirection.Remote : TcpMessageDirection.Local);
 
while(!responsePos.IsEnd)
{
bool moved;
 
lock(https)
{
requestEvent.Reset();
moved = httpEnum.MoveNext();
}
 
if(!moved)
{
if(!requestEvent.WaitOne())
throw new Exception("Cannot get next request");
 
lock(https)
{
if(!httpEnum.MoveNext())
throw new Exception("Tried to find response but no HTTP message available");
}
}
 
HttpMessage http = (HttpMessage)httpEnum.Current;
 
ParseResponse(responsePos, http);
if(http.Response.StatusCode == 100) // "100 (Continue)" response
{
http.GotContinueResponse();
ParseResponse(responsePos, http); // once again
}
 
http.Response.Complete = true;
http.UpdateHttpMessage();
responsePos.NextOctetAndBack(); // go forward to see end of connection
}
}
catch(Exception ex)
{
Console.WriteLine(ex.Message + " (" + ex.GetType().Name + ")\n" + ex.StackTrace);
}
}
 
private string GetToken(ParsePosition pos, int limit)
{
StringBuilder res = new StringBuilder(100);
int len = 0;
 
for(byte b = pos.NextOctet(); !pos.IsEnd && tokenChars[b]; b = pos.NextOctet())
{
res.Append(charValues[b]);
if(limit > 0 && limit < ++len) return null; // length limit
}
pos.Back();
 
return res.ToString();
}
 
private string GetUntilSpace(ParsePosition pos, int limit)
{
StringBuilder res = new StringBuilder(1024);
int len = 0;
 
for(byte b = pos.NextOctet(); !pos.IsEnd && b != 32 && b != 13 && b != 10; b = pos.NextOctet())
{ // <space> <cr> <lf>
res.Append(charValues[b]);
if(limit > 0 && limit < ++len) return null; // length limit
}
pos.Back();
 
return res.ToString();
}
 
private string GetUntilEoL(ParsePosition pos, int limit)
{
StringBuilder res = new StringBuilder(1024);
int len = 0;
 
for(byte b = pos.NextOctet(); !pos.IsEnd && b != 13; b = pos.NextOctet())
{ // <cr>
res.Append(charValues[b]);
if(limit > 0 && limit < ++len) return null; // length limit
}
pos.Back();
 
return res.ToString();
}
 
private void ExpectSpace(ParsePosition pos)
{
if(pos.IsEnd || pos.NextOctet() != 32)
throw new HttpParseException("Space expected");
}
 
private void ExpectCRLF(ParsePosition pos)
{
if(pos.IsEnd || pos.NextOctet() != 13)
throw new HttpParseException("Carriage return expected");
if(pos.IsEnd || pos.NextOctet() != 10)
throw new HttpParseException("Linefeed expected");
}
 
private void SkipEmptyLines(ParsePosition pos)
{
while(pos.NextOctetAndBack() == 13)
{
ExpectCRLF(pos);
}
}
 
private void ParseRequestLine(ParsePosition pos, HttpMessage http)
{
// method
http.Request.Method = GetToken(pos, 1024);
if(http.Request.Method == null || http.Request.Method.Length == 0)
throw new HttpParseException("Request method name expected");
ExpectSpace(pos);
 
// URI
http.Request.Uri = GetUntilSpace(pos, 1024);
if(http.Request.Uri == null || http.Request.Uri.Length == 0)
throw new HttpParseException("Request URI expected");
 
if(pos.IsEnd)
throw new HttpParseException("Unexpected end of message");
 
// EoL or version
byte b = pos.NextOctet();
if(b == 13)
{
pos.Back();
ExpectCRLF(pos);
http.Request.Version = HttpVersion.V0_9;
}
else if(b != 32)
{
throw new HttpParseException("HTTP version expected");
}
 
// check version
string versionStr = GetUntilEoL(pos, 20);
if(pos.IsEnd || versionStr == null || versionStr.Length == 0)
throw new HttpParseException("HTTP version expected");
 
if(versionStr == "HTTP/1.0")
{
http.Request.Version = HttpVersion.V1_0;
}
else if(versionStr == "HTTP/1.1")
{
http.Request.Version = HttpVersion.V1_1;
}
else
{
throw new HttpParseException("Unknown HTTP version: " + versionStr);
}
 
ExpectCRLF(pos);
}
 
private void ParseHeaders(ParsePosition pos, HttpMessage http, HttpHalfMessage half)
{
if(pos.IsEnd) return; // end of TCP messages
 
while(true)
{
if(pos.IsEnd)
throw new HttpParseException("Unexpected end of message");
 
if(pos.NextOctetAndBack() == 13)
{
ExpectCRLF(pos);
return; // end of header, move to body
}
if(pos.IsEnd) return; // end of TCP messages
 
string name = GetToken(pos, 0);
if(name == null || name.Length == 0)
throw new HttpParseException("Header name expected");
 
if(pos.IsEnd || pos.NextOctet() != 58) // :
throw new HttpParseException("Header value expected");
 
string s = TrimHeaderValue(GetUntilEoL(pos, 0));
 
ExpectCRLF(pos);
 
half.AddHeader(name, s);
}
}
 
enum HeaderValueState
{
Space,
Token,
Quoted
}
 
private string TrimHeaderValue(string s)
{
if(s == null) return null;
 
HeaderValueState state = HeaderValueState.Space;
StringBuilder buf = new StringBuilder();
 
for(int i = 0, l = s.Length; i < l; i++)
{
char c = s[i];
switch(state)
{
case HeaderValueState.Space:
if(c != ' ' && c != '\t')
{
if(c == '"')
{
if(buf.Length > 0) buf.Append(' ');
buf.Append(c);
state = HeaderValueState.Quoted;
}
else
{
if(buf.Length > 0) buf.Append(' ');
buf.Append(c);
state = HeaderValueState.Token;
}
}
break;
 
case HeaderValueState.Token:
if(c == ' ' || c == '\t')
{
state = HeaderValueState.Space;
}
else if(c == '"')
{
buf.Append(c);
state = HeaderValueState.Quoted;
}
else
{
buf.Append(c);
}
break;
 
case HeaderValueState.Quoted:
if(c == '"')
{
buf.Append(c);
i++;
if(i < l)
{
c = s[i];
if(c == ' ' || c == '\t')
{
state = HeaderValueState.Space;
}
else if(c == '"')
{
buf.Append(c);
state = HeaderValueState.Quoted;
}
else
{
buf.Append(c);
state = HeaderValueState.Token;
}
}
}
else
{
buf.Append(c);
}
break;
}
}
 
return buf.ToString();
}
 
private HttpTransferEncoding ParseTransferEncoding(string encoding)
{
if(encoding == null) return HttpTransferEncoding.None;
encoding = encoding.ToLower();
 
if(encoding == "identity")
return HttpTransferEncoding.Identity;
else if(encoding == "chunked")
return HttpTransferEncoding.Chunked;
else if(encoding == "gzip")
return HttpTransferEncoding.Gzip;
else if(encoding == "compress")
return HttpTransferEncoding.Compress;
else if(encoding == "deflate")
return HttpTransferEncoding.Deflate;
else
return HttpTransferEncoding.Unknown;
}
 
private HttpContentEncoding ParseContentEncoding(string encoding)
{
if(encoding == null) return HttpContentEncoding.None;
encoding = encoding.ToLower();
 
if(encoding == "identity")
return HttpContentEncoding.Identity;
else if(encoding == "gzip")
return HttpContentEncoding.Gzip;
else if(encoding == "compress")
return HttpContentEncoding.Compress;
else if(encoding == "deflate")
return HttpContentEncoding.Deflate;
else
return HttpContentEncoding.Unknown;
}
 
private void SetHttpProperties(HttpMessage http, HttpHalfMessage half)
{
// length
HttpHeader contentLength = half.GetHeader("content-length");
if(contentLength != null)
{
half.Length = int.Parse(contentLength.Values[0]);
}
 
// transfer encoding
HttpHeader transferEncoding = half.GetHeader("transfer-encoding");
half.TransferEncoding = ParseTransferEncoding((transferEncoding == null) ? null : transferEncoding.Values[0]);
if(HasBody(http, half) && half.TransferEncoding == HttpTransferEncoding.None)
half.TransferEncoding = HttpTransferEncoding.Identity;
 
// content encoding
HttpHeader contentEncoding = half.GetHeader("content-encoding");
half.ContentEncoding = ParseContentEncoding((contentEncoding == null) ? null : contentEncoding.Values[0]);
if(HasBody(http, half) && half.ContentEncoding == HttpContentEncoding.None)
half.ContentEncoding = HttpContentEncoding.Identity;
 
// type & charset
HttpHeader contentType = half.GetHeader("content-type");
if(contentType != null)
{
Match match = Regex.Match(contentType.Values[0], @"^\s*(\S+)/(\S+)\s*($|;\s*(charset=""?(\S+)""?)?)");
if(match.Success)
{
half.ContentType = match.Groups[1].Captures[0].Value;
half.ContentSubtype = match.Groups[2].Captures[0].Value;
if(match.Groups.Count >= 6 && match.Groups[5].Captures.Count > 0)
half.Charset = match.Groups[5].Captures[0].Value.Trim('"');
}
}
}
 
private void SetRequestProperties(HttpMessage http, HttpRequest request)
{
SetHttpProperties(http, request);
 
// soap action
HttpHeader soapAction = request.GetHeader("soapaction");
if(soapAction != null)
{
request.SoapAction = soapAction.Values[0];
}
}
 
// RFC 2616: 4.3
private bool HasBody(HttpMessage http, HttpHalfMessage half)
{
if(half.IsRequest)
{
return (http.Request.Length > 0)
|| (http.Request.TransferEncoding != HttpTransferEncoding.None);
}
else
{
if(http.Request.Method == "HEAD") return false;
if(http.Response.StatusCode < 200) return false;
if(http.Response.StatusCode == 204) return false;
if(http.Response.StatusCode == 304) return false;
 
return true;
}
}
 
private void ParseBody(ParsePosition pos, HttpMessage http, HttpHalfMessage half)
{
if(!HasBody(http, half)) return;
 
// FIXME parse and save on-the-fly, dont wait util end of message
 
byte[] bin = new byte[8*1024];
int len = 0; // current bin biffer length
int limit = half.Length;
int chunkLen = -1; // current length of current chunk
int chunkLimit = -1;
HttpTransferEncoding transferEncoding = half.TransferEncoding;
HttpContentEncoding contentEncoding = half.ContentEncoding;
string contentType = half.ContentType;
string contentSubtype = half.ContentSubtype;
string charset = half.Charset;
 
for(byte b = pos.NextOctet(); !pos.IsEnd; b = pos.NextOctet())
{
// RFC 2616: 3.6.1
if(transferEncoding == HttpTransferEncoding.Chunked && chunkLimit < 0)
{
// FIXME recognize chunk-extension here
 
// get chunk length
pos.Back();
string chunkLimitStr = GetUntilEoL(pos, 40);
if(pos.IsEnd || chunkLimitStr == null || chunkLimitStr.Length == 0)
throw new HttpParseException("Chunk length expected");
 
try
{
chunkLimit = Convert.ToInt32(chunkLimitStr, 16);
}
catch(Exception)
{
throw new HttpParseException("Cannot parse chunk length");
}
 
ExpectCRLF(pos);
 
if(chunkLimit == 0) { // the end marker
ExpectCRLF(pos);
break;
}
 
chunkLen = 0;
b = pos.NextOctet();
}
 
// grow array if full
if(len >= bin.Length)
{
byte[] newBin = new byte[bin.Length*2];
Array.Copy(bin, newBin, len);
bin = newBin;
}
 
bin[len++] = b;
chunkLen++;
if(chunkLimit > 0)
{
if(chunkLen >= chunkLimit) // full chunk
{
ExpectCRLF(pos);
chunkLimit = -1; // try to find length of next chunk on next iteration
}
}
else
{
if(limit > 0 && len >= limit) // full length
{
break;
}
}
}
if(transferEncoding == HttpTransferEncoding.Chunked && chunkLimit > 0 && chunkLen != chunkLimit)
{
throw new HttpParseException("Incomplete chunk found");
}
 
// FIXME parse entity-headers for chunked encoding here
 
string text = null;
if(contentEncoding == HttpContentEncoding.Identity && contentType == "text")
{
try
{
Encoding enc = Encoding.GetEncoding(charset == null ? (contentSubtype == "xml" ? "UTF-8" : "ASCII") : charset);
text = enc.GetString(bin, 0, len);
}
catch(NotSupportedException)
{
Console.WriteLine("Unsupported charset: " + charset);
}
}
 
half.Length = len;
half.Body = bin;
half.Text = text;
}
 
private void ParseResponse(ParsePosition pos, HttpMessage http)
{
ParseResponseLine(pos, http);
http.Response.StartTimestamp = pos.CurrentMessage.Timestamp;
http.UpdateHttpMessage();
 
ParseHeaders(pos, http, http.Response);
SetHttpProperties(http, http.Response);
http.UpdateHttpMessage();
 
ParseBody(pos, http, http.Response);
if("text" == http.Response.ContentType && "xml" == http.Response.ContentSubtype)
{
http.Response.Xml = new XmlMessage(http.Response.Text);
}
http.UpdateHttpMessage();
}
 
private void ParseResponseLine(ParsePosition pos, HttpMessage http)
{
// version
string versionStr = GetUntilSpace(pos, 20);
if(pos.IsEnd || versionStr == null || versionStr.Length == 0)
throw new HttpParseException("HTTP version expected");
 
if(versionStr == "HTTP/1.0")
{
http.Response.Version = HttpVersion.V1_0;
}
else if(versionStr == "HTTP/1.1")
{
http.Response.Version = HttpVersion.V1_1;
}
else
{
throw new HttpParseException("Unknown HTTP version: " + versionStr);
}
ExpectSpace(pos);
 
// status code
string code = GetToken(pos, 3);
if(code == null || code.Length != 3)
throw new HttpParseException("Status code expected");
 
try
{
int c = int.Parse(code);
if(c < 100 || c >= 1000) throw new HttpParseException("Status code expected");
http.Response.StatusCode = c;
}
catch(FormatException)
{
throw new HttpParseException("Status code expected");
}
ExpectSpace(pos);
 
// status message
http.Response.StatusMessage = GetUntilEoL(pos, 0);
 
if(pos.IsEnd)
throw new HttpParseException("Unexpected end of message");
 
ExpectCRLF(pos);
}
}
 
public class HttpHeader
{
private string name;
private string[] headerValues;
 
public string Name
{
get { return name; }
}
 
public string[] Values
{
get { return headerValues; }
}
 
internal HttpHeader()
{
}
 
internal HttpHeader(string name, string headerValue)
{
this.name = name;
AddValue(headerValue);
}
 
internal void AddValue(string value)
{
if(headerValues == null)
{
headerValues = new string[1];
}
else
{
string[] newValues = new string[headerValues.Length + 1];
Array.Copy(headerValues, 0, newValues, 0, headerValues.Length);
headerValues = newValues;
}
 
headerValues[headerValues.Length-1] = value;
}
}
 
public abstract class HttpHalfMessage
{
private bool complete = false;
private HttpVersion version;
protected LinkedList headers = new LinkedList();
protected Hashtable headersHash = new Hashtable();
private int length = -1; // -1 == unknown
private HttpTransferEncoding transferEncoding = HttpTransferEncoding.None;
private HttpContentEncoding contentEncoding = HttpContentEncoding.None;
private string contentType;
private string contentSubtype;
private string charset;
private byte[] body;
private string text;
private XmlMessage xml;
private DateTime startTimestamp = DateTime.MinValue;
 
public abstract bool IsRequest
{
get;
}
 
public bool Complete
{
get { return complete; }
set { complete = value; }
}
 
public HttpVersion Version
{
get { return version; }
set { version = value; }
}
 
public LinkedList Headers
{
get { return headers; }
}
 
public int Length
{
get { return length; }
set { length = value; }
}
 
public HttpTransferEncoding TransferEncoding
{
get { return transferEncoding; }
set { transferEncoding = value; }
}
 
public HttpContentEncoding ContentEncoding
{
get { return contentEncoding; }
set { contentEncoding = value; }
}
 
public string ContentType
{
get { return contentType; }
set { contentType = value; }
}
 
public string ContentSubtype
{
get { return contentSubtype; }
set { contentSubtype = value; }
}
 
public string Charset
{
get { return charset; }
set { charset = value; }
}
 
public byte[] Body
{
get { return body; }
set { body = value; }
}
 
public string Text
{
get { return text; }
set { text = value; }
}
 
public XmlMessage Xml
{
get { return xml; }
set { xml = value; }
}
 
public DateTime StartTimestamp
{
get { return startTimestamp; }
set { startTimestamp = value; }
}
 
public void AddHeader(string name, string headerValue)
{
HttpHeader header = (HttpHeader)headersHash[name];
if(header == null)
{
header = new HttpHeader(name, headerValue);
headers.Add(header);
headersHash.Add(name.ToLower(), header);
}
else
{
header.AddValue(headerValue);
}
}
 
public HttpHeader GetHeader(string name)
{
return (HttpHeader)headersHash[name.ToLower()];
}
}
 
public class HttpRequest : HttpHalfMessage
{
private string method;
private string uri;
private string soapAction;
 
public override bool IsRequest
{
get { return true; }
}
 
public string Method
{
get { return method; }
set { method = value; }
}
 
public string Uri
{
get { return uri; }
set { uri = value; }
}
 
public string SoapAction
{
get { return soapAction; }
set { soapAction = value; }
}
}
 
public class HttpResponse : HttpHalfMessage
{
private int statusCode;
private string statusMessage;
 
public override bool IsRequest
{
get { return false; }
}
 
public int StatusCode
{
get { return statusCode; }
set { statusCode = value; }
}
 
public string StatusMessage
{
get { return statusMessage; }
set { statusMessage = value; }
}
}
 
public class HttpMessage
{
private HttpRequest request = new HttpRequest();
private HttpResponse continueResponse = null;
private HttpResponse response = new HttpResponse();
 
public HttpRequest Request
{
get { return request; }
}
 
public HttpResponse ContinueResponse
{
get { return continueResponse; }
}
 
public HttpResponse Response
{
get { return response; }
}
 
public override string ToString() // FIXME delete the method
{
return (request.SoapAction != null ? ("SOAP " + request.SoapAction)
: (request.Method == null ? "" : request.Method) + " " + (request.Uri == null ? "" : request.Uri));
}
 
public event TcpEventHandler Update;
 
protected virtual void OnUpdate(TcpEventArgs e)
{
if(Update != null)
{
Update(this, e);
}
}
 
internal void UpdateHttpMessage()
{
OnUpdate(new TcpEventArgs());
}
 
internal void GotContinueResponse()
{
continueResponse = response;
response = new HttpResponse();
}
}
 
internal class HttpParseException : Exception
{
public HttpParseException() : base()
{
}
 
public HttpParseException(string message) : base(message)
{
}
 
public HttpParseException(System.Runtime.Serialization.SerializationInfo info,
System.Runtime.Serialization.StreamingContext context) : base(info, context)
{
}
 
public HttpParseException(string message, Exception innerException) : base(message, innerException)
{
}
}
 
public class XmlMessage
{
private XmlDocument xml;
private XmlException parseException;
 
public XmlDocument Xml
{
get { return xml; }
}
 
public XmlException ParseException
{
get { return parseException; }
}
 
internal XmlMessage(string text)
{
try
{
this.xml = new XmlDocument();
this.xml.LoadXml(text);
}
catch(XmlException ex)
{
parseException = ex;
}
}
}
 
public class TcpMessage
{
private TcpMessageDirection direction = TcpMessageDirection.None;
private byte[] bytes;
private int length = 0;
private DateTime timestamp;
 
public TcpMessageDirection Direction
{
get { return direction; }
set { direction = value; }
}
 
public int Length
{
get { return length; }
}
 
public byte[] Bytes
{
get
{
return bytes;
}
set
{
length = 0;
Append(value);
}
}
 
public DateTime Timestamp
{
get { return timestamp; }
}
 
internal void SetTimestamp(DateTime timestamp)
{
this.timestamp = timestamp;
}
 
internal TcpMessage()
{
this.timestamp = DateTime.Now;
this.bytes = new byte[1024];
}
 
internal TcpMessage(byte[] bytes, int length)
{
this.timestamp = DateTime.Now;
this.bytes = new byte[length];
this.length = length;
Array.Copy(this.bytes, bytes, length);
}
 
internal TcpMessage Append(byte[] newBytes)
{
if(newBytes == null) return this;
 
return Append(newBytes, newBytes.Length);
}
 
internal TcpMessage Append(byte[] newBytes, int length)
{
if(newBytes == null) return this;
 
lock(this)
{
// grow array
if(this.length + length > bytes.Length)
{
int newLength = bytes.Length;
while(this.length + length > newLength) newLength *= 2;
byte[] newArray = new byte[newLength];
 
Array.Copy(bytes, newArray, this.length);
bytes = newArray;
}
 
// store received bytes
Array.Copy(newBytes, 0, bytes, this.length, length);
this.length += length;
 
return this;
}
}
}
 
public class ThreadDebugger
{
private static ArrayList threads = new ArrayList();
private static ArrayList asyncs = new ArrayList();
 
internal static void Add(Thread thread, string comment)
{
threads.Add(new ThreadItem(thread, comment));
Console.WriteLine("ThreadDebugger: thread added {0}", comment);
}
 
internal static void Add(IAsyncResult async, string comment)
{
asyncs.Add(new AsyncItem(async, comment));
Console.WriteLine("ThreadDebugger: async added ", comment);
}
 
public static void PrintStatus()
{
Console.WriteLine("=== ThreadDebugger Status Begin ======");
Console.WriteLine("--- Threads --------------------------");
foreach(ThreadItem t in threads)
{
Console.WriteLine("{0} ({1}): {2}", t.thread.Name, t.comment, t.thread.IsAlive ? "alive" : "dead");
}
Console.WriteLine("--- Asyncs ---------------------------");
foreach(AsyncItem a in asyncs)
{
Console.WriteLine("{0}: {1}", a.comment, a.async.IsCompleted ? "alive" : "dead");
}
Console.WriteLine("=== ThreadDebugger Status End ========");
}
 
private class ThreadItem
{
public Thread thread;
public string comment;
 
public ThreadItem(Thread thread, string comment)
{
this.thread = thread;
this.comment = comment;
}
}
 
private class AsyncItem
{
public IAsyncResult async;
public string comment;
 
public AsyncItem(IAsyncResult async, string comment)
{
this.async = async;
this.comment = comment;
}
}
}
}
/TCPproxy/tags/0002-second_public/MainForm.cs
0,0 → 1,2840
using System;
using System.Drawing;
using System.Collections;
using System.Windows.Forms;
using System.IO;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;
 
// GENERAL DESIGN
//
// Methods which are not running in the main application thread
// are not allowed no interact with GUI elemenents.
 
// FIXME:
// - add soap validation
// - icons for all items in the tree
// - use pool of threads?
// - do not store intermediate info, just row packets and the parsed fragments to display
// - make the text fragment store switchable
// - set locks during log writing (?)
// - show '100 (Continue)' response info
namespace TCPproxy
{
public class MainForm : Form
{
#region constants and settings
 
private const int RECENT_LENGTH = 20;
public const string REGISTRY_KEY = @"Software\Anatoli Klassen\TCPproxy";
public const string REGISTRY_RECENT_SUBKEY = "Recent Listenings";
 
public const byte LOG_TYPE_BIN = 1;
public const string LOG_BIN_HEADER = "TCPproxy 1.0\n";
 
#endregion constants and settings
 
#region private fields
 
private ListenForm listenForm = new ListenForm();
private TcpListener tcpListener = null;
private LogMessages logMessages = null;
private Hashtable treeNodes = new Hashtable();
private ArrayList recentItems = new ArrayList();
 
private int listenPort;
private string resendHost;
private int resendPort;
private TcpShowMode tcpShowMode = TcpShowMode.ByDirection;
private bool autoExpand = true;
 
private string defaultCaption;
 
#endregion private fields
 
#region web forms fields
private System.ComponentModel.IContainer components;
private SaveFileDialog saveLogDialog;
private StatusBarPanel connectionStatusBar;
private MenuItem selectAllmenuItem;
private MenuItem copyMenuItem;
private ContextMenu viewContextMenu;
private MenuItem closeConnectionMenuItem;
private ImageList saveButtonImageList;
private StatusBar statusBar;
private ContextMenu messagesContextMenu;
private ImageList treeImageList;
private MenuItem wordWrapMenuItem;
private MainMenu mainMenu;
private MenuItem fileMenu;
private MenuItem loadBinLogMenuItem;
private MenuItem saveBinLogMenuItem;
private MenuItem menuSeparator1;
private MenuItem exitMenuItem;
private MenuItem viewMenu;
private MenuItem clearMainMenuItem;
private MenuItem messagesMenu;
private MenuItem saveLogMenuItem;
private MenuItem saveTcoLogMenuItem;
private MenuItem saveHttpLogMenuItem;
private MenuItem saveXmlLogMenuItem;
private MenuItem menuSeparator2;
private MenuItem allMessagesMenuItem;
private MenuItem importantMessagesMenuItem;
private MenuItem infoMessagesMenuItem;
private MenuItem menuSeparator3;
private MenuItem tcpShowByDirectionMenuItem;
private MenuItem tcpShowByTimeMenuItem;
private MenuItem menuSeparator4;
private MenuItem autoExpandMainMenuItem;
private MenuItem wordWrapMainMenuItem;
private MenuItem helpMenu;
private Panel panel1;
private Panel panel3;
private Panel panel4;
private ViewControl messagesBox;
private Splitter splitter2;
private ListBox logBox;
private Splitter splitter1;
private TreeView messageView;
private MenuItem startMenuItem;
private MenuItem stopMenuItem;
private MenuItem menuSeparator5;
private MenuItem recentListeningMenu;
private MenuItem recentListeningNoItem;
private SaveFileDialog saveBinLogDialog;
private OpenFileDialog loadBinLogDialog;
private MenuItem autoExpandMenuItem;
private MenuItem aboutMenuItem;
#endregion web forms fields
 
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent() {
this.components = new System.ComponentModel.Container();
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainForm));
this.wordWrapMenuItem = new System.Windows.Forms.MenuItem();
this.treeImageList = new System.Windows.Forms.ImageList(this.components);
this.messagesContextMenu = new System.Windows.Forms.ContextMenu();
this.selectAllmenuItem = new System.Windows.Forms.MenuItem();
this.copyMenuItem = new System.Windows.Forms.MenuItem();
this.statusBar = new System.Windows.Forms.StatusBar();
this.connectionStatusBar = new System.Windows.Forms.StatusBarPanel();
this.saveButtonImageList = new System.Windows.Forms.ImageList(this.components);
this.closeConnectionMenuItem = new System.Windows.Forms.MenuItem();
this.viewContextMenu = new System.Windows.Forms.ContextMenu();
this.saveLogDialog = new System.Windows.Forms.SaveFileDialog();
this.mainMenu = new System.Windows.Forms.MainMenu();
this.fileMenu = new System.Windows.Forms.MenuItem();
this.startMenuItem = new System.Windows.Forms.MenuItem();
this.stopMenuItem = new System.Windows.Forms.MenuItem();
this.recentListeningMenu = new System.Windows.Forms.MenuItem();
this.recentListeningNoItem = new System.Windows.Forms.MenuItem();
this.menuSeparator5 = new System.Windows.Forms.MenuItem();
this.loadBinLogMenuItem = new System.Windows.Forms.MenuItem();
this.saveBinLogMenuItem = new System.Windows.Forms.MenuItem();
this.menuSeparator1 = new System.Windows.Forms.MenuItem();
this.saveLogMenuItem = new System.Windows.Forms.MenuItem();
this.saveTcoLogMenuItem = new System.Windows.Forms.MenuItem();
this.saveHttpLogMenuItem = new System.Windows.Forms.MenuItem();
this.saveXmlLogMenuItem = new System.Windows.Forms.MenuItem();
this.menuSeparator2 = new System.Windows.Forms.MenuItem();
this.exitMenuItem = new System.Windows.Forms.MenuItem();
this.viewMenu = new System.Windows.Forms.MenuItem();
this.clearMainMenuItem = new System.Windows.Forms.MenuItem();
this.messagesMenu = new System.Windows.Forms.MenuItem();
this.allMessagesMenuItem = new System.Windows.Forms.MenuItem();
this.infoMessagesMenuItem = new System.Windows.Forms.MenuItem();
this.importantMessagesMenuItem = new System.Windows.Forms.MenuItem();
this.menuSeparator3 = new System.Windows.Forms.MenuItem();
this.tcpShowByDirectionMenuItem = new System.Windows.Forms.MenuItem();
this.tcpShowByTimeMenuItem = new System.Windows.Forms.MenuItem();
this.menuSeparator4 = new System.Windows.Forms.MenuItem();
this.autoExpandMainMenuItem = new System.Windows.Forms.MenuItem();
this.wordWrapMainMenuItem = new System.Windows.Forms.MenuItem();
this.helpMenu = new System.Windows.Forms.MenuItem();
this.aboutMenuItem = new System.Windows.Forms.MenuItem();
this.panel1 = new System.Windows.Forms.Panel();
this.panel3 = new System.Windows.Forms.Panel();
this.panel4 = new System.Windows.Forms.Panel();
this.splitter2 = new System.Windows.Forms.Splitter();
this.logBox = new System.Windows.Forms.ListBox();
this.splitter1 = new System.Windows.Forms.Splitter();
this.messageView = new System.Windows.Forms.TreeView();
this.saveBinLogDialog = new System.Windows.Forms.SaveFileDialog();
this.loadBinLogDialog = new System.Windows.Forms.OpenFileDialog();
this.autoExpandMenuItem = new System.Windows.Forms.MenuItem();
this.messagesBox = new TCPproxy.ViewControl();
((System.ComponentModel.ISupportInitialize)(this.connectionStatusBar)).BeginInit();
this.panel1.SuspendLayout();
this.panel3.SuspendLayout();
this.panel4.SuspendLayout();
this.SuspendLayout();
//
// wordWrapMenuItem
//
this.wordWrapMenuItem.Index = 2;
this.wordWrapMenuItem.Text = "Word &Wrap";
this.wordWrapMenuItem.Click += new System.EventHandler(this.wordWrapMenuItem_Click);
//
// treeImageList
//
this.treeImageList.ImageStream = ((System.Windows.Forms.ImageListStreamer)(resources.GetObject("treeImageList.ImageStream")));
this.treeImageList.TransparentColor = System.Drawing.Color.Magenta;
//
// messagesContextMenu
//
this.messagesContextMenu.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
this.selectAllmenuItem,
this.copyMenuItem,
this.wordWrapMenuItem});
//
// selectAllmenuItem
//
this.selectAllmenuItem.Index = 0;
this.selectAllmenuItem.Shortcut = System.Windows.Forms.Shortcut.CtrlA;
this.selectAllmenuItem.Text = "Select &All";
this.selectAllmenuItem.Click += new System.EventHandler(this.selectAllMenuItem_Click);
//
// copyMenuItem
//
this.copyMenuItem.Index = 1;
this.copyMenuItem.Shortcut = System.Windows.Forms.Shortcut.CtrlC;
this.copyMenuItem.Text = "&Copy";
this.copyMenuItem.Click += new System.EventHandler(this.copyMenuItem_Click);
//
// statusBar
//
this.statusBar.Location = new System.Drawing.Point(0, 339);
this.statusBar.Name = "statusBar";
this.statusBar.Panels.AddRange(new System.Windows.Forms.StatusBarPanel[] {
this.connectionStatusBar});
this.statusBar.ShowPanels = true;
this.statusBar.Size = new System.Drawing.Size(780, 22);
this.statusBar.TabIndex = 0;
//
// connectionStatusBar
//
this.connectionStatusBar.AutoSize = System.Windows.Forms.StatusBarPanelAutoSize.Spring;
this.connectionStatusBar.Width = 764;
//
// saveButtonImageList
//
this.saveButtonImageList.ImageStream = ((System.Windows.Forms.ImageListStreamer)(resources.GetObject("saveButtonImageList.ImageStream")));
this.saveButtonImageList.TransparentColor = System.Drawing.Color.Magenta;
//
// closeConnectionMenuItem
//
this.closeConnectionMenuItem.Index = 0;
this.closeConnectionMenuItem.Text = "&Close connection";
this.closeConnectionMenuItem.Click += new System.EventHandler(this.closeConnectionMenuItem_Click);
//
// viewContextMenu
//
this.viewContextMenu.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
this.closeConnectionMenuItem,
this.autoExpandMenuItem});
//
// saveLogDialog
//
this.saveLogDialog.DefaultExt = "txt";
this.saveLogDialog.Filter = "Text Files (*.txt)|*.txt|All Files|*.*";
this.saveLogDialog.Title = "Save Log";
//
// mainMenu
//
this.mainMenu.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
this.fileMenu,
this.viewMenu,
this.helpMenu});
//
// fileMenu
//
this.fileMenu.Index = 0;
this.fileMenu.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
this.startMenuItem,
this.stopMenuItem,
this.recentListeningMenu,
this.menuSeparator5,
this.loadBinLogMenuItem,
this.saveBinLogMenuItem,
this.menuSeparator1,
this.saveLogMenuItem,
this.saveTcoLogMenuItem,
this.saveHttpLogMenuItem,
this.saveXmlLogMenuItem,
this.menuSeparator2,
this.exitMenuItem});
this.fileMenu.Text = "&File";
//
// startMenuItem
//
this.startMenuItem.Index = 0;
this.startMenuItem.Text = "&Start...";
this.startMenuItem.Click += new System.EventHandler(this.startMenuItem_Click);
//
// stopMenuItem
//
this.stopMenuItem.Enabled = false;
this.stopMenuItem.Index = 1;
this.stopMenuItem.Text = "S&top";
this.stopMenuItem.Click += new System.EventHandler(this.stopMenuItem_Click);
//
// recentListeningMenu
//
this.recentListeningMenu.Index = 2;
this.recentListeningMenu.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
this.recentListeningNoItem});
this.recentListeningMenu.Text = "&Recent Listenings";
//
// recentListeningNoItem
//
this.recentListeningNoItem.Enabled = false;
this.recentListeningNoItem.Index = 0;
this.recentListeningNoItem.Text = "(no items)";
//
// menuSeparator5
//
this.menuSeparator5.Index = 3;
this.menuSeparator5.Text = "-";
//
// loadBinLogMenuItem
//
this.loadBinLogMenuItem.Index = 4;
this.loadBinLogMenuItem.Text = "&Load Bin Log...";
this.loadBinLogMenuItem.Click += new System.EventHandler(this.loadBinLogMenuItem_Click);
//
// saveBinLogMenuItem
//
this.saveBinLogMenuItem.Index = 5;
this.saveBinLogMenuItem.Text = "&Save Bin Log...";
this.saveBinLogMenuItem.Click += new System.EventHandler(this.saveBinLogMenuItem_Click);
//
// menuSeparator1
//
this.menuSeparator1.Index = 6;
this.menuSeparator1.Text = "-";
//
// saveLogMenuItem
//
this.saveLogMenuItem.Index = 7;
this.saveLogMenuItem.Text = "Save Log Messa&ges...";
this.saveLogMenuItem.Click += new System.EventHandler(this.saveLogMenuItem_Click);
//
// saveTcoLogMenuItem
//
this.saveTcoLogMenuItem.Index = 8;
this.saveTcoLogMenuItem.Text = "Save T&CP Log...";
this.saveTcoLogMenuItem.Click += new System.EventHandler(this.saveTcpMenuItem_Click);
//
// saveHttpLogMenuItem
//
this.saveHttpLogMenuItem.Index = 9;
this.saveHttpLogMenuItem.Text = "Save Htt&p Log...";
this.saveHttpLogMenuItem.Click += new System.EventHandler(this.saveHttpMenuItem_Click);
//
// saveXmlLogMenuItem
//
this.saveXmlLogMenuItem.Index = 10;
this.saveXmlLogMenuItem.Text = "Save X&ML Log...";
this.saveXmlLogMenuItem.Click += new System.EventHandler(this.saveXmlMenuItem_Click);
//
// menuSeparator2
//
this.menuSeparator2.Index = 11;
this.menuSeparator2.Text = "-";
//
// exitMenuItem
//
this.exitMenuItem.Index = 12;
this.exitMenuItem.Text = "E&xit";
//
// viewMenu
//
this.viewMenu.Index = 1;
this.viewMenu.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
this.clearMainMenuItem,
this.messagesMenu,
this.menuSeparator3,
this.tcpShowByDirectionMenuItem,
this.tcpShowByTimeMenuItem,
this.menuSeparator4,
this.autoExpandMainMenuItem,
this.wordWrapMainMenuItem});
this.viewMenu.Text = "&View";
//
// clearMainMenuItem
//
this.clearMainMenuItem.Index = 0;
this.clearMainMenuItem.Text = "&Clear";
this.clearMainMenuItem.Click += new System.EventHandler(this.clearMenuItem_Click);
//
// messagesMenu
//
this.messagesMenu.Index = 1;
this.messagesMenu.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
this.allMessagesMenuItem,
this.infoMessagesMenuItem,
this.importantMessagesMenuItem});
this.messagesMenu.Text = "&Messages";
//
// allMessagesMenuItem
//
this.allMessagesMenuItem.Checked = true;
this.allMessagesMenuItem.Index = 0;
this.allMessagesMenuItem.Text = "&All";
this.allMessagesMenuItem.Click += new System.EventHandler(this.messagesMenuItem_Click);
//
// infoMessagesMenuItem
//
this.infoMessagesMenuItem.Index = 1;
this.infoMessagesMenuItem.Text = "&Info";
this.infoMessagesMenuItem.Click += new System.EventHandler(this.messagesMenuItem_Click);
//
// importantMessagesMenuItem
//
this.importantMessagesMenuItem.Index = 2;
this.importantMessagesMenuItem.Text = "I&mportant";
this.importantMessagesMenuItem.Click += new System.EventHandler(this.messagesMenuItem_Click);
//
// menuSeparator3
//
this.menuSeparator3.Index = 2;
this.menuSeparator3.Text = "-";
//
// tcpShowByDirectionMenuItem
//
this.tcpShowByDirectionMenuItem.Checked = true;
this.tcpShowByDirectionMenuItem.Index = 3;
this.tcpShowByDirectionMenuItem.Text = "TCP Show by &Direction";
this.tcpShowByDirectionMenuItem.Click += new System.EventHandler(this.tcpShowByDirectionMenuItem_Click);
//
// tcpShowByTimeMenuItem
//
this.tcpShowByTimeMenuItem.Index = 4;
this.tcpShowByTimeMenuItem.Text = "TCP Show by &Time";
this.tcpShowByTimeMenuItem.Click += new System.EventHandler(this.tcpShowByTimeMenuItem_Click);
//
// menuSeparator4
//
this.menuSeparator4.Index = 5;
this.menuSeparator4.Text = "-";
//
// autoExpandMainMenuItem
//
this.autoExpandMainMenuItem.Index = 6;
this.autoExpandMainMenuItem.Text = "Auto E&xpand";
this.autoExpandMainMenuItem.Click += new System.EventHandler(this.autoExpandMenuItem_Click);
//
// wordWrapMainMenuItem
//
this.wordWrapMainMenuItem.Index = 7;
this.wordWrapMainMenuItem.Text = "Word &Wrap";
this.wordWrapMainMenuItem.Click += new System.EventHandler(this.wordWrapMenuItem_Click);
//
// helpMenu
//
this.helpMenu.Index = 2;
this.helpMenu.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {
this.aboutMenuItem});
this.helpMenu.Text = "&Help";
//
// aboutMenuItem
//
this.aboutMenuItem.Index = 0;
this.aboutMenuItem.Text = "&About...";
this.aboutMenuItem.Click += new System.EventHandler(this.aboutMenuItem_Click);
//
// panel1
//
this.panel1.Controls.Add(this.panel3);
this.panel1.Dock = System.Windows.Forms.DockStyle.Fill;
this.panel1.Location = new System.Drawing.Point(0, 0);
this.panel1.Name = "panel1";
this.panel1.Size = new System.Drawing.Size(780, 339);
this.panel1.TabIndex = 11;
//
// panel3
//
this.panel3.Controls.Add(this.panel4);
this.panel3.Controls.Add(this.splitter1);
this.panel3.Controls.Add(this.messageView);
this.panel3.Dock = System.Windows.Forms.DockStyle.Fill;
this.panel3.Location = new System.Drawing.Point(0, 0);
this.panel3.Name = "panel3";
this.panel3.Size = new System.Drawing.Size(780, 339);
this.panel3.TabIndex = 5;
//
// panel4
//
this.panel4.Controls.Add(this.messagesBox);
this.panel4.Controls.Add(this.splitter2);
this.panel4.Controls.Add(this.logBox);
this.panel4.Dock = System.Windows.Forms.DockStyle.Fill;
this.panel4.Location = new System.Drawing.Point(163, 0);
this.panel4.Name = "panel4";
this.panel4.Size = new System.Drawing.Size(617, 339);
this.panel4.TabIndex = 13;
//
// splitter2
//
this.splitter2.Dock = System.Windows.Forms.DockStyle.Bottom;
this.splitter2.Location = new System.Drawing.Point(0, 268);
this.splitter2.Name = "splitter2";
this.splitter2.Size = new System.Drawing.Size(617, 3);
this.splitter2.TabIndex = 9;
this.splitter2.TabStop = false;
//
// logBox
//
this.logBox.Dock = System.Windows.Forms.DockStyle.Bottom;
this.logBox.Font = new System.Drawing.Font("Courier New", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.logBox.HorizontalScrollbar = true;
this.logBox.ItemHeight = 16;
this.logBox.Location = new System.Drawing.Point(0, 271);
this.logBox.Name = "logBox";
this.logBox.ScrollAlwaysVisible = true;
this.logBox.Size = new System.Drawing.Size(617, 68);
this.logBox.TabIndex = 8;
//
// splitter1
//
this.splitter1.Location = new System.Drawing.Point(160, 0);
this.splitter1.Name = "splitter1";
this.splitter1.Size = new System.Drawing.Size(3, 339);
this.splitter1.TabIndex = 12;
this.splitter1.TabStop = false;
//
// messageView
//
this.messageView.ContextMenu = this.viewContextMenu;
this.messageView.Dock = System.Windows.Forms.DockStyle.Left;
this.messageView.HideSelection = false;
this.messageView.ImageIndex = 0;
this.messageView.ImageList = this.treeImageList;
this.messageView.Location = new System.Drawing.Point(0, 0);
this.messageView.Name = "messageView";
this.messageView.SelectedImageIndex = 0;
this.messageView.Size = new System.Drawing.Size(160, 339);
this.messageView.TabIndex = 11;
this.messageView.AfterSelect += new System.Windows.Forms.TreeViewEventHandler(this.messageView_AfterSelect);
this.messageView.BeforeSelect += new System.Windows.Forms.TreeViewCancelEventHandler(this.messageView_BeforeSelect);
//
// saveBinLogDialog
//
this.saveBinLogDialog.DefaultExt = "tcp";
this.saveBinLogDialog.Filter = "TCP Logs (*.bin)|*.bin|All Files|*.*";
this.saveBinLogDialog.Title = "Save Binary Log";
//
// loadBinLogDialog
//
this.loadBinLogDialog.FileName = "openFileDialog1";
this.loadBinLogDialog.Filter = "TCP Logs (*.bin)|*.bin|All Files|*.*";
this.loadBinLogDialog.Title = "Load Binary Log";
//
// autoExpandMenuItem
//
this.autoExpandMenuItem.Index = 1;
this.autoExpandMenuItem.Text = "Auto E&xpand";
this.autoExpandMenuItem.Click += new System.EventHandler(this.autoExpandMenuItem_Click);
//
// messagesBox
//
this.messagesBox.ContextMenu = this.messagesContextMenu;
this.messagesBox.Dock = System.Windows.Forms.DockStyle.Fill;
this.messagesBox.Font = new System.Drawing.Font("Courier New", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.messagesBox.Location = new System.Drawing.Point(0, 0);
this.messagesBox.Name = "messagesBox";
this.messagesBox.Size = new System.Drawing.Size(617, 268);
this.messagesBox.TabIndex = 7;
this.messagesBox.WordWrap = true;
//
// MainForm
//
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.ClientSize = new System.Drawing.Size(780, 361);
this.Controls.Add(this.panel1);
this.Controls.Add(this.statusBar);
this.Menu = this.mainMenu;
this.MinimumSize = new System.Drawing.Size(400, 200);
this.Name = "MainForm";
this.StartPosition = System.Windows.Forms.FormStartPosition.Manual;
this.Text = "TCPproxy";
((System.ComponentModel.ISupportInitialize)(this.connectionStatusBar)).EndInit();
this.panel1.ResumeLayout(false);
this.panel3.ResumeLayout(false);
this.panel4.ResumeLayout(false);
this.ResumeLayout(false);
 
}
#endregion
 
#region windows forms methods
public MainForm()
{
InitializeComponent();
logMessages = new LogMessages(logBox);
try {
LoadFromRegistry();
LoadRecentItemsFromRegistry();
}
catch(Exception) {}
 
// update visual elements
UpdateMessagesMenuItems();
 
if(tcpShowMode == TcpShowMode.ByDirection) {
tcpShowByDirectionMenuItem.Checked = true;
tcpShowByTimeMenuItem.Checked = false;
}
else {
tcpShowByDirectionMenuItem.Checked = false;
tcpShowByTimeMenuItem.Checked = true;
}
 
autoExpandMenuItem.Checked = autoExpand;
autoExpandMainMenuItem.Checked = autoExpand;
wordWrapMenuItem.Checked = messagesBox.WordWrap;
wordWrapMainMenuItem.Checked = messagesBox.WordWrap;
 
// save default values
defaultCaption = this.Text;
}
 
protected override void Dispose(bool disposing)
{
if(tcpListener != null) {
tcpListener.StopListening(); // stop listening
tcpListener.CancelAll(); // cancel all open connections
}
 
// save settings
SaveToRegistry();
SaveRecentItemsToRegistry();
 
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
 
ThreadDebugger.PrintStatus();
}
 
[STAThread]
static void Main()
{
Application.Run(new MainForm());
}
 
private void startMenuItem_Click(object sender, System.EventArgs e)
{
IPAddress resendIp;
 
if(!listenForm.Execute(this,
ref listenPort, ref resendHost, out resendIp, ref resendPort)) return;
 
StartListening(listenPort, resendIp, resendPort);
}
 
private void stopMenuItem_Click(object sender, System.EventArgs e)
{
StopListening();
}
 
private void clearMenuItem_Click(object sender, System.EventArgs e)
{
ClearAll();
}
 
private void selectAllMenuItem_Click(object sender, System.EventArgs e)
{
messagesBox.SelectAll();
}
 
private void copyMenuItem_Click(object sender, System.EventArgs e)
{
string sel = messagesBox.SelectedText;
if(sel != null) Clipboard.SetDataObject(sel);
}
 
private void wordWrapMenuItem_Click(object sender, System.EventArgs e)
{
messagesBox.WordWrap = !messagesBox.WordWrap;
wordWrapMenuItem.Checked = messagesBox.WordWrap;
wordWrapMainMenuItem.Checked = messagesBox.WordWrap;
}
 
private void autoExpandMenuItem_Click(object sender, System.EventArgs e)
{
autoExpand = !autoExpand;
autoExpandMenuItem.Checked = autoExpand;
autoExpandMainMenuItem.Checked = autoExpand;
}
 
private void aboutMenuItem_Click(object sender, EventArgs e)
{
(new AboutForm()).ShowDialog(this);
}
 
private void messageView_BeforeSelect(object sender, TreeViewCancelEventArgs e)
{
if(messageView.SelectedNode == null) return;
 
object tag = messageView.SelectedNode.Tag;
if(tag is TreeNodeData)
{
TreeNodeData data = (TreeNodeData)tag;
data.SaveViewState();
}
}
 
private void messageView_AfterSelect(object sender, TreeViewEventArgs e)
{
if(messageView.SelectedNode == null) return;
 
object tag = messageView.SelectedNode.Tag;
if(tag is TreeNodeData)
{
TreeNodeData data = (TreeNodeData)tag;
data.Show();
}
}
 
private void closeConnectionMenuItem_Click(object sender, System.EventArgs e)
{
if(messageView.SelectedNode == null) return;
 
object tag = messageView.SelectedNode.Tag;
if(tag is TcpNodeData)
CloseTcpConnection(((TcpNodeData)tag).Tcp);
else if(tag is TcpNodeData)
CloseTcpConnection(((TcpNodeData)messageView.SelectedNode.Parent.Tag).Tcp);
else if(tag is XmlNodeData)
CloseTcpConnection(((TcpNodeData)messageView.SelectedNode.Parent.Parent.Tag).Tcp);
}
 
private void tcpShowByDirectionMenuItem_Click(object sender, System.EventArgs e)
{
tcpShowMode = TcpShowMode.ByDirection;
tcpShowByDirectionMenuItem.Checked = true;
tcpShowByTimeMenuItem.Checked = false;
 
if(messageView.SelectedNode == null) return;
 
object tag = messageView.SelectedNode.Tag;
if(tag is TcpNodeData)
UpdateTcpNodeInternal(((TcpNodeData)tag).Tcp);
}
 
private void tcpShowByTimeMenuItem_Click(object sender, System.EventArgs e)
{
tcpShowMode = TcpShowMode.ByTime;
tcpShowByDirectionMenuItem.Checked = false;
tcpShowByTimeMenuItem.Checked = true;
 
if(messageView.SelectedNode == null) return;
 
object tag = messageView.SelectedNode.Tag;
if(tag is TcpNodeData)
UpdateTcpNodeInternal(((TcpNodeData)tag).Tcp);
}
 
private void saveLogMenuItem_Click(object sender, System.EventArgs e)
{
if(saveLogDialog.ShowDialog() == DialogResult.OK) {
try {
SaveLog(saveLogDialog.FileName);
}
catch(Exception ex) {
MessageBox.Show("Cannot save log to file " + saveLogDialog.FileName
+ ":\n" + ex.Message, "TCPproxy",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
 
private void saveTcpMenuItem_Click(object sender, System.EventArgs e)
{
if(saveLogDialog.ShowDialog() == DialogResult.OK) {
try {
SaveTcp(saveLogDialog.FileName);
}
catch(Exception ex) {
MessageBox.Show("Cannot save TCP log to file " + saveLogDialog.FileName
+ ":\n" + ex.Message, "TCPproxy",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
 
private void saveHttpMenuItem_Click(object sender, System.EventArgs e)
{
if(saveLogDialog.ShowDialog() == DialogResult.OK) {
try {
SaveHttp(saveLogDialog.FileName);
}
catch(Exception ex) {
MessageBox.Show("Cannot save HTTP log to file " + saveLogDialog.FileName
+ ":\n" + ex.Message, "TCPproxy",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
 
private void saveXmlMenuItem_Click(object sender, System.EventArgs e)
{
if(saveLogDialog.ShowDialog() == DialogResult.OK) {
try {
SaveXml(saveLogDialog.FileName);
}
catch(Exception ex) {
MessageBox.Show("Cannot save XML log to file " + saveLogDialog.FileName
+ ":\n" + ex.Message, "TCPproxy",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
 
private void saveBinLogMenuItem_Click(object sender, EventArgs e)
{
if(saveBinLogDialog.ShowDialog() == DialogResult.OK) {
try {
SaveBinLog(saveBinLogDialog.FileName);
}
catch(Exception ex) {
MessageBox.Show("Cannot save binary log to file " + saveBinLogDialog.FileName
+ ":\n" + ex.Message, "TCPproxy",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
 
private void loadBinLogMenuItem_Click(object sender, EventArgs e)
{
if(loadBinLogDialog.ShowDialog() == DialogResult.OK) {
try {
LoadBinLog(loadBinLogDialog.FileName);
}
catch(Exception ex) {
MessageBox.Show("Cannot load binary log from file " + loadBinLogDialog.FileName
+ ":\n" + ex.Message, "TCPproxy",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
 
private void messagesMenuItem_Click(object sender, EventArgs e)
{
if(sender == importantMessagesMenuItem)
logMessages.Level = LogLevel.Important;
else if(sender == infoMessagesMenuItem)
logMessages.Level = LogLevel.Info;
else
logMessages.Level = LogLevel.Debug;
 
UpdateMessagesMenuItems();
}
 
private void recentMenuItem_Click(object sender, EventArgs e)
{
int n = recentItems.Count-1;
foreach(MenuItem menuItem in recentListeningMenu.MenuItems) {
if(sender == menuItem) break;
n--;
}
if(n < 0)
throw new Exception("Unknown sender");
 
RecentItem recentItem = (RecentItem)recentItems[n];
IPAddress resendIp;
 
this.listenPort = recentItem.ListenPort;
this.resendHost = recentItem.ResendHost;
this.resendPort = recentItem.ResendPort;
 
try
{
resendIp = HostUtils.ResendHostToIp(resendHost);
}
catch(Exception ex)
{
MessageBox.Show("Cannot get host IP: " + ex.Message, "TCPproxy",
MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
 
StartListening(listenPort, resendIp, resendPort);
}
 
#endregion windows forms methods
 
#region core methods
 
private LogLevel ParseLogLevel(string str)
{
if(str == "Important")
return LogLevel.Important;
else if(str == "Info")
return LogLevel.Info;
else
return LogLevel.Debug;
}
 
private void LoadFromRegistry()
{
Microsoft.Win32.RegistryKey subkey = Microsoft.Win32.Registry.CurrentUser.OpenSubKey(
REGISTRY_KEY);
 
if(subkey == null) return;
 
listenPort = (int)subkey.GetValue("Listen Port", 0);
resendHost = (string)subkey.GetValue("Resend Host", "");
resendPort = (int)subkey.GetValue("Resend Port", 0);
logMessages.Level = ParseLogLevel((string)subkey.GetValue("Messages Level", ""));
autoExpand = (int)subkey.GetValue("Auto Expand", 1) == 1;
messagesBox.WordWrap = (int)subkey.GetValue("Word Wrap", 1) == 1;
 
object tcpShowModeStr = (object)subkey.GetValue("Tcp Show Mode", TcpShowMode.ByDirection);
tcpShowMode = (tcpShowModeStr as string) == "ByDirection"
? TcpShowMode.ByDirection : TcpShowMode.ByTime;
 
int winTop = (int)subkey.GetValue("Window.Top", -1);
int winLeft = (int)subkey.GetValue("Window.Left", -1);
 
if(winTop == -1 || winLeft == -1) {
this.StartPosition = FormStartPosition.WindowsDefaultBounds;
}
else {
this.Top = winTop;
this.Left = winLeft;
this.ClientSize = new Size(
(int)subkey.GetValue("Window.Width", this.ClientSize.Width),
(int)subkey.GetValue("Window.Hight", this.ClientSize.Height));
this.messageView.Width
= (int)subkey.GetValue("Splitter.Left", this.messageView.Width);
this.logBox.Height
= (int)subkey.GetValue("Splitter.Bottom", this.logBox.Height);
}
}
 
private void LoadRecentItemsFromRegistry()
{
Microsoft.Win32.RegistryKey subkey = Microsoft.Win32.Registry.CurrentUser.OpenSubKey(
REGISTRY_KEY + @"\" + REGISTRY_RECENT_SUBKEY);
 
if(subkey == null) return;
 
foreach(string name in subkey.GetValueNames()) {
if(name == "") continue;
AddRecentItem(RecentItem.LoadFromRegistry((string)subkey.GetValue(name, "")));
}
}
 
private void SaveToRegistry()
{
Microsoft.Win32.RegistryKey subkey = Microsoft.Win32.Registry.CurrentUser.CreateSubKey(
REGISTRY_KEY);
 
subkey.SetValue("Listen Port", listenPort);
subkey.SetValue("Resend Host", resendHost == null ? "" : resendHost);
subkey.SetValue("Resend Port", resendPort);
subkey.SetValue("Messages Level", logMessages.Level);
subkey.SetValue("Tcp Show Mode", tcpShowMode);
subkey.SetValue("Auto Expand", autoExpand ? 1 : 0);
subkey.SetValue("Word Wrap", messagesBox.WordWrap ? 1 : 0);
 
subkey.SetValue("Window.Top", this.Top);
subkey.SetValue("Window.Left", this.Left);
subkey.SetValue("Window.Hight", this.ClientSize.Height);
subkey.SetValue("Window.Width", this.ClientSize.Width);
subkey.SetValue("Splitter.Left", this.messageView.Width);
subkey.SetValue("Splitter.Bottom", this.logBox.Height);
}
 
private void SaveRecentItemsToRegistry()
{
Microsoft.Win32.RegistryKey subkey = Microsoft.Win32.Registry.CurrentUser.CreateSubKey(
REGISTRY_KEY + @"\" + REGISTRY_RECENT_SUBKEY);
 
// load existing from registry
ArrayList old = new ArrayList();
foreach(string name in subkey.GetValueNames()) {
if(name == "") continue;
old.Add(RecentItem.LoadFromRegistry((string)subkey.GetValue(name, "")));
}
 
// merge - for the case another program instance has changed the list
foreach(RecentItem item in old) {
int existingIdx = recentItems.IndexOf(item);
if(existingIdx >= 0)
((RecentItem)recentItems[existingIdx]).UpdateTimestamp(item);
else
recentItems.Add(item);
}
 
recentItems.Sort();
if(recentItems.Count > 0) // take tail
recentItems = recentItems.GetRange(Math.Max(0, recentItems.Count - RECENT_LENGTH),
Math.Min(recentItems.Count, RECENT_LENGTH));
 
int count = 0;
foreach(RecentItem item in recentItems) {
subkey.SetValue(string.Format("{0:0000}", count++), item.SaveToRegistry());
}
}
 
private void UpdateMessagesMenuItems()
{
switch(logMessages.Level) {
case LogLevel.Critical:
case LogLevel.Error:
case LogLevel.Warning:
case LogLevel.Important:
importantMessagesMenuItem.Checked = true;
infoMessagesMenuItem.Checked = false;
allMessagesMenuItem.Checked = false;
break;
 
case LogLevel.Info:
importantMessagesMenuItem.Checked = false;
infoMessagesMenuItem.Checked = true;
allMessagesMenuItem.Checked = false;
break;
 
case LogLevel.Debug:
importantMessagesMenuItem.Checked = false;
infoMessagesMenuItem.Checked = false;
allMessagesMenuItem.Checked = true;
break;
}
}
 
private void AddRecentItem(int listenPort, string resendHost, int resendPort)
{
RecentItem recentItem = new RecentItem(listenPort, resendHost, resendPort);
recentItem.UpdateTimestamp();
AddRecentItem(recentItem);
}
 
private void AddRecentItem(RecentItem recentItem)
{
int existingIdx = recentItems.IndexOf(recentItem);
if(existingIdx >= 0) {
recentItems.RemoveAt(existingIdx);
this.recentListeningMenu.MenuItems.RemoveAt(recentItems.Count - existingIdx);
}
 
if(recentItems.Count == 0 && this.recentListeningMenu.MenuItems.Count == 1)
this.recentListeningMenu.MenuItems.RemoveAt(0);
 
recentItems.Add(recentItem);
 
MenuItem menuItem = new MenuItem();
this.recentListeningMenu.MenuItems.Add(0, menuItem);
menuItem.Text = string.Format("&1 {0} to {1}:{2}",
recentItem.ListenPort, recentItem.ResendHost, recentItem.ResendPort);
menuItem.Click += new System.EventHandler(recentMenuItem_Click);
 
// check overflow
if(recentItems.Count > RECENT_LENGTH) {
recentItems.RemoveAt(0);
this.recentListeningMenu.MenuItems.RemoveAt(RECENT_LENGTH-1);
}
 
// update hot keys of old items
int count = 0;
foreach(MenuItem item in this.recentListeningMenu.MenuItems) {
if(count == 0) {
}
else if(count < 10)
item.Text = string.Format("&{3} {0} to {1}:{2}", recentItem.ListenPort,
recentItem.ResendHost, recentItem.ResendPort, count + 1);
else
item.Text = string.Format("{0} to {1}:{2}", recentItem.ListenPort,
recentItem.ResendHost, recentItem.ResendPort);
count++;
}
}
 
private void ClearAll()
{
// close all connetions
foreach(object tcp in treeNodes.Keys)
if(tcp is TcpConnection)
((TcpConnection)tcp).Cancel();
 
// FIXME wait for all log messages from network part
 
treeNodes.Clear();
messageView.Nodes.Clear();
messagesBox.Clear();
logMessages.Clear();
}
 
private void Start(int listenPort, IPAddress resendHost, int resendPort)
{
if(tcpListener != null) tcpListener.StopListening();
 
tcpListener = new TcpListener(listenPort, resendHost, resendPort);
tcpListener.Log += new TcpLogEventHandler(TcpConnectionLog);
tcpListener.NewTcp += new TcpConnectionEventHandler(AddTcpConnetion);
tcpListener.StartListening();
}
 
private void StartListening(int listenPort, IPAddress resendIp, int resendPort)
{
// listen to the port
try
{
Start(listenPort, resendIp, resendPort);
}
catch(Exception ex)
{
MessageBox.Show("Cannot start listening: " + ex.Message, "TCPproxy",
MessageBoxButtons.OK, MessageBoxIcon.Error);
Console.WriteLine(ex.Message + " (" + ex.GetType().Name + ")\n" + ex.StackTrace);
return;
}
 
AddRecentItem(listenPort, resendHost, resendPort);
 
startMenuItem.Enabled = false;
stopMenuItem.Enabled = true;
 
foreach(MenuItem subitem in recentListeningMenu.MenuItems) {
subitem.Enabled = false;
}
 
this.Text = string.Format("{0}: {1} to {2}:{3}",
defaultCaption, listenPort, resendHost, resendPort);
}
 
private void StopListening()
{
if(tcpListener != null) tcpListener.StopListening();
 
startMenuItem.Enabled = true;
stopMenuItem.Enabled = false;
 
if(recentItems.Count > 0) {
foreach(MenuItem subitem in recentListeningMenu.MenuItems) {
subitem.Enabled = true;
}
}
 
this.Text = defaultCaption;
}
 
private void CloseTcpConnection(TcpConnection tcp)
{
if(tcp == null) return;
 
tcp.Cancel();
}
 
private void SaveLog(string fileName)
{
StreamWriter writer = new StreamWriter(fileName);
 
foreach(LogMessage message in logMessages.Messages)
{
writer.WriteLine(message);
}
 
writer.Close();
}
 
private void SaveTcp(string fileName)
{
StreamWriter writer = new StreamWriter(fileName);
 
foreach(TreeNode tcpNode in messageView.Nodes)
{
TcpNodeData data = (TcpNodeData)tcpNode.Tag;
data.WriteLog(writer);
}
 
writer.Close();
}
 
private void SaveHttp(string fileName)
{
StreamWriter writer = new StreamWriter(fileName);
 
foreach(TreeNode tcpNode in messageView.Nodes)
{
foreach(TreeNode httpNode in tcpNode.Nodes)
{
HttpNodeData data = (HttpNodeData)httpNode.Tag;
data.WriteLog(writer);
}
}
 
writer.Close();
}
 
private void SaveXml(string fileName)
{
StreamWriter writer = new StreamWriter(fileName);
 
foreach(TreeNode tcpNode in messageView.Nodes)
{
foreach(TreeNode httpNode in tcpNode.Nodes)
{
foreach(TreeNode xmlNode in httpNode.Nodes)
{
XmlNodeData data = (XmlNodeData)xmlNode.Tag;
data.WriteLog(writer);
}
}
}
 
writer.Close();
}
 
private void SaveBinLog(string fileName)
{
FileStream file = new FileStream(fileName, FileMode.Create);
BinaryWriter writer = new BinaryWriter(file);
 
// header
writer.Write(Encoding.ASCII.GetBytes(LOG_BIN_HEADER));
writer.Write(LOG_TYPE_BIN);
 
// for each tcp connection
foreach(TreeNode tcpNode in messageView.Nodes)
((TcpNodeData)tcpNode.Tag).Tcp.WriteBinLog(writer);
 
// end of stream
writer.Write((byte)BinLogTypes.None);
 
writer.Close();
file.Close();
}
 
private void LoadBinLog(string fileName)
{
StopListening();
ClearAll();
 
FileStream file = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
BinaryReader reader = new BinaryReader(file);
 
// header
byte[] bufExpect = Encoding.ASCII.GetBytes(LOG_BIN_HEADER);
if(file.Length < (bufExpect.Length + 1 /* the type */))
throw new Exception("The file is too short");
 
byte[] bufRead = reader.ReadBytes(bufExpect.Length);
for(int i = 0; i < bufRead.Length; i++)
if(bufRead[i] != bufExpect[i])
throw new Exception("Wrong header of the file");
 
if(reader.ReadByte() != LOG_TYPE_BIN)
throw new Exception("Unknown log type");
 
tcpListener = new TcpListener(0, null, 0);
tcpListener.Log += new TcpLogEventHandler(TcpConnectionLog);
tcpListener.NewTcp += new TcpConnectionEventHandler(AddTcpConnetion);
tcpListener.ReadBinLog(reader); // it will send us usual event
 
reader.Close();
file.Close();
}
 
#endregion core methods
 
#region network events handlers
private void TcpConnectionLog(object sender, TcpLogEventArgs e)
{
lock(this)
{
TcpConnection tcp = sender as TcpConnection;
LogMessage message = new LogMessage(tcp, e.Level, e.Message, e.Exception);
 
try
{
this.BeginInvoke(new AddLogMessageHandler(AddLogMessageInternal), new object[] { message } );
}
catch(InvalidOperationException ex)
{
if(!this.Disposing && !this.IsDisposed)
Console.WriteLine(ex.Message + " (" + ex.GetType().Name + ")\n" + ex.StackTrace);
}
}
}
 
private void AddTcpConnetion(object sender, TcpConnectionEventArgs e)
{
lock(this)
{
e.Tcp.Log += new TcpLogEventHandler(TcpConnectionLog);
e.Tcp.Update += new TcpEventHandler(UpdateTcpNode);
e.Tcp.Close += new TcpEventHandler(UpdateTcpNode);
e.Tcp.NewHttp += new TcpHttpEventHandler(AddHttpMessageNode);
 
try
{
this.BeginInvoke(new AddTcpNodeHandler(AddTcpNodeInternal), new object[] { e.Tcp } );
}
catch(InvalidOperationException ex)
{
if(!this.Disposing && !this.IsDisposed)
Console.WriteLine(ex.Message + " (" + ex.GetType().Name + ")\n" + ex.StackTrace);
}
}
}
 
private void UpdateTcpNode(object sender, TcpEventArgs e)
{
lock(this)
{
try
{
this.BeginInvoke(new UpdateTcpNodeHandler(UpdateTcpNodeInternal), new object[] { (TcpConnection)sender } );
}
catch(InvalidOperationException ex)
{
if(!this.Disposing && !this.IsDisposed)
Console.WriteLine(ex.Message + " (" + ex.GetType().Name + ")\n" + ex.StackTrace);
}
}
}
 
private void AddHttpMessageNode(object sender, TcpHttpEventArgs e)
{
lock(this)
{
e.Http.Update += new TcpEventHandler(UpdateHttpNode);
 
try
{
this.BeginInvoke(new AddHttpNodeHandler(AddHttpNodeInternal),
new object[] { (TcpConnection)sender, e.Http } );
}
catch(InvalidOperationException ex)
{
if(!this.Disposing && !this.IsDisposed)
Console.WriteLine(ex.Message + " (" + ex.GetType().Name + ")\n" + ex.StackTrace);
}
}
}
 
private void UpdateHttpNode(object sender, TcpEventArgs e)
{
lock(this)
{
try
{
this.BeginInvoke(new UpdateHttpNodeHandler(UpdateHttpNodeInternal), new object[] { (HttpMessage)sender } );
}
catch(InvalidOperationException ex)
{
if(!this.Disposing && !this.IsDisposed)
Console.WriteLine(ex.Message + " (" + ex.GetType().Name + ")\n" + ex.StackTrace);
}
}
}
 
#endregion network events handlers
 
#region handlers for async GUI events
 
// All handlers are called in main application thread only, no locks needed.
// Other methods which are not running in the main application thread
// are not allowed no interact with GUI elemenents.
 
private delegate void AddLogMessageHandler(LogMessage message);
private delegate void AddTcpNodeHandler(TcpConnection tcp);
private delegate void UpdateTcpNodeHandler(TcpConnection tcp);
private delegate void AddHttpNodeHandler(TcpConnection tcp, HttpMessage http);
private delegate void UpdateHttpNodeHandler(HttpMessage http);
 
private void AddLogMessageInternal(LogMessage message)
{
logMessages.Add(message);
 
if(message.Exception != null)
{
Console.WriteLine(message.Exception.Message + " (" + message.Exception.GetType().Name
+ ")\n" + message.Exception.StackTrace);
}
}
 
private void AddTcpNodeInternal(TcpConnection tcp)
{
TreeNode treeNode = new TreeNode(tcp.ToString());
TcpNodeData data = new TcpNodeData(this, treeNode);
 
data.Tcp = tcp;
treeNode.Tag = data;
treeNode.ImageIndex = (tcp.LocalState == SocketState.Closed
&& tcp.RemoteState == SocketState.Closed) ? 2 : 1;
treeNode.SelectedImageIndex = treeNode.ImageIndex;
treeNodes[tcp] = data;
 
messageView.Nodes.Add(treeNode);
treeNode.EnsureVisible();
}
 
private void UpdateTcpNodeInternal(TcpConnection tcp)
{
TcpNodeData data = treeNodes[tcp] as TcpNodeData;
if(data == null) return; // might be call by Cancel
 
string title = tcp.ToString();
if(title != data.Node.Text) data.Node.Text = title;
if(tcp.LocalState == SocketState.Closed && tcp.RemoteState == SocketState.Closed)
{
data.Node.ImageIndex = 2;
data.Node.SelectedImageIndex = data.Node.ImageIndex;
}
 
if(messageView.SelectedNode == null || data != messageView.SelectedNode.Tag)
data.ViewExpired = true; // got update for invisible TCP node, update it later
else
data.Show();
}
 
private void AddHttpNodeInternal(TcpConnection tcp, HttpMessage http)
{
TreeNode treeNode = new TreeNode(http.ToString());
HttpNodeData data = new HttpNodeData(this, treeNode);
 
data.Http = http;
treeNode.Tag = data;
treeNodes[http] = data;
 
TcpNodeData tcpData = treeNodes[tcp] as TcpNodeData;
if(tcpData == null) throw new ArgumentException("No node found for TCP message");
 
tcpData.Node.Nodes.Add(treeNode);
if(autoExpand) tcpData.Node.Expand();
tcpData.Node.EnsureVisible();
}
 
private void UpdateHttpNodeInternal(HttpMessage http)
{
HttpNodeData httpData = treeNodes[http] as HttpNodeData;
if(httpData == null) return; // might be call by Cancel
 
string title = http.ToString();
if(httpData.Node.Text != title) httpData.Node.Text = title;
 
if(!httpData.RequestXmlShown && http.Request.Xml != null)
{
httpData.RequestXmlShown = true;
AddXmlNode(httpData.Node, "Request XML", http.Request.Xml);
if(autoExpand) httpData.Node.Expand();
}
 
if(!httpData.ResponseXmlShown && http.Response.Xml != null)
{
httpData.ResponseXmlShown = true;
AddXmlNode(httpData.Node, "Response XML", http.Response.Xml);
if(autoExpand) httpData.Node.Expand();
}
 
// update text view
if(messageView.SelectedNode == null || httpData != messageView.SelectedNode.Tag)
httpData.ViewExpired = true;
else
httpData.Show();
}
 
private void AddXmlNode(TreeNode parent, string title, XmlMessage xml)
{
TreeNode treeNode = new TreeNode(title);
XmlNodeData data = new XmlNodeData(this, treeNode);
 
data.Xml = xml;
treeNode.Tag = data;
treeNodes[xml] = data;
 
parent.Nodes.Add(treeNode);
}
 
#endregion handlers for async GUI events
 
#region node display classes
private abstract class TreeNodeData
{
protected TreeNode node;
protected object viewState;
protected bool viewExpired;
protected MainForm owner;
protected bool shown = false;
 
public TreeNode Node
{
get { return node; }
}
 
public TreeNodeData(MainForm owner, TreeNode node)
{
this.owner = owner;
this.node = node;
}
 
public bool ViewExpired
{
get { return viewExpired; }
set { viewExpired = value; }
}
 
public void SaveViewState()
{
viewState = owner.messagesBox.SaveState(false);
}
 
protected virtual bool ForceInitView()
{
return false;
}
 
protected abstract void InitView();
protected abstract void UpdateView();
 
public void Show()
{
if(!shown || viewState == null || ForceInitView())
{
InitView();
shown = true;
if(viewState == null) viewState = owner.messagesBox.SaveState(false);
}
else
{
owner.messagesBox.RestoreState(viewState, true);
if(viewExpired) UpdateView();
}
}
 
public abstract void WriteLog(StreamWriter writer);
}
 
private class TcpNodeData : TreeNodeData
{
private TcpConnection tcp;
 
private TcpShowMode lastShowMode;
private object localStateMarker = null;
private object remoteStateMarker = null;
private object startMarker = null;
private object localEndMarker = null;
private object remoteEndMarker = null;
private object clientMarker = null;
private object serverMarker = null;
private object sentMarker = null;
private object receivedMarker = null;
private object textMarker1 = null;
private object textMarker2 = null;
private IEnumerator messagesEnum = null;
 
public TcpConnection Tcp
{
get { return tcp; }
set { tcp = value; }
}
 
public TcpNodeData(MainForm owner, TreeNode node) : base(owner, node)
{
}
 
 
protected override bool ForceInitView()
{
return (lastShowMode != owner.tcpShowMode);
}
 
protected override void InitView()
{
lastShowMode = owner.tcpShowMode;
 
try
{
owner.messagesBox.BeginUpdate();
 
lock(tcp)
{
owner.messagesBox.Clear();
 
owner.messagesBox.AppendNewLine();
owner.messagesBox.AppendText("ID: ",
Color.DarkRed, Color.Transparent, false, false, 0, 0);
owner.messagesBox.AppendText(tcp.Id,
Color.DarkRed, Color.LightGray, false, false, 0, 12);
owner.messagesBox.AppendNewLine();
 
owner.messagesBox.AppendText("State: ",
Color.DarkRed, Color.Transparent, false, false, 0, 0);
localStateMarker = owner.messagesBox.BeginMark();
owner.messagesBox.AppendText(tcp.LocalState.ToString(),
Color.DarkRed, Color.LightGray, false, false, 0, 12);
owner.messagesBox.EndMark(localStateMarker);
owner.messagesBox.AppendText(null,
Color.DarkRed, Color.Transparent, false, false, 1, 12);
remoteStateMarker = owner.messagesBox.BeginMark();
owner.messagesBox.AppendText(tcp.RemoteState.ToString(),
Color.DarkRed, Color.LightGray, false, false, 0, 12);
owner.messagesBox.EndMark(remoteStateMarker);
owner.messagesBox.AppendNewLine();
 
owner.messagesBox.AppendText("Start: ",
Color.DarkRed, Color.Transparent, false, false, 0, 0);
startMarker = owner.messagesBox.BeginMark();
owner.messagesBox.AppendText(tcp.StartTimestamp.ToString("HH:mm:ss.ffff"),
Color.DarkRed, Color.LightGray, false, false, 0, 12);
owner.messagesBox.EndMark(startMarker);
owner.messagesBox.AppendNewLine();
owner.messagesBox.AppendText("Local End: ",
Color.DarkRed, Color.Transparent, false, false, 0, 0);
localEndMarker = owner.messagesBox.BeginMark();
owner.messagesBox.AppendText(tcp.LocalEndTimestamp.ToString("HH:mm:ss.ffff"),
Color.DarkRed, Color.LightGray, false, false, 0, 12);
owner.messagesBox.EndMark(localEndMarker);
owner.messagesBox.AppendNewLine();
owner.messagesBox.AppendText("Remote End: ",
Color.DarkRed, Color.Transparent, false, false, 0, 0);
remoteEndMarker = owner.messagesBox.BeginMark();
owner.messagesBox.AppendText(tcp.RemoteEndTimestamp.ToString("HH:mm:ss.ffff"),
Color.DarkRed, Color.LightGray, false, false, 0, 12);
owner.messagesBox.EndMark(remoteEndMarker);
owner.messagesBox.AppendNewLine();
 
owner.messagesBox.AppendText("Client: ",
Color.DarkRed, Color.Transparent, false, false, 0, 0);
clientMarker = owner.messagesBox.BeginMark();
owner.messagesBox.AppendText((tcp.LocalPoint == null) ? "" : tcp.LocalPoint.ToString(),
Color.DarkRed, Color.LightGray, false, false, 0, 12);
owner.messagesBox.EndMark(clientMarker);
owner.messagesBox.AppendNewLine();
owner.messagesBox.AppendText("Server: ",
Color.DarkRed, Color.Transparent, false, false, 0, 0);
serverMarker = owner.messagesBox.BeginMark();
owner.messagesBox.AppendText((tcp.RemotePoint == null) ? "" : tcp.RemotePoint.ToString(),
Color.DarkRed, Color.LightGray, false, false, 0, 12);
owner.messagesBox.EndMark(serverMarker);
owner.messagesBox.AppendNewLine();
 
owner.messagesBox.AppendText("Sent: ",
Color.DarkRed, Color.Transparent, false, false, 0, 0);
sentMarker = owner.messagesBox.BeginMark();
owner.messagesBox.AppendText(tcp.SentBytes.ToString(),
Color.DarkRed, Color.LightGray, false, false, 0, 12);
owner.messagesBox.EndMark(sentMarker);
owner.messagesBox.AppendNewLine();
owner.messagesBox.AppendText("Received: ",
Color.DarkRed, Color.Transparent, false, false, 0, 0);
receivedMarker = owner.messagesBox.BeginMark();
owner.messagesBox.AppendText(tcp.ReceivedBytes.ToString(),
Color.DarkRed, Color.LightGray, false, false, 0, 12);
owner.messagesBox.EndMark(receivedMarker);
owner.messagesBox.AppendNewLine();
owner.messagesBox.AppendNewLine();
 
messagesEnum = tcp.Messages.GetEnumerator();
 
textMarker1 = owner.messagesBox.BeginMark();
owner.messagesBox.EndMark(textMarker1);
 
owner.messagesBox.AppendNewLine();
textMarker2 = owner.messagesBox.BeginMark();
owner.messagesBox.EndMark(textMarker2);
 
ShowMessages();
}
}
finally
{
owner.messagesBox.EndUpdate();
}
}
 
protected override void UpdateView()
{
try
{
owner.messagesBox.BeginUpdate();
 
lock(tcp)
{
owner.messagesBox.ChangeText(localStateMarker, tcp.LocalState.ToString(),
Color.DarkRed, Color.LightGray, false, false, 0, 12);
owner.messagesBox.ChangeText(remoteStateMarker, tcp.RemoteState.ToString(),
Color.DarkRed, Color.LightGray, false, false, 0, 12);
 
owner.messagesBox.ChangeText(startMarker, tcp.StartTimestamp.ToString("HH:mm:ss.ffff"),
Color.DarkRed, Color.LightGray, false, false, 0, 12);
owner.messagesBox.ChangeText(localEndMarker, tcp.LocalEndTimestamp.ToString("HH:mm:ss.ffff"),
Color.DarkRed, Color.LightGray, false, false, 0, 12);
owner.messagesBox.ChangeText(remoteEndMarker, tcp.RemoteEndTimestamp.ToString("HH:mm:ss.ffff"),
Color.DarkRed, Color.LightGray, false, false, 0, 12);
 
owner.messagesBox.ChangeText(clientMarker, (tcp.LocalPoint == null) ? "" : tcp.LocalPoint.ToString(),
Color.DarkRed, Color.LightGray, false, false, 0, 12);
owner.messagesBox.ChangeText(serverMarker, (tcp.RemotePoint == null) ? "" : tcp.RemotePoint.ToString(),
Color.DarkRed, Color.LightGray, false, false, 0, 12);
 
owner.messagesBox.ChangeText(sentMarker, tcp.SentBytes.ToString(),
Color.DarkRed, Color.LightGray, false, false, 0, 12);
owner.messagesBox.ChangeText(receivedMarker, tcp.ReceivedBytes.ToString(),
Color.DarkRed, Color.LightGray, false, false, 0, 12);
 
ShowMessages();
}
}
finally
{
owner.messagesBox.EndUpdate();
}
}
 
private void ShowMessages()
{
while(messagesEnum.MoveNext())
{
TcpMessage message = (TcpMessage)messagesEnum.Current;
object marker = (owner.tcpShowMode == TcpShowMode.ByTime
|| message.Direction == TcpMessageDirection.Local) ? textMarker1 : textMarker2;
 
if(owner.tcpShowMode == TcpShowMode.ByTime)
{
owner.messagesBox.InsertText(marker,
message.Timestamp.ToString("HH:mm:ss.ffff") + " (" + message.Length + ")",
Color.DarkRed, Color.Transparent, false, false, 0, 0);
owner.messagesBox.InsertNewLine(marker);
}
 
string str = Utils.BytesToString(message.Bytes, message.Length);
ArrayList lines = Utils.SplitLine(str);
for(int i = 0; i < lines.Count; i++)
{
owner.messagesBox.InsertText(marker, (string)lines[i],
message.Direction == TcpMessageDirection.Local ? Color.Green : Color.Blue,
Color.LightGray, false, false, 0, 0);
 
if(owner.tcpShowMode == TcpShowMode.ByTime || i != lines.Count - 1)
owner.messagesBox.InsertNewLine(marker);
}
}
}
 
public override void WriteLog(StreamWriter writer)
{
writer.WriteLine(
"Start: " + tcp.StartTimestamp.ToString("HH:mm:ss.ffff")
+ "\r\nLocal End: " + tcp.LocalEndTimestamp.ToString("HH:mm:ss.ffff")
+ "\r\nRemote End: " + tcp.RemoteEndTimestamp.ToString("HH:mm:ss.ffff")
+ "\r\nClient: " + ((tcp.LocalPoint == null) ? "" : tcp.LocalPoint.ToString())
+ "\r\nServer: " + ((tcp.RemotePoint == null) ? "" : tcp.RemotePoint.ToString())
+ "\r\nSent: " + tcp.SentBytes
+ "\r\nReceived: " + tcp.ReceivedBytes);
 
foreach(TcpMessage message in tcp.Messages)
{
string str = Utils.BytesToString(message.Bytes, message.Length);
if(!str.EndsWith("\n")) str += "\r\n";
 
writer.WriteLine();
if(message.Direction == TcpMessageDirection.Local)
writer.WriteLine(">>> {0:HH:mm:ss.ffff} >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>", message.Timestamp);
else
writer.WriteLine("<<< {0:HH:mm:ss.ffff} <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<", message.Timestamp);
writer.Write(str);
}
writer.WriteLine("===============================================================");
writer.WriteLine();
}
}
 
private class HttpNodeData : TreeNodeData
{
private class MarkerSet
{
public object start;
public object method;
public object uri;
public object version;
public object status;
public object transferEncoding;
public object length;
public object contentEncoding;
public object contentType;
public object charset;
public object headers;
public object body;
public IEnumerator headersEnum;
}
 
private HttpMessage http;
private bool requestXmlShown = false;
private bool responseXmlShown = false;
 
private object stateMarker = null;
private MarkerSet requestMarkers = new MarkerSet();
private MarkerSet responseMarkers = new MarkerSet();
 
public HttpMessage Http
{
get { return http; }
set { http = value; }
}
 
public bool RequestXmlShown
{
get { return requestXmlShown; }
set { requestXmlShown = value; }
}
 
public bool ResponseXmlShown
{
get { return responseXmlShown; }
set { responseXmlShown = value; }
}
 
public HttpNodeData(MainForm owner, TreeNode node) : base(owner, node)
{
}
 
private string EncodingToString(HttpTransferEncoding encoding)
{
switch(encoding)
{
case HttpTransferEncoding.None: return "none";
case HttpTransferEncoding.Identity: return "identity";
case HttpTransferEncoding.Chunked: return "chunked";
case HttpTransferEncoding.Gzip: return "gzip";
case HttpTransferEncoding.Compress: return "compress";
case HttpTransferEncoding.Deflate: return "deflate";
default: return "<unknown>";
}
}
 
private string EncodingToString(HttpContentEncoding encoding)
{
switch(encoding)
{
case HttpContentEncoding.None: return "none";
case HttpContentEncoding.Identity: return "identity";
case HttpContentEncoding.Gzip: return "gzip";
case HttpContentEncoding.Compress: return "compress";
case HttpContentEncoding.Deflate: return "deflate";
default: return "<unknown>";
}
}
 
private string VersionToString(HttpVersion version)
{
switch(version)
{
case HttpVersion.V0_9: return "HTTP/0.9";
case HttpVersion.V1_0: return "HTTP/1.0";
default: return "HTTP/1.1";
}
}
 
private void InitHttpHalfView(HttpHalfMessage half, MarkerSet markers, string title)
{
string padding = "";
 
owner.messagesBox.AppendText(title + " Start: " + padding,
Color.DarkRed, Color.Transparent, false, false, 0, 0);
markers.start = owner.messagesBox.BeginMark();
owner.messagesBox.AppendText(half.StartTimestamp == DateTime.MinValue
? "<unknown>" : half.StartTimestamp.ToString("HH:mm:ss.ffff"),
Color.DarkRed, Color.LightGray, false, false, 0, 27);
owner.messagesBox.EndMark(markers.start);
owner.messagesBox.AppendNewLine();
 
if(half.IsRequest)
{
owner.messagesBox.AppendText(title + " Method: " + padding,
Color.DarkRed, Color.Transparent, false, false, 0, 0);
markers.method = owner.messagesBox.BeginMark();
owner.messagesBox.AppendText((half as HttpRequest).Method,
Color.DarkRed, Color.LightGray, false, false, 0, 27);
owner.messagesBox.EndMark(markers.method);
owner.messagesBox.AppendNewLine();
 
owner.messagesBox.AppendText(title + " URI: " + padding,
Color.DarkRed, Color.Transparent, false, false, 0, 0);
markers.uri = owner.messagesBox.BeginMark();
owner.messagesBox.AppendText((half as HttpRequest).Uri,
Color.DarkRed, Color.LightGray, false, false, 0, 27);
owner.messagesBox.EndMark(markers.uri);
owner.messagesBox.AppendNewLine();
}
else
{
owner.messagesBox.AppendText(title + " Status: " + padding,
Color.DarkRed, Color.Transparent, false, false, 0, 0);
markers.status = owner.messagesBox.BeginMark();
owner.messagesBox.AppendText((half as HttpResponse).StatusCode
+ " " + (half as HttpResponse).StatusMessage,
Color.DarkRed, Color.LightGray, false, false, 0, 27);
owner.messagesBox.EndMark(markers.status);
owner.messagesBox.AppendNewLine();
}
 
owner.messagesBox.AppendText(title + " Version: " + padding,
Color.DarkRed, Color.Transparent, false, false, 0, 0);
markers.version = owner.messagesBox.BeginMark();
owner.messagesBox.AppendText(VersionToString(half.Version),
Color.DarkRed, Color.LightGray, false, false, 0, 27);
owner.messagesBox.EndMark(markers.version);
owner.messagesBox.AppendNewLine();
 
owner.messagesBox.AppendText(title + " Transfer Encoding: " + padding,
Color.DarkRed, Color.Transparent, false, false, 0, 0);
markers.transferEncoding = owner.messagesBox.BeginMark();
owner.messagesBox.AppendText(EncodingToString(half.TransferEncoding),
Color.DarkRed, Color.LightGray, false, false, 0, 27);
owner.messagesBox.EndMark(markers.transferEncoding);
owner.messagesBox.AppendNewLine();
 
owner.messagesBox.AppendText(title + " Content Length: " + padding,
Color.DarkRed, Color.Transparent, false, false, 0, 0);
markers.length = owner.messagesBox.BeginMark();
owner.messagesBox.AppendText(half.Length < 0 ? "<unknown>" : half.Length.ToString(),
Color.DarkRed, Color.LightGray, false, false, 0, 27);
owner.messagesBox.EndMark(markers.length);
owner.messagesBox.AppendNewLine();
 
owner.messagesBox.AppendText(title + " Content Encoding: " + padding,
Color.DarkRed, Color.Transparent, false, false, 0, 0);
markers.contentEncoding = owner.messagesBox.BeginMark();
owner.messagesBox.AppendText(EncodingToString(half.ContentEncoding),
Color.DarkRed, Color.LightGray, false, false, 0, 27);
owner.messagesBox.EndMark(markers.contentEncoding);
owner.messagesBox.AppendNewLine();
 
owner.messagesBox.AppendText(title + " Content Type: " + padding,
Color.DarkRed, Color.Transparent, false, false, 0, 0);
markers.contentType = owner.messagesBox.BeginMark();
owner.messagesBox.AppendText(half.ContentType == null ? "<unknown>"
: half.ContentType + "/" + half.ContentSubtype,
Color.DarkRed, Color.LightGray, false, false, 0, 27);
owner.messagesBox.EndMark(markers.contentType);
owner.messagesBox.AppendNewLine();
 
owner.messagesBox.AppendText(title + " Content Charset: " + padding,
Color.DarkRed, Color.Transparent, false, false, 0, 0);
markers.charset = owner.messagesBox.BeginMark();
owner.messagesBox.AppendText(half.Charset == null ? "<unknown>" : half.Charset,
Color.DarkRed, Color.LightGray, false, false, 0, 27);
owner.messagesBox.EndMark(markers.charset);
owner.messagesBox.AppendNewLine();
 
owner.messagesBox.AppendText(title + " Headers:",
Color.DarkRed, Color.Transparent, false, false, 0, 0);
owner.messagesBox.AppendNewLine();
markers.headers = owner.messagesBox.BeginMark();
owner.messagesBox.EndMark(markers.headers);
 
markers.headersEnum = half.Headers.GetEnumerator();
ShowHeaders(markers.headersEnum, markers.headers);
}
 
private void UpdateHttpHalfView(HttpHalfMessage half, MarkerSet markers)
{
if(half.IsRequest)
{
owner.messagesBox.ChangeText(markers.method, (half as HttpRequest).Method,
Color.DarkRed, Color.LightGray, false, false, 0, 27);
owner.messagesBox.ChangeText(markers.uri, (half as HttpRequest).Uri,
Color.DarkRed, Color.LightGray, false, false, 0, 27);
}
else
{
owner.messagesBox.ChangeText(markers.status, (half as HttpResponse).StatusCode + " " + (half as HttpResponse).StatusMessage,
Color.DarkRed, Color.LightGray, false, false, 0, 27);
}
 
owner.messagesBox.ChangeText(markers.version, VersionToString(half.Version),
Color.DarkRed, Color.LightGray, false, false, 0, 27);
owner.messagesBox.ChangeText(markers.transferEncoding, EncodingToString(half.TransferEncoding),
Color.DarkRed, Color.LightGray, false, false, 0, 27);
owner.messagesBox.ChangeText(markers.length, half.Length < 0
? "<unknown>" : half.Length.ToString(),
Color.DarkRed, Color.LightGray, false, false, 0, 27);
owner.messagesBox.ChangeText(markers.contentEncoding, EncodingToString(half.ContentEncoding),
Color.DarkRed, Color.LightGray, false, false, 0, 27);
owner.messagesBox.ChangeText(markers.contentType, half.ContentType == null ? "<unknown>"
: half.ContentType + "/" + half.ContentSubtype,
Color.DarkRed, Color.LightGray, false, false, 0, 27);
owner.messagesBox.ChangeText(markers.charset, half.Charset == null
? "<unknown>" : half.Charset,
Color.DarkRed, Color.LightGray, false, false, 0, 27);
 
ShowHeaders(markers.headersEnum, markers.headers);
}
 
protected override void InitView()
{
try
{
lock(http)
{
owner.messagesBox.Clear();
 
// state
owner.messagesBox.AppendNewLine();
owner.messagesBox.AppendText("Complete: ",
Color.DarkRed, Color.Transparent, false, false, 0, 0);
stateMarker = owner.messagesBox.BeginMark();
owner.messagesBox.AppendText(http.Request.Complete && http.Response.Complete ? "YES" : "NO",
Color.DarkRed, Color.LightGray, false, false, 0, 27);
owner.messagesBox.EndMark(stateMarker);
owner.messagesBox.AppendNewLine();
owner.messagesBox.AppendNewLine();
 
// general info
InitHttpHalfView(http.Request, requestMarkers, "Request");
InitHttpHalfView(http.Response, responseMarkers, "Response");
 
// bodies
owner.messagesBox.AppendNewLine();
requestMarkers.body = owner.messagesBox.BeginMark();
owner.messagesBox.EndMark(requestMarkers.body);
ShowBody(requestMarkers, http.Request, Color.Green);
 
owner.messagesBox.AppendNewLine();
responseMarkers.body = owner.messagesBox.BeginMark();
owner.messagesBox.EndMark(responseMarkers.body);
ShowBody(responseMarkers, http.Response, Color.Blue);
}
}
finally
{
owner.messagesBox.EndUpdate();
}
}
 
protected override void UpdateView()
{
try
{
owner.messagesBox.BeginUpdate();
 
lock(http)
{
// state
owner.messagesBox.ChangeText(stateMarker, http.Request.Complete && http.Response.Complete ? "YES" : "NO",
Color.DarkRed, Color.LightGray, false, false, 0, 27);
 
// general info
UpdateHttpHalfView(http.Request, requestMarkers);
UpdateHttpHalfView(http.Response, responseMarkers);
 
// bodies
owner.messagesBox.DeleteText(requestMarkers.body);
ShowBody(requestMarkers, http.Request, Color.Green);
owner.messagesBox.DeleteText(responseMarkers.body);
ShowBody(responseMarkers, http.Response, Color.Blue);
}
}
finally
{
owner.messagesBox.EndUpdate();
}
}
 
private void ShowHeaders(IEnumerator headers, object marker)
{
while(headers.MoveNext())
{
HttpHeader h = (HttpHeader)headers.Current;
 
bool first = true;
foreach(string val in h.Values)
{
if(first)
{
owner.messagesBox.InsertText(marker, h.Name + ":",
Color.DarkRed, Color.Transparent, false, false, 4, 4);
owner.messagesBox.InsertText(marker, null,
Color.DarkRed, Color.Transparent, false, false, 6 - 4 - 1, 4);
first = false;
}
else
{
owner.messagesBox.InsertText(marker, null,
Color.DarkRed, Color.Transparent, false, false, 6 + h.Name.Length, 6 + h.Name.Length);
}
owner.messagesBox.InsertText(marker, val,
Color.DarkRed, Color.LightGray, false, false, 0, 6 + h.Name.Length);
owner.messagesBox.InsertNewLine(marker);
}
}
}
 
private void ShowBody(MarkerSet markers, HttpHalfMessage half, Color color)
{
if(half.Text != null)
{
ArrayList lines = Utils.SplitLine(half.Text);
for(int i = 0; i < lines.Count; i++)
{
owner.messagesBox.InsertText(markers.body, (string)lines[i], color, Color.LightGray, false, false, 0, 0);
owner.messagesBox.InsertNewLine(markers.body);
}
}
else if(half.Body != null)
{
ArrayList lines = Utils.SplitLine(Utils.BytesToString(half.Body, half.Length));
for(int i = 0; i < lines.Count; i++)
{
owner.messagesBox.InsertText(markers.body, (string)lines[i], color, Color.LightGray, false, false, 0, 0);
owner.messagesBox.InsertNewLine(markers.body);
}
}
}
 
public override void WriteLog(StreamWriter writer)
{
// request info
writer.WriteLine(
"Complete: " + (http.Request.Complete && http.Response.Complete ? "YES" : "NO") + "\r\n"
+ "\r\nRequest Method: " + http.Request.Method
+ "\r\nRequest URI: " + http.Request.Uri
+ "\r\nRequest Version: " + (http.Request.Version == HttpVersion.V0_9 ? "HTTP/0.9"
: http.Request.Version == HttpVersion.V1_0 ? "HTTP/1.0" : "HTTP/1.1")
+ "\r\nRequest Transfer Encoding: " + EncodingToString(http.Request.TransferEncoding)
+ "\r\nRequest Content Length: " + (http.Request.Length < 0 ? "<unknown>" : http.Request.Length.ToString())
+ "\r\nRequest Content Encoding: " + EncodingToString(http.Request.ContentEncoding)
+ "\r\nRequest Content Type: " + (http.Request.ContentType == null ? "<unknown>"
: http.Request.ContentType + "/" + http.Request.ContentSubtype)
+ "\r\nRequest Content Charset: " + (http.Request.Charset == null ? "<unknown>" : http.Request.Charset)
+ "\r\nRequest Headers:");
 
foreach(HttpHeader h in http.Request.Headers)
{
int indent = 0;
foreach(string val in h.Values)
{
if(indent == 0)
{
writer.WriteLine(" {0}: {1}", h.Name, val);
indent = 6 + h.Name.Length;
}
else
{
writer.WriteLine("{0," + indent + "}{1}", " ", val);
}
}
}
 
// response info
writer.WriteLine(
"\r\nResponse Version: " + (http.Response.Version == HttpVersion.V0_9
? "HTTP/0.9" : http.Response.Version == HttpVersion.V1_0 ? "HTTP/1.0" : "HTTP/1.1")
+ "\r\nResponse Status: " + http.Response.StatusCode + " " + http.Response.StatusMessage
+ "\r\nResponse Transfer Encoding: " + EncodingToString(http.Response.TransferEncoding)
+ "\r\nResponse Content Length: " + (http.Response.Length < 0
? "<unknown>" : http.Response.Length.ToString())
+ "\r\nResponse Content Encoding: " + EncodingToString(http.Response.ContentEncoding)
+ "\r\nResponse Content Type: " + (http.Response.ContentType == null ? "<unknown>"
: http.Response.ContentType + "/" + http.Response.ContentSubtype)
+ "\r\nResponse Content Charset: " + (http.Response.Charset == null ? "<unknown>" : http.Response.Charset)
+ "\r\nResponse Headers:");
 
foreach(HttpHeader h in http.Response.Headers)
{
int indent = 0;
foreach(string val in h.Values)
{
if(indent == 0)
{
writer.WriteLine(" {0}: {1}", h.Name, val);
indent = 6 + h.Name.Length;
}
else
{
writer.WriteLine("{0," + indent + "}{1}", " ", val);
}
}
}
writer.WriteLine();
 
// request body
if(http.Request.Text != null)
{
writer.WriteLine(http.Request.Text);
}
else if(http.Request.Body != null)
{
writer.WriteLine(Utils.BytesToString(http.Request.Body, http.Request.Length));
}
 
// response body
if(http.Response.Text != null)
{
writer.WriteLine(http.Response.Text);
}
else if(http.Response.Body != null)
{
writer.WriteLine(Utils.BytesToString(http.Response.Body, http.Response.Length));
}
 
writer.WriteLine("===============================================================");
writer.WriteLine();
}
}
 
private class XmlNodeData : TreeNodeData
{
private XmlMessage xml;
 
public XmlMessage Xml
{
get { return xml; }
set { xml = value; }
}
 
public XmlNodeData(MainForm owner, TreeNode node) : base(owner, node)
{
}
 
protected override void InitView()
{
try
{
// update main screen if necessary
owner.messagesBox.BeginUpdate();
 
lock(xml)
{
owner.messagesBox.Clear();
if(xml.ParseException != null)
{
owner.messagesBox.AppendText("Cannot parse XML:",
Color.Red, Color.Transparent, false, false, 0, 0);
owner.messagesBox.AppendNewLine();
owner.messagesBox.AppendText(xml.ParseException.Message,
Color.Red, Color.Transparent, false, false, 2, 2);
owner.messagesBox.AppendNewLine();
}
else if(xml.Xml != null)
{
ShowXmlNode(xml.Xml, 0);
}
}
}
finally
{
owner.messagesBox.EndUpdate();
}
}
 
protected override void UpdateView()
{
}
 
private void ShowXmlNode(XmlNode node, int indent)
{
if(node.NodeType == XmlNodeType.Document)
{
foreach(XmlNode subnode in node.ChildNodes)
{
ShowXmlNode(subnode, indent);
}
}
else if(node.NodeType == XmlNodeType.XmlDeclaration)
{
owner.messagesBox.AppendText("<?" + node.Name + " " + node.Value + " ?>",
Color.Blue, Color.Transparent, false, false, indent, indent+2);
owner.messagesBox.AppendNewLine();
}
else if(node.NodeType == XmlNodeType.Comment)
{
owner.messagesBox.AppendText("<!--", Color.Gray, Color.Transparent, false, false, indent, indent + 2);
ShowNormalizedXmlValue(node.Value, Color.Gray, Color.Thistle, false, false, indent);
owner.messagesBox.AppendText("-->", Color.Gray, Color.Transparent, false, false, 0, indent + 2);
owner.messagesBox.AppendNewLine();
}
else if(node.NodeType == XmlNodeType.Element)
{
owner.messagesBox.AppendText("<", Color.Blue, Color.Transparent, false, false, indent, indent + 2);
owner.messagesBox.AppendText(node.Name, Color.DarkRed, Color.Transparent, false, false, 0, indent + 2);
 
foreach(XmlAttribute attr in node.Attributes)
{
bool xmlAttr = attr.Name.StartsWith("xml");
owner.messagesBox.AppendText(attr.Name, xmlAttr ? Color.Red : Color.DarkRed, Color.Transparent, false, false, 1, indent + 2);
owner.messagesBox.AppendText("=\"", Color.Blue, Color.Transparent, false, false, 0, indent + 2);
owner.messagesBox.AppendText(attr.Value, xmlAttr ? Color.Red : Color.DarkRed, Color.Transparent, false, false, 0, indent + 2);
owner.messagesBox.AppendText("\"", Color.Blue, Color.Transparent, false, false, 0, indent + 2);
}
 
if(!node.HasChildNodes)
{
owner.messagesBox.AppendText(" />", Color.Blue, Color.Transparent, false, false, 0, indent + 2);
owner.messagesBox.AppendNewLine();
}
else
{
owner.messagesBox.AppendText(">", Color.Blue, Color.Transparent, false, false, 0, indent + 2);
 
bool elementExists = false;
bool lastText = true;
foreach(XmlNode subnode in node.ChildNodes)
{
if(subnode.NodeType == XmlNodeType.Text)
{
if(elementExists) owner.messagesBox.AppendNewLine();
ShowNormalizedXmlValue(subnode.Value, Color.Black, Color.LightGray, false, false, indent);
lastText = true;
}
else
{
if(lastText) owner.messagesBox.AppendNewLine();
ShowXmlNode(subnode, indent + 2);
elementExists = true;
lastText = false;
}
}
 
if(elementExists) owner.messagesBox.AppendText(null, Color.Black, Color.Transparent, false, false, indent, indent + 2);
owner.messagesBox.AppendText("</", Color.Blue, Color.Transparent, false, false, 0, indent + 2);
owner.messagesBox.AppendText(node.Name, Color.DarkRed, Color.Transparent, false, false, 0, indent + 2);
owner.messagesBox.AppendText(">", Color.Blue, Color.Transparent, false, false, 0, indent + 2);
owner.messagesBox.AppendNewLine();
}
}
}
 
private void ShowNormalizedXmlValue(string s, Color color, Color backColor, bool italic, bool bold, int indent)
{
int begin;
int end;
 
bool newLineFound = false;
foreach(char c in s)
{
if(c == '\n')
{
newLineFound = true;
break;
}
}
 
if(newLineFound)
{
owner.messagesBox.AppendNewLine();
owner.messagesBox.AppendText(null, color, Color.Transparent, italic, bold, indent, indent);
owner.messagesBox.AppendText(null, color, backColor, italic, bold, 2, 2);
}
 
// trim
for(begin = 0; begin < s.Length; begin++)
{
char c = s[begin];
if(c != '\n' && c != '\r' && c != '\t' && c != ' ') break;
}
for(end = s.Length-1; end >= 0; end--)
{
char c = s[end];
if(c != '\n' && c != '\r' && c != '\t' && c != ' ') break;
}
 
StringBuilder b = new StringBuilder(s.Length);
for(int i = begin; i <= end; i++)
{
char c = s[i];
 
switch(c)
{
case '\n':
if(b.Length > 0)
{
owner.messagesBox.AppendText(b.ToString(), color, backColor, italic, bold, 0, indent + 2);
b.Length = 0;
}
 
owner.messagesBox.AppendNewLine();
owner.messagesBox.AppendText(null, color, Color.Transparent, italic, bold, indent, indent);
owner.messagesBox.AppendText(null, color, backColor, italic, bold, 2, 2);
break;
 
case '\r':
break;
 
case '\t':
b.Append(" ");
break;
 
default:
b.Append(c);
break;
}
}
 
if(b.Length > 0)
owner.messagesBox.AppendText(b.ToString(), color, backColor, italic, bold, 0, indent + 2);
 
if(newLineFound)
{
owner.messagesBox.AppendNewLine();
owner.messagesBox.AppendText(null, color, Color.Transparent, italic, bold, indent, indent + 2);
}
}
 
public override void WriteLog(StreamWriter writer)
{
WriteXmlNode(writer, xml.Xml, "");
writer.WriteLine("===============================================================");
writer.WriteLine();
}
 
private void WriteXmlNode(StreamWriter writer, XmlNode node, string indent)
{
if(node.NodeType == XmlNodeType.Document)
{
foreach(XmlNode subnode in node.ChildNodes)
{
WriteXmlNode(writer, subnode, indent);
}
}
else if(node.NodeType == XmlNodeType.XmlDeclaration)
{
writer.WriteLine(indent + "<?" + node.Name + " " + node.Value + " ?>");
}
else if(node.NodeType == XmlNodeType.Comment)
{
writer.Write(indent + "<!--");
WriteNormalizedXmlValue(writer, node.Value, indent);
writer.WriteLine("-->");
}
else if(node.NodeType == XmlNodeType.Element)
{
writer.Write(indent + "<" + node.Name);
 
foreach(XmlAttribute attr in node.Attributes)
{
bool xmlAttr = attr.Name.StartsWith("xml");
writer.Write(" " + attr.Name + "=\"" + attr.Value + "\"");
}
 
if(!node.HasChildNodes)
{
writer.WriteLine(" />");
}
else
{
writer.Write(">");
 
bool elementExists = false;
bool lastText = true;
foreach(XmlNode subnode in node.ChildNodes)
{
if(subnode.NodeType == XmlNodeType.Text)
{
if(elementExists) writer.WriteLine();
WriteNormalizedXmlValue(writer, subnode.Value, indent);
lastText = true;
}
else
{
if(lastText) writer.WriteLine();
WriteXmlNode(writer, subnode, indent + " ");
elementExists = true;
lastText = false;
}
}
 
if(elementExists) writer.Write(indent);
writer.WriteLine("</" + node.Name + ">");
}
}
}
 
private void WriteNormalizedXmlValue(StreamWriter writer, string s, string indent)
{
int begin;
int end;
 
bool newLineFound = false;
foreach(char c in s)
{
if(c == '\n')
{
newLineFound = true;
break;
}
}
 
if(newLineFound)
{
writer.WriteLine();
writer.Write(indent + " ");
}
 
// trim
for(begin = 0; begin < s.Length; begin++)
{
char c = s[begin];
if(c != '\n' && c != '\r' && c != '\t' && c != ' ') break;
}
for(end = s.Length-1; end >= 0; end--)
{
char c = s[end];
if(c != '\n' && c != '\r' && c != '\t' && c != ' ') break;
}
 
StringBuilder b = new StringBuilder(s.Length);
for(int i = begin; i <= end; i++)
{
char c = s[i];
 
switch(c)
{
case '\n':
if(b.Length > 0)
{
writer.Write(b.ToString());
b.Length = 0;
}
 
writer.WriteLine();
writer.Write(indent + " ");
break;
 
case '\r':
break;
 
case '\t':
b.Append(" ");
break;
 
case '&':
b.Append("&amp;");
break;
 
case '<':
b.Append("&lt;");
break;
 
case '>':
b.Append("&gt;");
break;
 
default:
b.Append(c);
break;
}
}
 
if(b.Length > 0)
writer.Write(b.ToString());
 
if(newLineFound)
{
writer.WriteLine();
writer.Write(indent + " ");
}
}
}
 
#endregion node display classes
 
#region other classes
private abstract class Utils
{
public static string BytesToString(byte[] bytes, int length)
{
if(bytes == null) return null;
 
char[] chars = new char[length + 1];
Decoder decoder = System.Text.Encoding.UTF8.GetDecoder();
int charLen = decoder.GetChars(bytes, 0, length, chars, 0);
 
for(int i = 0; i < charLen; i++)
{
char c = chars[i];
if(c != '\n' && c != '\r' && Char.IsControl(c)) chars[i] = '.';
}
 
return (new System.String(chars)).Substring(0, charLen);
}
 
public static ArrayList SplitLine(string line)
{
if(line == null) return null;
 
ArrayList res = new ArrayList();
StringBuilder b = new StringBuilder(200);
 
foreach(char c in line)
{
switch(c)
{
case '\r':
break;
 
case '\n':
res.Add(b.ToString());
b.Length = 0;
break;
 
default:
b.Append(c);
break;
}
}
 
res.Add(b.ToString());
 
return res;
}
}
#endregion other classes
}
 
public class LogMessages
{
private ArrayList messages = new ArrayList();
private ListBox logBox;
private LogLevel level = LogLevel.Debug;
 
public LogLevel Level
{
get { return level; }
set { level = value; Update(); }
}
 
public LogMessages(ListBox logBox)
{
this.logBox = logBox;
}
 
public ArrayList Messages
{
get { return new ArrayList(messages); }
}
 
public void Add(LogMessage message)
{
messages.Add(message);
if(Show(message))
logBox.TopIndex = logBox.Items.Count - 1;
}
 
public void Clear()
{
messages.Clear();
logBox.Items.Clear();
}
 
public void Update()
{
logBox.BeginUpdate();
 
logBox.Items.Clear();
foreach(LogMessage message in messages)
{
Show(message);
}
logBox.TopIndex = logBox.Items.Count - 1;
 
logBox.EndUpdate();
}
 
private bool Show(LogMessage message)
{
if(message.Level > this.level) return false;
 
try
{
logBox.Items.Add(message.ToString());
}
catch(ObjectDisposedException) {}
 
return true;
}
}
 
public class LogMessage
{
private TcpConnection tcp;
private LogLevel level;
private string message;
private Exception exception;
private DateTime timestamp = DateTime.Now;
 
public TcpConnection Tcp
{
get { return tcp; }
set { tcp = value; }
}
 
public LogLevel Level
{
get { return level; }
set { level = value; }
}
 
public string Message
{
get { return message; }
set { message = value; }
}
 
public Exception Exception
{
get { return exception; }
set { exception = value; }
}
 
public DateTime Timestamp
{
get { return timestamp; }
set { timestamp = value; }
}
 
public LogMessage(TcpConnection tcp, LogLevel level, string message, Exception exception)
{
this.tcp = tcp;
this.level = level;
this.message = message;
this.exception = exception;
}
 
public override string ToString()
{
string l = " ";
switch(level)
{
case LogLevel.Debug: l = "debug"; break;
case LogLevel.Info: l = "info "; break;
case LogLevel.Important: l = "Imp "; break;
case LogLevel.Warning: l = "Warn "; break;
case LogLevel.Error: l = "ERROR"; break;
case LogLevel.Critical: l = "CRIT "; break;
}
 
return (tcp == null ? "...." : tcp.Id) + " " + l + " " + timestamp.ToString("HH:mm:ss.ffff") + " "
+ (exception != null ? exception.Message : message);
}
}
 
public enum TcpShowMode
{
ByDirection,
ByTime
}
 
public class RecentItem : IComparable
{
private int listenPort;
 
public int ListenPort
{
get { return listenPort; }
set { listenPort = value; }
}
 
private string resendHost;
 
public string ResendHost
{
get { return resendHost; }
set { resendHost = value; }
}
 
private int resendPort;
 
public int ResendPort
{
get { return resendPort; }
set { resendPort = value; }
}
 
private DateTime timestamp;
 
public DateTime Timestamp
{
get { return timestamp; }
}
 
public RecentItem(int listenPort, string resendHost, int resendPort)
{
this.listenPort = listenPort;
this.resendHost = resendHost;
this.resendPort = resendPort;
}
 
public override string ToString()
{
return string.Format("{0} to {1}:{2} at {3:HH:mm:ss.ff}",
listenPort, resendHost, resendPort, timestamp);
}
 
public override bool Equals(object obj)
{
if(!(obj is RecentItem)) return false;
 
RecentItem i2 = (RecentItem)obj;
 
return (i2.listenPort == this.listenPort) && (i2.resendHost == this.resendHost)
&& (i2.resendPort == this.resendPort);
}
 
public override int GetHashCode()
{
return this.resendHost.GetHashCode();
}
 
public string SaveToRegistry()
{
return string.Format("{0}:{1}:{2}:{3}",
listenPort, resendPort, timestamp.Ticks, resendHost);
}
 
public static RecentItem LoadFromRegistry(string str)
{
Regex re = new Regex(@"^(\d+):(\d+):(\d+):(.+)$");
Match m = re.Match(str);
if(!m.Success) throw new Exception("Cannot parse recent item");
 
RecentItem item = new RecentItem(int.Parse(m.Groups[1].Value),
m.Groups[4].Value, int.Parse(m.Groups[2].Value));
 
item.timestamp = new DateTime(long.Parse(m.Groups[3].Value));
 
return item;
}
 
public void UpdateTimestamp()
{
timestamp = DateTime.Now;
}
 
public void UpdateTimestamp(RecentItem second)
{
if(second.timestamp > timestamp)
timestamp = second.timestamp;
}
 
#region IComparable Members
 
int IComparable.CompareTo(object obj)
{
if(!(obj is RecentItem))
throw new Exception("Cannot compare");
 
return this.timestamp.CompareTo(((RecentItem)obj).timestamp);
}
 
#endregion
}
}
/TCPproxy/tags/0002-second_public/LinkedList.cs
0,0 → 1,107
using System;
using System.Collections;
 
namespace TCPproxy
{
public class LinkedList
{
private class ListNode
{
public object data;
public ListNode next;
}
 
private class LinkedEnumerator : IEnumerator
{
private LinkedList list;
private ListNode current;
 
public object Current
{
get
{
if(current == null) throw new InvalidOperationException("No current element");
 
return current.data;
}
}
 
public bool MoveNext()
{
if(current == null)
{
current = list.first;
return (current != null);
}
else
{
if(current.next == null)
{
return false;
}
else
{
current = current.next;
return true;
}
}
}
 
public void Reset()
{
current = null;
}
 
public LinkedEnumerator(LinkedList list)
{
this.list = list;
}
}
 
private ListNode first;
private ListNode last;
private int count = 0;
 
public int Count
{
get { return count; }
}
 
public IEnumerator GetEnumerator()
{
return new LinkedEnumerator(this);
}
 
public void Add(object obj)
{
ListNode node = new ListNode();
node.data = obj;
 
if(last == null)
{
first = node;
}
else
{
last.next = node;
}
last = node;
count++;
}
}
 
public class LinkedListReadOnly
{
private LinkedList origin;
 
public LinkedListReadOnly(LinkedList origin)
{
this.origin = origin;
}
 
public IEnumerator GetEnumerator()
{
return origin.GetEnumerator();
}
}
}
/TCPproxy/tags/0002-second_public/HostUtils.cs
0,0 → 1,48
using System;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
 
namespace TCPproxy
{
abstract class HostUtils
{
public static IPAddress ResendHostToIp(string host)
{
IPAddress ip;
 
if(host == "")
{
throw new Exception("Please enter the resend host");
}
 
Regex ipRegex = new Regex(@"^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$");
if(ipRegex.Match(host).Success)
{
try
{
ip = IPAddress.Parse(host);
}
catch(FormatException)
{
throw new Exception("Wrong IP address of the resend host");
}
}
else
{
IPHostEntry hostInfo = Dns.GetHostEntry(host);
 
if(hostInfo.AddressList.Length > 0)
{
ip = hostInfo.AddressList[0];
}
else
{
throw new Exception("Cannot find IP address of the resend host");
}
}
 
return ip;
}
}
}
/TCPproxy/tags/0002-second_public/ListenForm.cs
0,0 → 1,207
using System;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Net;
using System.Text;
using System.Windows.Forms;
 
namespace TCPproxy
{
public class ListenForm : Form
{
private int listenPort;
private string resendHost;
private IPAddress resendIp;
private int resendPort;
 
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
private Button startButton;
private Label label2;
private Label label1;
private TextBox resendPortBox;
private TextBox resendHostBox;
private TextBox listenPortBox;
private Button cancelButton;
 
#region Windows Form Designer generated code
 
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.startButton = new System.Windows.Forms.Button();
this.label2 = new System.Windows.Forms.Label();
this.label1 = new System.Windows.Forms.Label();
this.resendPortBox = new System.Windows.Forms.TextBox();
this.resendHostBox = new System.Windows.Forms.TextBox();
this.listenPortBox = new System.Windows.Forms.TextBox();
this.cancelButton = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// startButton
//
this.startButton.Location = new System.Drawing.Point(136, 62);
this.startButton.Name = "startButton";
this.startButton.Size = new System.Drawing.Size(75, 23);
this.startButton.TabIndex = 60;
this.startButton.Text = "Listen";
this.startButton.Click += new System.EventHandler(this.startButton_Click);
//
// label2
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(12, 35);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(56, 13);
this.label2.TabIndex = 30;
this.label2.Text = "&Resend to";
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(12, 9);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(71, 13);
this.label1.TabIndex = 10;
this.label1.Text = "Listen &on port";
//
// resendPortBox
//
this.resendPortBox.Location = new System.Drawing.Point(220, 32);
this.resendPortBox.Name = "resendPortBox";
this.resendPortBox.Size = new System.Drawing.Size(72, 20);
this.resendPortBox.TabIndex = 50;
//
// resendHostBox
//
this.resendHostBox.Location = new System.Drawing.Point(114, 32);
this.resendHostBox.Name = "resendHostBox";
this.resendHostBox.Size = new System.Drawing.Size(100, 20);
this.resendHostBox.TabIndex = 40;
//
// listenPortBox
//
this.listenPortBox.Location = new System.Drawing.Point(114, 6);
this.listenPortBox.Name = "listenPortBox";
this.listenPortBox.Size = new System.Drawing.Size(100, 20);
this.listenPortBox.TabIndex = 20;
//
// cancelButton
//
this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel;
this.cancelButton.Location = new System.Drawing.Point(217, 62);
this.cancelButton.Name = "cancelButton";
this.cancelButton.Size = new System.Drawing.Size(75, 23);
this.cancelButton.TabIndex = 70;
this.cancelButton.Text = "Cancel";
//
// ListenForm
//
this.AcceptButton = this.startButton;
this.CancelButton = this.cancelButton;
this.ClientSize = new System.Drawing.Size(301, 91);
this.Controls.Add(this.cancelButton);
this.Controls.Add(this.startButton);
this.Controls.Add(this.label2);
this.Controls.Add(this.label1);
this.Controls.Add(this.resendPortBox);
this.Controls.Add(this.resendHostBox);
this.Controls.Add(this.listenPortBox);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "ListenForm";
this.ShowInTaskbar = false;
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Text = "TCPproxy - Start Listening";
this.ResumeLayout(false);
this.PerformLayout();
 
}
 
#endregion
 
public ListenForm()
{
InitializeComponent();
}
 
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
 
public bool Execute(Form owner,
ref int listenPort, ref string resendHost, out IPAddress resendIp, ref int resendPort)
{
listenPortBox.Text = (listenPort == 0) ? "" : listenPort.ToString();
resendHostBox.Text = resendHost;
resendPortBox.Text = (resendPort == 0) ? "" : resendPort.ToString();
 
if(this.ShowDialog(owner) != DialogResult.OK) {
resendIp = null;
return false;
}
 
listenPort = this.listenPort;
resendHost = this.resendHost;
resendIp = this.resendIp;
resendPort = this.resendPort;
 
return true;
}
 
private void startButton_Click(object sender, EventArgs e)
{
// parse listen port
try
{
listenPort = int.Parse(listenPortBox.Text);
}
catch(FormatException)
{
MessageBox.Show("Listen port must be an integer number", "TCPproxy",
MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
 
// get resend host
try
{
resendIp = HostUtils.ResendHostToIp(resendHostBox.Text);
}
catch(Exception ex)
{
MessageBox.Show("Cannot get host IP: " + ex.Message, "TCPproxy",
MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
resendHost = resendHostBox.Text;
 
// parse resend port
try
{
resendPort = int.Parse(resendPortBox.Text);
}
catch(FormatException)
{
MessageBox.Show("Resend port must be an integer number", "TCPproxy",
MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
 
this.DialogResult = DialogResult.OK;
this.Close();
}
}
}
/TCPproxy/tags/0002-second_public/tcpproxy.build
0,0 → 1,27
<?xml version="1.0" ?>
<project name="TCPproxy" default="build">
<property name="debug" value="true"/>
<property name="build.dir" value="bin"/>
 
<target name="clean" description="cleans build directory">
<delete dir="${build.dir}" verbose="true" if="${directory::exists(build.dir)}" />
</target>
 
<target name="build">
<mkdir dir="${build.dir}"/>
<csc target="winexe" output="${build.dir}/tcpproxy.exe" debug="${debug}">
<sources>
<include name="*.cs" />
</sources>
<resources>
<include name="*.resx" />
</resources>
</csc>
</target>
 
<target name="run" depends="build">
<exec program="tcpproxy.exe" basedir="${build.dir}" />
</target>
 
<target name="all" depends="clean,build" />
</project>
/TCPproxy/tags/0002-second_public/TCPproxy.csproj
0,0 → 1,76
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>8.0.50727</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{5051426D-C5F7-4C1D-90F2-BD73A255FBD1}</ProjectGuid>
<OutputType>WinExe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>TCPproxy</RootNamespace>
<AssemblyName>TCPproxy</AssemblyName>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Deployment" />
<Reference Include="System.Drawing" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="AboutForm.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="HostUtils.cs" />
<Compile Include="LinkedList.cs" />
<Compile Include="ListenForm.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="MainForm.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Network.cs" />
<Compile Include="AssemblyInfo.cs" />
<EmbeddedResource Include="AboutForm.resx">
<SubType>Designer</SubType>
<DependentUpon>AboutForm.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="ListenForm.resx">
<SubType>Designer</SubType>
<DependentUpon>ListenForm.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="MainForm.resx">
<SubType>Designer</SubType>
<DependentUpon>MainForm.cs</DependentUpon>
</EmbeddedResource>
<Compile Include="ViewControl.cs">
<SubType>Component</SubType>
</Compile>
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>
/TCPproxy/tags/0002-second_public/AssemblyInfo.cs
0,0 → 1,33
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
 
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("TCPproxy")]
[assembly: AssemblyDescription("A proxy for any TCP connections which saves and shows all catched information, special processing for HTTP protocol and XML data in it.")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Anatoli Klassen")]
[assembly: AssemblyProduct("TCPproxy")]
[assembly: AssemblyCopyright("Copyleft 2005-2006 Anatoli Klassen. Public domain. No warranty.")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
 
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
 
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("e1b78820-40c9-41df-b27c-6edbd61b9131")]
 
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
[assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyFileVersion("1.0.0.0")]
/TCPproxy/tags/0002-second_public/AboutForm.cs
0,0 → 1,161
using System;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
 
namespace TCPproxy
{
public class AboutForm : Form
{
private System.ComponentModel.IContainer components = null;
private Label appLabel;
private Label copyrightLabel;
private Label license1Label;
private Button okButton;
private LinkLabel homePageLabel;
private Label versionLabel;
private Label license2Label;
private Label warrantyLabel;
 
#region Windows Form Designer generated code
 
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.appLabel = new Label();
this.copyrightLabel = new Label();
this.license1Label = new Label();
this.okButton = new Button();
this.homePageLabel = new LinkLabel();
this.versionLabel = new Label();
this.license2Label = new Label();
this.warrantyLabel = new Label();
this.SuspendLayout();
//
// appLabel
//
this.appLabel.AutoSize = true;
this.appLabel.Font = new System.Drawing.Font("Arial", 20.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.appLabel.Location = new System.Drawing.Point(12, 16);
this.appLabel.Name = "appLabel";
this.appLabel.Size = new System.Drawing.Size(146, 32);
this.appLabel.TabIndex = 0;
this.appLabel.Text = "TCPproxy";
//
// copyrightLabel
//
this.copyrightLabel.AutoSize = true;
this.copyrightLabel.Location = new System.Drawing.Point(32, 60);
this.copyrightLabel.Name = "copyrightLabel";
this.copyrightLabel.Size = new System.Drawing.Size(195, 13);
this.copyrightLabel.TabIndex = 1;
this.copyrightLabel.Text = "Copyright (c) 2005-2006 Anatoli Klassen";
//
// license1Label
//
this.license1Label.AutoSize = true;
this.license1Label.Location = new System.Drawing.Point(32, 108);
this.license1Label.Name = "license1Label";
this.license1Label.Size = new System.Drawing.Size(126, 13);
this.license1Label.TabIndex = 2;
this.license1Label.Text = "\"Public domain\" software";
//
// okButton
//
this.okButton.Anchor = ((AnchorStyles)((AnchorStyles.Bottom | AnchorStyles.Right)));
this.okButton.DialogResult = DialogResult.OK;
this.okButton.Location = new System.Drawing.Point(205, 217);
this.okButton.Name = "okButton";
this.okButton.Size = new System.Drawing.Size(75, 23);
this.okButton.TabIndex = 3;
this.okButton.Text = "OK";
//
// homePageLabel
//
this.homePageLabel.AutoSize = true;
this.homePageLabel.Location = new System.Drawing.Point(32, 188);
this.homePageLabel.Name = "homePageLabel";
this.homePageLabel.Size = new System.Drawing.Size(229, 13);
this.homePageLabel.TabIndex = 4;
this.homePageLabel.TabStop = true;
this.homePageLabel.Text = "http://www.26th.net/public/projects/tcpproxy/";
this.homePageLabel.LinkClicked += new LinkLabelLinkClickedEventHandler(this.homePageLabel_LinkClicked);
//
// versionLabel
//
this.versionLabel.AutoSize = true;
this.versionLabel.Location = new System.Drawing.Point(32, 76);
this.versionLabel.Name = "versionLabel";
this.versionLabel.Size = new System.Drawing.Size(60, 13);
this.versionLabel.TabIndex = 5;
this.versionLabel.Text = "Version 1.0";
//
// license2Label
//
this.license2Label.AutoSize = true;
this.license2Label.Location = new System.Drawing.Point(32, 124);
this.license2Label.Name = "license2Label";
this.license2Label.Size = new System.Drawing.Size(180, 13);
this.license2Label.TabIndex = 6;
this.license2Label.Text = "- you are allowed to use it in any way";
//
// warrantyLabel
//
this.warrantyLabel.AutoSize = true;
this.warrantyLabel.Location = new System.Drawing.Point(32, 156);
this.warrantyLabel.Name = "warrantyLabel";
this.warrantyLabel.Size = new System.Drawing.Size(64, 13);
this.warrantyLabel.TabIndex = 7;
this.warrantyLabel.Text = "No warranty";
//
// AboutForm
//
this.AcceptButton = this.okButton;
this.CancelButton = this.okButton;
this.ClientSize = new System.Drawing.Size(292, 252);
this.Controls.Add(this.warrantyLabel);
this.Controls.Add(this.license2Label);
this.Controls.Add(this.versionLabel);
this.Controls.Add(this.homePageLabel);
this.Controls.Add(this.okButton);
this.Controls.Add(this.license1Label);
this.Controls.Add(this.copyrightLabel);
this.Controls.Add(this.appLabel);
this.FormBorderStyle = FormBorderStyle.FixedDialog;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "AboutForm";
this.ShowInTaskbar = false;
this.StartPosition = FormStartPosition.CenterParent;
this.Text = "About TCPproxy";
this.ResumeLayout(false);
this.PerformLayout();
}
 
#endregion
 
public AboutForm()
{
InitializeComponent();
}
 
protected override void Dispose(bool disposing)
{
if(disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}
 
private void homePageLabel_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
homePageLabel.LinkVisited = true;
System.Diagnostics.Process.Start(homePageLabel.Text);
}
}
}
/TCPproxy/tags/0002-second_public/MainForm.resx
0,0 → 1,225
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
 
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="treeImageList.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>436, 17</value>
</metadata>
<data name="treeImageList.ImageStream" mimetype="application/x-microsoft.net.object.binary.base64">
<value>
AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj0yLjAuMC4w
LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0
ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAAAK
CAAAAk1TRnQBSQFMAgEBAwEAAQQBAAEEAQABEAEAARABAAT/AQkBAAj/AUIBTQE2AQQGAAE2AQQCAAEo
AwABQAMAARADAAEBAQABCAYAAQQYAAGAAgABgAMAAoABAAGAAwABgAEAAYABAAKAAgADwAEAAcAB3AHA
AQAB8AHKAaYBAAEzBQABMwEAATMBAAEzAQACMwIAAxYBAAMcAQADIgEAAykBAANVAQADTQEAA0IBAAM5
AQABgAF8Af8BAAJQAf8BAAGTAQAB1gEAAf8B7AHMAQABxgHWAe8BAAHWAucBAAGQAakBrQIAAf8BMwMA
AWYDAAGZAwABzAIAATMDAAIzAgABMwFmAgABMwGZAgABMwHMAgABMwH/AgABZgMAAWYBMwIAAmYCAAFm
AZkCAAFmAcwCAAFmAf8CAAGZAwABmQEzAgABmQFmAgACmQIAAZkBzAIAAZkB/wIAAcwDAAHMATMCAAHM
AWYCAAHMAZkCAALMAgABzAH/AgAB/wFmAgAB/wGZAgAB/wHMAQABMwH/AgAB/wEAATMBAAEzAQABZgEA
ATMBAAGZAQABMwEAAcwBAAEzAQAB/wEAAf8BMwIAAzMBAAIzAWYBAAIzAZkBAAIzAcwBAAIzAf8BAAEz
AWYCAAEzAWYBMwEAATMCZgEAATMBZgGZAQABMwFmAcwBAAEzAWYB/wEAATMBmQIAATMBmQEzAQABMwGZ
AWYBAAEzApkBAAEzAZkBzAEAATMBmQH/AQABMwHMAgABMwHMATMBAAEzAcwBZgEAATMBzAGZAQABMwLM
AQABMwHMAf8BAAEzAf8BMwEAATMB/wFmAQABMwH/AZkBAAEzAf8BzAEAATMC/wEAAWYDAAFmAQABMwEA
AWYBAAFmAQABZgEAAZkBAAFmAQABzAEAAWYBAAH/AQABZgEzAgABZgIzAQABZgEzAWYBAAFmATMBmQEA
AWYBMwHMAQABZgEzAf8BAAJmAgACZgEzAQADZgEAAmYBmQEAAmYBzAEAAWYBmQIAAWYBmQEzAQABZgGZ
AWYBAAFmApkBAAFmAZkBzAEAAWYBmQH/AQABZgHMAgABZgHMATMBAAFmAcwBmQEAAWYCzAEAAWYBzAH/
AQABZgH/AgABZgH/ATMBAAFmAf8BmQEAAWYB/wHMAQABzAEAAf8BAAH/AQABzAEAApkCAAGZATMBmQEA
AZkBAAGZAQABmQEAAcwBAAGZAwABmQIzAQABmQEAAWYBAAGZATMBzAEAAZkBAAH/AQABmQFmAgABmQFm
ATMBAAGZATMBZgEAAZkBZgGZAQABmQFmAcwBAAGZATMB/wEAApkBMwEAApkBZgEAA5kBAAKZAcwBAAKZ
Af8BAAGZAcwCAAGZAcwBMwEAAWYBzAFmAQABmQHMAZkBAAGZAswBAAGZAcwB/wEAAZkB/wIAAZkB/wEz
AQABmQHMAWYBAAGZAf8BmQEAAZkB/wHMAQABmQL/AQABzAMAAZkBAAEzAQABzAEAAWYBAAHMAQABmQEA
AcwBAAHMAQABmQEzAgABzAIzAQABzAEzAWYBAAHMATMBmQEAAcwBMwHMAQABzAEzAf8BAAHMAWYCAAHM
AWYBMwEAAZkCZgEAAcwBZgGZAQABzAFmAcwBAAGZAWYB/wEAAcwBmQIAAcwBmQEzAQABzAGZAWYBAAHM
ApkBAAHMAZkBzAEAAcwBmQH/AQACzAIAAswBMwEAAswBZgEAAswBmQEAA8wBAALMAf8BAAHMAf8CAAHM
Af8BMwEAAZkB/wFmAQABzAH/AZkBAAHMAf8BzAEAAcwC/wEAAcwBAAEzAQAB/wEAAWYBAAH/AQABmQEA
AcwBMwIAAf8CMwEAAf8BMwFmAQAB/wEzAZkBAAH/ATMBzAEAAf8BMwH/AQAB/wFmAgAB/wFmATMBAAHM
AmYBAAH/AWYBmQEAAf8BZgHMAQABzAFmAf8BAAH/AZkCAAH/AZkBMwEAAf8BmQFmAQAB/wKZAQAB/wGZ
AcwBAAH/AZkB/wEAAf8BzAIAAf8BzAEzAQAB/wHMAWYBAAH/AcwBmQEAAf8CzAEAAf8BzAH/AQAC/wEz
AQABzAH/AWYBAAL/AZkBAAL/AcwBAAJmAf8BAAFmAf8BZgEAAWYC/wEAAf8CZgEAAf8BZgH/AQAC/wFm
AQABIQEAAaUBAANfAQADdwEAA4YBAAOWAQADywEAA7IBAAPXAQAD3QEAA+MBAAPqAQAD8QEAA/gBAAHw
AfsB/wEAAaQCoAEAA4ADAAH/AgAB/wMAAv8BAAH/AwAB/wEAAf8BAAL/AgAD/1cABTMLAAXsKQAJMwcA
CewmAAszBQAL7CQADTMDAA3sIwANMwMADewiAA8zAQAP7CEADzMBAA/sIQAPMwEAD+whAA8zAQAP7CEA
DzMBAA/sIgANMwMADewjAA0zAwAN7CQACzMFAAvsJgAJMwcACewpAAUzCwAF7BUAAUIBTQE+BwABPgMA
ASgDAAFAAwABEAMAAQEBAAEBBQABgBcAA/8BAAb/AgAC/wH8AR8B/AEfAgAC/wHwAQcB8AEHAgAC/wHg
AQMB4AEDAgAC/wHAAQEBwAEBAgAC/wHAAQEBwAEBAgAC/wGAAQABgAMAAv8BgAEAAYADAAL/AYABAAGA
AwAC/wGAAQABgAMAAv8BgAEAAYADAAL/AcABAQHAAQECAAL/AcABAQHAAQECAAL/AeABAwHgAQMCAAL/
AfABBwHwAQcCAAL/AfwBHwH8AR8CAAs=
</value>
</data>
<metadata name="messagesContextMenu.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>556, 17</value>
</metadata>
<metadata name="saveButtonImageList.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
<data name="saveButtonImageList.ImageStream" mimetype="application/x-microsoft.net.object.binary.base64">
<value>
AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj0yLjAuMC4w
LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0
ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAABU
BwAAAk1TRnQBSQFMAwEBAAEEAQABBAEAAREBAAEQAQAE/wEJAQAI/wFCAU0BNgEEBgABNgEEAgABKAMA
AUQDAAEQAwABAQEAAQgFAAFAAQQYAAGAAgABgAMAAoABAAGAAwABgAEAAYABAAKAAgADwAEAAcAB3AHA
AQAB8AHKAaYBAAEzBQABMwEAATMBAAEzAQACMwIAAxYBAAMcAQADIgEAAykBAANVAQADTQEAA0IBAAM5
AQABgAF8Af8BAAJQAf8BAAGTAQAB1gEAAf8B7AHMAQABxgHWAe8BAAHWAucBAAGQAakBrQIAAf8BMwMA
AWYDAAGZAwABzAIAATMDAAIzAgABMwFmAgABMwGZAgABMwHMAgABMwH/AgABZgMAAWYBMwIAAmYCAAFm
AZkCAAFmAcwCAAFmAf8CAAGZAwABmQEzAgABmQFmAgACmQIAAZkBzAIAAZkB/wIAAcwDAAHMATMCAAHM
AWYCAAHMAZkCAALMAgABzAH/AgAB/wFmAgAB/wGZAgAB/wHMAQABMwH/AgAB/wEAATMBAAEzAQABZgEA
ATMBAAGZAQABMwEAAcwBAAEzAQAB/wEAAf8BMwIAAzMBAAIzAWYBAAIzAZkBAAIzAcwBAAIzAf8BAAEz
AWYCAAEzAWYBMwEAATMCZgEAATMBZgGZAQABMwFmAcwBAAEzAWYB/wEAATMBmQIAATMBmQEzAQABMwGZ
AWYBAAEzApkBAAEzAZkBzAEAATMBmQH/AQABMwHMAgABMwHMATMBAAEzAcwBZgEAATMBzAGZAQABMwLM
AQABMwHMAf8BAAEzAf8BMwEAATMB/wFmAQABMwH/AZkBAAEzAf8BzAEAATMC/wEAAWYDAAFmAQABMwEA
AWYBAAFmAQABZgEAAZkBAAFmAQABzAEAAWYBAAH/AQABZgEzAgABZgIzAQABZgEzAWYBAAFmATMBmQEA
AWYBMwHMAQABZgEzAf8BAAJmAgACZgEzAQADZgEAAmYBmQEAAmYBzAEAAWYBmQIAAWYBmQEzAQABZgGZ
AWYBAAFmApkBAAFmAZkBzAEAAWYBmQH/AQABZgHMAgABZgHMATMBAAFmAcwBmQEAAWYCzAEAAWYBzAH/
AQABZgH/AgABZgH/ATMBAAFmAf8BmQEAAWYB/wHMAQABzAEAAf8BAAH/AQABzAEAApkCAAGZATMBmQEA
AZkBAAGZAQABmQEAAcwBAAGZAwABmQIzAQABmQEAAWYBAAGZATMBzAEAAZkBAAH/AQABmQFmAgABmQFm
ATMBAAGZATMBZgEAAZkBZgGZAQABmQFmAcwBAAGZATMB/wEAApkBMwEAApkBZgEAA5kBAAKZAcwBAAKZ
Af8BAAGZAcwCAAGZAcwBMwEAAWYBzAFmAQABmQHMAZkBAAGZAswBAAGZAcwB/wEAAZkB/wIAAZkB/wEz
AQABmQHMAWYBAAGZAf8BmQEAAZkB/wHMAQABmQL/AQABzAMAAZkBAAEzAQABzAEAAWYBAAHMAQABmQEA
AcwBAAHMAQABmQEzAgABzAIzAQABzAEzAWYBAAHMATMBmQEAAcwBMwHMAQABzAEzAf8BAAHMAWYCAAHM
AWYBMwEAAZkCZgEAAcwBZgGZAQABzAFmAcwBAAGZAWYB/wEAAcwBmQIAAcwBmQEzAQABzAGZAWYBAAHM
ApkBAAHMAZkBzAEAAcwBmQH/AQACzAIAAswBMwEAAswBZgEAAswBmQEAA8wBAALMAf8BAAHMAf8CAAHM
Af8BMwEAAZkB/wFmAQABzAH/AZkBAAHMAf8BzAEAAcwC/wEAAcwBAAEzAQAB/wEAAWYBAAH/AQABmQEA
AcwBMwIAAf8CMwEAAf8BMwFmAQAB/wEzAZkBAAH/ATMBzAEAAf8BMwH/AQAB/wFmAgAB/wFmATMBAAHM
AmYBAAH/AWYBmQEAAf8BZgHMAQABzAFmAf8BAAH/AZkCAAH/AZkBMwEAAf8BmQFmAQAB/wKZAQAB/wGZ
AcwBAAH/AZkB/wEAAf8BzAIAAf8BzAEzAQAB/wHMAWYBAAH/AcwBmQEAAf8CzAEAAf8BzAH/AQAC/wEz
AQABzAH/AWYBAAL/AZkBAAL/AcwBAAJmAf8BAAFmAf8BZgEAAWYC/wEAAf8CZgEAAf8BZgH/AQAC/wFm
AQABIQEAAaUBAANfAQADdwEAA4YBAAOWAQADywEAA7IBAAPXAQAD3QEAA+MBAAPqAQAD8QEAA/gBAAHw
AfsB/wEAAaQCoAEAA4ADAAH/AgAB/wMAAv8BAAH/AwAB/wEAAf8BAAL/AgAD//8A/wD/AP8ARQABQgFN
AT4HAAE+AwABKAMAAUQDAAEQAwABAQEAAQEFAAHAFwAD/wEAAv8BgAkAAv8BgAkAAv8BgAkAAv8BgAkA
Av8BgAkAAv8BgAkAAf8BfwGACQAB/gE/AYAJAAH8AR8BgAkAAfgBDwGACQAC/wGACQAC/wGACQAC/wGA
CQAC/wGACQAC/wGACQAC/wGACQAL
</value>
</data>
<metadata name="viewContextMenu.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>173, 17</value>
</metadata>
<metadata name="saveLogDialog.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>313, 17</value>
</metadata>
<metadata name="mainMenu.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 54</value>
</metadata>
<metadata name="saveBinLogDialog.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>120, 54</value>
</metadata>
<metadata name="loadBinLogDialog.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>259, 52</value>
</metadata>
</root>
/TCPproxy/tags/0002-second_public/ListenForm.resx
0,0 → 1,120
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
 
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>
/TCPproxy/tags/0002-second_public/AboutForm.resx
0,0 → 1,120
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
 
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>
/TCPproxy/tags/0002-second_public/TCPproxy.sln
0,0 → 1,20

Microsoft Visual Studio Solution File, Format Version 9.00
# Visual C# Express 2005
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TCPproxy", "TCPproxy.csproj", "{5051426D-C5F7-4C1D-90F2-BD73A255FBD1}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{5051426D-C5F7-4C1D-90F2-BD73A255FBD1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5051426D-C5F7-4C1D-90F2-BD73A255FBD1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5051426D-C5F7-4C1D-90F2-BD73A255FBD1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5051426D-C5F7-4C1D-90F2-BD73A255FBD1}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal
/TCPproxy/tags/0002-second_public/ViewControl.cs
0,0 → 1,1709
using System;
using System.Drawing;
using System.Collections;
using System.Windows.Forms;
using System.Text;
using System.Runtime.InteropServices;
 
namespace TCPproxy
{
// FIXME scroll line-by-line in word-wrap mode
// FIXME implement copy by keyboard commands
// FIXME show bin data
// FIXME abillity to insert text after saved marker
// FIXME don't use double buffering
// FIXME move markers and selection when text is changed
public class ViewControl : Control
{
private struct LineFragment
{
public string text;
public Color color;
public Color backColor;
public bool italic;
public bool bold;
public int indent;
public int wrapIndent;
}
 
private class ViewControlState
{
public bool deep;
public ArrayList lineBackColors;
public ArrayList lineFragments;
public ArrayList validMarkers;
public int curLine;
public int curCol;
public int selBeginLineOrig;
public int selBeginPosOrig;
public int selEndLineOrig;
public int selEndPosOrig;
}
 
private class TextMarker
{
public int beginLine;
public int beginPos;
public int endLine;
public int endPos;
 
public override string ToString()
{
return string.Format("{0},{1} - {2},{3}", beginLine, beginPos, endLine, endPos); // FIXME debug only
}
}
 
private static int NEW_LINE_LENGTH = 20;
 
private ArrayList lineBackColors = new ArrayList();
private ArrayList lineFragments = new ArrayList();
private int lastLineLen = 0;
private int linesCount = 0;
private int maxLineLength = 0;
 
private int curLine = 0;
private int curCol = 0;
private bool updateBlocked = false;
private int linesVisible = 0;
private int colsVisible = 0;
private float fontWidth = -1.0f;
private float fontHeight = -1.0f;
private bool wordWrap = false;
 
private ArrayList validMarkers = new ArrayList();
 
private ScrollBarsControl vScrollBar;
private ScrollBarsControl hScrollBar;
 
private StringFormat fontMeasureFormat = (StringFormat)System.Drawing.StringFormat.GenericTypographic.Clone();
 
private Font normal;
private Font italic;
private Font bold;
private Font boldIt;
 
private int selBeginLine = -1;
private int selBeginPos = -1;
private int selBeginLineOrig = -1;
private int selBeginPosOrig = -1;
private int selEndLine = -1;
private int selEndPos = -1;
private int selEndLineOrig = -1;
private int selEndPosOrig = -1;
 
public ViewControl()
{
this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.DoubleBuffer
| ControlStyles.ResizeRedraw, true);
 
fontMeasureFormat.LineAlignment = StringAlignment.Near;
fontMeasureFormat.FormatFlags = StringFormatFlags.MeasureTrailingSpaces | StringFormatFlags.FitBlackBox
| StringFormatFlags.NoWrap | StringFormatFlags.NoClip;
 
vScrollBar = new ScrollBarsControl(this, true);
vScrollBar.SmallChange = 1;
vScrollBar.Value = 0;
vScrollBar.Minimum = 0;
vScrollBar.Maximum = 0;
vScrollBar.Scroll += new ScrollEventHandler(this.vScrollBarScroll);
 
hScrollBar = new ScrollBarsControl(this, false);
hScrollBar.Scroll += new ScrollEventHandler(this.hScrollBarScroll);
 
RecalcParams();
}
 
const int WS_VSCROLL = 0x200000;
const int WS_HSCROLL = 0x100000;
const int WS_EX_CLIENTEDGE = 0x000200;
 
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
if(!base.DesignMode)
{
cp.Style = cp.Style | WS_HSCROLL | WS_VSCROLL;
cp.ExStyle = cp.ExStyle | WS_EX_CLIENTEDGE;
}
return cp;
}
}
 
public bool WordWrap
{
get { return wordWrap; }
set
{
wordWrap = value;
RecalcParams();
Invalidate();
// FIXME recalc number of lines for the vertical scroll bar
}
}
 
public void Clear()
{
lock(this)
{
lineBackColors = new ArrayList();
lineFragments = new ArrayList();
validMarkers = new ArrayList();
lastLineLen = 0;
linesCount = 0;
maxLineLength = 0;
curLine = 0;
curCol = 0;
selBeginLine = -1;
selBeginPos = -1;
selBeginLineOrig = -1;
selBeginPosOrig = -1;
selEndLine = -1;
selEndPos = -1;
selEndLineOrig = -1;
selEndPosOrig = -1;
 
if(!updateBlocked)
{
vScrollBar.Update(curLine, 0, linesCount, 1, 1);
UpdateHScrollBar();
Invalidate();
}
}
}
 
private void UpdateHScrollBar()
{
if(wordWrap)
hScrollBar.Update(0, 0, 0, 1, 1);
else
hScrollBar.Update(curCol, 0, maxLineLength + 2, 1, colsVisible);
}
 
// FIXME allow to save 'frozen' state
public object SaveState(bool deep)
{
if(deep) throw new NotImplementedException("Deep save is not yet implemented");
 
ViewControlState state = new ViewControlState();
 
lock(this)
{
state.deep = deep;
state.lineBackColors = lineBackColors;
state.lineFragments = lineFragments;
state.validMarkers = validMarkers;
state.curLine = curLine;
state.curCol = curCol;
state.selBeginLineOrig = selBeginLineOrig;
state.selBeginPosOrig = selBeginPosOrig;
state.selEndLineOrig = selEndLineOrig;
state.selEndPosOrig = selEndPosOrig;
}
 
return state;
}
 
public void RestoreState(object state, bool restorePosition)
{
if(!(state is ViewControlState))
throw new ArgumentException("Can restore only from object returned by SaveState()");
 
ViewControlState s = (ViewControlState)state;
 
if(s.deep) throw new NotImplementedException("Deep restore is not yet implemented");
 
if(lineBackColors.Count != lineFragments.Count) throw new ArgumentException("Wrong state");
 
lock(this)
{
lineBackColors = s.lineBackColors;
lineFragments = s.lineFragments;
linesCount = lineFragments.Count;
 
validMarkers = s.validMarkers;
 
lastLineLen = (linesCount > 0) ? ((LineFragment[])lineFragments[linesCount-1]).Length : 0;
 
maxLineLength = 0;
foreach(LineFragment[] line in lineFragments)
{
int lineLength = 0;
foreach(LineFragment frag in line)
{
lineLength += frag.indent + (frag.text == null ? 0 : frag.text.Length);
}
if(lineLength > maxLineLength) maxLineLength = lineLength;
}
 
if(restorePosition)
{
curLine = s.curLine;
curCol = s.curCol;
selBeginLineOrig = s.selBeginLineOrig;
selBeginPosOrig = s.selBeginPosOrig;
selEndLineOrig = s.selEndLineOrig;
selEndPosOrig = s.selEndPosOrig;
}
else
{
curLine = 0;
curCol = 0;
selBeginLineOrig = -1;
selBeginPosOrig = -1;
selEndLineOrig = -1;
selEndPosOrig = -1;
}
 
UpdateSelection();
updateBlocked = false;
 
vScrollBar.Update(curLine, 0, linesCount, 1, linesVisible);
UpdateHScrollBar();
Invalidate();
}
}
 
public object BeginMark()
{
TextMarker marker = new TextMarker();
 
lock(this)
{
marker.beginLine = linesCount-1;
marker.beginPos = lastLineLen;
 
validMarkers.Add(new WeakReference(marker));
}
 
return marker;
}
 
public void EndMark(object marker)
{
lock(this)
{
TextMarker m = GetTextMarker(marker);
 
m.endLine = linesCount-1;
m.endPos = lastLineLen;
}
}
 
public void BeginUpdate()
{
updateBlocked = true;
}
 
public void EndUpdate()
{
lock(this)
{
updateBlocked = false;
 
vScrollBar.Update(curLine, 0, linesCount, 1, linesVisible);
UpdateHScrollBar();
Invalidate();
}
}
 
public void AppendText(string text)
{
lock(this)
{
LineFragment[] line = null;
int lineLen = 0;
LineFragment fragment;
 
for(int i = linesCount - 1; i >= 0; i--)
{
line = (LineFragment[])lineFragments[i];
lineLen = (i == linesCount - 1) ? lastLineLen : line.Length;
if(lineLen > 0) break;
}
 
if(line == null || lineLen == 0)
{
fragment = new LineFragment();
fragment.color = this.ForeColor;
fragment.backColor = this.BackColor;
fragment.italic = false;
fragment.bold = false;
fragment.indent = 0;
fragment.wrapIndent = 0;
}
else
{
fragment = line[lineLen-1];
fragment.indent = 0;
}
 
fragment.text = text;
AppendText(fragment);
}
}
 
public void AppendText(string text, Color color, Color backColor, bool italic, bool bold, int indent, int wrapIndent)
{
LineFragment fragment;
 
fragment.text = text;
fragment.color = color;
fragment.backColor = backColor;
fragment.italic = italic;
fragment.bold = bold;
fragment.indent = indent;
fragment.wrapIndent = wrapIndent;
 
lock(this)
{
AppendText(fragment);
}
}
 
// always called in lock(this)
private void AppendText(LineFragment fragment)
{
LineFragment[] lastLine = (linesCount > 0) ? (LineFragment[])lineFragments[linesCount-1] : null;
 
if(lastLine == null)
{
lastLine = AddEmptyLine(Color.Transparent);
}
else if(lastLine.Length == lastLineLen)
{
LineFragment[] newLine = new LineFragment[lastLineLen * 2];
Array.Copy(lastLine, 0, newLine, 0, lastLineLen);
lastLine = newLine;
lineFragments[linesCount-1] = lastLine;
}
 
lastLine[lastLineLen++] = fragment;
 
int lineLength = 0;
for(int i = 0; i < lastLineLen; i++)
{
lineLength += lastLine[i].indent + (lastLine[i].text == null ? 0 : lastLine[i].text.Length);
}
if(lineLength > maxLineLength)
{
maxLineLength = lineLength;
if(!updateBlocked && !wordWrap) hScrollBar.Maximum = maxLineLength + 2;
}
 
if(!updateBlocked)
{
if(curLine + linesVisible + 1 >= lastLineLen) Invalidate();
}
}
 
public void AppendNewLine()
{
AppendNewLine(Color.Transparent);
}
 
public void AppendNewLine(Color backColor)
{
lock(this)
{
SaveLastLine();
AddEmptyLine(backColor);
}
}
 
private TextMarker GetTextMarker(object marker)
{
if(!(marker is TextMarker))
throw new ArgumentException("Can mark only with object returned by BeginMark()");
 
TextMarker m = (TextMarker)marker;
 
// check if marker is valid
bool isValid = false;
for(int i = validMarkers.Count-1; i >= 0; i--)
{
WeakReference refer = (WeakReference)validMarkers[i];
 
if(!refer.IsAlive || refer.Target == null)
{
validMarkers.RemoveAt(i);
}
else
{
if(refer.Target == m)
{
isValid = true;
}
}
}
if(!isValid) throw new ArgumentException("Invalid marker");
 
if(m.beginLine > linesCount)
throw new ArgumentException("Wrong marker");
 
LineFragment[] line = (linesCount > 0) ? (LineFragment[])lineFragments[m.beginLine] : null;
int lineLen = (linesCount == 0 || m.beginLine == linesCount-1) ? lastLineLen : line.Length;
 
if(m.beginPos > lineLen)
throw new ArgumentException("Wrong marker");
 
return m;
}
 
public void DeleteText(object marker)
{
lock(this)
{
TextMarker m = GetTextMarker(marker);
 
// delete the first line
int lineDeleted = DeleteLinePart(m.beginLine, m.beginPos, (m.beginLine == m.endLine) ? m.endPos : -1);
 
// delete the middle
for(int i = m.beginLine + 1; i < m.endLine; i++)
{
lineFragments.RemoveAt(m.beginLine - lineDeleted + 1);
lineBackColors.RemoveAt(m.beginLine - lineDeleted + 1);
linesCount--;
}
if(m.endLine > m.beginLine) lineDeleted += m.endLine - m.beginLine - 1;
 
// delete the last line
if(m.beginLine != m.endLine && m.endLine < linesCount && m.endPos > 0)
DeleteLinePart(m.endLine - lineDeleted, 0, m.endPos);
 
// update screen
if(!updateBlocked)
{
Invalidate();
}
 
int lastLine = m.endLine;
ShiftTextMarkers(m.beginLine, m.beginPos, -lineDeleted, 0);
ShiftTextMarkers(lastLine, m.endPos, 0, -(m.endPos - (m.beginLine == lastLine ? m.beginPos : 0)));
}
}
 
private int DeleteLinePart(int lineNum, int begin, int end)
{
int deleted = 0;
LineFragment[] line = (LineFragment[])lineFragments[lineNum];
int lineLen = (lineNum == linesCount - 1) ? lastLineLen : line.Length;
 
if(end == 0) return 0;
 
if(end < 0) end = lineLen;
 
if(begin == 0 && end == lineLen)
{
// delete whole line
deleted = 1;
lineFragments.RemoveAt(lineNum);
lineBackColors.RemoveAt(lineNum);
linesCount--;
}
else
{
// delete part of line
LineFragment[] newLine = new LineFragment[(lineNum == linesCount - 1) ? line.Length : lineLen - end + begin];
if(begin > 0) Array.Copy(line, 0, newLine, 0, begin);
if(end < lineLen) Array.Copy(line, end, newLine, begin, lineLen - end);
if(lineNum == linesCount - 1) lastLineLen = lineLen - end + begin;
lineFragments[lineNum] = newLine;
}
 
return deleted;
}
 
public void ChangeText(object marker, string text, Color color, Color backColor,
bool italic, bool bold, int indent, int wrapIndent)
{
lock(this)
{
TextMarker m = GetTextMarker(marker);
 
// FIXME implement for multiple lines and fragments
 
LineFragment[] line = (LineFragment[])lineFragments[m.beginLine];
LineFragment fragment = line[m.beginPos];
 
if(fragment.text == text && fragment.color == color && fragment.backColor == backColor
&& fragment.italic == italic && fragment.bold == bold && fragment.indent == indent
&& fragment.wrapIndent == wrapIndent)
{
return; // the fragment is not changed
}
 
fragment.text = text;
fragment.color = color;
fragment.backColor = backColor;
fragment.italic = italic;
fragment.bold = bold;
fragment.indent = indent;
fragment.wrapIndent = wrapIndent;
 
line[m.beginPos] = fragment;
 
if(!updateBlocked)
{
if(m.beginLine >= curLine && m.beginLine <= curLine + linesVisible + 1) Invalidate();
}
}
}
 
// side effect - the given marker is moved to position after the inserted text
public void InsertText(object marker, string text, Color color, Color backColor,
bool italic, bool bold, int indent, int wrapIndent)
{
lock(this)
{
TextMarker m = GetTextMarker(marker);
 
LineFragment[] line = (linesCount > m.endLine) ? (LineFragment[])lineFragments[m.endLine] : null;
LineFragment fragment = new LineFragment();
 
fragment.text = text;
fragment.color = color;
fragment.backColor = backColor;
fragment.italic = italic;
fragment.bold = bold;
fragment.indent = indent;
fragment.wrapIndent = wrapIndent;
 
if(line == null)
{
line = AddEmptyLine(Color.Transparent);
lastLineLen++;
}
else if(m.endLine == linesCount - 1)
{
if(line.Length <= lastLineLen || m.endPos < lastLineLen)
{
LineFragment[] newLine = new LineFragment[lastLineLen * (line.Length + 1 >= lastLineLen ? 2 : 1)];
 
if(m.endPos > 0) Array.Copy(line, 0, newLine, 0, m.endPos);
if(lastLineLen > m.endPos) Array.Copy(line, m.endPos, newLine, m.endPos+1, lastLineLen - m.endPos);
 
line = newLine;
lineFragments[m.endLine] = line;
}
lastLineLen++;
}
else
{
LineFragment[] newLine = new LineFragment[line.Length + 1];
if(m.endPos > 0) Array.Copy(line, 0, newLine, 0, m.endPos);
if(line.Length > m.endPos) Array.Copy(line, m.endPos, newLine, m.endPos+1, line.Length - m.endPos);
line = newLine;
lineFragments[m.endLine] = line;
}
 
line[m.endPos] = fragment;
 
// recalc text width
int lineLength = 0;
int newLineLen = (m.endLine == linesCount - 1) ? lastLineLen : line.Length;
for(int i = 0; i < newLineLen; i++)
{
lineLength += line[i].indent + (line[i].text == null ? 0 : line[i].text.Length);
}
if(lineLength > maxLineLength)
{
maxLineLength = lineLength;
if(!updateBlocked && !wordWrap) hScrollBar.Maximum = maxLineLength + 2;
}
 
// update screen
if(!updateBlocked)
{
if(m.endLine >= curLine && m.endLine <= curLine + linesVisible + 1) Invalidate();
}
 
ShiftTextMarkers(m.endLine, m.endPos, 0, 1);
if(m.beginLine == m.endLine && m.beginPos == m.endPos) m.beginPos--; // return the begin back if empty marker
}
}
 
public void InsertNewLine(object marker)
{
InsertNewLine(marker, Color.Transparent);
}
 
public void InsertNewLine(object marker, Color backColor)
{
lock(this)
{
TextMarker m = GetTextMarker(marker);
 
if(m.endLine == linesCount || (m.endLine == linesCount - 1 && m.endPos == lastLineLen))
{
SaveLastLine();
AddEmptyLine(backColor);
}
else
{
LineFragment[] oldLine = (LineFragment[])lineFragments[m.endLine];
int oldLineLen = (m.endLine == linesCount - 1) ? lastLineLen : oldLine.Length;
LineFragment[] line1 = new LineFragment[m.endPos];
LineFragment[] line2 = new LineFragment[(m.endLine == linesCount - 1)
? (int)Math.Ceiling((double)(oldLineLen - m.endPos) / NEW_LINE_LENGTH) * NEW_LINE_LENGTH
: oldLineLen - m.endPos];
 
if(m.endPos > 0) Array.Copy(oldLine, 0, line1, 0, m.endPos);
if(oldLineLen - m.endPos > 0) Array.Copy(oldLine, m.endPos, line2, 0, oldLineLen - m.endPos);
 
lineFragments[m.endLine] = line1;
lineFragments.Insert(m.endLine + 1, line2);
lineBackColors.Insert(m.endLine + 1, backColor);
 
if(m.endLine == linesCount - 1) lastLineLen = oldLineLen - m.endPos;
linesCount++;
}
 
// update screen
if(!updateBlocked)
{
if(m.endLine >= curLine && m.endLine <= curLine + linesVisible + 1) Invalidate();
vScrollBar.Maximum = linesCount;
}
 
ShiftTextMarkers(m.endLine, m.endPos, 1, 0);
if(m.beginLine == m.endLine && m.beginLine == m.endLine) m.beginLine--; // return the begin back if empty marker
}
}
 
private void ShiftTextMarkers(int line, int pos, int lineDelta, int posDelta)
{
for(int i = validMarkers.Count-1; i >= 0; i--)
{
WeakReference refer = (WeakReference)validMarkers[i];
 
if(!refer.IsAlive || refer.Target == null)
{
validMarkers.RemoveAt(i);
}
else
{
TextMarker m = (TextMarker)refer.Target;
 
ShiftTextMarkerBorder(ref m.beginLine, ref m.beginPos, line, pos, lineDelta, posDelta);
ShiftTextMarkerBorder(ref m.endLine, ref m.endPos, line, pos, lineDelta, posDelta);
}
}
}
 
private void ShiftTextMarkerBorder(ref int borderLine, ref int borderPos,
int line, int pos, int lineDelta, int posDelta)
{
if(borderLine > line)
{
borderLine += lineDelta;
if(borderLine < line) borderLine = line;
}
else if(borderLine == line)
{
if(lineDelta != 0)
{
if(borderPos >= pos)
{
borderLine += lineDelta;
if(borderLine < line) borderLine = line;
 
borderPos -= pos - posDelta;
}
}
else
{
if(borderPos >= pos) borderPos += posDelta;
}
}
}
 
private void SaveLastLine()
{
if(linesCount > 0)
{
LineFragment[] lastLine = (LineFragment[])lineFragments[linesCount-1];
LineFragment[] newLine = new LineFragment[lastLineLen];
Array.Copy(lastLine, 0, newLine, 0, lastLineLen);
lineFragments[linesCount-1] = newLine;
}
}
 
private LineFragment[] AddEmptyLine(Color backColor)
{
LineFragment[] lastLine = new LineFragment[NEW_LINE_LENGTH];
lastLineLen = 0;
lineBackColors.Add(backColor);
lineFragments.Add(lastLine);
 
linesCount++;
 
if(!updateBlocked) vScrollBar.Maximum = linesCount;
 
return lastLine;
}
 
public override Font Font
{
get
{
return base.Font;
}
 
set
{
lock(this)
{
base.Font = value;
 
normal = new Font(this.Font.FontFamily, this.Font.Size);
italic = new Font(this.Font.FontFamily, this.Font.Size, FontStyle.Italic);
bold = new Font(this.Font.FontFamily, this.Font.Size, FontStyle.Bold);
boldIt = new Font(this.Font.FontFamily, this.Font.Size, FontStyle.Bold | FontStyle.Italic);
 
fontWidth = -1.0f;
RecalcParams();
Invalidate();
}
}
}
 
private void RecalcParams()
{
fontHeight = this.Font.GetHeight();
linesVisible = (fontHeight > 0) ? (int)Math.Ceiling(this.ClientSize.Height / fontHeight) : 0;
vScrollBar.LargeChange = linesVisible;
 
RecalcColsVisible();
}
 
private void UpdateFontWidth(Graphics g)
{
fontWidth = g.MeasureString("x", this.Font, int.MaxValue, fontMeasureFormat).Width;
RecalcColsVisible();
}
 
private void RecalcColsVisible()
{
colsVisible = (fontWidth > 0) ? (int)Math.Ceiling(this.ClientSize.Width / fontWidth) : 0;
UpdateHScrollBar();
}
 
protected override void OnSystemColorsChanged(EventArgs e)
{
Invalidate();
base.OnSystemColorsChanged(e);
}
 
protected override void OnResize(EventArgs e)
{
lock(this)
{
RecalcParams();
}
base.OnResize(e);
}
 
protected override void OnPaintBackground(PaintEventArgs pevent)
{
}
 
protected override void OnPaint(PaintEventArgs e)
{
lock(this)
{
try
{
Graphics g = e.Graphics;
SolidBrush brush = new SolidBrush(Color.Black);
 
g.FillRectangle(SystemBrushes.Window, g.Clip.GetBounds(g));
 
if(fontWidth <= 0) UpdateFontWidth(g);
 
int drawLine = 0;
for(int lineNumber = curLine; drawLine < linesVisible && lineNumber < linesCount; lineNumber++)
{
Color backColor = (Color)lineBackColors[lineNumber];
LineFragment[] line = (LineFragment[])lineFragments[lineNumber];
int lineLen = (lineNumber == linesCount-1) ? lastLineLen : line.Length;
 
// line background
if(lineLen == 0 && selBeginLine <= lineNumber && lineNumber <= selEndLine)
{
brush.Color = SystemColors.Highlight;
g.FillRectangle(brush, 0, drawLine * fontHeight, this.ClientSize.Width, fontHeight);
}
else if(backColor != Color.Transparent)
{
brush.Color = backColor;
g.FillRectangle(brush, 0, drawLine * fontHeight, this.ClientSize.Width, fontHeight);
}
 
int indent = 0;
int shift = curCol;
for(int j = 0; j < lineLen; j++)
{
LineFragment fragment = line[j];
int curIndent = fragment.indent;
string text = fragment.text;
int textLen = (text == null) ? 0 : text.Length;
 
// calc shift using curCol
if(shift > 0)
{
if(shift < fragment.indent)
{
curIndent -= shift;
shift = 0;
}
else if(shift < textLen + fragment.indent)
{
if(text != null)
{
text = text.Substring(shift - fragment.indent);
textLen = text.Length;
}
shift = 0;
curIndent = 0;
}
else
{
curIndent = 0;
shift -= textLen + fragment.indent;
text = null;
textLen = 0;
}
}
 
// draw the line, m.b. split it to several display lines
while(true)
{
string drawText = text;
int drawLen = textLen;
bool lineWraped = false;
 
// wrap line
// FIXME try to wrap whole words only
// FIXME test if wrap is in fragment indent
if(wordWrap)
{
int visibleLen = colsVisible - 2;
int availableLen = visibleLen - indent - curIndent;
if(textLen > availableLen)
{
if(textLen == 0 || availableLen < 0)
{
drawText = null;
drawLen = 0;
text = null;
textLen = 0;
}
else if(availableLen == 0)
{
drawText = null; // only wrap line, draw nothing on current one
drawLen = 0;
}
else
{
drawText = text.Substring(0, availableLen);
drawLen = drawText.Length;
text = text.Substring(availableLen);
textLen = text.Length;
}
lineWraped = true;
}
}
 
// draw background
if(fragment.backColor != Color.Transparent && (indent >= 0) && (drawLen + curIndent > 0))
{
brush.Color = fragment.backColor;
g.FillRectangle(brush, indent * fontWidth, drawLine * fontHeight,
(float)Math.Ceiling((drawLen + curIndent) * fontWidth), fontHeight);
}
 
// draw selection background
int selBegin = -1;
int selEnd = -1;
if(selBeginLine <= lineNumber && lineNumber <= selEndLine)
{
selBegin = (lineNumber == selBeginLine) ? Math.Max(selBeginPos - curCol, 0) : 0;
selEnd = (lineNumber == selEndLine) ? selEndPos - curCol : colsVisible;
 
int selBeginBack = Math.Max(indent, selBegin);
int selLen = (j == lineLen-1) ? (selEnd - selBeginBack)
: Math.Min(drawLen + curIndent, selEnd - selBeginBack);
 
if(selLen > 0)
{
brush.Color = SystemColors.Highlight;
g.FillRectangle(brush, selBeginBack * fontWidth, drawLine * fontHeight,
(float)Math.Ceiling(selLen* fontWidth), fontHeight);
}
}
 
// draw the text
indent += curIndent;
if(drawText != null)
{
Font font = (fragment.bold) ? (fragment.italic ? boldIt : bold) : (fragment.italic ? italic : normal);
 
// split the fragment text into 3 pieces: before selection, the selection, after selection
// (some may be empty)
string s1 = drawText, s2 = null, s3 = null;
int p2 = 0, p3 = 0;
 
if(selBegin >= 0)
{
int begin = Math.Max(0, selBegin - indent);
if(begin < drawLen)
{
int len = Math.Min(selEnd - indent - begin, drawLen - begin);
if(len > 0)
{
s1 = (begin == 0) ? null : drawText.Substring(0, begin);
s2 = (begin == 0 && len == drawLen) ? drawText : drawText.Substring(begin, len);
s3 = (begin + len >= drawLen) ? null : drawText.Substring(begin + len);
}
}
}
 
if(s1 != null)
{
brush.Color = fragment.color;
g.DrawString(s1, font, brush, indent * fontWidth, drawLine * fontHeight, fontMeasureFormat);
p2 = s1.Length;
}
 
if(s2 != null)
{
brush.Color = SystemColors.HighlightText;
g.DrawString(s2, font, brush, (indent + p2) * fontWidth, drawLine * fontHeight, fontMeasureFormat);
p3 = p2 + s2.Length;
}
 
if(s3 != null)
{
brush.Color = fragment.color;
g.DrawString(s3, font, brush, (indent + p3) * fontWidth, drawLine * fontHeight, fontMeasureFormat);
}
 
indent += drawLen;
}
 
if(lineWraped)
{
indent = fragment.wrapIndent;
curIndent = 0;
drawLine++;
 
if(drawLine >= linesVisible) break;
 
if(backColor != Color.Transparent)
{
brush.Color = backColor;
g.FillRectangle(brush, 0, drawLine * fontHeight, this.ClientSize.Width, fontHeight);
}
}
else
{
break;
}
}
}
 
drawLine++;
}
}
catch(Exception ex)
{
Console.WriteLine(ex.Message + "\n" + ex.StackTrace);
}
}
}
 
public void SelectAll()
{
lock(this)
{
selBeginLineOrig = 0;
selBeginPosOrig = 0;
selEndLineOrig = linesCount-1;
selEndPosOrig = 0;
 
if(linesCount > 0)
{
LineFragment[] line = (LineFragment[])lineFragments[linesCount - 1];
 
for(int i = 0; i < lastLineLen; i++)
{
selEndPosOrig += line[i].indent + ((line[i].text != null) ? line[i].text.Length : 0);
}
}
 
UpdateSelection();
Invalidate();
}
}
 
public string SelectedText
{
get
{
lock(this)
{
if(selBeginLine < 0) return null;
 
StringBuilder b = new StringBuilder((selEndLine - selBeginLine + 1) * maxLineLength);
 
for(int lineNumber = selBeginLine; lineNumber <= selEndLine && lineNumber < linesCount; lineNumber++)
{
int cur = 0;
int begin = (lineNumber == selBeginLine) ? Math.Max(selBeginPos, 0) : 0;
int end = (lineNumber == selEndLine) ? Math.Max(selEndPos, 0) : int.MaxValue;
 
LineFragment[] line = (LineFragment[])lineFragments[lineNumber];
int lineLen = (lineNumber == linesCount-1) ? lastLineLen : line.Length;
for(int i = 0; i < lineLen; i++)
{
LineFragment fragment = line[i];
 
if(cur + fragment.indent <= begin || cur >= end)
{
cur += fragment.indent;
}
else
{
for(int j = fragment.indent - 1; j >= 0; j--)
{
if(cur >= begin && cur < end) b.Append(' ');
cur++;
}
}
 
if(fragment.text != null)
{
int curEnd = cur + fragment.text.Length;
if(begin <= cur && curEnd <= end)
b.Append(fragment.text);
else if(begin <= cur && cur < end || begin < curEnd && curEnd <= end || cur <= begin && end <= curEnd)
{
int copyBegin = Math.Max(begin - cur, 0);
b.Append(fragment.text.Substring(copyBegin, Math.Min(end, curEnd) - cur - copyBegin));
}
 
cur += fragment.text.Length;
}
 
if(cur >= end) break;
if(i == lineLen - 1) b.Append("\r\n");
}
}
 
return b.ToString();
}
}
}
 
private void vScrollBarScroll(Object sender, ScrollEventArgs e)
{
MoveToVertical(e.NewValue, true);
}
 
private void hScrollBarScroll(Object sender, ScrollEventArgs e)
{
MoveToHorizontal(e.NewValue, true);
}
 
private void MoveToVertical(int pos, bool invalidate)
{
curLine = pos;
if(curLine + linesVisible > linesCount) curLine = linesCount - linesVisible + 1;
if(curLine < 0) curLine = 0;
vScrollBar.Value = curLine;
if(invalidate) this.Invalidate();
}
 
private void MoveByVertical(int delta, bool invalidate)
{
MoveToVertical(curLine + delta, invalidate);
}
 
private void MoveToHorizontal(int pos, bool invalidate)
{
if(wordWrap)
{
curCol = 0;
}
else
{
curCol = pos;
if(curCol < 0) curCol = 0;
hScrollBar.Value = curCol;
}
if(invalidate) this.Invalidate();
}
 
private void MoveByHorizontal(int delta, bool invalidate)
{
MoveToHorizontal(curCol + delta, invalidate);
}
 
protected override bool IsInputKey(Keys keyData)
{
switch(keyData)
{
case Keys.PageDown:
case Keys.PageUp:
case Keys.Down:
case Keys.Up:
case Keys.Left:
case Keys.Right:
case Keys.Home:
case Keys.End:
return true;
 
default:
return base.IsInputKey(keyData);
}
}
 
protected override void OnKeyDown(KeyEventArgs e)
{
switch(e.KeyCode)
{
case Keys.PageDown:
lock(this)
{
MoveByVertical(linesVisible, true);
}
break;
 
case Keys.PageUp:
lock(this)
{
MoveByVertical(-linesVisible, true);
}
break;
 
case Keys.Down:
lock(this)
{
MoveByVertical(1, true);
}
break;
 
case Keys.Up:
lock(this)
{
MoveByVertical(-1, true);
}
break;
 
case Keys.Right:
lock(this)
{
MoveByHorizontal(1, true);
}
break;
 
case Keys.Left:
lock(this)
{
MoveByHorizontal(-1, true);
}
break;
 
case Keys.Home:
lock(this)
{
MoveToHorizontal(0, false);
MoveToVertical(0, true);
}
break;
 
case Keys.End:
lock(this)
{
MoveToHorizontal(0, false);
MoveToVertical(linesCount - linesVisible + 1, true);
}
break;
 
default:
base.OnKeyDown(e);
break;
}
}
 
private void UpdateSelection()
{
if(selBeginLineOrig < 0)
{
selBeginLine = -1;
selBeginPos = -1;
selEndLine = -1;
selEndPos = -1;
}
else if(selEndLineOrig < selBeginLineOrig || selEndLineOrig == selBeginLineOrig && selEndPosOrig < selBeginPosOrig)
{ // swap if end before begin
selBeginLine = selEndLineOrig;
selBeginPos = selEndPosOrig;
selEndLine = selBeginLineOrig;
selEndPos = selBeginPosOrig;
}
else
{
selBeginLine = selBeginLineOrig;
selBeginPos = selBeginPosOrig;
selEndLine = selEndLineOrig;
selEndPos = selEndPosOrig;
}
}
 
private void UpdateSelectionEnd(int x, int y)
{
if(fontWidth > 0 && fontHeight > 0 && selBeginLineOrig >= 0)
{
selEndLineOrig = (int)(y / fontHeight) + curLine;
selEndPosOrig = (int)Math.Round(x / fontWidth) + curCol;
 
UpdateSelection();
Invalidate();
}
}
 
protected override void OnMouseMove(MouseEventArgs e)
{
if(e.Button == MouseButtons.Left)
{
lock(this)
{
UpdateSelectionEnd(e.X, e.Y);
// FIXME scroll if selection is dragged outside of the control, use timer to scroll
if(e.Y > this.ClientSize.Height && fontWidth > 0)
{
MoveToVertical(selEndLine - linesVisible + 1, true);
}
}
}
 
base.OnMouseMove(e);
}
 
protected override void OnMouseDown(MouseEventArgs e)
{
this.Focus();
 
if(e.Button == MouseButtons.Left)
{
lock(this)
{
if(fontWidth > 0 && fontHeight > 0)
{
selBeginLineOrig = (int)(e.Y / fontHeight) + curLine;
selBeginPosOrig = (int)Math.Round(e.X / fontWidth) + curCol;
selEndLineOrig = selBeginLineOrig;
selEndPosOrig = selBeginPosOrig;
 
UpdateSelection();
Invalidate();
}
}
}
 
base.OnMouseDown(e);
}
 
private const int WHEEL_DELTA = 120;
 
protected override void OnMouseWheel(MouseEventArgs e)
{
lock(this)
{
MoveByVertical(e.Delta / WHEEL_DELTA * SystemInformation.MouseWheelScrollLines * (-1), true);
}
 
base.OnMouseWheel(e);
}
 
private const int WM_ACTIVATE = 0x0006;
private const int WM_ACTIVATEAPP = 0x001C;
private const int WM_HSCROLL = 0x0114;
private const int WM_VSCROLL = 0x0115;
 
protected override void WndProc(ref System.Windows.Forms.Message m)
{
switch(m.Msg)
{
case WM_VSCROLL:
vScrollBar.HandleWindowMessage(ref m);
break;
 
case WM_HSCROLL:
hScrollBar.HandleWindowMessage(ref m);
break;
 
default:
base.WndProc(ref m);
break;
}
}
 
private class ScrollBarsControl
{
private Control owner;
private bool vertical;
private int smallChange;
private int largeChange;
private int minimum;
private int maximum;
private bool showAlways = true;
private int currentValue = 0;
private SCROLLINFO scrollInfo;
 
public event ScrollEventHandler Scroll;
 
public ScrollBarsControl(Control owner, bool vertical)
{
this.owner = owner;
this.vertical = vertical;
 
scrollInfo = new SCROLLINFO();
scrollInfo.cbSize = (uint)Marshal.SizeOf(typeof(SCROLLINFO));
 
// get initial state
scrollInfo.fMask = SIF_RANGE | SIF_PAGE;
GetScrollInfo(owner.Handle, vertical ? SB_VERT : SB_HORZ, ref scrollInfo);
minimum = scrollInfo.nMin;
maximum = scrollInfo.nMax;
smallChange = 1;
largeChange = (int)scrollInfo.nPage;
}
 
public void HandleWindowMessage(ref System.Windows.Forms.Message m)
{
switch(m.Msg)
{
case WM_VSCROLL:
if(!vertical)
throw new ArgumentException("I'm horizontal scroll bar, can not handle vertical scroll");
break;
 
case WM_HSCROLL:
if(vertical)
throw new ArgumentException("I'm vertical scroll bar, can not handle horizontal scroll");
break;
 
default:
throw new ArgumentException("Unknown message");
}
 
short cmd = (short)((uint)m.WParam & 0x0000FFFF);
ScrollEventType type;
bool update = false;
 
switch(cmd)
{
case SB_LINEDOWN:
type = ScrollEventType.SmallIncrement;
currentValue += smallChange;
update = true;
break;
 
case SB_LINEUP:
type = ScrollEventType.SmallDecrement;
currentValue -= smallChange;
update = true;
break;
 
case SB_PAGEDOWN:
type = ScrollEventType.LargeIncrement;
currentValue += largeChange;
update = true;
break;
 
case SB_PAGEUP:
type = ScrollEventType.LargeDecrement;
currentValue -= largeChange;
update = true;
break;
 
case SB_TOP:
type = ScrollEventType.First;
currentValue = minimum;
update = true;
break;
 
case SB_BOTTOM:
type = ScrollEventType.Last;
currentValue = maximum;
update = true;
break;
 
case SB_ENDSCROLL:
type = ScrollEventType.EndScroll;
break;
 
case SB_THUMBTRACK:
type = ScrollEventType.ThumbTrack;
currentValue = GetTrackPos();
break;
 
case SB_THUMBPOSITION:
type = ScrollEventType.ThumbPosition;
currentValue = GetTrackPos();
update = true;
break;
 
default:
throw new ArgumentException("Unknown command " + cmd);
}
 
UpdateCurrentValue(update);
ScrollEventArgs e = new ScrollEventArgs(type, currentValue);
OnScroll(e);
}
 
private int GetTrackPos()
{
lock(this)
{
scrollInfo.fMask = SIF_TRACKPOS;
GetScrollInfo(owner.Handle, vertical ? SB_VERT : SB_HORZ, ref scrollInfo);
return scrollInfo.nTrackPos;
}
}
 
public int Value
{
get
{
return currentValue;
}
 
set
{
currentValue = value;
UpdateCurrentValue(true);
}
}
 
public bool ShowAlways
{
get { return showAlways; }
set
{
showAlways = value;
SetRange(false);
}
}
 
public int Minimum
{
get
{
return minimum;
}
 
set
{
minimum = value;
SetRange(false);
}
}
 
public int Maximum
{
get
{
return maximum;
}
 
set
{
maximum = value;
SetRange(false);
}
}
 
public void Update(int value, int minimum, int maximum, int smallChange, int largeChange)
{
this.currentValue = value;
this.minimum = minimum;
this.maximum = maximum;
this.smallChange = smallChange;
this.largeChange = largeChange;
 
SetRange(true);
}
 
private void SetRange(bool setPos)
{
lock(this)
{
scrollInfo.fMask = SIF_RANGE | SIF_PAGE;
if(showAlways) scrollInfo.fMask |= SIF_DISABLENOSCROLL;
scrollInfo.nMin = minimum;
scrollInfo.nMax = maximum;
scrollInfo.nPage = (uint)largeChange;
 
if(setPos || currentValue < minimum || currentValue > maximum)
{
if(currentValue < minimum) currentValue = minimum;
if(currentValue > maximum) currentValue = maximum;
scrollInfo.fMask |= SIF_POS;
scrollInfo.nPos = currentValue;
}
 
if(setPos)
{
scrollInfo.fMask |= SIF_PAGE;
scrollInfo.nPage = (uint)largeChange;
}
 
SetScrollInfo(owner.Handle, vertical ? SB_VERT : SB_HORZ, ref scrollInfo, true);
}
}
 
public int SmallChange
{
get
{
return smallChange;
}
 
set
{
if(value < 0) throw new ArgumentException("SmallChange must be non-negative");
smallChange = value;
}
}
 
public int LargeChange
{
get
{
return largeChange;
}
 
set
{
if(value < 0) throw new ArgumentException("LargeChange must be non-negative");
largeChange = value;
 
lock(this)
{
scrollInfo.fMask = SIF_PAGE;
if(showAlways) scrollInfo.fMask |= SIF_DISABLENOSCROLL;
scrollInfo.nPage = (uint)largeChange;
SetScrollInfo(owner.Handle, vertical ? SB_VERT : SB_HORZ, ref scrollInfo, true);
}
}
}
 
private void UpdateCurrentValue(bool force)
{
if(!force && currentValue >= minimum && currentValue <= maximum) return;
 
if(currentValue < minimum) currentValue = minimum;
if(currentValue > maximum) currentValue = maximum;
 
lock(this)
{
scrollInfo.fMask = SIF_POS;
if(showAlways) scrollInfo.fMask |= SIF_DISABLENOSCROLL;
scrollInfo.nPos = currentValue;
SetScrollInfo(owner.Handle, vertical ? SB_VERT : SB_HORZ, ref scrollInfo, true);
}
}
 
protected virtual void OnScroll(ScrollEventArgs e)
{
if(Scroll != null)
{
Scroll(this, e);
}
}
 
private const short SB_HORZ = 0;
private const short SB_VERT = 1;
private const short SB_CTL = 2;
private const short SB_BOTH = 3;
 
private const short SB_LINEUP = 0;
private const short SB_LINELEFT = 0;
private const short SB_LINEDOWN = 1;
private const short SB_LINERIGHT = 1;
private const short SB_PAGEUP = 2;
private const short SB_PAGELEFT = 2;
private const short SB_PAGEDOWN = 3;
private const short SB_PAGERIGHT = 3;
private const short SB_THUMBPOSITION = 4;
private const short SB_THUMBTRACK = 5;
private const short SB_TOP = 6;
private const short SB_LEFT = 6;
private const short SB_BOTTOM = 7;
private const short SB_RIGHT = 7;
private const short SB_ENDSCROLL = 8;
 
private const uint SIF_RANGE = 0x0001;
private const uint SIF_PAGE = 0x0002;
private const uint SIF_POS = 0x0004;
private const uint SIF_DISABLENOSCROLL = 0x0008;
private const uint SIF_TRACKPOS = 0x0010;
private const uint SIF_ALL = (SIF_RANGE | SIF_PAGE | SIF_POS | SIF_TRACKPOS);
 
/*
typedef struct tagSCROLLINFO {
UINT cbSize;
UINT fMask;
int nMin;
int nMax;
UINT nPage;
int nPos;
int nTrackPos;
} SCROLLINFO, *LPSCROLLINFO;
typedef SCROLLINFO CONST *LPCSCROLLINFO;
*/
 
[StructLayout(LayoutKind.Sequential, Pack=4, CharSet=CharSet.Auto)]
private struct SCROLLINFO
{
public uint cbSize;
public uint fMask;
public int nMin;
public int nMax;
public uint nPage;
public int nPos;
public int nTrackPos;
}
 
/*
BOOL GetScrollInfo(
HWND hwnd,
int fnBar,
LPSCROLLINFO lpsi
);
*/
 
[DllImport("User32", CharSet=CharSet.Auto)]
private static extern bool GetScrollInfo(IntPtr hWnd, int fnBar, ref SCROLLINFO lpsi);
 
/*
int SetScrollInfo(
HWND hwnd,
int fnBar,
LPCSCROLLINFO lpsi,
BOOL fRedraw
);
*/
 
[DllImport("User32", CharSet=CharSet.Auto)]
private static extern int SetScrollInfo(IntPtr hWnd, int fnBar, ref SCROLLINFO lpsi, bool fRedraw);
}
}
}
/TCPproxy/tags/0002-second_public/resources/images/arrow.bmp
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Property changes:
Added: svn:mime-type
+application/octet-stream
\ No newline at end of property
/TCPproxy/tags/0002-second_public/resources/images/connected.bmp
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Property changes:
Added: svn:mime-type
+application/octet-stream
\ No newline at end of property
/TCPproxy/tags/0002-second_public/resources/images/disconnected.bmp
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Property changes:
Added: svn:mime-type
+application/octet-stream
\ No newline at end of property
/TCPproxy/tags/0002-second_public/resources/images/clear.bmp
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Property changes:
Added: svn:mime-type
+application/octet-stream
\ No newline at end of property
/TCPproxy/tags/0002-second_public
Property changes:
Added: svn:ignore
+bin
+obj
+TCPproxy.suo
+combine