//  
//   Rebex Sample Code License
// 
//   Copyright 2026, REBEX CR s.r.o.
//   All rights reserved.
//   https://www.rebex.net/
// 
//   Permission to use, copy, modify, and/or distribute this software for any
//   purpose with or without fee is hereby granted.
// 
//   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
//   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
//   OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
//   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
//   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
//   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
//   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
//   OTHER DEALINGS IN THE SOFTWARE.
// 

using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Resources;
using System.IO;
using System.Text;
using System.Diagnostics;
using System.Text.RegularExpressions;

using Rebex.IO;
using Rebex.Net;
using Rebex.Security.Certificates;

namespace Rebex.Samples
{
    /// <summary>
    /// very simple ftp client
    /// </summary>
    public class MainForm : System.Windows.Forms.Form
    {

        private System.ComponentModel.Container components = null;
        private System.Windows.Forms.RichTextBox tbLog;
        private System.Windows.Forms.ListView listServer;
        private System.Windows.Forms.ColumnHeader name;
        private System.Windows.Forms.ColumnHeader size;
        private System.Windows.Forms.Label lblServer;
        private System.Windows.Forms.ListView listLocal;
        private System.Windows.Forms.Label lblLocal;
        private System.Windows.Forms.Label lblServerDirectory;
        private System.Windows.Forms.ColumnHeader columnHeader1;
        private System.Windows.Forms.ColumnHeader columnHeader2;
        private System.Windows.Forms.Label lblLocalDirectory;
        private System.Windows.Forms.ColumnHeader date;
        private System.Windows.Forms.ColumnHeader columnHeader3;
        private System.Windows.Forms.Button btnStop;
        private System.Windows.Forms.Button btnDelete;
        private System.Windows.Forms.Button btnUpload;
        private System.Windows.Forms.Button btnDownload;
        private System.Windows.Forms.RadioButton rbBin;
        private System.Windows.Forms.RadioButton rbAsc;
        private System.Windows.Forms.Button btnRefresh;
        private System.Windows.Forms.Button btnMakeDir;
        private System.Windows.Forms.ComboBox cbDrives;
        private System.Windows.Forms.Button btnConnect;
        private System.Windows.Forms.GroupBox gbControl;
        private System.Windows.Forms.GroupBox gbTransfer;
        private System.Windows.Forms.PictureBox headerLeft;
        private System.Windows.Forms.Panel headerMiddle;
        private System.Windows.Forms.PictureBox headerRight;
        private System.Windows.Forms.TrackBar trackBar;
        private System.Windows.Forms.Label lblSpeed;
        private System.Windows.Forms.Button btnErrorWarning;
        private System.Windows.Forms.CheckBox cbModeZ;
        private System.Windows.Forms.PictureBox pbSecure;
        private System.Windows.Forms.CheckBox cbST;
        private StatusStrip mainStatusStrip;
        private ToolStripProgressBar pbTransfer;
        private ToolStripStatusLabel lblProgress;
        private MainForm.SplitContainer mainSplitContainer;

        private Ftp _ftp;                           // FTP session
        private ListView listActive;                // active list (server x local)
        private int _serverDirs;                    // number of dirs on server side
        private int _localDirs;                     // number of dirs on local side
        private int _selectedDrive = -1;            // selected drive (A:, C:, ...) in combo
        private string _localPath;                  // local path
        private bool _isWorking;                    // determines whether any operation is running
        private DateTime _transferTime;             // transfer launch-time
        private bool _cccMode = false;              // clear command channel enabled?
        private ProblemDetectedForm _problemForm;   // informs the user about problems while transferring data

        // constansts
        private const string SYMLINK = "--->";      // symlink tag
        private const string URL = "https://www.rebex.net/ftp-ssl.net/";  // url

        private string _exception = "";			  // last occured exception

        public void WriteToLog(Exception x)
        {
            ShowErrorWarning(x.ToString());

            // FtpExceptions are automatilcally logged by component logger.
            // No need to display them again.
            // We don't wont duplicate entries for such exceptions.
            if (x is FtpException)
                return;

            WriteToLog("* " + x.ToString(), RichTextBoxLogWriter.COLORERROR);
        }

        public void ShowErrorWarning(string message)
        {
            _exception = message;
            btnErrorWarning.Visible = true;
        }

        public void WriteToLog(string message, Color color)
        {
            tbLog.Focus();
            tbLog.SelectionColor = color;
            tbLog.AppendText(message + "\r\n");
        }

        
        /// <summary>
        /// format datetime to readable form in the panels
        /// </summary>
        /// <param name="dt">datetime</param>
        /// <returns>formatted string</returns>
        private static string FormatFileTime(DateTime dt)
        {
            return dt.ToString("yyyy-MM-dd HH:mm");
        }

        /// <summary>
        /// Converts transfer mode selected in connection form into SSL mode.
        /// </summary>
        /// <param name="transferMode">Selected transfer mode.</param>
        /// <returns>Converted SSL mode.</returns>
        private static SslMode ConvertToSslMode(FileTransferMode transferMode)
        {
            switch (transferMode)
            {
                case FileTransferMode.FtpSslExplicit:
                    return SslMode.Explicit;
                case FileTransferMode.FtpSslImplicit:
                    return SslMode.Implicit;
            }

            return SslMode.None;
        }

        /// <summary>
        /// Wrapper for Invoke method that doesn't throw an exception after the object has been
        /// disposed while the calling method was running in a background thread.
        /// </summary>
        /// <param name="method"></param>
        /// <param name="args"></param>
        private void SafeInvoke(Delegate method, params object[] args)
        {
            try
            {
                if (!IsDisposed)
                    Invoke(method, args);
            }
            catch (ObjectDisposedException)
            {
            }
        }

        /// <summary>
        /// event displaying ftp state
        /// </summary>
        private void StateChanged(object sender, FtpStateChangedEventArgs e)
        {
            switch (e.NewState)
            {
                case FtpState.Disconnected:
                case FtpState.Disposed:
                    pbSecure.Visible = false;
                    lblProgress.Text = "Disconnected";
                    btnConnect.Text = "Connect...";
                    listServer.Items.Clear();
                    break;
                case FtpState.Ready:
                    lblProgress.Text = "Ready";
                    break;
            }
        }

        /// <summary>
        /// handles the displaying traversing event
        /// </summary>
        void Traversing(object sender, FtpTraversingEventArgs e)
        {
            if (e.Action == TransferAction.Listing)
                return;

            string strBatchInfo = string.Format("({0} file{1} traversed)    ",
                e.FilesTotal, (e.FilesTotal > 1 ? "s" : null));

            switch (e.TraversingState)
            {
                case TraversingState.DirectoryRetrieved:
                    strBatchInfo += "Directory retrieved.";
                    break;
                case TraversingState.DirectoryRetrieving:
                    strBatchInfo += "Retrieving directory...";
                    break;
                case TraversingState.HierarchyRetrieved:
                    strBatchInfo += string.Format("Hierarchy retrieved ({0} byte{1} in {2} file{3}).",
                        e.BytesTotal, (e.BytesTotal > 1 ? "s" : null),
                        e.FilesTotal, (e.FilesTotal> 1 ? "s" : null));
                    break;
                case TraversingState.HierarchyRetrieving:
                    strBatchInfo += "Retrieving hierarchy...";
                    break;
            }

            lblProgress.Text = strBatchInfo;
        }

        /// <summary>
        /// handles the transfer progress changed event
        /// </summary>
        void TransferProgressChanged(object sender, FtpTransferProgressChangedEventArgs e)
        {
            string strBatchInfo = string.Format("({0} / {1} file{2} processed)    ",
                e.FilesProcessed, e.FilesTotal, (e.FilesProcessed > 1 ? "s" : null));

            SetProgressValue( Convert.ToInt32(e.ProgressPercentage));

            switch (e.TransferState)
            {
                case TransferProgressState.DataBlockProcessed:
                    strBatchInfo += e.BytesTransferred + " bytes";
                    break;
                case TransferProgressState.DirectoryProcessing:
                    strBatchInfo += "Processing directory...";
                    break;
                case TransferProgressState.FileTransferring:
                    strBatchInfo += "Transferring file...";
                    break;
                case TransferProgressState.FileTransferred:
                    strBatchInfo += "File transferred.";
                    break;
                case TransferProgressState.TransferCompleted:
                    strBatchInfo += "Transfer completed.";
                    ShowTransferStatus(e.BytesTransferred, e.FilesTransferred);
                    break;
            }

            lblProgress.Text = strBatchInfo;
        }

