Subversion Repositories general

Rev

Rev 1200 | Rev 1232 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

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 (?)
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 );
                }

                [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);
                        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.RequestXml != null)
                        {
                                httpData.RequestXmlShown = true;
                                AddXmlNode(httpData.Node, "Request XML", http.RequestXml);
                                if(autoExpand) httpData.Node.Expand();
                        }

                        if(!httpData.ResponseXmlShown && http.ResponseXml != null)
                        {
                                httpData.ResponseXmlShown = true;
                                AddXmlNode(httpData.Node, "Response XML", http.ResponseXml);
                                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 HttpMessage http;
                        private bool        requestXmlShown  = false;
                        private bool        responseXmlShown = false;

                        private object      stateMarker               = null;
                        private object      requestStartMarker        = null;
                        private object      requestMethodMarker       = null;
                        private object      requestUriMarker          = null;
                        private object      requestVersionMarker      = null;
                        private object      requestLengthMarker       = null;
                        private object      requestEncodingMarker     = null;
                        private object      requestContentTypeMarker  = null;
                        private object      requestCharsetMarker      = null;
                        private object      requestHeadersMarker      = null;
                        private object      responseStartMarker       = null;
                        private object      responseVersionMarker     = null;
                        private object      responseStatusMarker      = null;
                        private object      responseLengthMarker      = null;
                        private object      responseEncodingMarker    = null;
                        private object      responseContentTypeMarker = null;
                        private object      responseCharsetMarker     = null;
                        private object      responseHeadersMarker     = null;
                        private object      requestBodyMarker         = null;
                        private object      responseBodyMarker        = null;
                        private IEnumerator requestHeadersEnum        = null;
                        private IEnumerator responseHeadersEnum       = null;

                        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(HttpEncoding encoding)
                        {
                                switch(encoding)
                                {
                                        case HttpEncoding.Identify: return "identify";
                                        case HttpEncoding.Gzip:     return "gzip";
                                        case HttpEncoding.Compress: return "compress";
                                        case HttpEncoding.Deflate:  return "deflate";
                                        default:                    return "<unknown>";
                                }
                        }

                        protected override void InitView()
                        {
                                try
                                {
                                        lock(http)
                                        {
                                                owner.messagesBox.Clear();

                                                owner.messagesBox.AppendNewLine();
                                                owner.messagesBox.AppendText("Complete:                  ",
                                                        Color.DarkRed, Color.Transparent, false, false, 0, 0);
                                                stateMarker = owner.messagesBox.BeginMark();
                                                owner.messagesBox.AppendText(http.RequestComplete && http.ResponseComplete ? "YES" : "NO",
                                                        Color.DarkRed, Color.LightGray, false, false, 0, 27);
                                                owner.messagesBox.EndMark(stateMarker);
                                                owner.messagesBox.AppendNewLine();
                                                owner.messagesBox.AppendNewLine();

                                                // request info
                                                owner.messagesBox.AppendText("Request Start:             ",
                                                        Color.DarkRed, Color.Transparent, false, false, 0, 0);
                                                requestStartMarker = owner.messagesBox.BeginMark();
                                                owner.messagesBox.AppendText(http.RequestStartTimestamp == DateTime.MinValue
                                                        ? "<unknown>" : http.RequestStartTimestamp.ToString("HH:mm:ss.ffff"),
                                                        Color.DarkRed, Color.LightGray, false, false, 0, 27);
                                                owner.messagesBox.EndMark(requestStartMarker);
                                                owner.messagesBox.AppendNewLine();

                                                owner.messagesBox.AppendText("Request Method:            ",
                                                        Color.DarkRed, Color.Transparent, false, false, 0, 0);
                                                requestMethodMarker = owner.messagesBox.BeginMark();
                                                owner.messagesBox.AppendText(http.RequestMethod,
                                                        Color.DarkRed, Color.LightGray, false, false, 0, 27);
                                                owner.messagesBox.EndMark(requestMethodMarker);
                                                owner.messagesBox.AppendNewLine();

                                                owner.messagesBox.AppendText("Request URI:               ",
                                                        Color.DarkRed, Color.Transparent, false, false, 0, 0);
                                                requestUriMarker = owner.messagesBox.BeginMark();
                                                owner.messagesBox.AppendText(http.RequestUri,
                                                        Color.DarkRed, Color.LightGray, false, false, 0, 27);
                                                owner.messagesBox.EndMark(requestUriMarker);
                                                owner.messagesBox.AppendNewLine();

                                                owner.messagesBox.AppendText("Request Version:           ",
                                                        Color.DarkRed, Color.Transparent, false, false, 0, 0);
                                                requestVersionMarker = owner.messagesBox.BeginMark();
                                                owner.messagesBox.AppendText(http.RequestVersion == HttpVersion.V0_9 ? "HTTP/0.9"
                                                        : http.RequestVersion == HttpVersion.V1_0 ? "HTTP/1.0" : "HTTP/1.1",
                                                        Color.DarkRed, Color.LightGray, false, false, 0, 27);
                                                owner.messagesBox.EndMark(requestVersionMarker);
                                                owner.messagesBox.AppendNewLine();

                                                owner.messagesBox.AppendText("Request Content Length:    ",
                                                        Color.DarkRed, Color.Transparent, false, false, 0, 0);
                                                requestLengthMarker = owner.messagesBox.BeginMark();
                                                owner.messagesBox.AppendText(http.RequestLength < 0 ? "<unknown>" : http.RequestLength.ToString(),
                                                        Color.DarkRed, Color.LightGray, false, false, 0, 27);
                                                owner.messagesBox.EndMark(requestLengthMarker);
                                                owner.messagesBox.AppendNewLine();

                                                owner.messagesBox.AppendText("Request Content Encoding:  ",
                                                        Color.DarkRed, Color.Transparent, false, false, 0, 0);
                                                requestEncodingMarker = owner.messagesBox.BeginMark();
                                                owner.messagesBox.AppendText(EncodingToString(http.RequestEncoding),
                                                        Color.DarkRed, Color.LightGray, false, false, 0, 27);
                                                owner.messagesBox.EndMark(requestEncodingMarker);
                                                owner.messagesBox.AppendNewLine();

                                                owner.messagesBox.AppendText("Request Content Type:      ",
                                                        Color.DarkRed, Color.Transparent, false, false, 0, 0);
                                                requestContentTypeMarker = owner.messagesBox.BeginMark();
                                                owner.messagesBox.AppendText(http.RequestContentType == null ? "<unknown>"
                                                        : http.RequestContentType + "/" + http.RequestContentSubtype,
                                                        Color.DarkRed, Color.LightGray, false, false, 0, 27);
                                                owner.messagesBox.EndMark(requestContentTypeMarker);
                                                owner.messagesBox.AppendNewLine();

                                                owner.messagesBox.AppendText("Request Content Charset:   ",
                                                        Color.DarkRed, Color.Transparent, false, false, 0, 0);
                                                requestCharsetMarker = owner.messagesBox.BeginMark();
                                                owner.messagesBox.AppendText(http.RequestCharset == null ? "<unknown>" : http.RequestCharset,
                                                        Color.DarkRed, Color.LightGray, false, false, 0, 27);
                                                owner.messagesBox.EndMark(requestCharsetMarker);
                                                owner.messagesBox.AppendNewLine();

                                                owner.messagesBox.AppendText("Request Headers:",
                                                        Color.DarkRed, Color.Transparent, false, false, 0, 0);
                                                owner.messagesBox.AppendNewLine();
                                                requestHeadersMarker = owner.messagesBox.BeginMark();
                                                owner.messagesBox.EndMark(requestHeadersMarker);

                                                requestHeadersEnum = http.RequestHeaders.GetEnumerator();
                                                ShowHeaders(requestHeadersEnum, requestHeadersMarker);

                                                // response info
                                                owner.messagesBox.AppendNewLine();
                                                owner.messagesBox.AppendText("Response Start:            ",
                                                        Color.DarkRed, Color.Transparent, false, false, 0, 0);
                                                responseStartMarker = owner.messagesBox.BeginMark();
                                                owner.messagesBox.AppendText(http.ResponseStartTimestamp == DateTime.MinValue
                                                        ? "<unknown>" : http.ResponseStartTimestamp.ToString("HH:mm:ss.ffff"),
                                                        Color.DarkRed, Color.LightGray, false, false, 0, 27);
                                                owner.messagesBox.EndMark(responseStartMarker);
                                                owner.messagesBox.AppendNewLine();

                                                owner.messagesBox.AppendText("Response Version:          ",
                                                        Color.DarkRed, Color.Transparent, false, false, 0, 0);
                                                responseVersionMarker = owner.messagesBox.BeginMark();
                                                owner.messagesBox.AppendText(http.ResponseVersion == HttpVersion.V0_9
                                                        ? "HTTP/0.9" : http.ResponseVersion == HttpVersion.V1_0 ? "HTTP/1.0" : "HTTP/1.1",
                                                        Color.DarkRed, Color.LightGray, false, false, 0, 27);
                                                owner.messagesBox.EndMark(responseVersionMarker);
                                                owner.messagesBox.AppendNewLine();

                                                owner.messagesBox.AppendText("Response Status:           ",
                                                        Color.DarkRed, Color.Transparent, false, false, 0, 0);
                                                responseStatusMarker = owner.messagesBox.BeginMark();
                                                owner.messagesBox.AppendText(http.ResponseStatusCode + " " + http.ResponseStatusMessage,
                                                        Color.DarkRed, Color.LightGray, false, false, 0, 27);
                                                owner.messagesBox.EndMark(responseStatusMarker);
                                                owner.messagesBox.AppendNewLine();

                                                owner.messagesBox.AppendText("Response Content Length:   ",
                                                        Color.DarkRed, Color.Transparent, false, false, 0, 0);
                                                responseLengthMarker = owner.messagesBox.BeginMark();
                                                owner.messagesBox.AppendText(http.ResponseLength < 0 ? "<unknown>" : http.ResponseLength.ToString(),
                                                        Color.DarkRed, Color.LightGray, false, false, 0, 27);
                                                owner.messagesBox.EndMark(responseLengthMarker);
                                                owner.messagesBox.AppendNewLine();

                                                owner.messagesBox.AppendText("Response Content Encoding: ",
                                                        Color.DarkRed, Color.Transparent, false, false, 0, 0);
                                                responseEncodingMarker = owner.messagesBox.BeginMark();
                                                owner.messagesBox.AppendText(EncodingToString(http.ResponseEncoding),
                                                        Color.DarkRed, Color.LightGray, false, false, 0, 27);
                                                owner.messagesBox.EndMark(responseEncodingMarker);
                                                owner.messagesBox.AppendNewLine();

                                                owner.messagesBox.AppendText("Response Content Type:     ",
                                                        Color.DarkRed, Color.Transparent, false, false, 0, 0);
                                                responseContentTypeMarker = owner.messagesBox.BeginMark();
                                                owner.messagesBox.AppendText(http.ResponseContentType == null ? "<unknown>"
                                                        : http.ResponseContentType + "/" + http.ResponseContentSubtype,
                                                        Color.DarkRed, Color.LightGray, false, false, 0, 27);
                                                owner.messagesBox.EndMark(responseContentTypeMarker);
                                                owner.messagesBox.AppendNewLine();

                                                owner.messagesBox.AppendText("Response Content Charset:  ",
                                                        Color.DarkRed, Color.Transparent, false, false, 0, 0);
                                                responseCharsetMarker = owner.messagesBox.BeginMark();
                                                owner.messagesBox.AppendText(http.ResponseCharset == null ? "<unknown>" : http.ResponseCharset,
                                                        Color.DarkRed, Color.LightGray, false, false, 0, 27);
                                                owner.messagesBox.EndMark(responseCharsetMarker);
                                                owner.messagesBox.AppendNewLine();

                                                owner.messagesBox.AppendText("Response Headers:",
                                                        Color.DarkRed, Color.Transparent, false, false, 0, 0);
                                                owner.messagesBox.AppendNewLine();
                                                responseHeadersMarker = owner.messagesBox.BeginMark();
                                                owner.messagesBox.EndMark(responseHeadersMarker);

                                                responseHeadersEnum = http.ResponseHeaders.GetEnumerator();
                                                ShowHeaders(responseHeadersEnum, responseHeadersMarker);

                                                owner.messagesBox.AppendNewLine();
                                                requestBodyMarker = owner.messagesBox.BeginMark();
                                                owner.messagesBox.EndMark(requestBodyMarker);
                                                ShowBody(requestBodyMarker, http.RequestBody, http.RequestLength, http.RequestText, Color.Green);

                                                owner.messagesBox.AppendNewLine();
                                                responseBodyMarker = owner.messagesBox.BeginMark();
                                                owner.messagesBox.EndMark(responseBodyMarker);
                                                ShowBody(responseBodyMarker, http.ResponseBody, http.ResponseLength, http.ResponseText, Color.Blue);
                                        }
                                }
                                finally
                                {
                                        owner.messagesBox.EndUpdate();
                                }
                        }

                        protected override void UpdateView()
                        {
                                try
                                {
                                        owner.messagesBox.BeginUpdate();

                                        lock(http)
                                        {
                                                owner.messagesBox.ChangeText(stateMarker, http.RequestComplete && http.ResponseComplete ? "YES" : "NO",
                                                        Color.DarkRed, Color.LightGray, false, false, 0, 27);
                                                owner.messagesBox.ChangeText(requestMethodMarker, http.RequestMethod,
                                                        Color.DarkRed, Color.LightGray, false, false, 0, 27);
                                                owner.messagesBox.ChangeText(requestUriMarker, http.RequestUri,
                                                        Color.DarkRed, Color.LightGray, false, false, 0, 27);
                                                owner.messagesBox.ChangeText(requestVersionMarker, http.RequestVersion == HttpVersion.V0_9 ? "HTTP/0.9"
                                                        : http.RequestVersion == HttpVersion.V1_0 ? "HTTP/1.0" : "HTTP/1.1",
                                                        Color.DarkRed, Color.LightGray, false, false, 0, 27);
                                                owner.messagesBox.ChangeText(requestLengthMarker, http.RequestLength < 0
                                                        ? "<unknown>" : http.RequestLength.ToString(),
                                                        Color.DarkRed, Color.LightGray, false, false, 0, 27);
                                                owner.messagesBox.ChangeText(requestEncodingMarker, EncodingToString(http.RequestEncoding),
                                                        Color.DarkRed, Color.LightGray, false, false, 0, 27);
                                                owner.messagesBox.ChangeText(requestContentTypeMarker, http.RequestContentType == null ? "<unknown>"
                                                        : http.RequestContentType + "/" + http.RequestContentSubtype,
                                                        Color.DarkRed, Color.LightGray, false, false, 0, 27);
                                                owner.messagesBox.ChangeText(requestCharsetMarker, http.RequestCharset == null
                                                        ? "<unknown>" : http.RequestCharset,
                                                        Color.DarkRed, Color.LightGray, false, false, 0, 27);

                                                ShowHeaders(requestHeadersEnum, requestHeadersMarker);

                                                owner.messagesBox.ChangeText(responseVersionMarker, http.ResponseVersion == HttpVersion.V0_9
                                                        ? "HTTP/0.9" : http.ResponseVersion == HttpVersion.V1_0 ? "HTTP/1.0" : "HTTP/1.1",
                                                        Color.DarkRed, Color.LightGray, false, false, 0, 27);
                                                owner.messagesBox.ChangeText(responseStatusMarker, http.ResponseStatusCode + " " + http.ResponseStatusMessage,
                                                        Color.DarkRed, Color.LightGray, false, false, 0, 27);
                                                owner.messagesBox.ChangeText(responseLengthMarker, http.ResponseLength < 0
                                                        ? "<unknown>" : http.ResponseLength.ToString(),
                                                        Color.DarkRed, Color.LightGray, false, false, 0, 27);
                                                owner.messagesBox.ChangeText(responseEncodingMarker, EncodingToString(http.ResponseEncoding),
                                                        Color.DarkRed, Color.LightGray, false, false, 0, 27);
                                                owner.messagesBox.ChangeText(responseContentTypeMarker, http.ResponseContentType == null ? "<unknown>"
                                                        : http.ResponseContentType + "/" + http.ResponseContentSubtype,
                                                        Color.DarkRed, Color.LightGray, false, false, 0, 27);
                                                owner.messagesBox.ChangeText(responseCharsetMarker, http.ResponseCharset == null
                                                        ? "<unknown>" : http.ResponseCharset,
                                                        Color.DarkRed, Color.LightGray, false, false, 0, 27);

                                                ShowHeaders(responseHeadersEnum, responseHeadersMarker);

                                                owner.messagesBox.DeleteText(requestBodyMarker);
                                                ShowBody(requestBodyMarker, http.RequestBody, http.RequestLength, http.RequestText, Color.Green);
                                                owner.messagesBox.DeleteText(responseBodyMarker);
                                                ShowBody(responseBodyMarker, http.ResponseBody, http.ResponseLength, http.ResponseText, 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(object marker, byte[] body, int len, string text, Color color)
                        {
                                if(text != null)
                                {
                                        ArrayList lines = Utils.SplitLine(text);
                                        for(int i = 0; i < lines.Count; i++)
                                        {
                                                owner.messagesBox.InsertText(marker, (string)lines[i], color, Color.LightGray, false, false, 0, 0);
                                                owner.messagesBox.InsertNewLine(marker);
                                        }
                                }
                                else if(body != null)
                                {
                                        ArrayList lines = Utils.SplitLine(Utils.BytesToString(body, len));
                                        for(int i = 0; i < lines.Count; i++)
                                        {
                                                owner.messagesBox.InsertText(marker, (string)lines[i], color, Color.LightGray, false, false, 0, 0);
                                                owner.messagesBox.InsertNewLine(marker);
                                        }
                                }
                        }

                        public override void WriteLog(StreamWriter writer)
                        {
                                // request info
                                writer.WriteLine(
                                        "Complete:                  " + (http.RequestComplete && http.ResponseComplete ? "YES" : "NO") + "\r\n"
                                        + "\r\nRequest Method:            " + http.RequestMethod
                                        + "\r\nRequest URI:               " + http.RequestUri
                                        + "\r\nRequest Version:           " + (http.RequestVersion == HttpVersion.V0_9 ? "HTTP/0.9"
                                                : http.RequestVersion == HttpVersion.V1_0 ? "HTTP/1.0" : "HTTP/1.1")
                                        + "\r\nRequest Content Length:    " + (http.RequestLength < 0 ? "<unknown>" : http.RequestLength.ToString())
                                        + "\r\nRequest Content Encoding:  " + EncodingToString(http.RequestEncoding)
                                        + "\r\nRequest Content Type:      " + (http.RequestContentType == null ? "<unknown>"
                                                : http.RequestContentType + "/" + http.RequestContentSubtype)
                                        + "\r\nRequest Content Charset:   " + (http.RequestCharset == null ? "<unknown>" : http.RequestCharset)
                                        + "\r\nRequest Headers:");

                                foreach(HttpHeader h in http.RequestHeaders)
                                {
                                        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.ResponseVersion == HttpVersion.V0_9
                                                ? "HTTP/0.9" : http.ResponseVersion == HttpVersion.V1_0 ? "HTTP/1.0" : "HTTP/1.1")
                                        + "\r\nResponse Status:           " + http.ResponseStatusCode + " " + http.ResponseStatusMessage
                                        + "\r\nResponse Content Length:   " + (http.ResponseLength < 0
                                                ? "<unknown>" : http.ResponseLength.ToString())
                                        + "\r\nResponse Content Encoding: " + EncodingToString(http.ResponseEncoding)
                                        + "\r\nResponse Content Type:     " + (http.ResponseContentType == null ? "<unknown>"
                                                : http.ResponseContentType + "/" + http.ResponseContentSubtype)
                                        + "\r\nResponse Content Charset:  " + (http.ResponseCharset == null ? "<unknown>" : http.ResponseCharset)
                                        + "\r\nResponse Headers:");

                                foreach(HttpHeader h in http.ResponseHeaders)
                                {
                                        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.RequestText != null)
                                {
                                        writer.WriteLine(http.RequestText);
                                }
                                else if(http.RequestBody != null)
                                {
                                        writer.WriteLine(Utils.BytesToString(http.RequestBody, http.RequestLength));
                                }

                                // response body
                                if(http.ResponseText != null)
                                {
                                        writer.WriteLine(http.ResponseText);
                                }
                                else if(http.ResponseBody != null)
                                {
                                        writer.WriteLine(Utils.BytesToString(http.ResponseBody, http.ResponseLength));
                                }

                                writer.WriteLine("===============================================================");
                                writer.WriteLine();
                        }
                }

                private class XmlNodeData : TreeNodeData
                {
                        private XmlMessage xml;

                        public XmlMessage Xml
                        {
                                get { return xml; }
                                set { xml = value; }
                        }

                        public XmlNodeData(MainForm owner, TreeNode node) : base(owner, node)
                        {
                        }

                        protected override void InitView()
                        {
                                try
                                {
                                        // update main screen if necessary
                                        owner.messagesBox.BeginUpdate();

                                        lock(xml)
                                        {
                                                owner.messagesBox.Clear();
                                                if(xml.ParseException != null)
                                                {
                                                        owner.messagesBox.AppendText("Cannot parse XML:",
                                                                Color.Red, Color.Transparent, false, false, 0, 0);
                                                        owner.messagesBox.AppendNewLine();
                                                        owner.messagesBox.AppendText(xml.ParseException.Message,
                                                                Color.Red, Color.Transparent, false, false, 2, 2);
                                                        owner.messagesBox.AppendNewLine();
                                                }
                                                else if(xml.Xml != null)
                                                {
                                                        ShowXmlNode(xml.Xml, 0);
                                                }
                                        }
                                }
                                finally
                                {
                                        owner.messagesBox.EndUpdate();
                                }
                        }

                        protected override void UpdateView()
                        {
                        }

                        private void ShowXmlNode(XmlNode node, int indent)
                        {
                                if(node.NodeType == XmlNodeType.Document)
                                {
                                        foreach(XmlNode subnode in node.ChildNodes)
                                        {
                                                ShowXmlNode(subnode, indent);
                                        }
                                }
                                else if(node.NodeType == XmlNodeType.XmlDeclaration)
                                {
                                        owner.messagesBox.AppendText("<?" + node.Name + " " + node.Value + " ?>",
                                                Color.Blue, Color.Transparent, false, false, indent, indent+2);
                                        owner.messagesBox.AppendNewLine();
                                }
                                else if(node.NodeType == XmlNodeType.Comment)
                                {
                                        owner.messagesBox.AppendText("<!--", Color.Gray, Color.Transparent, false, false, indent, indent + 2);
                                        ShowNormalizedXmlValue(node.Value, Color.Gray, Color.Thistle, false, false, indent);
                                        owner.messagesBox.AppendText("-->", Color.Gray, Color.Transparent, false, false, 0, indent + 2);
                                        owner.messagesBox.AppendNewLine();
                                }
                                else if(node.NodeType == XmlNodeType.Element)
                                {
                                        owner.messagesBox.AppendText("<", Color.Blue, Color.Transparent, false, false, indent, indent + 2);
                                        owner.messagesBox.AppendText(node.Name, Color.DarkRed, Color.Transparent, false, false, 0, indent + 2);

                                        foreach(XmlAttribute attr in node.Attributes)
                                        {
                                                bool xmlAttr = attr.Name.StartsWith("xml");
                                                owner.messagesBox.AppendText(attr.Name, xmlAttr ? Color.Red : Color.DarkRed, Color.Transparent, false, false, 1, indent + 2);
                                                owner.messagesBox.AppendText("=\"", Color.Blue, Color.Transparent, false, false, 0, indent + 2);
                                                owner.messagesBox.AppendText(attr.Value, xmlAttr ? Color.Red : Color.DarkRed, Color.Transparent, false, false, 0, indent + 2);
                                                owner.messagesBox.AppendText("\"", Color.Blue, Color.Transparent, false, false, 0, indent + 2);
                                        }

                                        if(!node.HasChildNodes)
                                        {
                                                owner.messagesBox.AppendText(" />", Color.Blue, Color.Transparent, false, false, 0, indent + 2);
                                                owner.messagesBox.AppendNewLine();
                                        }
                                        else
                                        {
                                                owner.messagesBox.AppendText(">", Color.Blue, Color.Transparent, false, false, 0, indent + 2);

                                                bool elementExists = false;
                                                bool lastText      = true;
                                                foreach(XmlNode subnode in node.ChildNodes)
                                                {
                                                        if(subnode.NodeType == XmlNodeType.Text)
                                                        {
                                                                if(elementExists) owner.messagesBox.AppendNewLine();
                                                                ShowNormalizedXmlValue(subnode.Value, Color.Black, Color.LightGray, false, false, indent);
                                                                lastText = true;
                                                        }
                                                        else
                                                        {
                                                                if(lastText) owner.messagesBox.AppendNewLine();
                                                                ShowXmlNode(subnode, indent + 2);
                                                                elementExists = true;
                                                                lastText      = false;
                                                        }
                                                }

                                                if(elementExists) owner.messagesBox.AppendText(null, Color.Black, Color.Transparent, false, false, indent, indent + 2);
                                                owner.messagesBox.AppendText("</", Color.Blue, Color.Transparent, false, false, 0, indent + 2);
                                                owner.messagesBox.AppendText(node.Name, Color.DarkRed, Color.Transparent, false, false, 0, indent + 2);
                                                owner.messagesBox.AppendText(">", Color.Blue, Color.Transparent, false, false, 0, indent + 2);
                                                owner.messagesBox.AppendNewLine();
                                        }
                                }
                        }

                        private void ShowNormalizedXmlValue(string s, Color color, Color backColor, bool italic, bool bold, int indent)
                        {
                                int begin;
                                int end;

                                bool newLineFound = false;
                                foreach(char c in s)
                                {
                                        if(c == '\n')
                                        {
                                                newLineFound = true;
                                                break;
                                        }
                                }

                                if(newLineFound)
                                {
                                        owner.messagesBox.AppendNewLine();
                                        owner.messagesBox.AppendText(null, color, Color.Transparent, italic, bold, indent, indent);
                                        owner.messagesBox.AppendText(null, color, backColor, italic, bold, 2, 2);
                                }

                                // trim
                                for(begin = 0; begin < s.Length; begin++)
                                {
                                        char c = s[begin];
                                        if(c != '\n' && c != '\r' && c != '\t' && c != ' ') break;
                                }
                                for(end = s.Length-1; end >= 0; end--)
                                {
                                        char c = s[end];
                                        if(c != '\n' && c != '\r' && c != '\t' && c != ' ') break;
                                }

                                StringBuilder b = new StringBuilder(s.Length);
                                for(int i = begin; i <= end; i++)
                                {
                                        char c = s[i];

                                        switch(c)
                                        {
                                                case '\n':
                                                        if(b.Length > 0)
                                                        {
                                                                owner.messagesBox.AppendText(b.ToString(), color, backColor, italic, bold, 0, indent + 2);
                                                                b.Length = 0;
                                                        }

                                                        owner.messagesBox.AppendNewLine();
                                                        owner.messagesBox.AppendText(null, color, Color.Transparent, italic, bold, indent, indent);
                                                        owner.messagesBox.AppendText(null, color, backColor, italic, bold, 2, 2);
                                                        break;

                                                case '\r':
                                                        break;

                                                case '\t':
                                                        b.Append("  ");
                                                        break;

                                                default:
                                                        b.Append(c);
                                                        break;
                                        }
                                }

                                if(b.Length > 0)
                                        owner.messagesBox.AppendText(b.ToString(), color, backColor, italic, bold, 0, indent + 2);

                                if(newLineFound)
                                {
                                        owner.messagesBox.AppendNewLine();
                                        owner.messagesBox.AppendText(null, color, Color.Transparent, italic, bold, indent, indent + 2);
                                }
                        }

                        public override void WriteLog(StreamWriter writer)
                        {
                                WriteXmlNode(writer, xml.Xml, "");
                                writer.WriteLine("===============================================================");
                                writer.WriteLine();
                        }

                        private void WriteXmlNode(StreamWriter writer, XmlNode node, string indent)
                        {
                                if(node.NodeType == XmlNodeType.Document)
                                {
                                        foreach(XmlNode subnode in node.ChildNodes)
                                        {
                                                WriteXmlNode(writer, subnode, indent);
                                        }
                                }
                                else if(node.NodeType == XmlNodeType.XmlDeclaration)
                                {
                                        writer.WriteLine(indent + "<?" + node.Name + " " + node.Value + " ?>");
                                }
                                else if(node.NodeType == XmlNodeType.Comment)
                                {
                                        writer.Write(indent + "<!--");
                                        WriteNormalizedXmlValue(writer, node.Value, indent);
                                        writer.WriteLine("-->");
                                }
                                else if(node.NodeType == XmlNodeType.Element)
                                {
                                        writer.Write(indent + "<" + node.Name);

                                        foreach(XmlAttribute attr in node.Attributes)
                                        {
                                                bool xmlAttr = attr.Name.StartsWith("xml");
                                                writer.Write(" " + attr.Name + "=\"" + attr.Value + "\"");
                                        }

                                        if(!node.HasChildNodes)
                                        {
                                                writer.WriteLine(" />");
                                        }
                                        else
                                        {
                                                writer.Write(">");

                                                bool elementExists = false;
                                                bool lastText      = true;
                                                foreach(XmlNode subnode in node.ChildNodes)
                                                {
                                                        if(subnode.NodeType == XmlNodeType.Text)
                                                        {
                                                                if(elementExists) writer.WriteLine();
                                                                WriteNormalizedXmlValue(writer, subnode.Value, indent);
                                                                lastText = true;
                                                        }
                                                        else
                                                        {
                                                                if(lastText) writer.WriteLine();
                                                                WriteXmlNode(writer, subnode, indent + "  ");
                                                                elementExists = true;
                                                                lastText      = false;
                                                        }
                                                }

                                                if(elementExists) writer.Write(indent);
                                                writer.WriteLine("</" + node.Name + ">");
                                        }
                                }
                        }

                        private void WriteNormalizedXmlValue(StreamWriter writer, string s, string indent)
                        {
                                int begin;
                                int end;

                                bool newLineFound = false;
                                foreach(char c in s)
                                {
                                        if(c == '\n')
                                        {
                                                newLineFound = true;
                                                break;
                                        }
                                }

                                if(newLineFound)
                                {
                                        writer.WriteLine();
                                        writer.Write(indent + "  ");
                                }

                                // trim
                                for(begin = 0; begin < s.Length; begin++)
                                {
                                        char c = s[begin];
                                        if(c != '\n' && c != '\r' && c != '\t' && c != ' ') break;
                                }
                                for(end = s.Length-1; end >= 0; end--)
                                {
                                        char c = s[end];
                                        if(c != '\n' && c != '\r' && c != '\t' && c != ' ') break;
                                }

                                StringBuilder b = new StringBuilder(s.Length);
                                for(int i = begin; i <= end; i++)
                                {
                                        char c = s[i];

                                        switch(c)
                                        {
                                                case '\n':
                                                        if(b.Length > 0)
                                                        {
                                                                writer.Write(b.ToString());
                                                                b.Length = 0;
                                                        }

                                                        writer.WriteLine();
                                                        writer.Write(indent + "  ");
                                                        break;

                                                case '\r':
                                                        break;

                                                case '\t':
                                                        b.Append("  ");
                                                        break;

                                                case '&':
                                                        b.Append("&amp;");
                                                        break;

                                                case '<':
                                                        b.Append("&lt;");
                                                        break;

                                                case '>':
                                                        b.Append("&gt;");
                                                        break;

                                                default:
                                                        b.Append(c);
                                                        break;
                                        }
                                }

                                if(b.Length > 0)
                                        writer.Write(b.ToString());

                                if(newLineFound)
                                {
                                        writer.WriteLine();
                                        writer.Write(indent + "  ");
                                }
                        }
                }

                #endregion node display classes

                #region other classes
                private abstract class Utils
                {
                        public static string BytesToString(byte[] bytes, int length)
                        {
                                if(bytes == null) return null;

                                char[]  chars   = new char[length + 1];
                                Decoder decoder = System.Text.Encoding.UTF8.GetDecoder();
                                int     charLen = decoder.GetChars(bytes, 0, length, chars, 0);

                                for(int i = 0; i < charLen; i++)
                                {
                                        char c = chars[i];
                                        if(c != '\n' && c != '\r' && Char.IsControl(c)) chars[i] = '.';
                                }

                                return (new System.String(chars)).Substring(0, charLen);
                        }

                        public static ArrayList SplitLine(string line)
                        {
                                if(line == null) return null;

                                ArrayList    res = new ArrayList();
                                StringBuilder b  = new StringBuilder(200);

                                foreach(char c in line)
                                {
                                        switch(c)
                                        {
                                                case '\r':
                                                        break;

                                                case '\n':
                                                        res.Add(b.ToString());
                                                        b.Length = 0;
                                                        break;

                                                default:
                                                        b.Append(c);
                                                        break;
                                        }
                                }

                                res.Add(b.ToString());

                                return res;
                        }
                }
                #endregion other classes
        }

        public class LogMessages
        {
                private ArrayList messages = new ArrayList();
                private ListBox   logBox;
                private LogLevel  level = LogLevel.Debug;

                public LogLevel Level
                {
                        get { return level; }
                        set { level = value; Update(); }
                }

                public LogMessages(ListBox logBox)
                {
                        this.logBox = logBox;
                }

                public ArrayList Messages
                {
                        get { return new ArrayList(messages); }
                }

                public void Add(LogMessage message)
                {
                        messages.Add(message);
                        if(Show(message))
                                logBox.TopIndex = logBox.Items.Count - 1;
                }

                public void Clear()
                {
                        messages.Clear();
                        logBox.Items.Clear();
                }

                public void Update()
                {
                        logBox.BeginUpdate();

                        logBox.Items.Clear();
                        foreach(LogMessage message in messages)
                        {
                                Show(message);
                        }
                        logBox.TopIndex = logBox.Items.Count - 1;

                        logBox.EndUpdate();
                }

                private bool Show(LogMessage message)
                {
                        if(message.Level > this.level) return false;

                        try
                        {
                                logBox.Items.Add(message.ToString());
                        }
                        catch(ObjectDisposedException) {}

                        return true;
                }
        }

        public class LogMessage
        {
                private TcpConnection tcp;
                private LogLevel      level;
                private string        message;
                private Exception     exception;
                private DateTime      timestamp = DateTime.Now;

                public TcpConnection Tcp
                {
                        get { return tcp; }
                        set { tcp = value; }
                }

                public LogLevel Level
                {
                        get { return level; }
                        set { level = value; }
                }

                public string Message
                {
                        get { return message; }
                        set { message = value; }
                }

                public Exception Exception
                {
                        get { return exception; }
                        set { exception = value; }
                }

                public DateTime Timestamp
                {
                        get { return timestamp; }
                        set { timestamp = value; }
                }

                public LogMessage(TcpConnection tcp, LogLevel level, string message, Exception exception)
                {
                        this.tcp       = tcp;
                        this.level     = level;
                        this.message   = message;
                        this.exception = exception;
                }

                public override string ToString()
                {
                        string l = "     ";
                        switch(level)
                        {
                                case LogLevel.Debug:     l = "debug"; break;
                                case LogLevel.Info:      l = "info "; break;
                                case LogLevel.Important: l = "Imp  "; break;
                                case LogLevel.Warning:   l = "Warn "; break;
                                case LogLevel.Error:     l = "ERROR"; break;
                                case LogLevel.Critical:  l = "CRIT "; break;
                        }

                        return (tcp == null ? "...." : tcp.Id) + " " + l + " " + timestamp.ToString("HH:mm:ss.ffff") + " "
                                + (exception != null ? exception.Message : message);
                }
        }

        public enum TcpShowMode
        {
                ByDirection,
                ByTime
        }

        public class RecentItem : IComparable
        {
                private int listenPort;

                public int ListenPort
                {
                        get { return listenPort; }
                        set { listenPort = value; }
                }

                private string resendHost;

                public string ResendHost
                {
                        get { return resendHost; }
                        set { resendHost = value; }
                }

                private int resendPort;

                public int ResendPort
                {
                        get { return resendPort; }
                        set { resendPort = value; }
                }

                private DateTime timestamp;

                public DateTime Timestamp
                {
                        get { return timestamp; }
                }

                public RecentItem(int listenPort, string resendHost, int resendPort)
                {
                        this.listenPort = listenPort;
                        this.resendHost = resendHost;
                        this.resendPort = resendPort;
                }

                public override string ToString()
                {
                        return string.Format("{0} to {1}:{2} at {3:HH:mm:ss.ff}",
                                listenPort, resendHost, resendPort, timestamp);
                }

                public override bool Equals(object obj)
                {
                        if(!(obj is RecentItem)) return false;

                        RecentItem i2 = (RecentItem)obj;

                        return (i2.listenPort == this.listenPort) && (i2.resendHost == this.resendHost)
                                && (i2.resendPort == this.resendPort);
                }

                public override int GetHashCode()
                {
                        return this.resendHost.GetHashCode();
                }

                public string SaveToRegistry()
                {
                        return string.Format("{0}:{1}:{2}:{3}",
                                listenPort, resendPort, timestamp.Ticks, resendHost);
                }

                public static RecentItem LoadFromRegistry(string str)
                {
                        Regex re = new Regex(@"^(\d+):(\d+):(\d+):(.+)$");
                        Match m  = re.Match(str);
                        if(!m.Success) throw new Exception("Cannot parse recent item");

                        RecentItem item = new RecentItem(int.Parse(m.Groups[1].Value),
                                m.Groups[4].Value, int.Parse(m.Groups[2].Value));

                        item.timestamp = new DateTime(long.Parse(m.Groups[3].Value));

                        return item;
                }

                public void UpdateTimestamp()
                {
                        timestamp = DateTime.Now;
                }

                public void UpdateTimestamp(RecentItem second)
                {
                        if(second.timestamp > timestamp)
                                timestamp = second.timestamp;
                }

                #region IComparable Members

                int IComparable.CompareTo(object obj)
                {
                        if(!(obj is RecentItem))
                                throw new Exception("Cannot compare");

                        return this.timestamp.CompareTo(((RecentItem)obj).timestamp);
                }

                #endregion
        }
}