//  
//   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.Text.RegularExpressions;
using System.Diagnostics;
using System.Threading;

using Rebex;
using Rebex.IO;
using Rebex.Net;
using Rebex.Samples;
using System.Threading.Tasks;

namespace Rebex.Samples
{
    /// <summary>
    /// Simple SFTP 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.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.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.StatusStrip mainStatusStrip;
        private System.Windows.Forms.ToolStripProgressBar pbTransfer;
        private System.Windows.Forms.ToolStripStatusLabel lblProgress;
        private System.Windows.Forms.SplitContainer mainSplitContainer;
        private System.Windows.Forms.TableLayoutPanel tableLayout;
        private System.Windows.Forms.Panel panelMiddle;
        private System.Windows.Forms.Panel panelLeft;
        private System.Windows.Forms.Panel panelRight;

        private Sftp _sftp;                         // SFTP 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 combobox
        private string _localPath;                  // local path
        private bool _isWorking;                    // determines whether any operation is running
        private DateTime _transferTime;             // transfer launch-time
        private ProblemDetectedForm _problemForm;   // informs the user about problems while transferring data
        private ConnectionFormData _data;

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

        public void WriteToLog(Exception x)
        {
            if (x is AggregateException)
            {
                foreach (Exception inner in ((AggregateException)x).InnerExceptions)
                {
                    WriteToLog(inner);
                }
                return;
            }

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

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

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

        /// <summary>
        /// get filename from path (xxxxx/xxxx/yyy -> yyy)
        /// </summary>
        /// <param name="path">sftp path</param>
        /// <returns>filename</returns>
        private static string GetFilenameFromPath(string path)
        {
            int idx = path.LastIndexOf("/");

            if (idx != -1 && idx + 1 != path.Length)
            {
                path = path.Substring(idx + 1);
            }

            return path;
        }

        /// <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>
        /// event displaying sftp state
        /// </summary>
        public void StateChanged(object sender, SftpStateChangedEventArgs e)
        {
            switch (e.NewState)
            {
                case SftpState.Disconnected:
                case SftpState.Disposed:
                    lblProgress.Text = "Disconnected";
                    btnConnect.Text = "Connect";
                    listServer.Items.Clear();
                    break;
                case SftpState.Ready:
                    lblProgress.Text = "Ready";
                    break;
            }
        }

        /// <summary>
        /// handles the displaying traversing event
        /// </summary>
        void Traversing(object sender, SftpTraversingEventArgs 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, SftpTransferProgressChangedEventArgs 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, SftpDeleteProgressChangedEventArgs 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, SftpProblemDetectedEventArgs 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 sftp list
        /// </summary>
        public async void MakeSftpList()
        {
            int dirs = 0;
            int files = 0;
            long size = 0;

            listServer.Items.Clear();

            // not connected?

            if (_sftp == null)
            {
                lblServerDirectory.Text = "";
                lblServer.Text = "";
                _serverDirs = 0;
                return;
            }

            // make the list

            SftpItemCollection list = null;

            try
            {
                _isWorking = true;
                btnStop.Visible = true;

                list = await _sftp.GetListAsync((string)null);

                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 = _sftp.GetCurrentDirectory();

            _serverDirs = dirs;
        }

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

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

            trackBar.Enabled = false;
            lblServer.Text = "";
            listLocal.Focus();
            btnConnect.Select();

            MakeLocalList();
            _problemForm = new ProblemDetectedForm();
        }

        /// <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 = new System.Windows.Forms.ColumnHeader();
            this.size = new System.Windows.Forms.ColumnHeader();
            this.date = new System.Windows.Forms.ColumnHeader();
            this.listLocal = new System.Windows.Forms.ListView();
            this.columnHeader1 = new System.Windows.Forms.ColumnHeader();
            this.columnHeader2 = new System.Windows.Forms.ColumnHeader();
            this.columnHeader3 = 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.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.headerLeft = new System.Windows.Forms.PictureBox();
            this.headerMiddle = new System.Windows.Forms.Panel();
            this.headerRight = new System.Windows.Forms.PictureBox();
            this.trackBar = new System.Windows.Forms.TrackBar();
            this.lblSpeed = new System.Windows.Forms.Label();
            this.mainStatusStrip = new System.Windows.Forms.StatusStrip();
            this.pbTransfer = new System.Windows.Forms.ToolStripProgressBar();
            this.lblProgress = new System.Windows.Forms.ToolStripStatusLabel();
            this.mainSplitContainer = new System.Windows.Forms.SplitContainer();
            this.tableLayout = new System.Windows.Forms.TableLayoutPanel();
            this.panelRight = new System.Windows.Forms.Panel();
            this.panelMiddle = new System.Windows.Forms.Panel();
            this.panelLeft = new System.Windows.Forms.Panel();
            this.gbControl.SuspendLayout();
            ((System.ComponentModel.ISupportInitialize)(this.headerLeft)).BeginInit();
            ((System.ComponentModel.ISupportInitialize)(this.headerRight)).BeginInit();
            ((System.ComponentModel.ISupportInitialize)(this.trackBar)).BeginInit();
            this.mainStatusStrip.SuspendLayout();
            ((System.ComponentModel.ISupportInitialize)(this.mainSplitContainer)).BeginInit();
            this.mainSplitContainer.Panel1.SuspendLayout();
            this.mainSplitContainer.Panel2.SuspendLayout();
            this.mainSplitContainer.SuspendLayout();
            this.tableLayout.SuspendLayout();
            this.panelRight.SuspendLayout();
            this.panelMiddle.SuspendLayout();
            this.panelLeft.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) 
            | System.Windows.Forms.AnchorStyles.Right)));
            this.listServer.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
            this.name,
            this.size,
            this.date});
            this.listServer.FullRowSelect = true;
            this.listServer.HideSelection = false;
            this.listServer.Location = new System.Drawing.Point(0, 20);
            this.listServer.MultiSelect = false;
            this.listServer.Name = "listServer";
            this.listServer.Size = new System.Drawing.Size(314, 380);
            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.Name = "name";
            this.name.Text = "Name";
            this.name.Width = 120;
            // 
            // size
            // 
            this.size.Name = "size";
            this.size.Text = "Size";
            this.size.TextAlign = System.Windows.Forms.HorizontalAlignment.Right;
            // 
            // date
            // 
            this.date.Name = "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) 
            | System.Windows.Forms.AnchorStyles.Right)));
            this.listLocal.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
            this.columnHeader1,
            this.columnHeader2,
            this.columnHeader3});
            this.listLocal.FullRowSelect = true;
            this.listLocal.HideSelection = false;
            this.listLocal.Location = new System.Drawing.Point(0, 20);
            this.listLocal.MultiSelect = false;
            this.listLocal.Name = "listLocal";
            this.listLocal.Size = new System.Drawing.Size(314, 429);
            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.Name = "columnHeader1";
            this.columnHeader1.Text = "Name";
            this.columnHeader1.Width = 120;
            // 
            // columnHeader2
            // 
            this.columnHeader2.Name = "columnHeader2";
            this.columnHeader2.Text = "Size";
            this.columnHeader2.TextAlign = System.Windows.Forms.HorizontalAlignment.Right;
            // 
            // columnHeader3
            // 
            this.columnHeader3.Name = "columnHeader3";
            this.columnHeader3.Text = "Date";
            this.columnHeader3.Width = 120;
            // 
            // tbLog
            // 
            this.tbLog.AccessibleRole = System.Windows.Forms.AccessibleRole.None;
            this.tbLog.BackColor = System.Drawing.SystemColors.Control;
            this.tbLog.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
            this.tbLog.Cursor = System.Windows.Forms.Cursors.IBeam;
            this.tbLog.Dock = System.Windows.Forms.DockStyle.Fill;
            this.tbLog.Font = new System.Drawing.Font("Lucida Console", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
            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(784, 110);
            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) 
            | System.Windows.Forms.AnchorStyles.Right)));
            this.lblServer.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
            this.lblServer.Location = new System.Drawing.Point(4, 407);
            this.lblServer.Name = "lblServer";
            this.lblServer.Size = new System.Drawing.Size(187, 25);
            this.lblServer.TabIndex = 9;
            this.lblServer.Text = "12345";
            // 
            // lblServerDirectory
            // 
            this.lblServerDirectory.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
            | System.Windows.Forms.AnchorStyles.Right)));
            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);
            this.lblServerDirectory.ForeColor = System.Drawing.SystemColors.InactiveCaptionText;
            this.lblServerDirectory.Location = new System.Drawing.Point(0, 0);
            this.lblServerDirectory.Name = "lblServerDirectory";
            this.lblServerDirectory.Size = new System.Drawing.Size(314, 20);
            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) 
            | System.Windows.Forms.AnchorStyles.Right)));
            this.lblLocal.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
            this.lblLocal.Location = new System.Drawing.Point(4, 454);
            this.lblLocal.Name = "lblLocal";
            this.lblLocal.Size = new System.Drawing.Size(310, 16);
            this.lblLocal.TabIndex = 12;
            this.lblLocal.Text = "12345";
            // 
            // lblLocalDirectory
            // 
            this.lblLocalDirectory.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
            | System.Windows.Forms.AnchorStyles.Right)));
            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);
            this.lblLocalDirectory.ForeColor = System.Drawing.SystemColors.InactiveCaptionText;
            this.lblLocalDirectory.Location = new System.Drawing.Point(0, 0);
            this.lblLocalDirectory.Name = "lblLocalDirectory";
            this.lblLocalDirectory.Size = new System.Drawing.Size(314, 20);
            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(19, 277);
            this.btnStop.Name = "btnStop";
            this.btnStop.Size = new System.Drawing.Size(90, 28);
            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(19, 160);
            this.btnDelete.Name = "btnDelete";
            this.btnDelete.Size = new System.Drawing.Size(90, 28);
            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(19, 241);
            this.btnUpload.Name = "btnUpload";
            this.btnUpload.Size = new System.Drawing.Size(90, 29);
            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(19, 206);
            this.btnDownload.Name = "btnDownload";
            this.btnDownload.Size = new System.Drawing.Size(90, 28);
            this.btnDownload.TabIndex = 13;
            this.btnDownload.Text = "Download >";
            this.btnDownload.Click += new System.EventHandler(this.btnDownload_Click);
            // 
            // btnRefresh
            // 
            this.btnRefresh.FlatStyle = System.Windows.Forms.FlatStyle.System;
            this.btnRefresh.Location = new System.Drawing.Point(19, 89);
            this.btnRefresh.Name = "btnRefresh";
            this.btnRefresh.Size = new System.Drawing.Size(90, 28);
            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(19, 124);
            this.btnMakeDir.Name = "btnMakeDir";
            this.btnMakeDir.Size = new System.Drawing.Size(90, 29);
            this.btnMakeDir.TabIndex = 7;
            this.btnMakeDir.Text = "Create folder";
            this.btnMakeDir.Click += new System.EventHandler(this.btnMakeDir_Click);
            // 
            // cbDrives
            // 
            this.cbDrives.ItemHeight = 15;
            this.cbDrives.Location = new System.Drawing.Point(20, 49);
            this.cbDrives.Name = "cbDrives";
            this.cbDrives.Size = new System.Drawing.Size(88, 23);
            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(19, 20);
            this.btnConnect.Name = "btnConnect";
            this.btnConnect.Size = new System.Drawing.Size(90, 28);
            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)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
            | System.Windows.Forms.AnchorStyles.Left) 
            | System.Windows.Forms.AnchorStyles.Right)));
            this.gbControl.Controls.Add(this.btnStop);
            this.gbControl.Controls.Add(this.btnDownload);
            this.gbControl.Controls.Add(this.btnDelete);
            this.gbControl.Controls.Add(this.btnUpload);
            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(5, 4);
            this.gbControl.Name = "gbControl";
            this.gbControl.Size = new System.Drawing.Size(127, 469);
            this.gbControl.TabIndex = 25;
            this.gbControl.TabStop = false;
            this.gbControl.Text = "Controls";
            // 
            // 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(470, 59);
            this.headerLeft.TabIndex = 26;
            this.headerLeft.TabStop = false;
            // 
            // headerMiddle
            // 
            this.headerMiddle.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
            | System.Windows.Forms.AnchorStyles.Right)));
            this.headerMiddle.BackColor = System.Drawing.Color.White;
            this.headerMiddle.BackgroundImage = ((System.Drawing.Image)(resources.GetObject("headerMiddle.BackgroundImage")));
            this.headerMiddle.Location = new System.Drawing.Point(470, 0);
            this.headerMiddle.Name = "headerMiddle";
            this.headerMiddle.Size = new System.Drawing.Size(164, 59);
            this.headerMiddle.TabIndex = 27;
            // 
            // 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(634, 0);
            this.headerRight.Margin = new System.Windows.Forms.Padding(0);
            this.headerRight.Name = "headerRight";
            this.headerRight.Size = new System.Drawing.Size(150, 59);
            this.headerRight.TabIndex = 28;
            this.headerRight.TabStop = false;
            this.headerRight.Click += new System.EventHandler(this.headerRight_Click);
            // 
            // trackBar
            // 
            this.trackBar.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) 
            | System.Windows.Forms.AnchorStyles.Right)));
            this.trackBar.AutoSize = false;
            this.trackBar.Location = new System.Drawing.Point(4, 434);
            this.trackBar.Maximum = 100;
            this.trackBar.Minimum = 1;
            this.trackBar.Name = "trackBar";
            this.trackBar.Size = new System.Drawing.Size(307, 39);
            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.Right)));
            this.lblSpeed.Location = new System.Drawing.Point(197, 407);
            this.lblSpeed.Name = "lblSpeed";
            this.lblSpeed.Size = new System.Drawing.Size(114, 25);
            this.lblSpeed.TabIndex = 31;
            this.lblSpeed.Text = "Speed: Unlimited";
            this.lblSpeed.TextAlign = System.Drawing.ContentAlignment.TopRight;
            // 
            // mainStatusStrip
            // 
            this.mainStatusStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
            this.pbTransfer,
            this.lblProgress});
            this.mainStatusStrip.Location = new System.Drawing.Point(0, 656);
            this.mainStatusStrip.Name = "mainStatusStrip";
            this.mainStatusStrip.Size = new System.Drawing.Size(784, 25);
            this.mainStatusStrip.TabIndex = 32;
            this.mainStatusStrip.Text = "statusStrip1";
            // 
            // pbTransfer
            // 
            this.pbTransfer.Name = "pbTransfer";
            this.pbTransfer.Size = new System.Drawing.Size(192, 19);
            // 
            // lblProgress
            // 
            this.lblProgress.Name = "lblProgress";
            this.lblProgress.Size = new System.Drawing.Size(60, 20);
            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, 59);
            this.mainSplitContainer.Name = "mainSplitContainer";
            this.mainSplitContainer.Orientation = System.Windows.Forms.Orientation.Horizontal;
            // 
            // mainSplitContainer.Panel1
            // 
            this.mainSplitContainer.Panel1.Controls.Add(this.tableLayout);
            this.mainSplitContainer.Panel1MinSize = 320;
            // 
            // mainSplitContainer.Panel2
            // 
            this.mainSplitContainer.Panel2.Controls.Add(this.tbLog);
            this.mainSplitContainer.Panel2MinSize = 30;
            this.mainSplitContainer.Size = new System.Drawing.Size(784, 597);
            this.mainSplitContainer.SplitterDistance = 483;
            this.mainSplitContainer.TabIndex = 33;
            // 
            // tableLayout
            // 
            this.tableLayout.ColumnCount = 3;
            this.tableLayout.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F));
            this.tableLayout.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 144F));
            this.tableLayout.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F));
            this.tableLayout.Controls.Add(this.panelRight, 2, 0);
            this.tableLayout.Controls.Add(this.panelMiddle, 1, 0);
            this.tableLayout.Controls.Add(this.panelLeft, 0, 0);
            this.tableLayout.Dock = System.Windows.Forms.DockStyle.Fill;
            this.tableLayout.Location = new System.Drawing.Point(0, 0);
            this.tableLayout.Name = "tableLayout";
            this.tableLayout.RowCount = 1;
            this.tableLayout.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
            this.tableLayout.Size = new System.Drawing.Size(784, 483);
            this.tableLayout.TabIndex = 18;
            // 
            // panelRight
            // 
            this.panelRight.Controls.Add(this.lblLocalDirectory);
            this.panelRight.Controls.Add(this.lblLocal);
            this.panelRight.Controls.Add(this.listLocal);
            this.panelRight.Dock = System.Windows.Forms.DockStyle.Fill;
            this.panelRight.Location = new System.Drawing.Point(467, 3);
            this.panelRight.Name = "panelRight";
            this.panelRight.Size = new System.Drawing.Size(314, 477);
            this.panelRight.TabIndex = 2;
            // 
            // panelMiddle
            // 
            this.panelMiddle.Controls.Add(this.gbControl);
            this.panelMiddle.Dock = System.Windows.Forms.DockStyle.Fill;
            this.panelMiddle.Location = new System.Drawing.Point(323, 3);
            this.panelMiddle.Name = "panelMiddle";
            this.panelMiddle.Size = new System.Drawing.Size(138, 477);
            this.panelMiddle.TabIndex = 1;
            // 
            // panelLeft
            // 
            this.panelLeft.Controls.Add(this.lblServerDirectory);
            this.panelLeft.Controls.Add(this.trackBar);
            this.panelLeft.Controls.Add(this.listServer);
            this.panelLeft.Controls.Add(this.lblSpeed);
            this.panelLeft.Controls.Add(this.lblServer);
            this.panelLeft.Dock = System.Windows.Forms.DockStyle.Fill;
            this.panelLeft.Location = new System.Drawing.Point(3, 3);
            this.panelLeft.Name = "panelLeft";
            this.panelLeft.Size = new System.Drawing.Size(314, 477);
            this.panelLeft.TabIndex = 0;
            // 
            // MainForm
            // 
            this.AutoScaleBaseSize = new System.Drawing.Size(6, 16);
            this.ClientSize = new System.Drawing.Size(784, 681);
            this.Controls.Add(this.mainSplitContainer);
            this.Controls.Add(this.mainStatusStrip);
            this.Controls.Add(this.headerLeft);
            this.Controls.Add(this.headerRight);
            this.Controls.Add(this.headerMiddle);
            this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
            this.KeyPreview = true;
            this.MinimumSize = new System.Drawing.Size(640, 720);
            this.Name = "MainForm";
            this.Text = "Simple WinForm SFTP Client";
            this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.MainForm_Closing);
            this.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.MainForm_KeyPress);
            this.gbControl.ResumeLayout(false);
            ((System.ComponentModel.ISupportInitialize)(this.headerLeft)).EndInit();
            ((System.ComponentModel.ISupportInitialize)(this.headerRight)).EndInit();
            ((System.ComponentModel.ISupportInitialize)(this.trackBar)).EndInit();
            this.mainStatusStrip.ResumeLayout(false);
            this.mainStatusStrip.PerformLayout();
            this.mainSplitContainer.Panel1.ResumeLayout(false);
            this.mainSplitContainer.Panel2.ResumeLayout(false);
            ((System.ComponentModel.ISupportInitialize)(this.mainSplitContainer)).EndInit();
            this.mainSplitContainer.ResumeLayout(false);
            this.tableLayout.ResumeLayout(false);
            this.panelRight.ResumeLayout(false);
            this.panelMiddle.ResumeLayout(false);
            this.panelLeft.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.EnableVisualStyles();
            Application.Run(new MainForm());
        }

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

                // disconnect

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

            ConnectionFormData connectionData = new ConnectionFormData(FileTransferComponent.Sftp);
            connectionData.LoadConfig();

            // connection dialog
            ConnectionForm con = new ConnectionForm(connectionData);

            // show connection dialog

            con.ShowDialog();

            // not confirmed

            if (con.DialogResult != DialogResult.OK)
                return;

            // save confirmed data
            connectionData.SaveConfig();

            _data = connectionData;

            // create sftp object
            _sftp = new Sftp();

            // assign the logger for logging CommandSent and ResponseRead events (and exceptions thrown by Sftp class)
            _sftp.LogWriter = new RichTextBoxLogWriter(tbLog, tbLog.MaxLength, con.Data.LogLevel);

            _sftp.Settings.UseLargeBuffers = true;

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

            _sftp.FingerprintCheck += _sftp_FingerprintCheck;

            // connect

            try
            {
                _sftp.Timeout = 30000;

                // proxy

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

                // set preferred SSH host key algorithm 
                if (_data.ServerKeyVerifyingMode == KeyVerifyingMode.LocalyStoredFingerprint)
                    _sftp.Settings.SshParameters.PreferredHostKeyAlgorithm = _data.ServerKeyAlgorithm;

                if (_data.AllowWeakSuites)
                {
                    _sftp.Settings.SshParameters.EncryptionAlgorithms = SshEncryptionAlgorithm.Any;
                    _sftp.Settings.SshParameters.HostKeyAlgorithms = SshHostKeyAlgorithm.Any;
                    _sftp.Settings.SshParameters.KeyExchangeAlgorithms = SshKeyExchangeAlgorithm.Any;
                    _sftp.Settings.SshParameters.MacAlgorithms = SshMacAlgorithm.Any;
                    _sftp.Settings.SshParameters.MinimumDiffieHellmanKeySize = 512;
                    _sftp.Settings.SshParameters.MinimumRsaKeySize = 512;
                }

                try
                {
                    btnConnect.Visible = true;
                    _isWorking = true;

                    await _sftp.ConnectAsync(con.Data.ServerHost, con.Data.ServerPort);
                }
                finally
                {
                    btnStop.Visible = false;
                    _isWorking = false;
                }

                // login

                trackBar.Enabled = true;
                if (!con.Data.UseGssApi)
                {
                    if (con.Data.PrivateKey != null)
                    {
                        if (string.IsNullOrEmpty(con.Data.ServerPassword))
                        {
                            await _sftp.LoginAsync(con.Data.ServerUser, null, con.Data.PrivateKey);
                        }
                        else
                        {
                            await _sftp.LoginAsync(con.Data.ServerUser, con.Data.ServerPassword, con.Data.PrivateKey);
                        }
                    }
                    else
                    {
                        await _sftp.LoginAsync(con.Data.ServerUser, con.Data.ServerPassword);
                    }
                }
                else
                {
                    await _sftp.LoginAsync(new SshGssApiCredentials());
                }

                btnConnect.Text = "Disconnect";
                MakeSftpList();
            }
            catch (Exception ex)
            {
                WriteToLog(ex);
                _sftp.Dispose();
                _sftp = null;
                return;
            }
            finally
            {
                _isWorking = false;
            }
        }

        void _sftp_FingerprintCheck(object sender, SshFingerprintEventArgs e)
        {
            switch (_data.ServerKeyVerifyingMode)
            {
                case KeyVerifyingMode.AcceptAnyKey:
                    e.Accept = true;
                    break;

                case KeyVerifyingMode.LocalyStoredFingerprint:
                    string fingerprint = e.Fingerprint.ToString(_data.ServerKeyHashAlgorithm);
                    if (fingerprint == _data.ServerKeyFingerprint)
                    {
                        e.Accept = true;
                        break;
                    }

                    ResolveDialogMode resolveMode;
                    string resolveMessage;

                    if (!string.IsNullOrEmpty(_data.ServerKeyFingerprint))
                    {
                        resolveMode = ResolveDialogMode.EntryDiffer;
                        resolveMessage = "Stored fingerprint does not match the server key fingerprint.";
                    }
                    else
                    {
                        resolveMode = ResolveDialogMode.EntryNotFound;
                        resolveMessage = "Server key is not known yet.";
                    }

                    // ask user for an action, if the stored fingerprint does not match
                    ResolveInvalidKeyDialog dlg = new ResolveInvalidKeyDialog(
                        resolveMode,
                        resolveMessage,
                        _data.ServerKeyHashAlgorithm,
                        _data.ServerKeyAlgorithm,
                        _data.ServerKeyFingerprint,
                        e.ServerKey.KeyAlgorithm,
                        fingerprint);

                    // a) user has not specified any solution
                    if (dlg.ShowDialog() != DialogResult.OK)
                    {
                        e.Accept = false;
                        break;
                    }

                    // b) user has specified a solution, that changes the current mode
                    KeyVerifyingMode mode = dlg.GetChoosenMode();
                    switch (mode)
                    {
                        case KeyVerifyingMode.AcceptAnyKey:
                            e.Accept = true;
                            break;
                        case KeyVerifyingMode.LocalyStoredFingerprint:
                            _data.ServerKeyFingerprint = fingerprint;
                            _data.SaveConfig(); // save the changed mode
                            e.Accept = true;
                            break;
                        case KeyVerifyingMode.RejectKey:
                            e.Accept = false;
                            break;
                        default:
                            throw new InvalidOperationException(string.Format("Unsupported enum value '{0}'", mode));
                    }
                    break;
                default:
                    throw new InvalidOperationException(string.Format("Unsupported KeyVerifyingMode '{0}'.", _data.ServerKeyVerifyingMode));
            }
        }

        private void AuthenticationRequest(object sender, SshAuthenticationRequestEventArgs e)
        {
            // get responses from user
            AuthenticationRequestDialog dialog = new AuthenticationRequestDialog(e);
            DialogResult result = dialog.ShowDialog();

            // process result
            if (result != DialogResult.OK)
                e.Cancel = true;
        }

        /// <summary>
        /// disconnect on exit
        /// </summary>
        private void MainForm_Closing(object sender, System.Windows.Forms.FormClosingEventArgs e)
        {
            if (_sftp != null)
                _sftp.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();

                // sftp state test

                if (_isWorking)
                    return;

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

                // parse listitem

                bool file = (index >= _serverDirs);
                string path = item.Text;

                // symlink click
                if (item.SubItems.Count > 1 && item.SubItems[1].Text == SYMLINK)
                {
                    path = _sftp.ResolveSymlink(path);
                    SftpItem info = _sftp.GetInfo(path);
                    switch (info.Type)
                    {
                        case SftpItemType.Directory:
                            file = false;
                            break;
                        case SftpItemType.RegularFile:
                            file = true;
                            break;
                        default:
                            WriteToLog("* unsupported symlink type", RichTextBoxLogWriter.COLORERROR);
                            return;
                    }
                }

                if (file)
                {
                    // GET file

                    DownloadFiles(path, _localPath);
                }
                else
                {
                    string directory;
                    if (index == 0)
                        directory = "..";
                    else
                        directory = path;

                    // CWD

                    _sftp.ChangeDirectory(directory);
                    MakeSftpList();
                }
            }
            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();

                // SFTP: MKDIR

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

                    dir.ShowDialog();
                    if (dir.Directory != null)
                    {
                        _sftp.CreateDirectory(dir.Directory);
                        MakeSftpList();
                    }
                }
                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;

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

        private void PrepareTransfer()
        {
            _isWorking = true;
            btnStop.Visible = true;
            _problemForm.Initialize();
            _transferTime = DateTime.Now;
        }

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

            PrepareTransfer();

            // transfer
            try
            {
                await _sftp.DownloadAsync(fileName, localPath, TraversalMode.Recursive, TransferMethod.Copy, ActionOnExistingFiles.ThrowException);
            }
            catch (Exception ex)
            {
                WriteToLog(ex);
            }
            finally
            {
                MakeLocalList();
                TransferCompleted();
            }
        }

        /// <summary>
        /// upload file or directory tree
        /// </summary>
        /// <param name="fileName">local file name</param>
        /// <param name="localPath">local path</param>
        private async void UploadFiles(string fileName, string localPath)
        {
            // sftp state test
            if (_sftp == null)
            {
                WriteToLog("Not connected!", Color.DarkOrange);
                return;
            }
            if (_isWorking)
                return;

            localPath = Path.Combine(localPath, fileName);

            PrepareTransfer();

            try
            {
                await _sftp.UploadAsync(localPath, ".", TraversalMode.Recursive, TransferMethod.Copy, ActionOnExistingFiles.ThrowException);
            }
            catch (Exception ex)
            {
                WriteToLog(ex);
            }
            finally
            {
                MakeSftpList();
                TransferCompleted();
            }
        }

        private void TransferCompleted()
        {
            SetProgressValue(0);
            btnStop.Visible = false;
            _isWorking = false;
        }

        /// <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)
                            {
                                _sftp.DeleteFile(item.Text);
                                MakeSftpList();
                            }
                        }
                        else
                        {
                            DeleteDialog dialog = new DeleteDialog(DeleteDialog.DialogPrompt.DeleteDirectory);
                            dialog.Path = item.Text;
                            dialog.ShowDialog();

                            if (dialog.OK)
                            {
                                _sftp.RemoveDirectory(item.Text);
                                MakeSftpList();
                            }
                        }
                    }
                }
                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>
        /// stop transfer when something is being transferred
        /// </summary>
        private void btnStop_Click(object sender, System.EventArgs e)
        {
            _sftp.AbortTransfer();
        }

        /// <summary>
        /// go to the url
        /// </summary>
        private void headerRight_Click(object sender, System.EventArgs e)
        {
            var info = new ProcessStartInfo(URL);
            info.UseShellExecute = true;
            info.Verb = "open";
            Process.Start(info);
        }

        /// <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 trackBar_Scroll(object sender, EventArgs e)
        {
            if (trackBar.Value == 100)
            {
                lblSpeed.Text = "Speed: Unlimited";
                _sftp.MaxDownloadSpeed = 0;
                _sftp.MaxUploadSpeed = 0;
            }
            else
            {
                lblSpeed.Text = string.Format("Speed: {0} kB/s", trackBar.Value);
                _sftp.MaxDownloadSpeed = trackBar.Value;
                _sftp.MaxUploadSpeed = trackBar.Value;
            }
        }
    }

    #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;

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

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

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

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

    #endregion
}