        private void SetProgressValue(int value)
        {
            // workaround for progress bar smoothing
            if (value == 100)
            {
                pbTransfer.Maximum = 101;
                pbTransfer.Value = 101;
                pbTransfer.Maximum = 100;
                System.Threading.Thread.Sleep(200);
            }
            else
            {
                pbTransfer.Value = value;
            }
        }

        /// <summary>
        /// handles the delete progress changed event
        /// </summary>
        private void DeleteProgressChanged(object sender, FtpDeleteProgressChangedEventArgs e)
        {
            string strDeleteInfo = string.Format("({0} / {1} file{2} deleted)    ",
                e.FilesProcessed, e.FilesTotal, (e.FilesProcessed > 1 ? "s" : null));

            switch (e.DeleteState)
            {
                case DeleteProgressState.DeleteCompleted:
                    strDeleteInfo += "Delete completed.";
                    break;
                case DeleteProgressState.DirectoryDeleted:
                    strDeleteInfo += "Directory deleted.";
                    break;
                case DeleteProgressState.DirectoryProcessing:
                    strDeleteInfo += "Processing directory...";
                    break;
                case DeleteProgressState.FileDeleted:
                    strDeleteInfo += "File deleted.";
                    break;
                case DeleteProgressState.FileDeleting:
                    strDeleteInfo += "Deleting file...";
                    break;
            }

            lblProgress.Text = strDeleteInfo;
        }

        /// <summary>
        /// handles the batch transfer problem detected event
        /// </summary>
        private void ProblemDetected(object sender, FtpProblemDetectedEventArgs e)
        {
            _problemForm.ShowModal(this, e);
        }

        /// <summary>
        /// make local list + drives combo box
        /// </summary>
        public void MakeLocalList()
        {
            int dirs = 0;
            int files = 0;
            long size = 0;

            // make a logical drives list [first run only]

            if (cbDrives.Items.Count <= 0)
            {
                string[] drives = Directory.GetLogicalDrives();
                string lowerDrive = @"c:\";

                // find "lower" acceptable drive

                for (int c = drives.Length - 1; c >= 0; c--)
                {
                    if (drives[c].ToLower().CompareTo(@"c:\") >= 0) lowerDrive = drives[c].ToLower();
                }

                // feed the drives' list and select one

                for (int c = 0; c < drives.Length; c++)
                {
                    string drive = drives[c];

                    cbDrives.Items.Add(drive);

                    if (_selectedDrive == -1 && drive.ToLower().CompareTo(lowerDrive) == 0)
                    {
                        cbDrives.SelectedIndex = c;
                        _selectedDrive = c;
                    }
                }

                if (cbDrives.SelectedIndex <= 0) cbDrives.SelectedIndex = 0;
            }

            // create the list
            listLocal.Items.Clear();

            // directory up
            if (_localPath != null && _localPath.Length > 3)
            {
                listLocal.Items.Add(new ListViewItem("..", 0));
                dirs++;
            }
            else
            {
                _localPath = cbDrives.SelectedItem.ToString();
            }

            DirectoryInfo directory = new DirectoryInfo(_localPath);
            DirectoryInfo[] list = directory.GetDirectories();

            for (int c = 0; c < list.Length; c++)
            {
                string[] row = new string[3];
                row[0] = list[c].Name;
                row[1] = "";

                // time [on read-only devices LWT is not available for dirs?]

                try
                {
                    row[2] = FormatFileTime(list[c].LastWriteTime);
                }
                catch
                {
                    row[2] = "";
                }

                listLocal.Items.Add(new ListViewItem(row, 1 + c));
                dirs++;
            }

            _localDirs = dirs;

            // files

            FileInfo[] list2 = directory.GetFiles();

            for (int c = 0; c < list2.Length; c++)
            {
                string[] row = new string[3];
                row[0] = list2[c].Name;
                row[1] = list2[c].Length.ToString();
                row[2] = FormatFileTime(list2[c].LastWriteTime);

                listLocal.Items.Add(new ListViewItem(row, 1 + dirs + files));
                size += list2[c].Length;
                files++;
            }

            // stats

            lblLocal.Text = dirs + " folder" + (dirs > 1 ? "s" : "") + ", " +
                files + " file" + (files > 1 ? "s" : "") + ", " +
                size / 1024 + " K";

            // working directory

            lblLocalDirectory.Text = directory.FullName;
            _localPath = directory.FullName;
        }

        /// <summary>
        /// make ftp list
        /// </summary>
        public void MakeFtpList()
        {
            int dirs = 0;
            int files = 0;
            long size = 0;

            listServer.Items.Clear();

            // not connected?

            if (_ftp == null)
            {
                lblServerDirectory.Text = "";
                lblServer.Text = "";
                _serverDirs = 0;
                return;
            }
            // secure transfers

            if (cbST.Enabled && !_cccMode)
                _ftp.SecureTransfers = cbST.Checked;
            // make the list

            FtpItemCollection list = null;

            try
            {
                _isWorking = true;
                btnStop.Visible = true;
                IAsyncResult ar = _ftp.BeginGetList(null, null);
                while (!ar.IsCompleted)
                {
                    Application.DoEvents();
                    System.Threading.Thread.Sleep(1);
                }
                list = _ftp.EndGetList(ar);
                list.Sort();
            }
            catch (Exception ex)
            {
                WriteToLog(ex);
                listServer.Items.Add(new ListViewItem("..", 0));
                return;
            }
            finally
            {
                btnStop.Visible = false;
                _isWorking = false;
            }

            // items

            listServer.Items.Add(new ListViewItem("..", 0));
            dirs++;

            for (int c = 0; c < list.Count; c++)
            {
                string[] row = new string[3];
                row[0] = list[c].Name;
                row[1] = null;
                row[2] = FormatFileTime(list[c].LastWriteTime.Value);

                if (list[c].IsLink)
                {
                    row[1] = SYMLINK;
                }
                else if (list[c].IsFile)
                {
                    row[1] = list[c].Length.ToString();
                    size += list[c].Length;
                    files++;
                }
                else if (list[c].IsDirectory)
                {
                    dirs++;
                }

                ListViewItem lvi = new ListViewItem(row);
                lvi.Tag = list[c];
                listServer.Items.Add(lvi);
                                
            }
                        
            listServer.ListViewItemSorter = new ListViewColumnSorter();
            listServer.Sort();

            // stats
            lblServer.Text = dirs + " folder" + (dirs > 1 ? "s" : "") + ", " +
                files + " file" + (files > 1 ? "s" : "") + ", " +
                size / 1024 + " K";

            // working directory
            lblServerDirectory.Text = _ftp.GetCurrentDirectory();

            _serverDirs = dirs;
        }

        /// <summary>
        /// constructor
        /// </summary>
        public MainForm()
        {
            Application.EnableVisualStyles();
            CheckForIllegalCrossThreadCalls = true;
            InitializeComponent();

            rbBin.Checked = true;
            trackBar.Enabled = false;
            btnErrorWarning.Visible = false;
            lblServer.Text = "";
            listLocal.Focus();
            btnConnect.Select();

            MakeLocalList();
            _problemForm = new ProblemDetectedForm();

            WriteToLog("Welcome to Rebex FTP!", Color.DarkGreen);

        }

        /// <summary>
        /// clean up any resources being used
        /// </summary>
        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (components != null)
                {
                    components.Dispose();
                }
            }
            base.Dispose(disposing);
        }

