'  
'   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.
' 

Imports System
Imports System.Drawing
Imports System.Collections
Imports System.ComponentModel
Imports System.Windows.Forms
Imports System.Xml
Imports System.Resources
Imports System.IO
Imports System.Text
Imports System.Diagnostics
Imports System.Text.RegularExpressions
Imports System.Threading.Tasks

Imports Rebex
Imports Rebex.IO
Imports Rebex.Net
Imports Rebex.Security.Certificates

'' <summary>
'' very simple ftp client
'' </summary>
Public Class MainForm
    Inherits System.Windows.Forms.Form

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

    Private _ftp As Ftp                                ' FTP session
    Private listActive As ListView                    ' active list (server x local)
    Private _serverDirs As Integer                    ' number of dirs on server side
    Private _localDirs As Integer                    ' number of dirs on local side
    Private _selectedDrive As Integer = -1            ' selected drive (A:, C:, ...) in combobox
    Private _localPath As String                    ' local path
    Private _isWorking As Boolean                    ' determines whether any operation is running
    Private _currentFileLength As Long                ' current transferring file length
    Private _currentBytesTransferred As Long        ' bytes transferred of the current transferring file
    Private _transferTime As DateTime                ' transfer launch-time
    Private _cccMode As Boolean                        ' clear command channel enabled?
    Private _problemForm As ProblemDetectedForm        ' informs the user about problems while transferring data

    ' constansts
    Private Const SYMLINK As String = "--->"        ' symlink tag
    Private Const URL As String = "https://www.rebex.net/ftp-ssl.net/"     ' url

    Private _exception As String

    Public Overloads Sub WriteToLog(ByVal x As Exception)
        If (TypeOf x Is AggregateException) Then
            Dim aggregateEx As AggregateException = CType(x, AggregateException)
            For Each inner As Exception In aggregateEx.InnerExceptions()
                WriteToLog(inner)
            Next
        End If

        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 TypeOf x Is FtpException Then Return

        WriteToLog("* " & x.ToString(), RichTextBoxLogWriter.COLORERROR)
    End Sub

    Private Sub ShowErrorWarning(ByVal message As String)
        _exception = message
        btnErrorWarning.Visible = True
    End Sub

    Public Overloads Sub WriteToLog(ByVal message As String, ByVal color As Color)
        tbLog.Focus()
        tbLog.SelectionColor = color
        tbLog.AppendText((message & ControlChars.Cr & ControlChars.Lf))
    End Sub


    '' <summary>
    '' format datetime to readable form in the panels
    '' </summary>
    '' <param name="dt">datetime</param>
    '' <returns>formatted string</returns>
    Private Shared Function FormatFileTime(ByVal dt As DateTime) As String
        Return dt.ToString("yyyy-MM-dd HH:mm")
    End Function 'FormatFileTime

    ''' <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 Shared Function ConvertToSslMode(ByVal transferMode As FileTransferMode) As SslMode
        Select Case transferMode
            Case FileTransferMode.FtpSslExplicit
                Return SslMode.Explicit
            Case FileTransferMode.FtpSslImplicit
                Return SslMode.Implicit
        End Select

        Return SslMode.None
    End Function

    ''<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 Sub SafeInvoke(ByVal method As [Delegate], ByVal args() As Object)
        Try
            If Not IsDisposed Then Invoke(method, args)
        Catch x As ObjectDisposedException
        End Try
    End Sub

    '' <summary>
    '' event displaying ftp state
    '' </summary>
    Private Sub StateChanged(ByVal sender As Object, ByVal e As FtpStateChangedEventArgs)
        Select Case e.NewState
            Case FtpState.Disconnected, FtpState.Disposed
                pbSecure.Visible = False
                lblProgress.Text = "Disconnected"
                btnConnect.Text = "Connect..."
                listServer.Items.Clear()
            Case FtpState.Ready
                lblProgress.Text = "Ready"
        End Select
    End Sub

    ''' <summary>
    ''' handles the displaying traversing event
    ''' </summary>
    Private Sub Traversing(ByVal sender As Object, ByVal e As FtpTraversingEventArgs)
        If e.Action = TransferAction.Listing Then
            Return
        End If

        Dim strBatchInfo As String = String.Format("({0} file{1} traversed)    ", e.FilesTotal, (IIf(e.FilesTotal > 1, "s", Nothing)))

        Select Case e.TraversingState
            Case TraversingState.DirectoryRetrieved
                strBatchInfo &= "Directory retrieved."
                Exit Select
            Case TraversingState.DirectoryRetrieving
                strBatchInfo &= "Retrieving directory..."
                Exit Select
            Case TraversingState.HierarchyRetrieved
                strBatchInfo &= String.Format("Hierarchy retrieved ({0} byte{1} in {2} file{3}).", _
                 e.BytesTotal, (IIf(e.BytesTotal > 1, "s", "")), e.FilesTotal, (IIf(e.FilesTotal > 1, "s", Nothing)))
                Exit Select
            Case TraversingState.HierarchyRetrieving
                strBatchInfo &= "Retrieving hierarchy..."
                Exit Select
        End Select

        lblProgress.Text = strBatchInfo
    End Sub


    ''' <summary>
    ''' handles the transfer progress changed event
    ''' </summary>
    Private Sub TransferProgressChanged(ByVal sender As Object, ByVal e As FtpTransferProgressChangedEventArgs)
        Dim strBatchInfo As String = String.Format("({0} / {1} file{2} processed)    ", e.FilesProcessed, e.FilesTotal, (IIf(e.FilesProcessed > 1, "s", "")))

        SetProgressValue(Convert.ToInt32(e.ProgressPercentage))

        Select Case e.TransferState
            Case TransferProgressState.DataBlockProcessed
                strBatchInfo &= e.BytesTransferred & " bytes"
                Exit Select
            Case TransferProgressState.DirectoryProcessing
                strBatchInfo &= "Processing directory..."
                Exit Select
            Case TransferProgressState.FileTransferring
                strBatchInfo &= "Transferring file..."
                Exit Select
            Case TransferProgressState.FileTransferred
                strBatchInfo &= "File transferred."
                Exit Select
            Case TransferProgressState.TransferCompleted
                strBatchInfo &= "Transfer completed."
                ShowTransferStatus(e.BytesTransferred, e.FilesTransferred)
                Exit Select
        End Select

        lblProgress.Text = strBatchInfo
    End Sub

    Private Sub SetProgressValue(ByVal value As Integer)
        ' workaround for progress bar smoothing
        If (value = 100) Then
            pbTransfer.Maximum = 101
            pbTransfer.Value = 101
            pbTransfer.Maximum = 100
            System.Threading.Thread.Sleep(200)
        Else
            pbTransfer.Value = value
        End If
    End Sub

    ''' <summary>
    ''' handles the delete progress changed event
    ''' </summary>
    Private Sub DeleteProgressChanged(ByVal sender As Object, ByVal e As FtpDeleteProgressChangedEventArgs)
        Dim strDeleteInfo As String = String.Format("({0} / {1} file{2} deleted)    ", e.FilesProcessed, e.FilesTotal, (IIf(e.FilesProcessed > 1, "s", "")))

        Select Case e.DeleteState
            Case DeleteProgressState.DeleteCompleted
                strDeleteInfo &= "Delete completed."
                Exit Select
            Case DeleteProgressState.DirectoryDeleted
                strDeleteInfo &= "Directory deleted."
                Exit Select
            Case DeleteProgressState.DirectoryProcessing
                strDeleteInfo &= "Processing directory..."
                Exit Select
            Case DeleteProgressState.FileDeleted
                strDeleteInfo &= "File deleted."
                Exit Select
            Case DeleteProgressState.FileDeleting
                strDeleteInfo &= "Deleting file..."
                Exit Select
        End Select

        lblProgress.Text = strDeleteInfo
    End Sub

    ''' <summary>
    ''' handles the batch transfer problem detected event
    ''' </summary>
    Private Sub ProblemDetected(ByVal sender As Object, ByVal e As FtpProblemDetectedEventArgs)
        _problemForm.ShowModal(Me, e)
    End Sub

    '' <summary>
    '' make local list + drives combo box
    '' </summary>
    Public Sub MakeLocalList()
        Dim dirs As Integer = 0
        Dim files As Integer = 0
        Dim size As Long = 0
        Dim c As Integer
        ' make a logical drives list [first run only]
        If cbDrives.Items.Count <= 0 Then

            Dim drives As String() = Directory.GetLogicalDrives()

            Dim lowerDrive As String = "c:\"

            ' find "lower" acceptable drive
            For c = drives.Length - 1 To 0 Step -1
                If drives(c).ToLower().CompareTo("c:\") >= 0 Then
                    lowerDrive = drives(c).ToLower()
                End If
            Next c

            ' feed the drives' list and select one
            For c = 0 To drives.Length - 1
                Dim drive As String = drives(c)

                cbDrives.Items.Add(drive)

                If _selectedDrive = -1 AndAlso drive.ToLower().CompareTo(lowerDrive) = 0 Then
                    cbDrives.SelectedIndex = c
                    _selectedDrive = c
                End If
            Next c

            If cbDrives.SelectedIndex <= 0 Then
                cbDrives.SelectedIndex = 0
            End If
        End If
        ' create the list
        listLocal.Items.Clear()

        ' directory up
        If Not (_localPath Is Nothing) AndAlso _localPath.Length > 3 Then
            listLocal.Items.Add(New ListViewItem("..", 0))
            dirs += 1
        Else
            _localPath = cbDrives.SelectedItem.ToString()
        End If

        Dim currentDirectory As New DirectoryInfo(_localPath)
        Dim list As DirectoryInfo() = currentDirectory.GetDirectories()

        For c = 0 To list.Length - 1
            Dim row(2) As String
            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) = ""
            End Try

            listLocal.Items.Add(New ListViewItem(row, 1 + c))
            dirs += 1
        Next c

        _localDirs = dirs

        ' files
        Dim list2 As FileInfo() = currentDirectory.GetFiles()

        For c = 0 To list2.Length - 1
            Dim row(2) As String
            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 += 1
        Next c

        ' stats
        lblLocal.Text = dirs.ToString() & " folder" & IIf(dirs > 1, "s", "") & ", " & files.ToString() & " file" & IIf(files > 1, "s", "") & ", " & System.Convert.ToInt32(size / 1024).ToString() & " K"

        ' working directory
        lblLocalDirectory.Text = currentDirectory.FullName
        _localPath = currentDirectory.FullName
    End Sub

    '' <summary>
    '' make ftp list
    '' </summary>
    Public Sub MakeFtpList()
        Dim dirs As Integer = 0
        Dim files As Integer = 0
        Dim size As Long = 0

        listServer.Items.Clear()

        ' not connected?
        If _ftp Is Nothing Then
            lblServerDirectory.Text = ""
            lblServer.Text = ""
            _serverDirs = 0
            Return
        End If

        ' secure transfers
        If cbST.Enabled AndAlso Not _cccMode Then _ftp.SecureTransfers = cbST.Checked

        ' make the list
        Dim list As FtpItemCollection = Nothing

        Try
            _isWorking = True
            btnStop.Visible = True
            Dim getList As Task(Of FtpItemCollection) = _ftp.GetListAsync()
            While (Not getList.IsCompleted)
                Application.DoEvents()
                System.Threading.Thread.Sleep(1)
            End While

            If (getList.IsFaulted) Then
                If (getList.Exception.InnerException IsNot Nothing) Then
                    Throw getList.Exception.InnerException
                End If
                Throw getList.Exception
            End If

            If getList.IsCanceled Then
                Throw New OperationCanceledException("Operation has been canceled.")
            End If

            list = getList.Result
            list.Sort()
        Catch ex As Exception
            WriteToLog(ex)
            listServer.Items.Add(New ListViewItem("..", 0))
            Return
        Finally
            btnStop.Visible = False
            _isWorking = False
        End Try

        ' items
        listServer.Items.Add(New ListViewItem("..", 0))
        dirs += 1

        Dim c As Integer
        For c = 0 To list.Count - 1
            Dim row(2) As String
            row(0) = list(c).Name
            row(1) = Nothing
            row(2) = FormatFileTime(list(c).LastWriteTime.Value)

            If list(c).IsLink Then
                row(1) = SYMLINK
            ElseIf list(c).IsFile Then
                row(1) = list(c).Length.ToString()
                size += list(c).Length
                files += 1
            ElseIf list(c).IsDirectory Then
                dirs += 1
            End If

            Dim lvi As ListViewItem
            lvi = New ListViewItem(row)
            lvi.Tag = list(c)
            listServer.Items.Add(lvi)

        Next c

        listServer.ListViewItemSorter = New ListViewColumnSorter()
        listServer.Sort()

        ' stats
        lblServer.Text = dirs.ToString() & " folder" & IIf(dirs > 1, "s", "") & ", " & files.ToString() & " file" & IIf(files > 1, "s", "") & ", " & System.Convert.ToInt32(size / 1024).ToString() & " K"

        ' working directory
        lblServerDirectory.Text = _ftp.GetCurrentDirectory()

        _serverDirs = dirs
    End Sub

    '' <summary>
    '' constructor
    '' </summary>
    Public Sub New()
        MyBase.New()
        InitializeComponent()

        CheckForIllegalCrossThreadCalls = True
        Application.EnableVisualStyles()

        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)

    End Sub

    '' <summary>
    '' clean up any resources being used
    '' </summary>
    Protected Overloads Sub Dispose(ByVal disposing As Boolean)
        If disposing Then
            If Not (components Is Nothing) Then
                components.Dispose()
            End If
        End If
        MyBase.Dispose(disposing)
    End Sub

#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 Sub InitializeComponent()
        Dim resources As System.ComponentModel.ComponentResourceManager = New System.ComponentModel.ComponentResourceManager(GetType(MainForm))
        Me.listServer = New System.Windows.Forms.ListView()
        Me.columnName = CType(New System.Windows.Forms.ColumnHeader(), System.Windows.Forms.ColumnHeader)
        Me.columnSize = CType(New System.Windows.Forms.ColumnHeader(), System.Windows.Forms.ColumnHeader)
        Me.columnDate = CType(New System.Windows.Forms.ColumnHeader(), System.Windows.Forms.ColumnHeader)
        Me.listLocal = New System.Windows.Forms.ListView()
        Me.columnHeader1 = CType(New System.Windows.Forms.ColumnHeader(), System.Windows.Forms.ColumnHeader)
        Me.columnHeader2 = CType(New System.Windows.Forms.ColumnHeader(), System.Windows.Forms.ColumnHeader)
        Me.columnHeader3 = CType(New System.Windows.Forms.ColumnHeader(), System.Windows.Forms.ColumnHeader)
        Me.tbLog = New System.Windows.Forms.RichTextBox()
        Me.lblServer = New System.Windows.Forms.Label()
        Me.lblServerDirectory = New System.Windows.Forms.Label()
        Me.lblLocal = New System.Windows.Forms.Label()
        Me.lblLocalDirectory = New System.Windows.Forms.Label()
        Me.btnStop = New System.Windows.Forms.Button()
        Me.btnDelete = New System.Windows.Forms.Button()
        Me.btnUpload = New System.Windows.Forms.Button()
        Me.btnDownload = New System.Windows.Forms.Button()
        Me.rbBin = New System.Windows.Forms.RadioButton()
        Me.rbAsc = New System.Windows.Forms.RadioButton()
        Me.btnRefresh = New System.Windows.Forms.Button()
        Me.btnMakeDir = New System.Windows.Forms.Button()
        Me.cbDrives = New System.Windows.Forms.ComboBox()
        Me.btnConnect = New System.Windows.Forms.Button()
        Me.gbControl = New System.Windows.Forms.GroupBox()
        Me.gbTransfer = New System.Windows.Forms.GroupBox()
        Me.cbModeZ = New System.Windows.Forms.CheckBox()
        Me.cbST = New System.Windows.Forms.CheckBox()
        Me.headerLeft = New System.Windows.Forms.PictureBox()
        Me.headerMiddle = New System.Windows.Forms.Panel()
        Me.pbSecure = New System.Windows.Forms.PictureBox()
        Me.headerRight = New System.Windows.Forms.PictureBox()
        Me.btnErrorWarning = New System.Windows.Forms.Button()
        Me.trackBar = New System.Windows.Forms.TrackBar()
        Me.lblSpeed = New System.Windows.Forms.Label()
        Me.mainStatusStrip = New System.Windows.Forms.StatusStrip()
        Me.pbTransfer = New System.Windows.Forms.ToolStripProgressBar()
        Me.lblProgress = New System.Windows.Forms.ToolStripStatusLabel()
        Me.mainSplitContainer = New MainForm.SplitContainer()
        Me.gbControl.SuspendLayout()
        Me.gbTransfer.SuspendLayout()
        CType(Me.headerLeft, System.ComponentModel.ISupportInitialize).BeginInit()
        Me.headerMiddle.SuspendLayout()
        CType(Me.pbSecure, System.ComponentModel.ISupportInitialize).BeginInit()
        CType(Me.headerRight, System.ComponentModel.ISupportInitialize).BeginInit()
        CType(Me.trackBar, System.ComponentModel.ISupportInitialize).BeginInit()
        Me.mainStatusStrip.SuspendLayout()
        CType(Me.mainSplitContainer, System.ComponentModel.ISupportInitialize).BeginInit()
        Me.mainSplitContainer.Panel1.SuspendLayout()
        Me.mainSplitContainer.Panel2.SuspendLayout()
        Me.mainSplitContainer.SuspendLayout()
        Me.SuspendLayout()
        '
        'listServer
        '
        Me.listServer.Anchor = CType(((System.Windows.Forms.AnchorStyles.Top Or System.Windows.Forms.AnchorStyles.Bottom) _
            Or System.Windows.Forms.AnchorStyles.Left), System.Windows.Forms.AnchorStyles)
        Me.listServer.Columns.AddRange(New System.Windows.Forms.ColumnHeader() {Me.columnName, Me.columnSize, Me.columnDate})
        Me.listServer.FullRowSelect = True
        Me.listServer.Location = New System.Drawing.Point(8, 80)
        Me.listServer.MultiSelect = False
        Me.listServer.Name = "listServer"
        Me.listServer.Size = New System.Drawing.Size(264, 286)
        Me.listServer.TabIndex = 2
        Me.listServer.UseCompatibleStateImageBehavior = False
        Me.listServer.View = System.Windows.Forms.View.Details
        '
        'columnName
        '
        Me.columnName.Text = "Name"
        Me.columnName.Width = 120
        '
        'columnSize
        '
        Me.columnSize.Text = "Size"
        Me.columnSize.TextAlign = System.Windows.Forms.HorizontalAlignment.Right
        '
        'columnDate
        '
        Me.columnDate.Text = "Date"
        Me.columnDate.Width = 120
        '
        'listLocal
        '
        Me.listLocal.Anchor = CType(((System.Windows.Forms.AnchorStyles.Top Or System.Windows.Forms.AnchorStyles.Bottom) _
            Or System.Windows.Forms.AnchorStyles.Left), System.Windows.Forms.AnchorStyles)
        Me.listLocal.Columns.AddRange(New System.Windows.Forms.ColumnHeader() {Me.columnHeader1, Me.columnHeader2, Me.columnHeader3})
        Me.listLocal.FullRowSelect = True
        Me.listLocal.Location = New System.Drawing.Point(396, 80)
        Me.listLocal.MultiSelect = False
        Me.listLocal.Name = "listLocal"
        Me.listLocal.Size = New System.Drawing.Size(256, 338)
        Me.listLocal.TabIndex = 25
        Me.listLocal.UseCompatibleStateImageBehavior = False
        Me.listLocal.View = System.Windows.Forms.View.Details
        '
        'columnHeader1
        '
        Me.columnHeader1.Text = "Name"
        Me.columnHeader1.Width = 120
        '
        'columnHeader2
        '
        Me.columnHeader2.Text = "Size"
        Me.columnHeader2.TextAlign = System.Windows.Forms.HorizontalAlignment.Right
        '
        'columnHeader3
        '
        Me.columnHeader3.Text = "Date"
        Me.columnHeader3.Width = 120
        '
        'tbLog
        '
        Me.tbLog.AccessibleRole = System.Windows.Forms.AccessibleRole.None
        Me.tbLog.Anchor = CType((((System.Windows.Forms.AnchorStyles.Top Or System.Windows.Forms.AnchorStyles.Bottom) _
            Or System.Windows.Forms.AnchorStyles.Left) _
            Or System.Windows.Forms.AnchorStyles.Right), System.Windows.Forms.AnchorStyles)
        Me.tbLog.BackColor = System.Drawing.SystemColors.Control
        Me.tbLog.Cursor = System.Windows.Forms.Cursors.IBeam
        Me.tbLog.Font = New System.Drawing.Font("Courier New", 8.25!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(0, Byte))
        Me.tbLog.Location = New System.Drawing.Point(0, 0)
        Me.tbLog.MaxLength = 1000000
        Me.tbLog.Name = "tbLog"
        Me.tbLog.ReadOnly = True
        Me.tbLog.Size = New System.Drawing.Size(644, 94)
        Me.tbLog.TabIndex = 8
        Me.tbLog.Text = ""
        Me.tbLog.WordWrap = False
        '
        'lblServer
        '
        Me.lblServer.Anchor = CType((System.Windows.Forms.AnchorStyles.Bottom Or System.Windows.Forms.AnchorStyles.Left), System.Windows.Forms.AnchorStyles)
        Me.lblServer.Font = New System.Drawing.Font("Microsoft Sans Serif", 8.25!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(0, Byte))
        Me.lblServer.Location = New System.Drawing.Point(5, 372)
        Me.lblServer.Name = "lblServer"
        Me.lblServer.Size = New System.Drawing.Size(171, 13)
        Me.lblServer.TabIndex = 9
        Me.lblServer.Text = "12345"
        '
        'lblServerDirectory
        '
        Me.lblServerDirectory.BackColor = System.Drawing.SystemColors.InactiveCaption
        Me.lblServerDirectory.FlatStyle = System.Windows.Forms.FlatStyle.Popup
        Me.lblServerDirectory.Font = New System.Drawing.Font("Microsoft Sans Serif", 8.25!, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, CType(0, Byte))
        Me.lblServerDirectory.ForeColor = System.Drawing.SystemColors.InactiveCaptionText
        Me.lblServerDirectory.Location = New System.Drawing.Point(8, 64)
        Me.lblServerDirectory.Name = "lblServerDirectory"
        Me.lblServerDirectory.Size = New System.Drawing.Size(264, 16)
        Me.lblServerDirectory.TabIndex = 1
        Me.lblServerDirectory.Text = "/root"
        Me.lblServerDirectory.TextAlign = System.Drawing.ContentAlignment.MiddleLeft
        '
        'lblLocal
        '
        Me.lblLocal.Anchor = CType((System.Windows.Forms.AnchorStyles.Bottom Or System.Windows.Forms.AnchorStyles.Left), System.Windows.Forms.AnchorStyles)
        Me.lblLocal.Font = New System.Drawing.Font("Microsoft Sans Serif", 8.25!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(0, Byte))
        Me.lblLocal.Location = New System.Drawing.Point(396, 420)
        Me.lblLocal.Name = "lblLocal"
        Me.lblLocal.Size = New System.Drawing.Size(256, 13)
        Me.lblLocal.TabIndex = 12
        Me.lblLocal.Text = "12345"
        '
        'lblLocalDirectory
        '
        Me.lblLocalDirectory.BackColor = System.Drawing.SystemColors.InactiveCaption
        Me.lblLocalDirectory.FlatStyle = System.Windows.Forms.FlatStyle.Popup
        Me.lblLocalDirectory.Font = New System.Drawing.Font("Microsoft Sans Serif", 8.25!, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, CType(0, Byte))
        Me.lblLocalDirectory.ForeColor = System.Drawing.SystemColors.InactiveCaptionText
        Me.lblLocalDirectory.Location = New System.Drawing.Point(396, 64)
        Me.lblLocalDirectory.Name = "lblLocalDirectory"
        Me.lblLocalDirectory.Size = New System.Drawing.Size(256, 16)
        Me.lblLocalDirectory.TabIndex = 23
        Me.lblLocalDirectory.Text = "/root"
        Me.lblLocalDirectory.TextAlign = System.Drawing.ContentAlignment.MiddleLeft
        '
        'btnStop
        '
        Me.btnStop.FlatStyle = System.Windows.Forms.FlatStyle.System
        Me.btnStop.Location = New System.Drawing.Point(16, 221)
        Me.btnStop.Name = "btnStop"
        Me.btnStop.Size = New System.Drawing.Size(75, 23)
        Me.btnStop.TabIndex = 17
        Me.btnStop.Text = "Abort"
        Me.btnStop.Visible = False
        '
        'btnDelete
        '
        Me.btnDelete.FlatStyle = System.Windows.Forms.FlatStyle.System
        Me.btnDelete.Location = New System.Drawing.Point(16, 125)
        Me.btnDelete.Name = "btnDelete"
        Me.btnDelete.Size = New System.Drawing.Size(75, 23)
        Me.btnDelete.TabIndex = 11
        Me.btnDelete.Text = "Delete"
        '
        'btnUpload
        '
        Me.btnUpload.FlatStyle = System.Windows.Forms.FlatStyle.System
        Me.btnUpload.Location = New System.Drawing.Point(16, 194)
        Me.btnUpload.Name = "btnUpload"
        Me.btnUpload.Size = New System.Drawing.Size(75, 23)
        Me.btnUpload.TabIndex = 15
        Me.btnUpload.Text = "< Upload"
        '
        'btnDownload
        '
        Me.btnDownload.FlatStyle = System.Windows.Forms.FlatStyle.System
        Me.btnDownload.Location = New System.Drawing.Point(16, 167)
        Me.btnDownload.Name = "btnDownload"
        Me.btnDownload.Size = New System.Drawing.Size(75, 23)
        Me.btnDownload.TabIndex = 13
        Me.btnDownload.Text = "Download >"
        '
        'rbBin
        '
        Me.rbBin.FlatStyle = System.Windows.Forms.FlatStyle.System
        Me.rbBin.Location = New System.Drawing.Point(16, 16)
        Me.rbBin.Name = "rbBin"
        Me.rbBin.Size = New System.Drawing.Size(65, 20)
        Me.rbBin.TabIndex = 19
        Me.rbBin.Text = "Binary"
        '
        'rbAsc
        '
        Me.rbAsc.FlatStyle = System.Windows.Forms.FlatStyle.System
        Me.rbAsc.Location = New System.Drawing.Point(16, 37)
        Me.rbAsc.Name = "rbAsc"
        Me.rbAsc.Size = New System.Drawing.Size(68, 20)
        Me.rbAsc.TabIndex = 21
        Me.rbAsc.Text = "ASCII"
        '
        'btnRefresh
        '
        Me.btnRefresh.FlatStyle = System.Windows.Forms.FlatStyle.System
        Me.btnRefresh.Location = New System.Drawing.Point(16, 71)
        Me.btnRefresh.Name = "btnRefresh"
        Me.btnRefresh.Size = New System.Drawing.Size(75, 23)
        Me.btnRefresh.TabIndex = 5
        Me.btnRefresh.Text = "Refresh"
        '
        'btnMakeDir
        '
        Me.btnMakeDir.FlatStyle = System.Windows.Forms.FlatStyle.System
        Me.btnMakeDir.Location = New System.Drawing.Point(16, 98)
        Me.btnMakeDir.Name = "btnMakeDir"
        Me.btnMakeDir.Size = New System.Drawing.Size(75, 23)
        Me.btnMakeDir.TabIndex = 7
        Me.btnMakeDir.Text = "Create folder"
        '
        'cbDrives
        '
        Me.cbDrives.ItemHeight = 13
        Me.cbDrives.Location = New System.Drawing.Point(17, 40)
        Me.cbDrives.Name = "cbDrives"
        Me.cbDrives.Size = New System.Drawing.Size(73, 21)
        Me.cbDrives.TabIndex = 4
        '
        'btnConnect
        '
        Me.btnConnect.FlatStyle = System.Windows.Forms.FlatStyle.System
        Me.btnConnect.Location = New System.Drawing.Point(16, 16)
        Me.btnConnect.Name = "btnConnect"
        Me.btnConnect.Size = New System.Drawing.Size(75, 23)
        Me.btnConnect.TabIndex = 3
        Me.btnConnect.Text = "Connect..."
        '
        'gbControl
        '
        Me.gbControl.Anchor = System.Windows.Forms.AnchorStyles.Top
        Me.gbControl.Controls.Add(Me.btnStop)
        Me.gbControl.Controls.Add(Me.btnDelete)
        Me.gbControl.Controls.Add(Me.btnUpload)
        Me.gbControl.Controls.Add(Me.btnDownload)
        Me.gbControl.Controls.Add(Me.btnRefresh)
        Me.gbControl.Controls.Add(Me.btnMakeDir)
        Me.gbControl.Controls.Add(Me.cbDrives)
        Me.gbControl.Controls.Add(Me.btnConnect)
        Me.gbControl.FlatStyle = System.Windows.Forms.FlatStyle.System
        Me.gbControl.Location = New System.Drawing.Point(286, 64)
        Me.gbControl.Name = "gbControl"
        Me.gbControl.Size = New System.Drawing.Size(106, 250)
        Me.gbControl.TabIndex = 25
        Me.gbControl.TabStop = False
        Me.gbControl.Text = "Controls"
        '
        'gbTransfer
        '
        Me.gbTransfer.Anchor = System.Windows.Forms.AnchorStyles.Top
        Me.gbTransfer.Controls.Add(Me.rbBin)
        Me.gbTransfer.Controls.Add(Me.rbAsc)
        Me.gbTransfer.Controls.Add(Me.cbModeZ)
        Me.gbTransfer.Controls.Add(Me.cbST)
        Me.gbTransfer.FlatStyle = System.Windows.Forms.FlatStyle.System
        Me.gbTransfer.Location = New System.Drawing.Point(286, 321)
        Me.gbTransfer.Name = "gbTransfer"
        Me.gbTransfer.Size = New System.Drawing.Size(106, 106)
        Me.gbTransfer.TabIndex = 29
        Me.gbTransfer.TabStop = False
        Me.gbTransfer.Text = "Transfer"
        '
        'cbModeZ
        '
        Me.cbModeZ.Location = New System.Drawing.Point(16, 56)
        Me.cbModeZ.Name = "cbModeZ"
        Me.cbModeZ.Size = New System.Drawing.Size(68, 24)
        Me.cbModeZ.TabIndex = 29
        Me.cbModeZ.Text = "Mode Z"
        '
        'cbST
        '
        Me.cbST.Location = New System.Drawing.Point(16, 77)
        Me.cbST.Name = "cbST"
        Me.cbST.Size = New System.Drawing.Size(68, 24)
        Me.cbST.TabIndex = 30
        Me.cbST.Text = "Secure"
        '
        'headerLeft
        '
        Me.headerLeft.BackColor = System.Drawing.Color.White
        Me.headerLeft.Image = CType(resources.GetObject("headerLeft.Image"), System.Drawing.Image)
        Me.headerLeft.Location = New System.Drawing.Point(0, 0)
        Me.headerLeft.Margin = New System.Windows.Forms.Padding(0)
        Me.headerLeft.Name = "headerLeft"
        Me.headerLeft.Size = New System.Drawing.Size(472, 59)
        Me.headerLeft.TabIndex = 26
        Me.headerLeft.TabStop = False
        '
        'headerMiddle
        '
        Me.headerMiddle.BackColor = System.Drawing.Color.White
        Me.headerMiddle.BackgroundImage = CType(resources.GetObject("headerMiddle.BackgroundImage"), System.Drawing.Image)
        Me.headerMiddle.Controls.Add(Me.pbSecure)
        Me.headerMiddle.Location = New System.Drawing.Point(472, 0)
        Me.headerMiddle.Name = "headerMiddle"
        Me.headerMiddle.Size = New System.Drawing.Size(64, 59)
        Me.headerMiddle.TabIndex = 27
        '
        'pbSecure
        '
        Me.pbSecure.Image = CType(resources.GetObject("pbSecure.Image"), System.Drawing.Image)
        Me.pbSecure.Location = New System.Drawing.Point(24, 16)
        Me.pbSecure.Name = "pbSecure"
        Me.pbSecure.Size = New System.Drawing.Size(32, 32)
        Me.pbSecure.TabIndex = 29
        Me.pbSecure.TabStop = False
        Me.pbSecure.Visible = False
        '
        'headerRight
        '
        Me.headerRight.Anchor = CType((System.Windows.Forms.AnchorStyles.Top Or System.Windows.Forms.AnchorStyles.Right), System.Windows.Forms.AnchorStyles)
        Me.headerRight.BackColor = System.Drawing.Color.White
        Me.headerRight.Cursor = System.Windows.Forms.Cursors.Hand
        Me.headerRight.Image = CType(resources.GetObject("headerRight.Image"), System.Drawing.Image)
        Me.headerRight.Location = New System.Drawing.Point(535, 0)
        Me.headerRight.Margin = New System.Windows.Forms.Padding(0)
        Me.headerRight.Name = "headerRight"
        Me.headerRight.Size = New System.Drawing.Size(136, 59)
        Me.headerRight.TabIndex = 28
        Me.headerRight.TabStop = False
        '
        'btnErrorWarning
        '
        Me.btnErrorWarning.Dock = System.Windows.Forms.DockStyle.Right
        Me.btnErrorWarning.FlatStyle = System.Windows.Forms.FlatStyle.Popup
        Me.btnErrorWarning.Font = New System.Drawing.Font("Microsoft Sans Serif", 8.25!, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, CType(238, Byte))
        Me.btnErrorWarning.Location = New System.Drawing.Point(647, 0)
        Me.btnErrorWarning.Name = "btnErrorWarning"
        Me.btnErrorWarning.Size = New System.Drawing.Size(21, 93)
        Me.btnErrorWarning.TabIndex = 29
        Me.btnErrorWarning.Text = "!"
        '
        'trackBar
        '
        Me.trackBar.Anchor = CType((System.Windows.Forms.AnchorStyles.Bottom Or System.Windows.Forms.AnchorStyles.Left), System.Windows.Forms.AnchorStyles)
        Me.trackBar.LargeChange = 10
        Me.trackBar.Location = New System.Drawing.Point(3, 388)
        Me.trackBar.Maximum = 100
        Me.trackBar.Minimum = 1
        Me.trackBar.Name = "trackBar"
        Me.trackBar.Size = New System.Drawing.Size(282, 45)
        Me.trackBar.SmallChange = 5
        Me.trackBar.TabIndex = 30
        Me.trackBar.TickFrequency = 5
        Me.trackBar.Value = 100
        '
        'lblSpeed
        '
        Me.lblSpeed.Anchor = CType((System.Windows.Forms.AnchorStyles.Bottom Or System.Windows.Forms.AnchorStyles.Left), System.Windows.Forms.AnchorStyles)
        Me.lblSpeed.Location = New System.Drawing.Point(182, 372)
        Me.lblSpeed.Name = "lblSpeed"
        Me.lblSpeed.Size = New System.Drawing.Size(90, 13)
        Me.lblSpeed.TabIndex = 0
        Me.lblSpeed.Text = "Speed: Unlimited"
        '
        'mainStatusStrip
        '
        Me.mainStatusStrip.Items.AddRange(New System.Windows.Forms.ToolStripItem() {Me.pbTransfer, Me.lblProgress})
        Me.mainStatusStrip.Location = New System.Drawing.Point(0, 538)
        Me.mainStatusStrip.Name = "mainStatusStrip"
        Me.mainStatusStrip.Size = New System.Drawing.Size(668, 22)
        Me.mainStatusStrip.TabIndex = 31
        Me.mainStatusStrip.Text = "StatusStrip1"
        '
        'pbTransfer
        '
        Me.pbTransfer.Name = "pbTransfer"
        Me.pbTransfer.Size = New System.Drawing.Size(160, 16)
        '
        'lblProgress
        '
        Me.lblProgress.Name = "lblProgress"
        Me.lblProgress.Size = New System.Drawing.Size(60, 17)
        Me.lblProgress.Text = "Welcome!"
        '
        'mainSplitContainer
        '
        Me.mainSplitContainer.Anchor = CType((((System.Windows.Forms.AnchorStyles.Top Or System.Windows.Forms.AnchorStyles.Bottom) _
            Or System.Windows.Forms.AnchorStyles.Left) _
            Or System.Windows.Forms.AnchorStyles.Right), System.Windows.Forms.AnchorStyles)
        Me.mainSplitContainer.Location = New System.Drawing.Point(0, -1)
        Me.mainSplitContainer.Name = "mainSplitContainer"
        Me.mainSplitContainer.Orientation = System.Windows.Forms.Orientation.Horizontal
        '
        'mainSplitContainer.Panel1
        '
        Me.mainSplitContainer.Panel1.Controls.Add(Me.gbTransfer)
        Me.mainSplitContainer.Panel1.Controls.Add(Me.trackBar)
        Me.mainSplitContainer.Panel1.Controls.Add(Me.listServer)
        Me.mainSplitContainer.Panel1.Controls.Add(Me.listLocal)
        Me.mainSplitContainer.Panel1.Controls.Add(Me.lblServer)
        Me.mainSplitContainer.Panel1.Controls.Add(Me.lblLocal)
        Me.mainSplitContainer.Panel1.Controls.Add(Me.lblLocalDirectory)
        Me.mainSplitContainer.Panel1.Controls.Add(Me.gbControl)
        Me.mainSplitContainer.Panel1.Controls.Add(Me.headerLeft)
        Me.mainSplitContainer.Panel1.Controls.Add(Me.headerMiddle)
        Me.mainSplitContainer.Panel1.Controls.Add(Me.headerRight)
        Me.mainSplitContainer.Panel1.Controls.Add(Me.lblServerDirectory)
        Me.mainSplitContainer.Panel1.Controls.Add(Me.lblSpeed)
        Me.mainSplitContainer.Panel1MinSize = 430
        '
        'mainSplitContainer.Panel2
        '
        Me.mainSplitContainer.Panel2.Controls.Add(Me.tbLog)
        Me.mainSplitContainer.Panel2.Controls.Add(Me.btnErrorWarning)
        Me.mainSplitContainer.Panel2MinSize = 30
        Me.mainSplitContainer.Size = New System.Drawing.Size(668, 536)
        Me.mainSplitContainer.SplitterDistance = 439
        Me.mainSplitContainer.TabIndex = 32
        '
        'MainForm
        '
        Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
        Me.ClientSize = New System.Drawing.Size(668, 560)
        Me.Controls.Add(Me.mainSplitContainer)
        Me.Controls.Add(Me.mainStatusStrip)
        Me.Icon = CType(resources.GetObject("$this.Icon"), System.Drawing.Icon)
        Me.KeyPreview = True
        Me.MinimumSize = New System.Drawing.Size(664, 590)
        Me.Name = "MainForm"
        Me.Text = "Simple WinForm FTP Client"
        Me.gbControl.ResumeLayout(False)
        Me.gbTransfer.ResumeLayout(False)
        CType(Me.headerLeft, System.ComponentModel.ISupportInitialize).EndInit()
        Me.headerMiddle.ResumeLayout(False)
        CType(Me.pbSecure, System.ComponentModel.ISupportInitialize).EndInit()
        CType(Me.headerRight, System.ComponentModel.ISupportInitialize).EndInit()
        CType(Me.trackBar, System.ComponentModel.ISupportInitialize).EndInit()
        Me.mainStatusStrip.ResumeLayout(False)
        Me.mainStatusStrip.PerformLayout()
        Me.mainSplitContainer.Panel1.ResumeLayout(False)
        Me.mainSplitContainer.Panel1.PerformLayout()
        Me.mainSplitContainer.Panel2.ResumeLayout(False)
        CType(Me.mainSplitContainer, System.ComponentModel.ISupportInitialize).EndInit()
        Me.mainSplitContainer.ResumeLayout(False)
        Me.ResumeLayout(False)
        Me.PerformLayout()

    End Sub    'InitializeComponent 
#End Region

    '' <summary>
    '' win form client starts here
    '' </summary>
    <STAThread()> _
    Shared Sub Main()
            ' set the trial license key
            Rebex.Licensing.Key = Rebex.Samples.TrialKey.Key


        Application.Run(New MainForm)
    End Sub

    '' <summary>
    '' connect/disconnect ftp server
    '' </summary>
    Private Sub btnConnect_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnConnect.Click
        If Not (_ftp Is Nothing) Then
            If _isWorking Then Return

            ' disconnect
            trackBar.Enabled = False
            _ftp.Disconnect()
            _ftp.Dispose()
            _ftp = Nothing
            btnConnect.Text = "Connect..."
            MakeFtpList()    ' clearing
            Return
        End If

        Dim connectionData As ConnectionFormData = New ConnectionFormData(FileTransferComponent.Ftp)
        connectionData.LoadConfig()

        ' connection dialog
        Dim con As New ConnectionForm(connectionData)

        ' show connection dialog
        con.ShowDialog()

        ' not confirmed
        If con.DialogResult <> DialogResult.OK Then 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
        AddHandler _ftp.StateChanged, AddressOf StateChanged
        AddHandler _ftp.Traversing, AddressOf Traversing
        AddHandler _ftp.TransferProgressChanged, AddressOf TransferProgressChanged
        AddHandler _ftp.DeleteProgressChanged, AddressOf DeleteProgressChanged
        AddHandler _ftp.ProblemDetected, AddressOf ProblemDetected

        Try
            _ftp.Passive = con.Data.PassiveMode

            _ftp.Settings.UseLargeBuffers = True

            _ftp.Timeout = 30000

            ' proxy
            If con.Data.ProxyType <> FileTransferProxyType.None Then
                _ftp.Proxy = New FtpProxy(CType(con.Data.ProxyType, FtpProxyType), con.Data.ProxyHost, con.Data.ProxyPort)
                If con.Data.ProxyUser.Length > 0 Then
                    _ftp.Proxy.Credentials = New System.Net.NetworkCredential(con.Data.ProxyUser, con.Data.ProxyPassword)
                End If
            End If

            Dim securityMode As SslMode = ConvertToSslMode(con.Data.FileTransferMode)

            If securityMode <> SslMode.None Then
                AddHandler _ftp.ValidatingCertificate, AddressOf Verifier.ValidatingCertificate

                If con.Data.ClientCertificateFilename IsNot Nothing And con.Data.ClientCertificateFilename.Trim() <> String.Empty Then
                    Dim pp As PassphraseDialog = New PassphraseDialog
                    If pp.ShowDialog() = DialogResult.OK Then
                        Dim chain As CertificateChain = CertificateChain.LoadPfx(con.Data.ClientCertificateFilename, pp.Passphrase)
                        _ftp.Settings.SslClientCertificateRequestHandler = CertificateRequestHandler.CreateRequestHandler(chain)
                    Else
                        Throw New ApplicationException("Passphrase wasn't entered.")
                    End If
                Else
                    _ftp.Settings.SslClientCertificateRequestHandler = New RequestHandler
                End If

                _ftp.SecureTransfers = cbST.Checked
            End If

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

            ' connect

            btnStop.Visible = True
            _isWorking = True

            Try
                Dim connect As Task = _ftp.ConnectAsync(con.Data.ServerHost, con.Data.ServerPort, securityMode)
                While (Not connect.IsCompleted)
                    Application.DoEvents()
                    System.Threading.Thread.Sleep(1)
                End While
                If (connect.IsFaulted) Then
                    If (connect.Exception.InnerException IsNot Nothing) Then
                        Throw connect.Exception.InnerException
                    End If

                    Throw connect.Exception
                End If

                If connect.IsCanceled Then
                    Throw New OperationCanceledException("Operation has been canceled.")
                End If
            Finally
                btnStop.Visible = False
                _isWorking = False
            End Try

            If securityMode = SslMode.None Then
                cbST.Checked = False
                cbST.Enabled = False
                pbSecure.Visible = False
            Else
                cbST.Checked = True
                cbST.Enabled = True
                pbSecure.Visible = True
            End If

            ' login
            If Not _ftp.IsAuthenticated Then
                _ftp.Login(con.Data.ServerUser, con.Data.ServerPassword)
            End If

            If cbModeZ.Checked Then
                _ftp.TransferMode = FtpTransferMode.Zlib
            End If

            ' ccc 
            If con.Data.ClearCommandChannel And securityMode <> SslMode.None Then
                Try
                    _ftp.SecureTransfers = True
                    _ftp.ClearCommandChannel()
                    cbST.Checked = True
                    cbST.Enabled = False
                    _cccMode = True
                Catch ex As Exception
                    WriteToLog(ex)
                End Try
            End If

            trackBar.Enabled = True
            btnErrorWarning.Visible = False
            btnConnect.Text = "Disconnect"
            MakeFtpList()
        Catch ex As Exception
            WriteToLog(ex)
            If Not _ftp Is Nothing Then
                _ftp.Dispose()
                _ftp = Nothing
            End If
            Return
        Finally
            _isWorking = False
        End Try
    End Sub


    '' <summary>
    '' disconnect on exit
    '' </summary>
    Private Sub MainForm_Closing(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosingEventArgs) Handles MyBase.FormClosing
        If Not (_ftp Is Nothing) Then
            _ftp.Dispose()
        End If
    End Sub

    '' <summary>
    '' CWD or GET file command on server side
    '' </summary>
    Private Sub ListServerClick()
        Try
            If listServer.Items.Count < 1 Then Return

            If listServer.SelectedItems.Count < 1 Then
                listServer.Items(0).Selected = True
            End If

            listServer.Focus()

            ' ftp state test
            If _isWorking Then Return

            Dim index As Integer = listServer.SelectedIndices(0)
            Dim item As ListViewItem = listServer.SelectedItems(0)

            If index >= _serverDirs Then
                ' GET file
                DownloadFiles(item.Text, _localPath)
            Else
                ' CWD
                Dim directory As String
                If index = 0 Then
                    directory = ".."
                Else
                    directory = item.Text
                End If

                _ftp.ChangeDirectory(directory)

                MakeFtpList()
            End If
        Catch ex As Exception
            WriteToLog(ex)
        End Try
    End Sub

    Private Overloads Sub listServer_KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPressEventArgs) Handles listServer.KeyPress
        If e.KeyChar = ControlChars.Cr Then
            ListServerClick()
            listServer.Focus()
        End If
    End Sub

    Private Overloads Sub listServer_DoubleClick(ByVal sender As Object, ByVal e As System.EventArgs) Handles listServer.DoubleClick
        ListServerClick()
        listServer.Focus()
    End Sub

    '' <summary>
    '' change drive letter
    '' </summary>
    Private Sub cbDrives_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles cbDrives.SelectedIndexChanged
        Try
            _localPath = cbDrives.SelectedItem.ToString()
            MakeLocalList()
        Catch ex As Exception
            WriteToLog(ex)
            listLocal.Items.Clear()
            lblLocalDirectory.Text = (lblLocal.Text = Nothing)
        End Try
    End Sub

    '' <summary>
    '' LCD or PUT command on local side
    '' </summary>
    Private Sub ListLocalClick()
        Try
            If listLocal.Items.Count < 1 Then Return

            If listLocal.SelectedItems.Count < 1 Then
                listLocal.Items(0).Selected = True
            End If

            listLocal.Focus()

            Dim index As Integer = listLocal.SelectedIndices(0)
            Dim item As ListViewItem = listLocal.SelectedItems(0)

            If index >= _localDirs Then
                ' upload file
                UploadFiles(item.Text, _localPath)
            Else
                ' local change directory
                _localPath = Path.Combine(_localPath, item.Text)
                MakeLocalList()
            End If
        Catch ex As Exception
            WriteToLog(ex)
        End Try
    End Sub

    Private Overloads Sub listLocal_KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPressEventArgs) Handles listLocal.KeyPress ', listLocal.DoubleClick
        If e.KeyChar = ControlChars.Cr Then
            ListLocalClick()
            If listLocal.Items.Count > 0 Then
                listLocal.Focus()
            End If
        End If
    End Sub

    Private Overloads Sub listLocal_DoubleClick(ByVal sender As Object, ByVal e As System.EventArgs) Handles listLocal.DoubleClick 'listLocal.KeyPress, 
        ListLocalClick()
        listLocal.Focus()
    End Sub

    '' <summary>
    '' make dir
    '' </summary>
    Private Sub btnMakeDir_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnMakeDir.Click
        Try
            Dim dir As New MakeDir

            ' FTP: MKDIR
            If listActive Is listServer Then
                If _isWorking Then Return

                dir.ShowDialog()
                If Not (dir.Directory Is Nothing) Then
                    _ftp.CreateDirectory(dir.Directory)
                    MakeFtpList()
                End If
            Else
                ' LOCAL: MKDIR
                dir.ShowDialog()

                If Not (dir.Directory Is Nothing) Then
                    Directory.CreateDirectory(Path.Combine(_localPath, dir.Directory))
                    MakeLocalList()
                End If
            End If
        Catch ex As Exception
            WriteToLog(ex)
        End Try
    End Sub

    '' <summary>
    '' refresh listing
    '' </summary>
    Private Sub btnRefresh_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnRefresh.Click
        Try
            If listActive Is listServer Then
                If _isWorking Then Return

                MakeFtpList()
            Else
                MakeLocalList()
            End If
        Catch ex As Exception
            WriteToLog(ex)
        End Try
    End Sub

    '' <summary>
    '' download file or directory tree
    '' </summary>
    '' <param name="fileName">remote file name</param>
    '' <param name="localPath">local path</param>
    Private Sub DownloadFiles(ByVal fileName As String, ByVal localPath As String)
        ' ftp state test
        If _isWorking Then Return

        ' secure transfers
        If cbST.Enabled AndAlso Not _cccMode Then _ftp.SecureTransfers = cbST.Checked

        ' set asc/bin transfers
        If rbBin.Checked Then
            _ftp.TransferType = FtpTransferType.Binary
        Else
            _ftp.TransferType = FtpTransferType.Ascii
        End If

        ' transfer
        Try
            _isWorking = True
            btnStop.Visible = True
            _problemForm.Initialize()
            _transferTime = DateTime.Now

            Dim download As Task = _ftp.DownloadAsync(fileName, localPath, TraversalMode.Recursive, TransferMethod.Copy, ActionOnExistingFiles.ThrowException)
            download.ContinueWith(AddressOf DownloadTaskCompleted)
        Catch ex As Exception
            btnStop.Visible = False
            _isWorking = False
            WriteToLog(ex)
        End Try
    End Sub

    '' <summary>
    '' upload file or directory tree
    '' </summary>
    '' <param name="fileName">remote file name</param>
    '' <param name="localPath">local path</param>
    Private Sub UploadFiles(ByVal fileName As String, ByVal localPath As String)
        localPath = Path.Combine(localPath, fileName)

        ' ftp state test
        If _ftp Is Nothing Then
            WriteToLog("Not connected!", Color.DarkOrange)
            Return
        End If
        If _isWorking Then Return

        ' secure transfers
        If cbST.Enabled AndAlso Not _cccMode Then _ftp.SecureTransfers = cbST.Checked

        ' set asc/bin transfers
        If rbBin.Checked Then
            _ftp.TransferType = FtpTransferType.Binary
        Else
            _ftp.TransferType = FtpTransferType.Ascii
        End If

        ' transfer
        Try
            _isWorking = True
            btnStop.Visible = True
            _problemForm.Initialize()
            _transferTime = DateTime.Now

            Dim upload As Task = _ftp.UploadAsync(localPath, ".", TraversalMode.Recursive, TransferMethod.Copy, ActionOnExistingFiles.ThrowException)
            upload.ContinueWith(AddressOf UploadTaskCompleted)
        Catch ex As Exception
            btnStop.Visible = False
            _isWorking = False
            WriteToLog(ex)
        End Try
    End Sub

    Private Sub DownloadTaskCompleted(ByVal task As Task)
        TransferFinished(task.Exception, False)
    End Sub

    Private Sub UploadTaskCompleted(ByVal task As Task)
        TransferFinished(task.Exception, True)
    End Sub

    Public Delegate Sub TransferFinishedDelegate(ByVal err As Exception, ByVal refreshFtp As Boolean)

    Private Sub TransferFinished(ByVal err As Exception, ByVal refreshFtp As Boolean)
        If (Me.InvokeRequired) Then
            SafeInvoke(New TransferFinishedDelegate(AddressOf TransferFinished), New Object() {err, refreshFtp})
            Return
        End If

        If Not err Is Nothing Then
            WriteToLog(err)
        End If

        _isWorking = False
        SetProgressValue(0)
        btnStop.Visible = False

        Try
            If refreshFtp Then
                MakeFtpList()
            Else
                MakeLocalList()
            End If
        Catch ex As Exception
            WriteToLog(ex)
        End Try
    End Sub

    '' <summary>
    '' show transfer status: files, bytes, time, speed
    '' </summary>
    Private Sub ShowTransferStatus(ByVal bytesTransferred As Long, ByVal filesTransferred As Integer)
        ' unknown bytes rtansferred
        If bytesTransferred = 0 Then Return

        ' files and bytes transferred
        Dim outstring As String = String.Format("{0} file{1} ({2} byte{3}) transferred in", _
         filesTransferred, IIf(filesTransferred > 1, "s", ""), _
         bytesTransferred, IIf(bytesTransferred > 1, "s", ""))

        ' time spent
        Dim ts As TimeSpan = DateTime.Now.Subtract(_transferTime)

        ' speed
        If ts.TotalSeconds > 1 Then
            outstring &= IIf(ts.Days > 0, " " & ts.Days.ToString() & " day" & IIf(ts.Days > 1, "s", Nothing), Nothing)
            outstring &= IIf(ts.Hours > 0, " " & ts.Hours.ToString() & " hour" & IIf(ts.Hours > 1, "s", Nothing), Nothing)
            outstring &= IIf(ts.Minutes > 0, " " & ts.Minutes.ToString() & " min" & IIf(ts.Minutes > 1, "s", Nothing), Nothing)
            outstring &= IIf(ts.Seconds > 0, " " & ts.Seconds.ToString() & " sec" & IIf(ts.Seconds > 1, "s", Nothing), Nothing)
        Else
            outstring &= " in " & ts.TotalSeconds.ToString() & " sec"
        End If

        Dim speed As Double = bytesTransferred / ts.TotalSeconds
        If speed < 1 Then
            outstring &= String.Format(" at {0:F3} B/s", speed)
        ElseIf speed < 1024 Then
            outstring &= String.Format(" at {0:F0} B/s", speed)
        Else
            outstring &= String.Format(" at {0:F0} KB/s", speed / 1024)
        End If

        WriteToLog("> " & outstring, RichTextBoxLogWriter.COLORCOMMAND)
    End Sub

    '' <summary>
    '' handle download button click
    '' </summary>
    Private Sub btnDownload_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnDownload.Click
        Try
            If Not listActive Is listServer Then Return

            If _isWorking Then Return

            If listServer.SelectedItems.Count > 0 Then
                DownloadFiles(listServer.SelectedItems(0).Text, _localPath)
            End If
        Catch ex As Exception
            WriteToLog(ex)
        End Try
    End Sub

    '' <summary>
    '' handle upload button click
    '' </summary>
    Private Sub btnUpload_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnUpload.Click
        Try
            If listActive Is listServer Then Return

            If _isWorking Then Return

            If listLocal.SelectedItems.Count > 0 Then
                UploadFiles(listLocal.SelectedItems(0).Text, _localPath)
            End If
        Catch ex As Exception
            WriteToLog(ex)
        End Try
    End Sub

    '' <summary>
    '' handle delete button click
    '' </summary>
    Private Sub btnDelete_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnDelete.Click
        Try
            If listActive Is listServer Then
                If _isWorking Then Return

                If listServer.SelectedItems.Count > 0 Then
                    Dim index As Integer = listServer.SelectedIndices(0)
                    Dim item As ListViewItem = listServer.SelectedItems(0)

                    If index >= _serverDirs Then
                        Dim dialog As New DeleteDialog(DeleteDialog.DialogPrompt.DeleteFile)
                        dialog.Path = item.Text
                        dialog.ShowDialog()

                        If dialog.OK Then
                            _ftp.DeleteFile(item.Text)
                            MakeFtpList()
                        End If
                    Else
                        Dim dialog As New DeleteDialog(DeleteDialog.DialogPrompt.DeleteDirectory)
                        dialog.Path = item.Text
                        dialog.ShowDialog()

                        If dialog.OK Then
                            _ftp.Delete(item.Text, TraversalMode.Recursive)
                            MakeFtpList()
                        End If
                    End If
                End If
            Else
                If listLocal.SelectedItems.Count > 0 Then
                    Dim index As Integer = listLocal.SelectedIndices(0)
                    Dim item As ListViewItem = listLocal.SelectedItems(0)
                    Dim localPath As String = Path.Combine(_localPath, item.Text)

                    If index >= _localDirs Then
                        Dim dialog As New DeleteDialog(DeleteDialog.DialogPrompt.DeleteFile)
                        dialog.Path = item.Text
                        dialog.ShowDialog()

                        If dialog.OK Then
                            File.Delete(localPath)
                            MakeLocalList()
                        End If
                    Else
                        Dim dialog As New DeleteDialog(DeleteDialog.DialogPrompt.DeleteDirectory)
                        dialog.Path = item.Text
                        dialog.ShowDialog()

                        If dialog.OK Then
                            Directory.Delete(localPath)
                            MakeLocalList()
                        End If
                    End If
                End If
            End If
        Catch ex As Exception
            WriteToLog(ex)
        End Try
    End Sub

    '' <summary>
    '' activate panel on focus event
    '' </summary>
    Private Sub listServer_Enter(ByVal sender As Object, ByVal e As System.EventArgs) Handles listServer.Enter, lblServerDirectory.Click
        listActive = listServer
        lblServerDirectory.BackColor = SystemColors.ActiveCaption
        lblServerDirectory.ForeColor = SystemColors.ActiveCaptionText
        lblLocalDirectory.BackColor = SystemColors.InactiveCaption
        lblLocalDirectory.ForeColor = SystemColors.InactiveCaptionText
    End Sub

    '' <summary>
    '' activate panel on focus event
    '' </summary>
    Private Sub listLocal_Enter(ByVal sender As Object, ByVal e As System.EventArgs) Handles listLocal.Enter, lblLocalDirectory.Click
        listActive = listLocal
        lblServerDirectory.BackColor = SystemColors.InactiveCaption
        lblServerDirectory.ForeColor = SystemColors.InactiveCaptionText
        lblLocalDirectory.BackColor = SystemColors.ActiveCaption
        lblLocalDirectory.ForeColor = SystemColors.ActiveCaptionText
    End Sub

    '' <summary>
    '' "smart" resizing
    '' </summary>
    Private Sub SetRightSizeValues()
        ' header
        headerMiddle.Width = headerRight.Left - headerMiddle.Left
        headerMiddle.Left = headerLeft.Right
        headerLeft.Width = 472

        ' server directory
        Dim wLeft As Integer
        wLeft = gbControl.Left - listServer.Left - 6
        listServer.Width = wLeft
        lblServerDirectory.Width = wLeft
        trackBar.Width = wLeft
        lblSpeed.Left = gbControl.Left - lblSpeed.Width - 6

        ' local directory
        listLocal.Bounds = New Rectangle(gbControl.Right + 6, listLocal.Top, headerRight.Right - gbControl.Right - 18, listLocal.Height)
        lblLocalDirectory.Bounds = New Rectangle(gbControl.Right + 6, lblLocalDirectory.Top, headerRight.Right - gbControl.Right - 18, lblLocalDirectory.Height)
        lblLocal.Left = listLocal.Left

        ' position of secure icon
        pbSecure.Location = New Point(headerMiddle.Width - 32, pbSecure.Top)
    End Sub

    Private Sub MainForm_Resize(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Resize
        If WindowState <> FormWindowState.Minimized Then
            SetRightSizeValues()
        End If
    End Sub

    '' <summary>
    '' stop transfer when something is being transferred
    '' </summary>
    Private Sub btnStop_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnStop.Click
        _ftp.Abort()
    End Sub

    '' <summary>
    '' go to the url
    '' </summary>
    Private Sub headerRight_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles headerRight.Click
        If Environment.OSVersion.Platform = PlatformID.Win32NT Then
            System.Diagnostics.Process.Start(URL)
        End If
    End Sub

    '' <summary>
    '' space bar for changing panels server <-> local
    '' </summary>
    Private Sub MainForm_KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPressEventArgs) Handles MyBase.KeyPress
        If e.KeyChar = Convert.ToChar(Keys.Space) Then
            If listActive Is listLocal Then
                listServer.Focus()
            Else
                listLocal.Focus()
            End If
        End If
    End Sub

    Private Sub btnErrorWarning_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnErrorWarning.Click
        MessageBox.Show(_exception, "Last occured error", MessageBoxButtons.OK, MessageBoxIcon.Error)
    End Sub

    Private Sub trackBar_Scroll(ByVal sender As Object, ByVal e As System.EventArgs) Handles trackBar.Scroll
        If trackBar.Value = 100 Then
            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
        End If
    End Sub

    Private Sub cbModeZ_CheckedChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cbModeZ.CheckedChanged
        Dim client As Ftp = _ftp
        If client Is Nothing Then Return

        If cbModeZ.Checked Then
            client.TransferMode = FtpTransferMode.Zlib
        Else
            client.TransferMode = FtpTransferMode.Stream
        End If
    End Sub

    ' Workaround for a bug in Mono 2.10 (where SplitContainer doesn't implement ISupportInitialize)
    ' (this is not needed on Windows)
    Public Class SplitContainer
        Inherits System.Windows.Forms.SplitContainer
        Implements System.ComponentModel.ISupportInitialize

        Private Sub ISupportInitialize_BeginInit() Implements ISupportInitialize.BeginInit
        End Sub

        Private Sub ISupportInitialize_EndInit() Implements ISupportInitialize.EndInit
        End Sub
    End Class

End Class

Public Class ListViewColumnSorter
    Inherits MultiComparer
    Implements IComparer

    Public Sub New()
        MyBase.New(New FileSystemItemComparer(FileSystemItemComparerType.FileType, SortingOrder.Ascending), New FileSystemItemComparer(FileSystemItemComparerType.Name))
    End Sub

    Private Overloads Function Compare(x As Object, y As Object) As Integer Implements IComparer.Compare
        Dim listViewX As ListViewItem = DirectCast(x, ListViewItem)
        Dim listViewY As ListViewItem = DirectCast(y, ListViewItem)

        Dim itemX As FtpItem = DirectCast(listViewX.Tag, FtpItem)
        Dim itemY As FtpItem = DirectCast(listViewY.Tag, FtpItem)

        If itemX Is Nothing Then
            If itemY Is Nothing Then
                Return 0
            End If

            Return -1
        ElseIf itemY Is Nothing Then
            Return 1
        End If

        Return MyBase.Compare(itemX, itemY)

    End Function 'Compare
End Class 'ListViewColumnSorter