/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("&"); |
break; |
case '<': |
b.Append("<"); |
break; |
case '>': |
b.Append(">"); |
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 |