        #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()
        {
            System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainForm));
            this.listServer = new System.Windows.Forms.ListView();
            this.name = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
            this.size = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
            this.date = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
            this.listLocal = new System.Windows.Forms.ListView();
            this.columnHeader1 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
            this.columnHeader2 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
            this.columnHeader3 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
            this.tbLog = new System.Windows.Forms.RichTextBox();
            this.lblServer = new System.Windows.Forms.Label();
            this.lblServerDirectory = new System.Windows.Forms.Label();
            this.lblLocal = new System.Windows.Forms.Label();
            this.lblLocalDirectory = new System.Windows.Forms.Label();
            this.btnStop = new System.Windows.Forms.Button();
            this.btnDelete = new System.Windows.Forms.Button();
            this.btnUpload = new System.Windows.Forms.Button();
            this.btnDownload = new System.Windows.Forms.Button();
            this.rbBin = new System.Windows.Forms.RadioButton();
            this.rbAsc = new System.Windows.Forms.RadioButton();
            this.btnRefresh = new System.Windows.Forms.Button();
            this.btnMakeDir = new System.Windows.Forms.Button();
            this.cbDrives = new System.Windows.Forms.ComboBox();
            this.btnConnect = new System.Windows.Forms.Button();
            this.gbControl = new System.Windows.Forms.GroupBox();
            this.gbTransfer = new System.Windows.Forms.GroupBox();
            this.cbModeZ = new System.Windows.Forms.CheckBox();
            this.cbST = new System.Windows.Forms.CheckBox();
            this.pbSecure = new System.Windows.Forms.PictureBox();
            this.headerLeft = new System.Windows.Forms.PictureBox();
            this.headerRight = new System.Windows.Forms.PictureBox();
            this.btnErrorWarning = new System.Windows.Forms.Button();
            this.trackBar = new System.Windows.Forms.TrackBar();
            this.lblSpeed = new System.Windows.Forms.Label();
            this.headerMiddle = new System.Windows.Forms.Panel();
            this.mainStatusStrip = new System.Windows.Forms.StatusStrip();
            this.pbTransfer = new System.Windows.Forms.ToolStripProgressBar();
            this.lblProgress = new System.Windows.Forms.ToolStripStatusLabel();
            this.mainSplitContainer = new Rebex.Samples.MainForm.SplitContainer();
            this.gbControl.SuspendLayout();
            this.gbTransfer.SuspendLayout();
            ((System.ComponentModel.ISupportInitialize)(this.pbSecure)).BeginInit();
            ((System.ComponentModel.ISupportInitialize)(this.headerLeft)).BeginInit();
            ((System.ComponentModel.ISupportInitialize)(this.headerRight)).BeginInit();
            ((System.ComponentModel.ISupportInitialize)(this.trackBar)).BeginInit();
            this.headerMiddle.SuspendLayout();
            this.mainStatusStrip.SuspendLayout();
            ((System.ComponentModel.ISupportInitialize)(this.mainSplitContainer)).BeginInit();
            this.mainSplitContainer.Panel1.SuspendLayout();
            this.mainSplitContainer.Panel2.SuspendLayout();
            this.mainSplitContainer.SuspendLayout();
            this.SuspendLayout();
            // 
            // listServer
            // 
            this.listServer.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
            | System.Windows.Forms.AnchorStyles.Left)));
            this.listServer.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
            this.name,
            this.size,
            this.date});
            this.listServer.FullRowSelect = true;
            this.listServer.Location = new System.Drawing.Point(8, 80);
            this.listServer.MultiSelect = false;
            this.listServer.Name = "listServer";
            this.listServer.Size = new System.Drawing.Size(264, 282);
            this.listServer.TabIndex = 2;
            this.listServer.UseCompatibleStateImageBehavior = false;
            this.listServer.View = System.Windows.Forms.View.Details;
            this.listServer.DoubleClick += new System.EventHandler(this.listServer_DoubleClick);
            this.listServer.Enter += new System.EventHandler(this.listServer_Enter);
            this.listServer.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.listServer_KeyPress);
            // 
            // name
            // 
            this.name.Text = "Name";
            this.name.Width = 120;
            // 
            // size
            // 
            this.size.Text = "Size";
            this.size.TextAlign = System.Windows.Forms.HorizontalAlignment.Right;
            // 
            // date
            // 
            this.date.Text = "Date";
            this.date.Width = 120;
            // 
            // listLocal
            // 
            this.listLocal.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
            | System.Windows.Forms.AnchorStyles.Left)));
            this.listLocal.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
            this.columnHeader1,
            this.columnHeader2,
            this.columnHeader3});
            this.listLocal.FullRowSelect = true;
            this.listLocal.Location = new System.Drawing.Point(390, 78);
            this.listLocal.MultiSelect = false;
            this.listLocal.Name = "listLocal";
            this.listLocal.Size = new System.Drawing.Size(271, 338);
            this.listLocal.TabIndex = 25;
            this.listLocal.UseCompatibleStateImageBehavior = false;
            this.listLocal.View = System.Windows.Forms.View.Details;
            this.listLocal.DoubleClick += new System.EventHandler(this.listLocal_DoubleClick);
            this.listLocal.Enter += new System.EventHandler(this.listLocal_Enter);
            this.listLocal.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.listLocal_KeyPress);
            // 
            // columnHeader1
            // 
            this.columnHeader1.Text = "Name";
            this.columnHeader1.Width = 120;
            // 
            // columnHeader2
            // 
            this.columnHeader2.Text = "Size";
            this.columnHeader2.TextAlign = System.Windows.Forms.HorizontalAlignment.Right;
            // 
            // columnHeader3
            // 
            this.columnHeader3.Text = "Date";
            this.columnHeader3.Width = 120;
            // 
            // tbLog
            // 
            this.tbLog.AccessibleRole = System.Windows.Forms.AccessibleRole.None;
            this.tbLog.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
            | System.Windows.Forms.AnchorStyles.Left) 
            | System.Windows.Forms.AnchorStyles.Right)));
            this.tbLog.BackColor = System.Drawing.SystemColors.Control;
            this.tbLog.Cursor = System.Windows.Forms.Cursors.IBeam;
            this.tbLog.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
            this.tbLog.Location = new System.Drawing.Point(0, 0);
            this.tbLog.MaxLength = 1000000;
            this.tbLog.Name = "tbLog";
            this.tbLog.ReadOnly = true;
            this.tbLog.Size = new System.Drawing.Size(650, 94);
            this.tbLog.TabIndex = 8;
            this.tbLog.Text = "";
            this.tbLog.WordWrap = false;
            // 
            // lblServer
            // 
            this.lblServer.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
            this.lblServer.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
            this.lblServer.Location = new System.Drawing.Point(12, 368);
            this.lblServer.Name = "lblServer";
            this.lblServer.Size = new System.Drawing.Size(167, 13);
            this.lblServer.TabIndex = 9;
            this.lblServer.Text = "12345";
            // 
            // lblServerDirectory
            // 
            this.lblServerDirectory.BackColor = System.Drawing.SystemColors.InactiveCaption;
            this.lblServerDirectory.FlatStyle = System.Windows.Forms.FlatStyle.Popup;
            this.lblServerDirectory.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
            this.lblServerDirectory.ForeColor = System.Drawing.SystemColors.InactiveCaptionText;
            this.lblServerDirectory.Location = new System.Drawing.Point(8, 64);
            this.lblServerDirectory.Name = "lblServerDirectory";
            this.lblServerDirectory.Size = new System.Drawing.Size(264, 16);
            this.lblServerDirectory.TabIndex = 1;
            this.lblServerDirectory.Text = "/root";
            this.lblServerDirectory.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
            this.lblServerDirectory.Click += new System.EventHandler(this.listServer_Enter);
            // 
            // lblLocal
            // 
            this.lblLocal.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
            this.lblLocal.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
            this.lblLocal.Location = new System.Drawing.Point(390, 419);
            this.lblLocal.Name = "lblLocal";
            this.lblLocal.Size = new System.Drawing.Size(271, 13);
            this.lblLocal.TabIndex = 12;
            this.lblLocal.Text = "12345";
            // 
            // lblLocalDirectory
            // 
            this.lblLocalDirectory.BackColor = System.Drawing.SystemColors.InactiveCaption;
            this.lblLocalDirectory.FlatStyle = System.Windows.Forms.FlatStyle.Popup;
            this.lblLocalDirectory.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
            this.lblLocalDirectory.ForeColor = System.Drawing.SystemColors.InactiveCaptionText;
            this.lblLocalDirectory.Location = new System.Drawing.Point(390, 62);
            this.lblLocalDirectory.Name = "lblLocalDirectory";
            this.lblLocalDirectory.Size = new System.Drawing.Size(271, 16);
            this.lblLocalDirectory.TabIndex = 23;
            this.lblLocalDirectory.Text = "/root";
            this.lblLocalDirectory.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
            this.lblLocalDirectory.Click += new System.EventHandler(this.listLocal_Enter);
            // 
            // btnStop
            // 
            this.btnStop.FlatStyle = System.Windows.Forms.FlatStyle.System;
            this.btnStop.Location = new System.Drawing.Point(16, 221);
            this.btnStop.Name = "btnStop";
            this.btnStop.Size = new System.Drawing.Size(75, 23);
            this.btnStop.TabIndex = 17;
            this.btnStop.Text = "Abort";
            this.btnStop.Visible = false;
            this.btnStop.Click += new System.EventHandler(this.btnStop_Click);
            // 
            // btnDelete
            // 
            this.btnDelete.FlatStyle = System.Windows.Forms.FlatStyle.System;
            this.btnDelete.Location = new System.Drawing.Point(16, 125);
            this.btnDelete.Name = "btnDelete";
            this.btnDelete.Size = new System.Drawing.Size(75, 23);
            this.btnDelete.TabIndex = 11;
            this.btnDelete.Text = "Delete";
            this.btnDelete.Click += new System.EventHandler(this.btnDelete_Click);
            // 
            // btnUpload
            // 
            this.btnUpload.FlatStyle = System.Windows.Forms.FlatStyle.System;
            this.btnUpload.Location = new System.Drawing.Point(16, 194);
            this.btnUpload.Name = "btnUpload";
            this.btnUpload.Size = new System.Drawing.Size(75, 23);
            this.btnUpload.TabIndex = 15;
            this.btnUpload.Text = "< Upload";
            this.btnUpload.Click += new System.EventHandler(this.btnUpload_Click);
            // 
            // btnDownload
            // 
            this.btnDownload.FlatStyle = System.Windows.Forms.FlatStyle.System;
            this.btnDownload.Location = new System.Drawing.Point(16, 167);
            this.btnDownload.Name = "btnDownload";
            this.btnDownload.Size = new System.Drawing.Size(75, 23);
            this.btnDownload.TabIndex = 13;
            this.btnDownload.Text = "Download >";
            this.btnDownload.Click += new System.EventHandler(this.btnDownload_Click);
            // 
            // rbBin
            // 
            this.rbBin.FlatStyle = System.Windows.Forms.FlatStyle.System;
            this.rbBin.Location = new System.Drawing.Point(16, 16);
            this.rbBin.Name = "rbBin";
            this.rbBin.Size = new System.Drawing.Size(65, 20);
            this.rbBin.TabIndex = 19;
            this.rbBin.Text = "Binary";
            // 
            // rbAsc
            // 
            this.rbAsc.FlatStyle = System.Windows.Forms.FlatStyle.System;
            this.rbAsc.Location = new System.Drawing.Point(16, 37);
            this.rbAsc.Name = "rbAsc";
            this.rbAsc.Size = new System.Drawing.Size(68, 20);
            this.rbAsc.TabIndex = 21;
            this.rbAsc.Text = "ASCII";
            // 
            // btnRefresh
            // 
            this.btnRefresh.FlatStyle = System.Windows.Forms.FlatStyle.System;
            this.btnRefresh.Location = new System.Drawing.Point(16, 71);
            this.btnRefresh.Name = "btnRefresh";
            this.btnRefresh.Size = new System.Drawing.Size(75, 23);
            this.btnRefresh.TabIndex = 5;
            this.btnRefresh.Text = "Refresh";
            this.btnRefresh.Click += new System.EventHandler(this.btnRefresh_Click);
            // 
            // btnMakeDir
            // 
            this.btnMakeDir.FlatStyle = System.Windows.Forms.FlatStyle.System;
            this.btnMakeDir.Location = new System.Drawing.Point(16, 98);
            this.btnMakeDir.Name = "btnMakeDir";
            this.btnMakeDir.Size = new System.Drawing.Size(75, 23);
            this.btnMakeDir.TabIndex = 7;
            this.btnMakeDir.Text = "Create folder";
            this.btnMakeDir.Click += new System.EventHandler(this.btnMakeDir_Click);
            // 
            // cbDrives
            // 
            this.cbDrives.ItemHeight = 13;
            this.cbDrives.Location = new System.Drawing.Point(17, 40);
            this.cbDrives.Name = "cbDrives";
            this.cbDrives.Size = new System.Drawing.Size(73, 21);
            this.cbDrives.TabIndex = 4;
            this.cbDrives.SelectedIndexChanged += new System.EventHandler(this.cbDrives_SelectedIndexChanged);
            // 
            // btnConnect
            // 
            this.btnConnect.FlatStyle = System.Windows.Forms.FlatStyle.System;
            this.btnConnect.Location = new System.Drawing.Point(16, 16);
            this.btnConnect.Name = "btnConnect";
            this.btnConnect.Size = new System.Drawing.Size(75, 23);
            this.btnConnect.TabIndex = 3;
            this.btnConnect.Text = "Connect...";
            this.btnConnect.Click += new System.EventHandler(this.btnConnect_Click);
            // 
            // gbControl
            // 
            this.gbControl.Anchor = System.Windows.Forms.AnchorStyles.Top;
            this.gbControl.Controls.Add(this.btnStop);
            this.gbControl.Controls.Add(this.btnDelete);
            this.gbControl.Controls.Add(this.btnUpload);
            this.gbControl.Controls.Add(this.btnDownload);
            this.gbControl.Controls.Add(this.btnRefresh);
            this.gbControl.Controls.Add(this.btnMakeDir);
            this.gbControl.Controls.Add(this.cbDrives);
            this.gbControl.Controls.Add(this.btnConnect);
            this.gbControl.FlatStyle = System.Windows.Forms.FlatStyle.System;
            this.gbControl.Location = new System.Drawing.Point(278, 64);
            this.gbControl.Name = "gbControl";
            this.gbControl.Size = new System.Drawing.Size(106, 250);
            this.gbControl.TabIndex = 25;
            this.gbControl.TabStop = false;
            this.gbControl.Text = "Controls";
            // 
            // gbTransfer
            // 
            this.gbTransfer.Anchor = System.Windows.Forms.AnchorStyles.Top;
            this.gbTransfer.Controls.Add(this.rbBin);
            this.gbTransfer.Controls.Add(this.rbAsc);
            this.gbTransfer.Controls.Add(this.cbModeZ);
            this.gbTransfer.Controls.Add(this.cbST);
            this.gbTransfer.FlatStyle = System.Windows.Forms.FlatStyle.System;
            this.gbTransfer.Location = new System.Drawing.Point(278, 320);
            this.gbTransfer.Name = "gbTransfer";
            this.gbTransfer.Size = new System.Drawing.Size(106, 106);
            this.gbTransfer.TabIndex = 29;
            this.gbTransfer.TabStop = false;
            this.gbTransfer.Text = "Transfer";
            // 
            // cbModeZ
            // 
            this.cbModeZ.Location = new System.Drawing.Point(16, 56);
            this.cbModeZ.Name = "cbModeZ";
            this.cbModeZ.Size = new System.Drawing.Size(68, 24);
            this.cbModeZ.TabIndex = 29;
            this.cbModeZ.Text = "Mode Z";
            this.cbModeZ.CheckedChanged += new System.EventHandler(this.cbModeZ_CheckedChanged);
            // 
            // cbST
            // 
            this.cbST.Location = new System.Drawing.Point(16, 77);
            this.cbST.Name = "cbST";
            this.cbST.Size = new System.Drawing.Size(68, 24);
            this.cbST.TabIndex = 30;
            this.cbST.Text = "Secure";
            // 
            // pbSecure
            // 
            this.pbSecure.Image = ((System.Drawing.Image)(resources.GetObject("pbSecure.Image")));
            this.pbSecure.Location = new System.Drawing.Point(24, 16);
            this.pbSecure.Name = "pbSecure";
            this.pbSecure.Size = new System.Drawing.Size(32, 32);
            this.pbSecure.TabIndex = 29;
            this.pbSecure.TabStop = false;
            this.pbSecure.Visible = false;
            // 
            // headerLeft
            // 
            this.headerLeft.BackColor = System.Drawing.Color.White;
            this.headerLeft.Image = ((System.Drawing.Image)(resources.GetObject("headerLeft.Image")));
            this.headerLeft.Location = new System.Drawing.Point(0, 0);
            this.headerLeft.Margin = new System.Windows.Forms.Padding(0);
            this.headerLeft.Name = "headerLeft";
            this.headerLeft.Size = new System.Drawing.Size(472, 59);
            this.headerLeft.TabIndex = 26;
            this.headerLeft.TabStop = false;
            // 
            // headerRight
            // 
            this.headerRight.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
            this.headerRight.BackColor = System.Drawing.Color.White;
            this.headerRight.Cursor = System.Windows.Forms.Cursors.Hand;
            this.headerRight.Image = ((System.Drawing.Image)(resources.GetObject("headerRight.Image")));
            this.headerRight.Location = new System.Drawing.Point(536, 0);
            this.headerRight.Margin = new System.Windows.Forms.Padding(0);
            this.headerRight.Name = "headerRight";
            this.headerRight.Size = new System.Drawing.Size(136, 59);
            this.headerRight.TabIndex = 28;
            this.headerRight.TabStop = false;
            this.headerRight.Click += new System.EventHandler(this.headerRight_Click);
            // 
            // btnErrorWarning
            // 
            this.btnErrorWarning.Dock = System.Windows.Forms.DockStyle.Right;
            this.btnErrorWarning.FlatStyle = System.Windows.Forms.FlatStyle.Popup;
            this.btnErrorWarning.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
            this.btnErrorWarning.Location = new System.Drawing.Point(652, 0);
            this.btnErrorWarning.Name = "btnErrorWarning";
            this.btnErrorWarning.Size = new System.Drawing.Size(21, 92);
            this.btnErrorWarning.TabIndex = 29;
            this.btnErrorWarning.Text = "!";
            this.btnErrorWarning.Click += new System.EventHandler(this.btnErrorWarning_Click);
            // 
            // trackBar
            // 
            this.trackBar.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
            this.trackBar.LargeChange = 10;
            this.trackBar.Location = new System.Drawing.Point(2, 387);
            this.trackBar.Maximum = 100;
            this.trackBar.Minimum = 1;
            this.trackBar.Name = "trackBar";
            this.trackBar.Size = new System.Drawing.Size(273, 45);
            this.trackBar.SmallChange = 5;
            this.trackBar.TabIndex = 30;
            this.trackBar.TickFrequency = 5;
            this.trackBar.Value = 100;
            this.trackBar.Scroll += new System.EventHandler(this.trackBar_Scroll);
            // 
            // lblSpeed
            // 
            this.lblSpeed.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
            this.lblSpeed.Location = new System.Drawing.Point(185, 368);
            this.lblSpeed.Name = "lblSpeed";
            this.lblSpeed.Size = new System.Drawing.Size(90, 13);
            this.lblSpeed.TabIndex = 0;
            this.lblSpeed.Text = "Speed: Unlimited";
            // 
            // headerMiddle
            // 
            this.headerMiddle.BackColor = System.Drawing.Color.White;
            this.headerMiddle.BackgroundImage = ((System.Drawing.Image)(resources.GetObject("headerMiddle.BackgroundImage")));
            this.headerMiddle.Controls.Add(this.pbSecure);
            this.headerMiddle.Location = new System.Drawing.Point(472, 0);
            this.headerMiddle.Name = "headerMiddle";
            this.headerMiddle.Size = new System.Drawing.Size(64, 59);
            this.headerMiddle.TabIndex = 27;
            // 
            // mainStatusStrip
            // 
            this.mainStatusStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
            this.pbTransfer,
            this.lblProgress});
            this.mainStatusStrip.Location = new System.Drawing.Point(0, 538);
            this.mainStatusStrip.Name = "mainStatusStrip";
            this.mainStatusStrip.Size = new System.Drawing.Size(673, 22);
            this.mainStatusStrip.TabIndex = 31;
            this.mainStatusStrip.Text = "statusStrip1";
            // 
            // pbTransfer
            // 
            this.pbTransfer.Name = "pbTransfer";
            this.pbTransfer.Size = new System.Drawing.Size(160, 16);
            // 
            // lblProgress
            // 
            this.lblProgress.Name = "lblProgress";
            this.lblProgress.Size = new System.Drawing.Size(60, 17);
            this.lblProgress.Text = "Welcome!";
            // 
            // mainSplitContainer
            // 
            this.mainSplitContainer.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
            | System.Windows.Forms.AnchorStyles.Left) 
            | System.Windows.Forms.AnchorStyles.Right)));
            this.mainSplitContainer.Location = new System.Drawing.Point(0, 0);
            this.mainSplitContainer.Name = "mainSplitContainer";
            this.mainSplitContainer.Orientation = System.Windows.Forms.Orientation.Horizontal;
            // 
            // mainSplitContainer.Panel1
            // 
            this.mainSplitContainer.Panel1.Controls.Add(this.lblSpeed);
            this.mainSplitContainer.Panel1.Controls.Add(this.lblLocal);
            this.mainSplitContainer.Panel1.Controls.Add(this.listLocal);
            this.mainSplitContainer.Panel1.Controls.Add(this.lblLocalDirectory);
            this.mainSplitContainer.Panel1.Controls.Add(this.gbTransfer);
            this.mainSplitContainer.Panel1.Controls.Add(this.gbControl);
            this.mainSplitContainer.Panel1.Controls.Add(this.trackBar);
            this.mainSplitContainer.Panel1.Controls.Add(this.lblServer);
            this.mainSplitContainer.Panel1.Controls.Add(this.listServer);
            this.mainSplitContainer.Panel1.Controls.Add(this.lblServerDirectory);
            this.mainSplitContainer.Panel1.Controls.Add(this.headerLeft);
            this.mainSplitContainer.Panel1.Controls.Add(this.headerMiddle);
            this.mainSplitContainer.Panel1.Controls.Add(this.headerRight);
            this.mainSplitContainer.Panel1MinSize = 430;
            // 
            // mainSplitContainer.Panel2
            // 
            this.mainSplitContainer.Panel2.Controls.Add(this.btnErrorWarning);
            this.mainSplitContainer.Panel2.Controls.Add(this.tbLog);
            this.mainSplitContainer.Panel2MinSize = 30;
            this.mainSplitContainer.Size = new System.Drawing.Size(673, 535);
            this.mainSplitContainer.SplitterDistance = 439;
            this.mainSplitContainer.TabIndex = 32;
            // 
            // MainForm
            // 
            this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
            this.ClientSize = new System.Drawing.Size(673, 560);
            this.Controls.Add(this.mainSplitContainer);
            this.Controls.Add(this.mainStatusStrip);
            this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
            this.KeyPreview = true;
            this.MinimumSize = new System.Drawing.Size(664, 590);
            this.Name = "MainForm";
            this.Text = "Simple WinForm FTP Client";
            this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.MainForm_Closing);
            this.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.MainForm_KeyPress);
            this.Resize += new System.EventHandler(this.MainForm_Resize);
            this.gbControl.ResumeLayout(false);
            this.gbTransfer.ResumeLayout(false);
            ((System.ComponentModel.ISupportInitialize)(this.pbSecure)).EndInit();
            ((System.ComponentModel.ISupportInitialize)(this.headerLeft)).EndInit();
            ((System.ComponentModel.ISupportInitialize)(this.headerRight)).EndInit();
            ((System.ComponentModel.ISupportInitialize)(this.trackBar)).EndInit();
            this.headerMiddle.ResumeLayout(false);
            this.mainStatusStrip.ResumeLayout(false);
            this.mainStatusStrip.PerformLayout();
            this.mainSplitContainer.Panel1.ResumeLayout(false);
            this.mainSplitContainer.Panel1.PerformLayout();
            this.mainSplitContainer.Panel2.ResumeLayout(false);
            ((System.ComponentModel.ISupportInitialize)(this.mainSplitContainer)).EndInit();
            this.mainSplitContainer.ResumeLayout(false);
            this.ResumeLayout(false);
            this.PerformLayout();

        }
        #endregion

        /// <summary>
        /// win form client starts here
        /// </summary>
        [STAThread]
        static void Main()
        {
            // set the trial license key
            Rebex.Licensing.Key = Rebex.Samples.TrialKey.Key;


            Application.Run(new MainForm());
        }

        /// <summary>
        /// connect/disconnect ftp server
        /// </summary>
        private void btnConnect_Click(object sender, System.EventArgs e)
        {
            if (_ftp != null)
            {
                if (_isWorking)
                    return;

                // disconnect

                trackBar.Enabled = false;
                _ftp.Disconnect();
                _ftp.Dispose();
                _ftp = null;
                btnConnect.Text = "Connect...";
                MakeFtpList(); // clearing
                return;
            }

            // connection dialog
            ConnectionFormData connectionData = new ConnectionFormData(FileTransferComponent.Ftp);
            connectionData.LoadConfig();

            ConnectionForm con = new ConnectionForm(connectionData);

            // show connection dialog
            con.ShowDialog();

            // not confirmed
            if (con.DialogResult != DialogResult.OK)
                return;

            // save confirmed data
            connectionData.SaveConfig();

            // create ftp object
            _ftp = new Ftp();

            // assign the logger
            _ftp.LogWriter = new RichTextBoxLogWriter(tbLog, tbLog.MaxLength, con.Data.LogLevel);

            // set event handlers
            _ftp.StateChanged += StateChanged;
            _ftp.Traversing += Traversing;
            _ftp.TransferProgressChanged += TransferProgressChanged;
            _ftp.DeleteProgressChanged += DeleteProgressChanged;
            _ftp.ProblemDetected += ProblemDetected;

            try
            {
                _ftp.Passive = con.Data.PassiveMode;
                _ftp.Settings.UseLargeBuffers = true;
                _ftp.Timeout = 30000;

                // proxy

                if (con.Data.ProxyType != FileTransferProxyType.None)
                {
                    _ftp.Proxy = new FtpProxy((FtpProxyType)con.Data.ProxyType, con.Data.ProxyHost, con.Data.ProxyPort);
                    if (con.Data.ProxyUser.Length > 0)
                        _ftp.Proxy.Credentials = new System.Net.NetworkCredential(con.Data.ProxyUser, con.Data.ProxyPassword);
                }

                SslMode securityMode = ConvertToSslMode(con.Data.FileTransferMode);
                if (securityMode != SslMode.None)
                {
                    _ftp.ValidatingCertificate += Verifier.ValidatingCertificate;

                    if (con.Data.ClientCertificateFilename != null && con.Data.ClientCertificateFilename.Trim() != string.Empty)
                    {
                        PassphraseDialog pp = new PassphraseDialog();
                        if (pp.ShowDialog() == DialogResult.OK)
                        {
                            CertificateChain chain = CertificateChain.LoadPfx(con.Data.ClientCertificateFilename, pp.Passphrase);
                            _ftp.Settings.SslClientCertificateRequestHandler = CertificateRequestHandler.CreateRequestHandler(chain);
                        }
                        else
                        {
                            throw new ApplicationException("Passphrase wasn't entered.");
                        }
                    }
                    else
                    {
                        _ftp.Settings.SslClientCertificateRequestHandler = new RequestHandler();
                    }

                    _ftp.SecureTransfers = cbST.Checked;
                }

                _ftp.Settings.SslAllowedSuites = con.Data.AllowedSuite;
                _ftp.Settings.SslAllowedVersions = con.Data.TlsProtocol;

                // connect

                btnStop.Visible = true;
                _isWorking = true;

                try
                {
                    IAsyncResult ar = _ftp.BeginConnect(con.Data.ServerHost, con.Data.ServerPort, securityMode, null, null);
                    while (!ar.IsCompleted)
                    {
                        Application.DoEvents();
                        System.Threading.Thread.Sleep(1);
                    }
                    _ftp.EndConnect(ar);
                }
                finally
                {
                    btnStop.Visible = false;
                    _isWorking = false;
                }
                if (securityMode == SslMode.None)
                {
                    cbST.Checked = false;
                    cbST.Enabled = false;
                    pbSecure.Visible = false;
                }
                else
                {
                    cbST.Checked = true;
                    cbST.Enabled = true;
                    pbSecure.Visible = true;
                }
                // login
                if (!_ftp.IsAuthenticated)
                    _ftp.Login(con.Data.ServerUser, con.Data.ServerPassword);

                if (cbModeZ.Checked)
                    _ftp.TransferMode = FtpTransferMode.Zlib;

                // ccc
                if (con.Data.ClearCommandChannel && securityMode != SslMode.None)
                {
                    try
                    {
                        _ftp.SecureTransfers = true;
                        _ftp.ClearCommandChannel();
                        cbST.Checked = true;
                        cbST.Enabled = false;
                        _cccMode = true;
                    }
                    catch (Exception ex)
                    {
                        WriteToLog(ex);
                    }
                }
                trackBar.Enabled = true;
                btnErrorWarning.Visible = false;
                btnConnect.Text = "Disconnect";
                MakeFtpList();
            }
            catch (Exception ex)
            {
                WriteToLog(ex);
                if (_ftp != null)
                {
                    _ftp.Dispose();
                    _ftp = null;
                }
                return;
            }
            finally
            {
                _isWorking = false;
            }
        }

        /// <summary>
        /// disconnect on exit
        /// </summary>
        private void MainForm_Closing(object sender, System.Windows.Forms.FormClosingEventArgs e)
        {
            if (_ftp != null)
                _ftp.Dispose();
        }

        /// <summary>
        /// CWD or GET file command on server side
        /// </summary>
        private void ListServerClick()
        {
            try
            {
                if (listServer.Items.Count < 1)
                    return;

                if (listServer.SelectedItems.Count < 1)
                    listServer.Items[0].Selected = true;

                listServer.Focus();

                // ftp state test

                if (_isWorking)
                    return;

                int index = listServer.SelectedIndices[0];
                ListViewItem item = listServer.SelectedItems[0];

                if (index >= _serverDirs)
                {
                    // GET file

                    DownloadFiles(item.Text, _localPath);
                }
                else
                {
                    // CWD

                    string directory;
                    if (index == 0)
                        directory = "..";
                    else
                        directory = item.Text;

                    _ftp.ChangeDirectory(directory);

                    MakeFtpList();
                }
            }
            catch (Exception ex)
            {
                WriteToLog(ex);
            }
        }

        private void listServer_KeyPress(object sender, System.Windows.Forms.KeyPressEventArgs e)
        {
            if (e.KeyChar == '\r')
            {
                ListServerClick();
                listServer.Focus();
            }
        }

        private void listServer_DoubleClick(object sender, System.EventArgs e)
        {
            ListServerClick();
            listServer.Focus();
        }

        /// <summary>
        /// change drive letter
        /// </summary>
        private void cbDrives_SelectedIndexChanged(object sender, System.EventArgs e)
        {
            try
            {
                _localPath = cbDrives.SelectedItem.ToString();
                MakeLocalList();
            }
            catch (Exception ex)
            {
                WriteToLog(ex);
                listLocal.Items.Clear();
                lblLocalDirectory.Text = lblLocal.Text = null;
            }
        }

        /// <summary>
        /// LCD or PUT command on local side
        /// </summary>
        private void ListLocalClick()
        {
            try
            {
                if (listLocal.Items.Count < 1)
                    return;

                if (listLocal.SelectedItems.Count < 1)
                    listLocal.Items[0].Selected = true;

                listLocal.Focus();

                int index = listLocal.SelectedIndices[0];
                ListViewItem item = listLocal.SelectedItems[0];

                if (index >= _localDirs)
                {
                    // upload file

                    UploadFiles(item.Text, _localPath);
                }
                else
                {
                    // local change directory

                    _localPath = Path.Combine(_localPath, item.Text);
                    MakeLocalList();
                }
            }
            catch (Exception ex)
            {
                WriteToLog(ex);
            }
        }

        private void listLocal_KeyPress(object sender, System.Windows.Forms.KeyPressEventArgs e)
        {
            if (e.KeyChar == '\r')
            {
                ListLocalClick();
                if (listLocal.Items.Count > 0)
                {
                    listLocal.Focus();
                }
            }
        }

        private void listLocal_DoubleClick(object sender, System.EventArgs e)
        {
            ListLocalClick();
            listLocal.Focus();
        }

        /// <summary>
        /// make dir
        /// </summary>
        private void btnMakeDir_Click(object sender, System.EventArgs e)
        {
            try
            {
                MakeDir dir = new MakeDir();

                // FTP: MKDIR

                if (listActive == listServer)
                {
                    if (_isWorking)
                        return;

                    dir.ShowDialog();
                    if (dir.Directory != null)
                    {
                        _ftp.CreateDirectory(dir.Directory);
                        MakeFtpList();
                    }
                }
                else
                {
                    // LOCAL: MKDIR

                    dir.ShowDialog();

                    if (dir.Directory != null)
                    {
                        Directory.CreateDirectory(Path.Combine(_localPath, dir.Directory));
                        MakeLocalList();
                    }
                }
            }
            catch (Exception ex)
            {
                WriteToLog(ex);
            }
        }

        /// <summary>
        /// refresh listing
        /// </summary>
        private void btnRefresh_Click(object sender, System.EventArgs e)
        {
            try
            {
                if (listActive == listServer)
                {
                    if (_isWorking)
                        return;

                    MakeFtpList();
                }
                else
                {
                    MakeLocalList();
                }
            }
            catch (Exception ex)
            {
                WriteToLog(ex);
            }
        }

        /// <summary>
        /// download file or directory tree
        /// </summary>
        /// <param name="fileName">remote file name</param>
        /// <param name="localPath">local path</param>
        private void DownloadFiles(string fileName, string localPath)
        {
            // ftp state test

            if (_isWorking)
                return;

            // secure transfers

            if (cbST.Enabled && !_cccMode)
                _ftp.SecureTransfers = cbST.Checked;

            // set ascii/binary transfers

            if (rbBin.Checked)
                _ftp.TransferType = FtpTransferType.Binary;
            else
                _ftp.TransferType = FtpTransferType.Ascii;

            // transfer

            try
            {
                _isWorking = true;
                btnStop.Visible = true;
                _problemForm.Initialize();
                _transferTime = DateTime.Now;

                _ftp.BeginDownload(fileName, localPath, TraversalMode.Recursive, TransferMethod.Copy, ActionOnExistingFiles.ThrowException,
                    new AsyncCallback(DownloadCallback), null);
            }
            catch (Exception ex)
            {
                btnStop.Visible = false;
                _isWorking = false;
                WriteToLog(ex);
            }
        }

        /// <summary>
        /// upload file or directory tree
        /// </summary>
        /// <param name="fileName">local file name</param>
        /// <param name="localPath">local path</param>
        private void UploadFiles(string fileName, string localPath)
        {
            localPath = Path.Combine(localPath, fileName);

            // ftp state test
            if (_ftp == null)
            {
                WriteToLog("Not connected!", Color.DarkOrange);
                return;
            }

            if (_isWorking)
                return;

            // secure transfers

            if (cbST.Enabled && !_cccMode)
                _ftp.SecureTransfers = cbST.Checked;

            // set asc/bin transfers

            if (rbBin.Checked)
                _ftp.TransferType = FtpTransferType.Binary;
            else
                _ftp.TransferType = FtpTransferType.Ascii;

            // transfer

            try
            {
                _isWorking = true;
                btnStop.Visible = true;
                _problemForm.Initialize();
                _transferTime = DateTime.Now;
                _ftp.BeginUpload(localPath, ".", TraversalMode.Recursive, TransferMethod.Copy, ActionOnExistingFiles.ThrowException,
                    new AsyncCallback(UploadCallback), null);
            }
            catch (Exception ex)
            {
                btnStop.Visible = false;
                _isWorking = false;
                WriteToLog(ex);
            }
        }
        /// <summary>
        /// callback for download finished
        /// </summary>
        public void DownloadCallback(IAsyncResult asyncResult)
        {
            try
            {
                _ftp.EndDownload(asyncResult);
                TransferFinished(null, false);
            }
            catch (Exception ex)
            {
                TransferFinished(ex, false);
            }
        }

        /// <summary>
        /// callback for upload finished
        /// </summary>
        public void UploadCallback(IAsyncResult asyncResult)
        {
            try
            {
                _ftp.EndUpload(asyncResult);
                TransferFinished(null, true);
            }
            catch (Exception ex)
            {
                TransferFinished(ex, true);
            }
        }		


        private delegate void TransferFinishedDelegate(Exception error, bool refreshFtp);

        private void TransferFinished(Exception error, bool refreshFtp)
        {
            if (this.InvokeRequired)
            {
                SafeInvoke(new TransferFinishedDelegate(TransferFinished), error, refreshFtp);
                return;
            }

            if (error != null)
            {
                WriteToLog(error);
            }

            

            _isWorking = false;
            SetProgressValue(0);
            btnStop.Visible = false;

            try
            {
                if (refreshFtp)
                    MakeFtpList();
                else
                    MakeLocalList();
            }
            catch (Exception ex)
            {
                WriteToLog(ex);
            }
        }

        /// <summary>
        /// show transfer status: files, bytes, time, speed
        /// </summary>
        private void ShowTransferStatus(long bytesTransferred, int filesTransferred)
        {
            // unknown bytes transferred
            if (bytesTransferred == 0)
                return;

            // files and bytes transferred
            string outstring = string.Format("{0} file{1} ({2} byte{3}) transferred in",
                filesTransferred, (filesTransferred > 1 ? "s" : null),
                bytesTransferred, (bytesTransferred > 1 ? "s" : null));

            // time spent
            TimeSpan ts = DateTime.Now - _transferTime;

            // speed
            if (ts.TotalSeconds > 1)
            {
                outstring += (ts.Days > 0 ? " " + ts.Days + " day" + (ts.Days > 1 ? "s" : null) : null);
                outstring += (ts.Hours > 0 ? " " + ts.Hours + " hour" + (ts.Hours > 1 ? "s" : null) : null);
                outstring += (ts.Minutes > 0 ? " " + ts.Minutes + " min" + (ts.Minutes > 1 ? "s" : null) : null);
                outstring += (ts.Seconds > 0 ? " " + ts.Seconds + " sec" + (ts.Seconds > 1 ? "s" : null) : null);
            }
            else
            {
                outstring += " " + ts.TotalSeconds + " sec";
            }

            double speed = bytesTransferred / ts.TotalSeconds;
            if (speed < 1)
                outstring += string.Format(" at {0:F3} B/s", speed);
            else if (speed < 1024)
                outstring += string.Format(" at {0:F0} B/s", speed);
            else
                outstring += string.Format(" at {0:F0} KB/s", speed / 1024);


            WriteToLog("> " + outstring, RichTextBoxLogWriter.COLORCOMMAND);
        }

        /// <summary>
        /// handle download button click
        /// </summary>
        private void btnDownload_Click(object sender, System.EventArgs e)
        {
            try
            {
                if (listActive != listServer)
                    return;

                if (_isWorking)
                    return;

                if (listServer.SelectedItems.Count > 0)
                    DownloadFiles(listServer.SelectedItems[0].Text, _localPath);
            }
            catch (Exception ex)
            {
                WriteToLog(ex);
            }
        }

        /// <summary>
        /// handle upload button click
        /// </summary>
        private void btnUpload_Click(object sender, System.EventArgs e)
        {
            try
            {
                if (listActive == listServer)
                    return;

                if (_isWorking)
                    return;

                if (listLocal.SelectedItems.Count > 0)
                    UploadFiles(listLocal.SelectedItems[0].Text, _localPath);
            }
            catch (Exception ex)
            {
                WriteToLog(ex);
            }
        }

        /// <summary>
        /// handle delete button click
        /// </summary>
        private void btnDelete_Click(object sender, System.EventArgs e)
        {
            try
            {
                if (listActive == listServer)
                {
                    if (_isWorking)
                        return;

                    if (listServer.SelectedItems.Count > 0)
                    {
                        int index = listServer.SelectedIndices[0];
                        ListViewItem item = listServer.SelectedItems[0];

                        if (index >= _serverDirs)
                        {
                            DeleteDialog dialog = new DeleteDialog(DeleteDialog.DialogPrompt.DeleteFile);
                            dialog.Path = item.Text;
                            dialog.ShowDialog();

                            if (dialog.OK)
                            {
                                _ftp.DeleteFile(item.Text);
                                MakeFtpList();
                            }
                        }
                        else
                        {
                            DeleteDialog dialog = new DeleteDialog(DeleteDialog.DialogPrompt.DeleteDirectory);
                            dialog.Path = item.Text;
                            dialog.ShowDialog();

                            if (dialog.OK)
                            {
                                _ftp.Delete(item.Text, TraversalMode.Recursive);
                                MakeFtpList();
                            }
                        }
                    }
                }
                else
                {
                    if (listLocal.SelectedItems.Count > 0)
                    {
                        int index = listLocal.SelectedIndices[0];
                        ListViewItem item = listLocal.SelectedItems[0];
                        string path = Path.Combine(_localPath, item.Text);

                        if (index >= _localDirs)
                        {
                            DeleteDialog dialog = new DeleteDialog(DeleteDialog.DialogPrompt.DeleteFile);
                            dialog.Path = item.Text;
                            dialog.ShowDialog();

                            if (dialog.OK)
                            {
                                File.Delete(path);
                                MakeLocalList();
                            }
                        }
                        else
                        {
                            DeleteDialog dialog = new DeleteDialog(DeleteDialog.DialogPrompt.DeleteDirectory);
                            dialog.Path = item.Text;
                            dialog.ShowDialog();

                            if (dialog.OK)
                            {
                                Directory.Delete(path, false);
                                MakeLocalList();
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                WriteToLog(ex);
            }
        }

        /// <summary>
        /// activate panel on focus event
        /// </summary>
        private void listServer_Enter(object sender, System.EventArgs e)
        {
            listActive = listServer;
            lblServerDirectory.BackColor = SystemColors.ActiveCaption;
            lblServerDirectory.ForeColor = SystemColors.ActiveCaptionText;
            lblLocalDirectory.BackColor = SystemColors.InactiveCaption;
            lblLocalDirectory.ForeColor = SystemColors.InactiveCaptionText;
        }

        /// <summary>
        /// activate panel on focus event
        /// </summary>
        private void listLocal_Enter(object sender, System.EventArgs e)
        {
            listActive = listLocal;
            lblServerDirectory.BackColor = SystemColors.InactiveCaption;
            lblServerDirectory.ForeColor = SystemColors.InactiveCaptionText;
            lblLocalDirectory.BackColor = SystemColors.ActiveCaption;
            lblLocalDirectory.ForeColor = SystemColors.ActiveCaptionText;
        }

        /// <summary>
        /// "smart" resizing
        /// </summary>
        private void SetRightSizeValues()
        {
            // header
            headerMiddle.Width = headerRight.Left - headerMiddle.Left;
            headerMiddle.Left = headerLeft.Right;
            headerLeft.Width = 472;

            // server directory
            listServer.Width = lblServerDirectory.Width = trackBar.Width = gbControl.Left - listServer.Left - 6;
            lblSpeed.Left = gbControl.Left - lblSpeed.Width - 6;

            // local directory
            listLocal.Bounds = new Rectangle(gbControl.Right + 6, listLocal.Top, headerRight.Right - gbControl.Right - 9, listLocal.Height);
            lblLocalDirectory.Bounds = new Rectangle(gbControl.Right + 6, lblLocalDirectory.Top, headerRight.Right - gbControl.Right - 9, lblLocalDirectory.Height);
            lblLocal.Left = listLocal.Left;

            // position of secure icon

            pbSecure.Location = new Point(headerMiddle.Width - 32, pbSecure.Top);
        }

        private void MainForm_Resize(object sender, System.EventArgs e)
        {
            if (WindowState != FormWindowState.Minimized)
                SetRightSizeValues();
        }

        /// <summary>
        /// stop transfer when something is being transferred
        /// </summary>
        private void btnStop_Click(object sender, System.EventArgs e)
        {
            _ftp.Abort();
        }

        /// <summary>
        /// go to the url
        /// </summary>
        private void headerRight_Click(object sender, System.EventArgs e)
        {
            if (Environment.OSVersion.Platform == PlatformID.Win32NT)
                System.Diagnostics.Process.Start(URL);
        }

        /// <summary>
        /// space bar for changing panels server <-> local
        /// </summary>
        private void MainForm_KeyPress(object sender, System.Windows.Forms.KeyPressEventArgs e)
        {
            if (e.KeyChar == Convert.ToChar(Keys.Space))
            {
                if (listActive == listLocal)
                    listServer.Focus();
                else
                    listLocal.Focus();
            }
        }

        private void btnErrorWarning_Click(object sender, System.EventArgs e)
        {
            MessageBox.Show(_exception, "Last occured error", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }

        private void trackBar_Scroll(object sender, EventArgs e)
        {
            if (trackBar.Value == 100)
            {
                lblSpeed.Text = "Speed: Unlimited";
                _ftp.MaxDownloadSpeed = 0;
                _ftp.MaxUploadSpeed = 0;
            }
            else
            {
                lblSpeed.Text = string.Format("Speed: {0} kB/s", trackBar.Value);
                _ftp.MaxDownloadSpeed = trackBar.Value;
                _ftp.MaxUploadSpeed = trackBar.Value;
            }
        }

        private void cbModeZ_CheckedChanged(object sender, EventArgs e)
        {
            Ftp client = _ftp;
            if (client == null)
                return;

            if (cbModeZ.Checked)
                client.TransferMode = FtpTransferMode.Zlib;
            else
                client.TransferMode = FtpTransferMode.Stream;
        }

        


        #region Mono-specific workarounds

        // Workaround for a bug in Mono 2.10 (where SplitContainer doesn't implement ISupportInitialize)
        // (this is not needed on Windows)
        private class SplitContainer : System.Windows.Forms.SplitContainer, System.ComponentModel.ISupportInitialize
        {
            void ISupportInitialize.BeginInit()
            {
            }

            void ISupportInitialize.EndInit()
            {
            }
        }

        #endregion
    }


    #region ListViewColumnSorter

    public class ListViewColumnSorter : MultiComparer, IComparer
    {
        public ListViewColumnSorter()
            : base(new FileSystemItemComparer(FileSystemItemComparerType.FileType, SortingOrder.Ascending), new FileSystemItemComparer(FileSystemItemComparerType.Name))
        {
        }

        int IComparer.Compare(object x, object y)
        {
            ListViewItem listViewX = (ListViewItem)x;
            ListViewItem listViewY = (ListViewItem)y;

            FtpItem itemX = (FtpItem)listViewX.Tag;
            FtpItem itemY = (FtpItem)listViewY.Tag;

            if (itemX == null)
            {
                if (itemY == null)
                    return 0;

                return -1;
            }
            else if (itemY == null)
            {
                return 1;
            }

            return base.Compare(itemX, itemY);
        }
    }

    #endregion
}
